1387f7481SDmitri Tikhonov/*
2387f7481SDmitri Tikhonov * QPACK Interop -- encode to intermediate format
3387f7481SDmitri Tikhonov *
4387f7481SDmitri Tikhonov * https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop
5387f7481SDmitri Tikhonov */
6387f7481SDmitri Tikhonov
7a4e3cfd9SDmitri Tikhonov#include <assert.h>
8446dba69SDmitri Tikhonov
9446dba69SDmitri Tikhonov#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
10446dba69SDmitri Tikhonov#include <sys/endian.h>
11446dba69SDmitri Tikhonov#define bswap_16 bswap16
12446dba69SDmitri Tikhonov#define bswap_32 bswap32
13446dba69SDmitri Tikhonov#define bswap_64 bswap64
14446dba69SDmitri Tikhonov#elif defined(__APPLE__)
15446dba69SDmitri Tikhonov#include <libkern/OSByteOrder.h>
16446dba69SDmitri Tikhonov#define bswap_16 OSSwapInt16
17446dba69SDmitri Tikhonov#define bswap_32 OSSwapInt32
18446dba69SDmitri Tikhonov#define bswap_64 OSSwapInt64
19446dba69SDmitri Tikhonov#elif defined(WIN32)
20446dba69SDmitri Tikhonov#define bswap_16 _byteswap_ushort
21446dba69SDmitri Tikhonov#define bswap_32 _byteswap_ulong
22446dba69SDmitri Tikhonov#define bswap_64 _byteswap_uint64
23446dba69SDmitri Tikhonov#else
24387f7481SDmitri Tikhonov#include <byteswap.h>
25446dba69SDmitri Tikhonov#endif
26446dba69SDmitri Tikhonov
27387f7481SDmitri Tikhonov#include <errno.h>
28387f7481SDmitri Tikhonov#include <stdio.h>
29387f7481SDmitri Tikhonov#include <stdlib.h>
30387f7481SDmitri Tikhonov#include <string.h>
31205a2804SDmitri Tikhonov#ifdef WIN32
32948d3c75STyler Young#include <getopt.h>
33205a2804SDmitri Tikhonov#else
34387f7481SDmitri Tikhonov#include <unistd.h>
35205a2804SDmitri Tikhonov#endif
36387f7481SDmitri Tikhonov#include <sys/types.h>
37387f7481SDmitri Tikhonov#include <sys/stat.h>
38387f7481SDmitri Tikhonov#include <fcntl.h>
390e41c471SDmitri Tikhonov#include <inttypes.h>
40387f7481SDmitri Tikhonov
41387f7481SDmitri Tikhonov#include "lsqpack.h"
4260620859SDmitri Tikhonov#include "lsxpack_header.h"
43387f7481SDmitri Tikhonov
441999dccfSDmitri Tikhonovstatic int s_verbose;
451999dccfSDmitri Tikhonov
46a4e3cfd9SDmitri Tikhonovunsigned char *
47a4e3cfd9SDmitri Tikhonovlsqpack_enc_int (unsigned char *dst, unsigned char *const end, uint64_t value,
48a4e3cfd9SDmitri Tikhonov                                                        unsigned prefix_bits);
49a4e3cfd9SDmitri Tikhonov
50387f7481SDmitri Tikhonovstatic void
51387f7481SDmitri Tikhonovusage (const char *name)
52387f7481SDmitri Tikhonov{
53387f7481SDmitri Tikhonov    fprintf(stderr,
54387f7481SDmitri Tikhonov"Usage: %s [options] [-i input] [-o output]\n"
55387f7481SDmitri Tikhonov"\n"
56387f7481SDmitri Tikhonov"Options:\n"
57387f7481SDmitri Tikhonov"   -i FILE     Input file.  If not specified or set to `-', the input is\n"
58387f7481SDmitri Tikhonov"                 read from stdin.\n"
59387f7481SDmitri Tikhonov"   -o FILE     Output file.  If not spepcified or set to `-', the output\n"
60387f7481SDmitri Tikhonov"                 is written to stdout.\n"
61387f7481SDmitri Tikhonov"   -s NUMBER   Maximum number of risked streams.  Defaults to %u.\n"
62387f7481SDmitri Tikhonov"   -t NUMBER   Dynamic table size.  Defaults to %u.\n"
630e41c471SDmitri Tikhonov"   -a MODE     Header acknowledgement mode.  0 means headers are never\n"
640e41c471SDmitri Tikhonov"                 acknowledged, non-zero means header blocks are acknowledged\n"
650e41c471SDmitri Tikhonov"                 immediately.  Default value is 0.\n"
66fdc428d6SDmitri Tikhonov"   -n          Process annotations.\n"
67f3eec0d9SDmitri Tikhonov"   -S          Server mode.\n"
6834b3ff7cSDmitri Tikhonov"   -D          Do not emit \"Duplicate\" instructions.\n"
69d35900e5SDmitri Tikhonov"   -A          Aggressive indexing.\n"
70ecec593fSDmitri Tikhonov"   -M          Turn off memory guard.\n"
712499f175SDmitri Tikhonov"   -f          Fast: use maximum output buffers.\n"
721999dccfSDmitri Tikhonov"   -v          Verbose: print various messages to stderr.\n"
73387f7481SDmitri Tikhonov"\n"
74387f7481SDmitri Tikhonov"   -h          Print this help screen and exit\n"
75387f7481SDmitri Tikhonov    , name, LSQPACK_DEF_MAX_RISKED_STREAMS, LSQPACK_DEF_DYN_TABLE_SIZE);
76387f7481SDmitri Tikhonov}
77387f7481SDmitri Tikhonov
78387f7481SDmitri Tikhonov
798624e82bSDmitri Tikhonovstatic void
80205a2804SDmitri Tikhonovwrite_enc_stream (FILE *out, const unsigned char *enc_buf, size_t enc_sz)
818624e82bSDmitri Tikhonov{
828624e82bSDmitri Tikhonov    uint64_t stream_id_enc;
838624e82bSDmitri Tikhonov    uint32_t length_enc;
84205a2804SDmitri Tikhonov    size_t written;
858624e82bSDmitri Tikhonov
8650c5c9faSStephen Petrides    if (enc_sz <= 0)
8750c5c9faSStephen Petrides        return;
888624e82bSDmitri Tikhonov    stream_id_enc = 0;
898624e82bSDmitri Tikhonov    length_enc = enc_sz;
908624e82bSDmitri Tikhonov#if __BYTE_ORDER == __LITTLE_ENDIAN
918624e82bSDmitri Tikhonov    stream_id_enc = bswap_64(stream_id_enc);
928624e82bSDmitri Tikhonov    length_enc = bswap_32(length_enc);
938624e82bSDmitri Tikhonov#endif
94205a2804SDmitri Tikhonov    written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out);
95205a2804SDmitri Tikhonov    if (written != sizeof(stream_id_enc))
96205a2804SDmitri Tikhonov    {
97205a2804SDmitri Tikhonov        perror("fwrite");
98205a2804SDmitri Tikhonov        exit(EXIT_FAILURE);
99205a2804SDmitri Tikhonov    }
100205a2804SDmitri Tikhonov    written = fwrite(&length_enc, 1, sizeof(length_enc), out);
101205a2804SDmitri Tikhonov    if (written != sizeof(length_enc))
102205a2804SDmitri Tikhonov    {
103205a2804SDmitri Tikhonov        perror("fwrite");
104205a2804SDmitri Tikhonov        exit(EXIT_FAILURE);
105205a2804SDmitri Tikhonov    }
106205a2804SDmitri Tikhonov    written = fwrite(enc_buf, 1, enc_sz, out);
107205a2804SDmitri Tikhonov    if (written != enc_sz)
108205a2804SDmitri Tikhonov    {
109205a2804SDmitri Tikhonov        perror("fwrite");
110205a2804SDmitri Tikhonov        exit(EXIT_FAILURE);
111205a2804SDmitri Tikhonov    }
1128624e82bSDmitri Tikhonov}
1138624e82bSDmitri Tikhonov
1148624e82bSDmitri Tikhonov
115387f7481SDmitri Tikhonovstatic void
116205a2804SDmitri Tikhonovwrite_enc_and_header_streams (FILE *out, unsigned stream_id,
117387f7481SDmitri Tikhonov                              const unsigned char *enc_buf, size_t enc_sz,
118387f7481SDmitri Tikhonov                              const unsigned char *pref_buf, size_t pref_sz,
119387f7481SDmitri Tikhonov                              const unsigned char *hea_buf, size_t hea_sz)
120387f7481SDmitri Tikhonov{
121387f7481SDmitri Tikhonov    uint64_t stream_id_enc;
122387f7481SDmitri Tikhonov    uint32_t length_enc;
123205a2804SDmitri Tikhonov    size_t written;
124205a2804SDmitri Tikhonov
125205a2804SDmitri Tikhonov    if (s_verbose)
126205a2804SDmitri Tikhonov        fprintf(stderr, "%s: stream %"PRIu32"\n", __func__, stream_id);
127387f7481SDmitri Tikhonov
128387f7481SDmitri Tikhonov    if (enc_sz)
129387f7481SDmitri Tikhonov    {
130387f7481SDmitri Tikhonov        stream_id_enc = 0;
131387f7481SDmitri Tikhonov        length_enc = enc_sz;
132387f7481SDmitri Tikhonov#if __BYTE_ORDER == __LITTLE_ENDIAN
133387f7481SDmitri Tikhonov        stream_id_enc = bswap_64(stream_id_enc);
134387f7481SDmitri Tikhonov        length_enc = bswap_32(length_enc);
135387f7481SDmitri Tikhonov#endif
136205a2804SDmitri Tikhonov        written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out);
137205a2804SDmitri Tikhonov        if (written != sizeof(stream_id_enc))
138205a2804SDmitri Tikhonov            goto write_err;
139205a2804SDmitri Tikhonov        written = fwrite(&length_enc, 1, sizeof(length_enc), out);
140205a2804SDmitri Tikhonov        if (written != sizeof(length_enc))
141205a2804SDmitri Tikhonov            goto write_err;
142205a2804SDmitri Tikhonov        written = fwrite(enc_buf, 1, enc_sz, out);
143205a2804SDmitri Tikhonov        if (written != enc_sz)
144205a2804SDmitri Tikhonov            goto write_err;
145387f7481SDmitri Tikhonov    }
146387f7481SDmitri Tikhonov
147387f7481SDmitri Tikhonov    stream_id_enc = stream_id;
148387f7481SDmitri Tikhonov    length_enc = pref_sz + hea_sz;
149387f7481SDmitri Tikhonov#if __BYTE_ORDER == __LITTLE_ENDIAN
150387f7481SDmitri Tikhonov    stream_id_enc = bswap_64(stream_id_enc);
151387f7481SDmitri Tikhonov    length_enc = bswap_32(length_enc);
152387f7481SDmitri Tikhonov#endif
153205a2804SDmitri Tikhonov    written = fwrite(&stream_id_enc, 1, sizeof(stream_id_enc), out);
154205a2804SDmitri Tikhonov    if (written != sizeof(stream_id_enc))
155205a2804SDmitri Tikhonov        goto write_err;
156205a2804SDmitri Tikhonov    written = fwrite(&length_enc, 1, sizeof(length_enc), out);
157205a2804SDmitri Tikhonov    if (written != sizeof(length_enc))
158205a2804SDmitri Tikhonov        goto write_err;
159205a2804SDmitri Tikhonov    written = fwrite(pref_buf, 1, pref_sz, out);
160205a2804SDmitri Tikhonov    if (written != pref_sz)
161205a2804SDmitri Tikhonov        goto write_err;
162205a2804SDmitri Tikhonov    written = fwrite(hea_buf, 1, hea_sz, out);
163205a2804SDmitri Tikhonov    if (written != hea_sz)
164205a2804SDmitri Tikhonov        goto write_err;
165205a2804SDmitri Tikhonov    return;
166205a2804SDmitri Tikhonov
167205a2804SDmitri Tikhonov  write_err:
168205a2804SDmitri Tikhonov    perror("fwrite");
169205a2804SDmitri Tikhonov    exit(EXIT_FAILURE);
170387f7481SDmitri Tikhonov}
171387f7481SDmitri Tikhonov
172387f7481SDmitri Tikhonov
173e6970152SDmitri Tikhonovstatic unsigned s_saved_ins_count;
174e6970152SDmitri Tikhonovstatic int
175e6970152SDmitri Tikhonovack_last_entry_id (struct lsqpack_enc *encoder)
176e6970152SDmitri Tikhonov{
177e6970152SDmitri Tikhonov    unsigned char *end_cmd;
178e6970152SDmitri Tikhonov    unsigned char cmd[80];
179e6970152SDmitri Tikhonov    unsigned val;
180e6970152SDmitri Tikhonov
181e6970152SDmitri Tikhonov    if (s_verbose)
182e6970152SDmitri Tikhonov        fprintf(stderr, "ACK entry ID %u\n", encoder->qpe_ins_count);
183e6970152SDmitri Tikhonov
184e6970152SDmitri Tikhonov    cmd[0] = 0x00;
185e6970152SDmitri Tikhonov    val = encoder->qpe_ins_count - s_saved_ins_count;
186e6970152SDmitri Tikhonov    s_saved_ins_count = encoder->qpe_ins_count;
187e6970152SDmitri Tikhonov    end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), val, 6);
188e6970152SDmitri Tikhonov    assert(end_cmd > cmd);
189e6970152SDmitri Tikhonov    return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd);
190e6970152SDmitri Tikhonov}
191e6970152SDmitri Tikhonov
192e6970152SDmitri Tikhonov
1930e41c471SDmitri Tikhonovstatic int
1940e41c471SDmitri Tikhonovack_stream (struct lsqpack_enc *encoder, uint64_t stream_id)
1950e41c471SDmitri Tikhonov{
1960e41c471SDmitri Tikhonov    unsigned char *end_cmd;
1970e41c471SDmitri Tikhonov    unsigned char cmd[80];
1980e41c471SDmitri Tikhonov
1990e41c471SDmitri Tikhonov    if (s_verbose)
2000e41c471SDmitri Tikhonov        fprintf(stderr, "ACK stream ID %"PRIu64"\n", stream_id);
2010e41c471SDmitri Tikhonov
2020e41c471SDmitri Tikhonov    cmd[0] = 0x80;
2030e41c471SDmitri Tikhonov    end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), stream_id, 7);
2040e41c471SDmitri Tikhonov    assert(end_cmd > cmd);
2050e41c471SDmitri Tikhonov    return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd);
2060e41c471SDmitri Tikhonov}
2070e41c471SDmitri Tikhonov
2080e41c471SDmitri Tikhonov
20904dd4598SDmitri Tikhonovstatic int
21091dfe80fSStephen Petridessync_table (struct lsqpack_enc *encoder, uint64_t num_inserts)
21191dfe80fSStephen Petrides{
21291dfe80fSStephen Petrides    unsigned char *end_cmd;
21391dfe80fSStephen Petrides    unsigned char cmd[80];
21491dfe80fSStephen Petrides
21591dfe80fSStephen Petrides    if (s_verbose)
21691dfe80fSStephen Petrides        fprintf(stderr, "Sync table num inserts %"PRIu64"\n", num_inserts);
21791dfe80fSStephen Petrides
21891dfe80fSStephen Petrides    cmd[0] = 0x00;
21991dfe80fSStephen Petrides    end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), num_inserts, 6);
22091dfe80fSStephen Petrides    assert(end_cmd > cmd);
22191dfe80fSStephen Petrides    return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd);
22291dfe80fSStephen Petrides}
22391dfe80fSStephen Petrides
22491dfe80fSStephen Petrides
22591dfe80fSStephen Petridesstatic int
22604dd4598SDmitri Tikhonovcancel_stream (struct lsqpack_enc *encoder, uint64_t stream_id)
22704dd4598SDmitri Tikhonov{
22804dd4598SDmitri Tikhonov    unsigned char *end_cmd;
22904dd4598SDmitri Tikhonov    unsigned char cmd[80];
23004dd4598SDmitri Tikhonov
23104dd4598SDmitri Tikhonov    if (s_verbose)
23204dd4598SDmitri Tikhonov        fprintf(stderr, "Cancel stream ID %"PRIu64"\n", stream_id);
23304dd4598SDmitri Tikhonov
23404dd4598SDmitri Tikhonov    cmd[0] = 0x40;
23504dd4598SDmitri Tikhonov    end_cmd = lsqpack_enc_int(cmd, cmd + sizeof(cmd), stream_id, 6);
23604dd4598SDmitri Tikhonov    assert(end_cmd > cmd);
23704dd4598SDmitri Tikhonov    return lsqpack_enc_decoder_in(encoder, cmd, end_cmd - cmd);
23804dd4598SDmitri Tikhonov}
23904dd4598SDmitri Tikhonov
24004dd4598SDmitri Tikhonov
241387f7481SDmitri Tikhonovint
242387f7481SDmitri Tikhonovmain (int argc, char **argv)
243387f7481SDmitri Tikhonov{
244387f7481SDmitri Tikhonov    FILE *in = stdin;
245205a2804SDmitri Tikhonov    FILE *out = stdout;
246387f7481SDmitri Tikhonov    int opt;
247387f7481SDmitri Tikhonov    unsigned dyn_table_size     = LSQPACK_DEF_DYN_TABLE_SIZE,
248387f7481SDmitri Tikhonov             max_risked_streams = LSQPACK_DEF_MAX_RISKED_STREAMS;
249387f7481SDmitri Tikhonov    unsigned lineno, stream_id;
250387f7481SDmitri Tikhonov    struct lsqpack_enc encoder;
251387f7481SDmitri Tikhonov    char *line, *end, *tab;
252387f7481SDmitri Tikhonov    ssize_t pref_sz;
253387f7481SDmitri Tikhonov    enum lsqpack_enc_status st;
25407831cdfSDmitri Tikhonov    enum lsqpack_enc_opts enc_opts = 0;
255387f7481SDmitri Tikhonov    size_t enc_sz, hea_sz, enc_off, hea_off;
256e6970152SDmitri Tikhonov    int header_opened, r;
257a4e3cfd9SDmitri Tikhonov    unsigned arg;
2580e41c471SDmitri Tikhonov    enum { ACK_NEVER, ACK_IMMEDIATE, } ack_mode = ACK_NEVER;
259fdc428d6SDmitri Tikhonov    int process_annotations = 0;
260387f7481SDmitri Tikhonov    char line_buf[0x1000];
261a122a7bdSDmitri Tikhonov    unsigned char tsu_buf[LSQPACK_LONGEST_SDTC];
2628624e82bSDmitri Tikhonov    size_t tsu_buf_sz;
2633ce33568SDmitri Tikhonov    enum lsqpack_enc_header_flags hflags;
2642499f175SDmitri Tikhonov    int fast = 0;
26560620859SDmitri Tikhonov    struct lsxpack_header xhdr;
266387f7481SDmitri Tikhonov    unsigned char enc_buf[0x1000], hea_buf[0x1000], pref_buf[0x20];
267387f7481SDmitri Tikhonov
2682499f175SDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, "ADMSa:i:no:s:t:hvf")))
269387f7481SDmitri Tikhonov    {
270387f7481SDmitri Tikhonov        switch (opt)
271387f7481SDmitri Tikhonov        {
272f3eec0d9SDmitri Tikhonov        case 'S':
273f3eec0d9SDmitri Tikhonov            enc_opts |= LSQPACK_ENC_OPT_SERVER;
274f3eec0d9SDmitri Tikhonov            break;
275f3eec0d9SDmitri Tikhonov        case 'D':
27607831cdfSDmitri Tikhonov            enc_opts |= LSQPACK_ENC_OPT_NO_DUP;
277f3eec0d9SDmitri Tikhonov            break;
278d35900e5SDmitri Tikhonov        case 'A':
2798fb62aa5SDmitri Tikhonov            enc_opts |= LSQPACK_ENC_OPT_IX_AGGR;
280d35900e5SDmitri Tikhonov            break;
281ecec593fSDmitri Tikhonov        case 'M':
282ecec593fSDmitri Tikhonov            enc_opts |= LSQPACK_ENC_OPT_NO_MEM_GUARD;
283ecec593fSDmitri Tikhonov            break;
284fdc428d6SDmitri Tikhonov        case 'n':
285fdc428d6SDmitri Tikhonov            ++process_annotations;
286fdc428d6SDmitri Tikhonov            break;
2870e41c471SDmitri Tikhonov        case 'a':
2880e41c471SDmitri Tikhonov            ack_mode = atoi(optarg) ? ACK_IMMEDIATE : ACK_NEVER;
2890e41c471SDmitri Tikhonov            break;
290387f7481SDmitri Tikhonov        case 'i':
291387f7481SDmitri Tikhonov            if (0 != strcmp(optarg, "-"))
292387f7481SDmitri Tikhonov            {
293387f7481SDmitri Tikhonov                in = fopen(optarg, "r");
294387f7481SDmitri Tikhonov                if (!in)
295387f7481SDmitri Tikhonov                {
296387f7481SDmitri Tikhonov                    fprintf(stderr, "cannot open `%s' for reading: %s\n",
297387f7481SDmitri Tikhonov                                                optarg, strerror(errno));
298387f7481SDmitri Tikhonov                    exit(EXIT_FAILURE);
299387f7481SDmitri Tikhonov                }
300387f7481SDmitri Tikhonov            }
301387f7481SDmitri Tikhonov            break;
302387f7481SDmitri Tikhonov        case 'o':
303387f7481SDmitri Tikhonov            if (0 != strcmp(optarg, "-"))
304387f7481SDmitri Tikhonov            {
305205a2804SDmitri Tikhonov                out = fopen(optarg, "wb");
306205a2804SDmitri Tikhonov                if (!out)
307387f7481SDmitri Tikhonov                {
308387f7481SDmitri Tikhonov                    fprintf(stderr, "cannot open `%s' for writing: %s\n",
309387f7481SDmitri Tikhonov                                                optarg, strerror(errno));
310387f7481SDmitri Tikhonov                    exit(EXIT_FAILURE);
311387f7481SDmitri Tikhonov                }
312387f7481SDmitri Tikhonov            }
313387f7481SDmitri Tikhonov            break;
314387f7481SDmitri Tikhonov        case 's':
315387f7481SDmitri Tikhonov            max_risked_streams = atoi(optarg);
316387f7481SDmitri Tikhonov            break;
317387f7481SDmitri Tikhonov        case 't':
318387f7481SDmitri Tikhonov            dyn_table_size = atoi(optarg);
319387f7481SDmitri Tikhonov            break;
320387f7481SDmitri Tikhonov        case 'h':
321387f7481SDmitri Tikhonov            usage(argv[0]);
322387f7481SDmitri Tikhonov            exit(EXIT_SUCCESS);
3232499f175SDmitri Tikhonov        case 'f':
3242499f175SDmitri Tikhonov            fast = 1;
3252499f175SDmitri Tikhonov            break;
3261999dccfSDmitri Tikhonov        case 'v':
3271999dccfSDmitri Tikhonov            ++s_verbose;
3281999dccfSDmitri Tikhonov            break;
329387f7481SDmitri Tikhonov        default:
330387f7481SDmitri Tikhonov            exit(EXIT_FAILURE);
331387f7481SDmitri Tikhonov        }
332387f7481SDmitri Tikhonov    }
333387f7481SDmitri Tikhonov
334b43d0c9bSDmitri Tikhonov    tsu_buf_sz = sizeof(tsu_buf);
3359dbae519SStephen Petrides    if (0 != lsqpack_enc_init(&encoder, s_verbose ? stderr : NULL, dyn_table_size,
336b43d0c9bSDmitri Tikhonov                    dyn_table_size, max_risked_streams, enc_opts, tsu_buf,
337b43d0c9bSDmitri Tikhonov                    &tsu_buf_sz))
338387f7481SDmitri Tikhonov    {
339387f7481SDmitri Tikhonov        perror("lsqpack_enc_init");
340387f7481SDmitri Tikhonov        exit(EXIT_FAILURE);
341387f7481SDmitri Tikhonov    }
342387f7481SDmitri Tikhonov
343387f7481SDmitri Tikhonov    lineno = 0;
344387f7481SDmitri Tikhonov    stream_id = 0;
345387f7481SDmitri Tikhonov    enc_off = 0;
346387f7481SDmitri Tikhonov    hea_off = 0;
3474df4823bSDmitri Tikhonov    header_opened = 0;
348387f7481SDmitri Tikhonov
349205a2804SDmitri Tikhonov    while (line = fgets(line_buf, sizeof(line_buf), in), line != NULL)
350387f7481SDmitri Tikhonov    {
351387f7481SDmitri Tikhonov        ++lineno;
352387f7481SDmitri Tikhonov        end = strchr(line, '\n');
353387f7481SDmitri Tikhonov        if (!end)
354387f7481SDmitri Tikhonov        {
355387f7481SDmitri Tikhonov            fprintf(stderr, "no newline on line %u\n", lineno);
356387f7481SDmitri Tikhonov            exit(EXIT_FAILURE);
357387f7481SDmitri Tikhonov        }
358387f7481SDmitri Tikhonov        *end = '\0';
359387f7481SDmitri Tikhonov
3604df4823bSDmitri Tikhonov        if (end == line)
361387f7481SDmitri Tikhonov        {
3624df4823bSDmitri Tikhonov            if (header_opened)
363387f7481SDmitri Tikhonov            {
364bcc44931SStephen Petrides                size_t sz, pref_max = sizeof(pref_buf);
3659ed0fb1cSDmitri Tikhonov                for (sz = (fast ? pref_max : 0); sz <= pref_max; sz++)
366bcc44931SStephen Petrides                {
3673ce33568SDmitri Tikhonov                    pref_sz = lsqpack_enc_end_header(&encoder, pref_buf, sz, &hflags);
368bcc44931SStephen Petrides                    if (pref_sz > 0)
3693ce33568SDmitri Tikhonov                    {
3703ce33568SDmitri Tikhonov                        if (max_risked_streams == 0)
3713ce33568SDmitri Tikhonov                            assert(!(hflags & LSQECH_REF_AT_RISK));
372bcc44931SStephen Petrides                        break;
3733ce33568SDmitri Tikhonov                    }
374bcc44931SStephen Petrides                }
375a122a7bdSDmitri Tikhonov                assert(pref_sz <= lsqpack_enc_header_block_prefix_size(&encoder));
3764df4823bSDmitri Tikhonov                if (pref_sz < 0)
3774df4823bSDmitri Tikhonov                {
3784df4823bSDmitri Tikhonov                    fprintf(stderr, "end_header failed: %s", strerror(errno));
3794df4823bSDmitri Tikhonov                    exit(EXIT_FAILURE);
3804df4823bSDmitri Tikhonov                }
381e6970152SDmitri Tikhonov                if (ack_mode == ACK_IMMEDIATE)
3820e41c471SDmitri Tikhonov                {
383e6970152SDmitri Tikhonov                    if (!(2 == pref_sz && pref_buf[0] == 0 && pref_buf[1] == 0))
384e6970152SDmitri Tikhonov                        r = ack_stream(&encoder, stream_id);
38548cefec0SDmitri Tikhonov                    else
38648cefec0SDmitri Tikhonov                        r = 0;
387f4a5c91dSDmitri Tikhonov                    if (r == 0 && encoder.qpe_ins_count > s_saved_ins_count)
388e6970152SDmitri Tikhonov                        r = ack_last_entry_id(&encoder);
389e6970152SDmitri Tikhonov                    else
390e6970152SDmitri Tikhonov                        r = 0;
391e6970152SDmitri Tikhonov                    if (r != 0)
392e6970152SDmitri Tikhonov                    {
393e6970152SDmitri Tikhonov                        fprintf(stderr, "acking stream %u failed: %s", stream_id,
394e6970152SDmitri Tikhonov                                                                    strerror(errno));
395e6970152SDmitri Tikhonov                        exit(EXIT_FAILURE);
396e6970152SDmitri Tikhonov                    }
3970e41c471SDmitri Tikhonov                }
3981678c8a4SDmitri Tikhonov                if (s_verbose)
3991678c8a4SDmitri Tikhonov                    fprintf(stderr, "compression ratio: %.3f\n",
4001678c8a4SDmitri Tikhonov                        lsqpack_enc_ratio(&encoder));
4014df4823bSDmitri Tikhonov                write_enc_and_header_streams(out, stream_id, enc_buf, enc_off,
4024df4823bSDmitri Tikhonov                                             pref_buf, pref_sz, hea_buf, hea_off);
4034df4823bSDmitri Tikhonov                enc_off = 0;
4044df4823bSDmitri Tikhonov                hea_off = 0;
4054df4823bSDmitri Tikhonov                header_opened = 0;
406387f7481SDmitri Tikhonov            }
407387f7481SDmitri Tikhonov            continue;
408387f7481SDmitri Tikhonov        }
409387f7481SDmitri Tikhonov
410387f7481SDmitri Tikhonov        if (*line == '#')
411a4e3cfd9SDmitri Tikhonov        {
412fdc428d6SDmitri Tikhonov            if (!process_annotations)
413fdc428d6SDmitri Tikhonov                continue;
414fdc428d6SDmitri Tikhonov
415a4e3cfd9SDmitri Tikhonov            /* Lines starting with ## are potential annotations */
4160e41c471SDmitri Tikhonov            if (ack_mode != ACK_IMMEDIATE
4170e41c471SDmitri Tikhonov                /* Ignore ACK annotations in immediate ACK mode, as we do
4180e41c471SDmitri Tikhonov                 * not tolerate duplicate ACKs.
4190e41c471SDmitri Tikhonov                 */
4200e41c471SDmitri Tikhonov                                && 1 == sscanf(line, "## %*[a] %u ", &arg))
421a4e3cfd9SDmitri Tikhonov            {
4220e41c471SDmitri Tikhonov                if (0 != ack_stream(&encoder, arg))
423a4e3cfd9SDmitri Tikhonov                {
424a4e3cfd9SDmitri Tikhonov                    fprintf(stderr, "ACKing stream ID %u failed\n", arg);
425a4e3cfd9SDmitri Tikhonov                    exit(EXIT_FAILURE);
426a4e3cfd9SDmitri Tikhonov                }
427a4e3cfd9SDmitri Tikhonov            }
42891dfe80fSStephen Petrides            else if (1 == sscanf(line, "## %*[s] %u", &arg))
42991dfe80fSStephen Petrides                sync_table(&encoder, arg);
43004dd4598SDmitri Tikhonov            else if (1 == sscanf(line, "## %*[c] %u", &arg))
43104dd4598SDmitri Tikhonov                cancel_stream(&encoder, arg);
4328624e82bSDmitri Tikhonov            else if (1 == sscanf(line, "## %*[t] %u", &arg))
4338624e82bSDmitri Tikhonov            {
4348624e82bSDmitri Tikhonov                tsu_buf_sz = sizeof(tsu_buf);
4358624e82bSDmitri Tikhonov                if (0 != lsqpack_enc_set_max_capacity(&encoder, arg, tsu_buf,
4368624e82bSDmitri Tikhonov                                                                &tsu_buf_sz))
4378624e82bSDmitri Tikhonov                {
4388624e82bSDmitri Tikhonov                    fprintf(stderr, "cannot set capacity to %u: %s\n", arg,
4398624e82bSDmitri Tikhonov                        strerror(errno));
4408624e82bSDmitri Tikhonov                    exit(EXIT_FAILURE);
4418624e82bSDmitri Tikhonov                }
4428624e82bSDmitri Tikhonov                write_enc_stream(out, tsu_buf, tsu_buf_sz);
4438624e82bSDmitri Tikhonov            }
444387f7481SDmitri Tikhonov            continue;
445a4e3cfd9SDmitri Tikhonov        }
446387f7481SDmitri Tikhonov
447387f7481SDmitri Tikhonov        tab = strchr(line, '\t');
448387f7481SDmitri Tikhonov        if (!tab)
449387f7481SDmitri Tikhonov        {
450387f7481SDmitri Tikhonov            fprintf(stderr, "no TAB on line %u\n", lineno);
451387f7481SDmitri Tikhonov            exit(EXIT_FAILURE);
452387f7481SDmitri Tikhonov        }
453387f7481SDmitri Tikhonov
4544df4823bSDmitri Tikhonov        if (!header_opened)
4554df4823bSDmitri Tikhonov        {
4564df4823bSDmitri Tikhonov            ++stream_id;
4574df4823bSDmitri Tikhonov            if (0 != lsqpack_enc_start_header(&encoder, stream_id, 0))
4584df4823bSDmitri Tikhonov            {
4594df4823bSDmitri Tikhonov                fprintf(stderr, "start_header failed: %s\n", strerror(errno));
4604df4823bSDmitri Tikhonov                exit(EXIT_FAILURE);
4614df4823bSDmitri Tikhonov            }
4624df4823bSDmitri Tikhonov            header_opened = 1;
4634df4823bSDmitri Tikhonov        }
4642499f175SDmitri Tikhonov        if (fast)
4652499f175SDmitri Tikhonov        {
4662499f175SDmitri Tikhonov            enc_sz = sizeof(enc_buf) - enc_off;
4672499f175SDmitri Tikhonov            hea_sz = sizeof(hea_buf) - hea_off;
4682499f175SDmitri Tikhonov        }
4692499f175SDmitri Tikhonov        else
4702499f175SDmitri Tikhonov        {
4712499f175SDmitri Tikhonov            /* Increase buffers one by one to exercise error conditions */
4722499f175SDmitri Tikhonov            enc_sz = 0;
4732499f175SDmitri Tikhonov            hea_sz = 0;
4742499f175SDmitri Tikhonov        }
4759dbae519SStephen Petrides        while (1)
4769dbae519SStephen Petrides        {
4779e982dfdSDmitri Tikhonov            lsxpack_header_set_offset2(&xhdr, line, 0, tab - line,
4789e982dfdSDmitri Tikhonov                                            tab + 1 - line, end - tab - 1);
4799dbae519SStephen Petrides            st = lsqpack_enc_encode(&encoder, enc_buf + enc_off, &enc_sz,
48060620859SDmitri Tikhonov                        hea_buf + hea_off, &hea_sz, &xhdr, 0);
4819dbae519SStephen Petrides            switch (st)
4829dbae519SStephen Petrides            {
4839dbae519SStephen Petrides            case LQES_NOBUF_ENC:
4849dbae519SStephen Petrides                if (enc_sz < sizeof(enc_buf) - enc_off)
4859dbae519SStephen Petrides                    ++enc_sz;
4869dbae519SStephen Petrides                else
4879dbae519SStephen Petrides                    assert(0);
4889dbae519SStephen Petrides                break;
4899dbae519SStephen Petrides            case LQES_NOBUF_HEAD:
4909dbae519SStephen Petrides                if (hea_sz < sizeof(hea_buf) - hea_off)
4919dbae519SStephen Petrides                    ++hea_sz;
4929dbae519SStephen Petrides                else
4939dbae519SStephen Petrides                    assert(0);
4949dbae519SStephen Petrides                break;
4959dbae519SStephen Petrides            default:
4969dbae519SStephen Petrides                assert(st == LQES_OK);
4979dbae519SStephen Petrides                goto end_encode_one_header;
4989dbae519SStephen Petrides            }
4999dbae519SStephen Petrides        }
500387f7481SDmitri Tikhonov        if (st != LQES_OK)
501387f7481SDmitri Tikhonov        {
502387f7481SDmitri Tikhonov            /* It could only run of of output space, so it's not really an
503387f7481SDmitri Tikhonov             * error, but we make no provision in the interop encoder to
504387f7481SDmitri Tikhonov             * grow the buffers.
505387f7481SDmitri Tikhonov             */
506387f7481SDmitri Tikhonov            fprintf(stderr, "Could not encode header on line %u: %u\n",
507387f7481SDmitri Tikhonov                                                                lineno, st);
508387f7481SDmitri Tikhonov            exit(EXIT_FAILURE);
509387f7481SDmitri Tikhonov        }
5109dbae519SStephen Petrides    end_encode_one_header:
511387f7481SDmitri Tikhonov        enc_off += enc_sz;
512387f7481SDmitri Tikhonov        hea_off += hea_sz;
513387f7481SDmitri Tikhonov    }
514387f7481SDmitri Tikhonov
515205a2804SDmitri Tikhonov    if (s_verbose)
516205a2804SDmitri Tikhonov        fprintf(stderr, "exited while loop\n");
517205a2804SDmitri Tikhonov
518387f7481SDmitri Tikhonov    (void) fclose(in);
519387f7481SDmitri Tikhonov
5204df4823bSDmitri Tikhonov    if (header_opened)
521387f7481SDmitri Tikhonov    {
522205a2804SDmitri Tikhonov        if (s_verbose)
523205a2804SDmitri Tikhonov            fprintf(stderr, "close opened header\n");
5243ce33568SDmitri Tikhonov        pref_sz = lsqpack_enc_end_header(&encoder, pref_buf, sizeof(pref_buf),
5253ce33568SDmitri Tikhonov                                                                    &hflags);
526387f7481SDmitri Tikhonov        if (pref_sz < 0)
527387f7481SDmitri Tikhonov        {
528387f7481SDmitri Tikhonov            fprintf(stderr, "end_header failed: %s", strerror(errno));
529387f7481SDmitri Tikhonov            exit(EXIT_FAILURE);
530387f7481SDmitri Tikhonov        }
5313ce33568SDmitri Tikhonov        if (max_risked_streams == 0)
5323ce33568SDmitri Tikhonov            assert(!(hflags & LSQECH_REF_AT_RISK));
5330e41c471SDmitri Tikhonov        if (ack_mode == ACK_IMMEDIATE
534dd11e024SDmitri Tikhonov            && !(2 == pref_sz && pref_buf[0] == 0 && pref_buf[1] == 0)
5350e41c471SDmitri Tikhonov            && 0 != ack_stream(&encoder, stream_id))
5360e41c471SDmitri Tikhonov        {
5370e41c471SDmitri Tikhonov            fprintf(stderr, "acking stream %u failed: %s", stream_id,
5380e41c471SDmitri Tikhonov                                                                strerror(errno));
5390e41c471SDmitri Tikhonov            exit(EXIT_FAILURE);
5400e41c471SDmitri Tikhonov        }
5411678c8a4SDmitri Tikhonov        if (s_verbose)
5421678c8a4SDmitri Tikhonov            fprintf(stderr, "compression ratio: %.3f\n",
5431678c8a4SDmitri Tikhonov                lsqpack_enc_ratio(&encoder));
544387f7481SDmitri Tikhonov        write_enc_and_header_streams(out, stream_id, enc_buf, enc_off, pref_buf,
545387f7481SDmitri Tikhonov                                     pref_sz, hea_buf, hea_off);
546387f7481SDmitri Tikhonov    }
547387f7481SDmitri Tikhonov
548387f7481SDmitri Tikhonov    lsqpack_enc_cleanup(&encoder);
549387f7481SDmitri Tikhonov
550205a2804SDmitri Tikhonov    if (0 != fclose(out))
551205a2804SDmitri Tikhonov    {
552205a2804SDmitri Tikhonov        perror("fclose(out)");
553205a2804SDmitri Tikhonov        exit(EXIT_FAILURE);
554205a2804SDmitri Tikhonov    }
555205a2804SDmitri Tikhonov
556387f7481SDmitri Tikhonov    exit(EXIT_SUCCESS);
557387f7481SDmitri Tikhonov}
558