1#!/usr/bin/env perl 2# 3# Generate C file that contains version strings 4 5($header # This is lsquic.h that contains version enum which we parse 6 , $outfile # This is destination C file 7 ) = @ARGV; 8 9open HEADER, $header 10 or die "cannot open $header for reading: $!"; 11open OUT, ">$outfile" 12 or die "cannot open $outfile for writing: $!"; 13 14while (<HEADER>) { 15 if (/^enum lsquic_version$/ .. /^}/) { 16 if (/^\s*(LSQVER_0*(\d+)),\s*$/ && $1 ne 'LSQVER_098') { 17 if ($2 < 50) { 18 push @enums, $1; 19 push @versions, $2; 20 } 21 push @all_versions, $1; 22 push @all_alpns, "h3-Q0$2"; 23 } 24 if (/^\s*(LSQVER_ID(\d+))\b/) { 25 push @draft_versions, $2; 26 push @all_versions, $1; 27 push @all_alpns, "h3-$2"; 28 } 29 if (/^\s*(LSQVER_I(\d{3}))\b/) { 30 push @all_versions, $1; 31 if (not grep 'h3' eq $_, @all_alpns) { 32 push @all_alpns, "h3"; 33 } 34 } 35 } 36} 37 38close HEADER; 39 40$timestamp = localtime; 41 42print OUT <<C_CODE; 43/* 44 * Auto-generated by $0 on $timestamp 45 */ 46 47#include <assert.h> 48#include <string.h> 49 50#include "lsquic.h" 51 52struct lsquic_engine; 53 54static const char *const versions_to_string[ 1 << N_LSQVER ] = { 55C_CODE 56 57$max_mask = (1 << @versions) - 1; 58 59for ($mask = 0; $mask <= $max_mask; ++$mask) { 60 my @indexes; 61 for ($i = 0; $i < @versions; ++$i) { 62 if ($mask & (1 << $i)) { 63 push @indexes, $i; 64 } 65 } 66 print OUT " [", 67 join('|', map "(1<<$_)", @enums[@indexes]) || 0, 68 "] = \"", 69 join(',', @versions[@indexes]), 70 "\",\n"; 71} 72 73$enums = join '|', map "(1<<$_)", sort @enums; 74 75print OUT <<"C_CODE"; 76}; 77 78 79const char * 80lsquic_get_alt_svc_versions (unsigned versions) 81{ 82 /* Limit to versions in versions_to_string: */ 83 versions &= ($enums); 84 return versions_to_string[ versions ]; 85} 86 87C_CODE 88 89 90$all_version_count_and_null = scalar(@all_versions) + 1; 91 92print OUT <<"C_CODE"; 93static const struct { 94 unsigned versions; 95 const char *h3_alpns[$all_version_count_and_null]; 96} vers_2_h3_alnps[] = { 97 { 0, { NULL }}, 98C_CODE 99 100for ($i = 0; $i < (1 << @all_versions); ++$i) 101{ 102 my (@vers, @alpns); 103 for ($j = 0; $j < @all_versions; ++$j) 104 { 105 if ($i & (1 << $j)) 106 { 107 push @vers, $all_versions[$j]; 108 push @alpns, $all_alpns[$j]; 109 } 110 } 111 if (@vers) { 112 print OUT " {", join("|", map "(1<<$_)", @vers), ", ", 113 "{ ", join(", ", (map qq("$_"), @alpns), "NULL"), " }},\n"; 114 } 115} 116 117$all_versions = join "|", map "(1<<$_)", @all_versions; 118 119print OUT <<"C_CODE"; 120}; 121 122const char *const * 123lsquic_get_h3_alpns (unsigned versions) 124{ 125 unsigned i; 126 127 versions &= ($all_versions); 128 129 for (i = 0; i < sizeof(vers_2_h3_alnps) / sizeof(vers_2_h3_alnps[0]); ++i) 130 if (versions == vers_2_h3_alnps[i].versions) 131 return vers_2_h3_alnps[i].h3_alpns; 132 133 assert(0); 134 return vers_2_h3_alnps[0].h3_alpns; 135} 136C_CODE 137 138 139print OUT <<'C_CODE'; 140 141enum lsquic_version 142lsquic_alpn2ver (const char *alpn, size_t len) 143{ 144 static const struct el { 145 size_t len; 146 char alpn[10]; 147 enum lsquic_version version; 148 } map[] = { 149C_CODE 150 151for ($i = 0; $i < @all_alpns; ++$i) { 152 print OUT " {sizeof(\"$all_alpns[$i]\")-1,\"$all_alpns[$i]\", $all_versions[$i]},\n"; 153} 154 155print OUT <<'C_CODE'; 156 }; 157 const struct el *el; 158 159 if (alpn) 160 for (el = map; el < map + sizeof(map) / sizeof(map[0]); ++el) 161 if (el->len == len && 0 == strncmp(el->alpn, alpn, len)) 162 return el->version; 163 164 return -1; 165} 166C_CODE 167 168close OUT; 169