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