#!/usr/bin/env perl # # Generate C file that contains version strings ($header # This is lsquic.h that contains version enum which we parse , $outfile # This is destination C file ) = @ARGV; open HEADER, $header or die "cannot open $header for reading: $!"; open OUT, ">$outfile" or die "cannot open $outfile for writing: $!"; while (
) { if (/^enum lsquic_version$/ .. /^}/) { if (/^\s*(LSQVER_0*(\d+)),\s*$/ && $1 ne 'LSQVER_098') { if ($2 < 50) { push @enums, $1; push @versions, $2; } push @all_versions, $1; push @all_alpns, "h3-Q0$2"; } if (/^\s*(LSQVER_ID(\d+))\b/) { push @draft_versions, $2; push @all_versions, $1; push @all_alpns, "h3-$2"; } } } close HEADER; $timestamp = localtime; print OUT < #include #include "lsquic.h" struct lsquic_engine; static const char *const versions_to_string[ 1 << N_LSQVER ] = { C_CODE $max_mask = (1 << @versions) - 1; for ($mask = 0; $mask <= $max_mask; ++$mask) { my @indexes; for ($i = 0; $i < @versions; ++$i) { if ($mask & (1 << $i)) { push @indexes, $i; } } print OUT " [", join('|', map "(1<<$_)", @enums[@indexes]) || 0, "] = \"", join(',', @versions[@indexes]), "\",\n"; } $enums = join '|', map "(1<<$_)", sort @enums; print OUT <<"C_CODE"; }; const char * lsquic_get_alt_svc_versions (unsigned versions) { /* Limit to versions in versions_to_string: */ versions &= ($enums); return versions_to_string[ versions ]; } C_CODE $all_version_count_and_null = scalar(@all_versions) + 1; print OUT <<"C_CODE"; static const struct { unsigned versions; const char *h3_alpns[$all_version_count_and_null]; } vers_2_h3_alnps[] = { { 0, { NULL }}, C_CODE for ($i = 0; $i < (1 << @all_versions); ++$i) { my (@vers, @alpns); for ($j = 0; $j < @all_versions; ++$j) { if ($i & (1 << $j)) { push @vers, $all_versions[$j]; push @alpns, $all_alpns[$j]; } } if (@vers) { print OUT " {", join("|", map "(1<<$_)", @vers), ", ", "{ ", join(", ", (map qq("$_"), @alpns), "NULL"), " }},\n"; } } $all_versions = join "|", map "(1<<$_)", @all_versions; print OUT <<"C_CODE"; }; const char *const * lsquic_get_h3_alpns (unsigned versions) { unsigned i; versions &= ($all_versions); for (i = 0; i < sizeof(vers_2_h3_alnps) / sizeof(vers_2_h3_alnps[0]); ++i) if (versions == vers_2_h3_alnps[i].versions) return vers_2_h3_alnps[i].h3_alpns; assert(0); return vers_2_h3_alnps[0].h3_alpns; } C_CODE print OUT <<'C_CODE'; enum lsquic_version lsquic_alpn2ver (const char *alpn, size_t len) { static const struct el { size_t len; char alpn[10]; enum lsquic_version version; } map[] = { C_CODE for ($i = 0; $i < @versions; ++$i) { print OUT " {sizeof(\"h3-Q0$versions[$i]\")-1,\"h3-Q0$versions[$i]\", $enums[$i]},\n"; } for ($i = 0; $i < @draft_versions; ++$i) { print OUT " {sizeof(\"h3-$draft_versions[$i]\")-1,\"h3-$draft_versions[$i]\", LSQVER_ID$draft_versions[$i]},\n"; } print OUT <<'C_CODE'; }; const struct el *el; if (alpn) for (el = map; el < map + sizeof(map) / sizeof(map[0]); ++el) if (el->len == len && 0 == strncmp(el->alpn, alpn, len)) return el->version; return -1; } C_CODE close OUT;