1e1657cf2SDmitri Tikhonov/*
2e1657cf2SDmitri Tikhonov * Read QIF and encode it using HPACK.  Use for benchmarking.
3e1657cf2SDmitri Tikhonov *
4e1657cf2SDmitri Tikhonov * Based on ls-qpack's interop-encode.
5e1657cf2SDmitri Tikhonov *
6e1657cf2SDmitri Tikhonov * How it works: read in QIF into a list of header set objects and encode
7e1657cf2SDmitri Tikhonov * the list a number of times.
8e1657cf2SDmitri Tikhonov *
9e1657cf2SDmitri Tikhonov * QIF Format:
10e1657cf2SDmitri Tikhonov * https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop
11e1657cf2SDmitri Tikhonov */
12e1657cf2SDmitri Tikhonov
13e1657cf2SDmitri Tikhonov#define _GNU_SOURCE /* for memmem */
14e1657cf2SDmitri Tikhonov#include <assert.h>
15e1657cf2SDmitri Tikhonov
16e1657cf2SDmitri Tikhonov#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
17e1657cf2SDmitri Tikhonov#include <sys/endian.h>
18e1657cf2SDmitri Tikhonov#define bswap_16 bswap16
19e1657cf2SDmitri Tikhonov#define bswap_32 bswap32
20e1657cf2SDmitri Tikhonov#define bswap_64 bswap64
21e1657cf2SDmitri Tikhonov#elif defined(__APPLE__)
22e1657cf2SDmitri Tikhonov#include <libkern/OSByteOrder.h>
23e1657cf2SDmitri Tikhonov#define bswap_16 OSSwapInt16
24e1657cf2SDmitri Tikhonov#define bswap_32 OSSwapInt32
25e1657cf2SDmitri Tikhonov#define bswap_64 OSSwapInt64
26e1657cf2SDmitri Tikhonov#elif defined(WIN32)
27e1657cf2SDmitri Tikhonov#error Not supported on Windows
28e1657cf2SDmitri Tikhonov#else
29e1657cf2SDmitri Tikhonov#include <byteswap.h>
30e1657cf2SDmitri Tikhonov#endif
31e1657cf2SDmitri Tikhonov
32e1657cf2SDmitri Tikhonov#include <errno.h>
33e1657cf2SDmitri Tikhonov#include <stdio.h>
34e1657cf2SDmitri Tikhonov#include <stdlib.h>
35e1657cf2SDmitri Tikhonov#include <string.h>
36e1657cf2SDmitri Tikhonov#include <unistd.h>
37e1657cf2SDmitri Tikhonov#include <sys/types.h>
38e1657cf2SDmitri Tikhonov#include <sys/stat.h>
39e1657cf2SDmitri Tikhonov#include <fcntl.h>
40e1657cf2SDmitri Tikhonov#include <inttypes.h>
41e1657cf2SDmitri Tikhonov#include <sys/mman.h>
42e1657cf2SDmitri Tikhonov
43e1657cf2SDmitri Tikhonov#include "lshpack.h"
44e1657cf2SDmitri Tikhonov
45e1657cf2SDmitri Tikhonovstatic int s_verbose;
46e1657cf2SDmitri Tikhonov
47e1657cf2SDmitri Tikhonov#define TABLE_SIZE 4096
48e1657cf2SDmitri Tikhonov
49e1657cf2SDmitri Tikhonovstatic void
50e1657cf2SDmitri Tikhonovusage (const char *name)
51e1657cf2SDmitri Tikhonov{
52e1657cf2SDmitri Tikhonov    fprintf(stderr,
53e1657cf2SDmitri Tikhonov"Usage: %s [options] [-i input] [-o output]\n"
54e1657cf2SDmitri Tikhonov"\n"
55e1657cf2SDmitri Tikhonov"Options:\n"
56e1657cf2SDmitri Tikhonov"   -i FILE     Input file.\n"
57e1657cf2SDmitri Tikhonov"   -o FILE     Output file.  If not spepcified or set to `-', the output\n"
58e1657cf2SDmitri Tikhonov"                 is written to stdout.\n"
59e1657cf2SDmitri Tikhonov"   -K          Discard output: encoded output is discarded.\n"
6072061d40SDmitri Tikhonov"   -H          Do not use the history heuristic.\n"
61e1657cf2SDmitri Tikhonov"   -n NUMBER   Number of times to iterate over the header set list.\n"
62e1657cf2SDmitri Tikhonov"   -t NUMBER   Dynamic table size.  Defaults to %u.\n"
63e1657cf2SDmitri Tikhonov"   -v          Verbose: print various messages to stderr.\n"
64e1657cf2SDmitri Tikhonov"\n"
65e1657cf2SDmitri Tikhonov"   -h          Print this help screen and exit\n"
66e1657cf2SDmitri Tikhonov    , name, TABLE_SIZE);
67e1657cf2SDmitri Tikhonov}
68e1657cf2SDmitri Tikhonov
69e1657cf2SDmitri Tikhonov
70e1657cf2SDmitri Tikhonovstruct header
71e1657cf2SDmitri Tikhonov{
72e1657cf2SDmitri Tikhonov    const char    *name;
73e1657cf2SDmitri Tikhonov    const char    *val;
74e1657cf2SDmitri Tikhonov    size_t         name_len;
75e1657cf2SDmitri Tikhonov    size_t         val_len;
76e1657cf2SDmitri Tikhonov};
77e1657cf2SDmitri Tikhonov
78e1657cf2SDmitri Tikhonov
79e1657cf2SDmitri Tikhonov#define MAX_HEADERS 32
80e1657cf2SDmitri Tikhonov
81e1657cf2SDmitri Tikhonovstruct header_set
82e1657cf2SDmitri Tikhonov{
83e1657cf2SDmitri Tikhonov    STAILQ_ENTRY(header_set)    next;
84e1657cf2SDmitri Tikhonov    unsigned                    n_headers;
85e1657cf2SDmitri Tikhonov    struct header               headers[MAX_HEADERS];
86e1657cf2SDmitri Tikhonov};
87e1657cf2SDmitri Tikhonov
88e1657cf2SDmitri Tikhonov
89600751b9SDmitri Tikhonovstatic inline void
90600751b9SDmitri Tikhonovlsxpack_header_set_ptr(lsxpack_header_t *hdr,
91600751b9SDmitri Tikhonov                       const char *name, size_t name_len,
92600751b9SDmitri Tikhonov                       const char *val, size_t val_len)
93600751b9SDmitri Tikhonov{
94600751b9SDmitri Tikhonov    static char buf[65536];
95600751b9SDmitri Tikhonov    memcpy(buf, name, name_len);
96600751b9SDmitri Tikhonov    memcpy(&buf[name_len], val, val_len);
97600751b9SDmitri Tikhonov    lsxpack_header_set_offset2(hdr, buf, 0, name_len, name_len, val_len);
98600751b9SDmitri Tikhonov}
99600751b9SDmitri Tikhonov
100600751b9SDmitri Tikhonov
101e1657cf2SDmitri Tikhonovint
102e1657cf2SDmitri Tikhonovmain (int argc, char **argv)
103e1657cf2SDmitri Tikhonov{
104e1657cf2SDmitri Tikhonov    FILE *out = stdout;
1052a690864SDmitri Tikhonov    int opt, qif_fd = -1;
10672061d40SDmitri Tikhonov    int discard = 0, use_history = 1;
107e1657cf2SDmitri Tikhonov    unsigned n_iters = 1, n, i;
108e1657cf2SDmitri Tikhonov    unsigned dyn_table_size     = TABLE_SIZE;
109e1657cf2SDmitri Tikhonov    STAILQ_HEAD(, header_set) header_sets = STAILQ_HEAD_INITIALIZER(header_sets);
110e1657cf2SDmitri Tikhonov    struct stat st;
111e1657cf2SDmitri Tikhonov    const unsigned char *qif = NULL, *p, *tab, *nl, *nlnl;
112e1657cf2SDmitri Tikhonov    unsigned char *s;
113e1657cf2SDmitri Tikhonov    struct header *header;
114e1657cf2SDmitri Tikhonov    struct header_set *hset;
115e1657cf2SDmitri Tikhonov    struct lshpack_enc encoder;
116e1657cf2SDmitri Tikhonov    unsigned char buf[0x2000];
117e1657cf2SDmitri Tikhonov
11872061d40SDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, "Hi:o:Kn:t:vh")))
119e1657cf2SDmitri Tikhonov    {
120e1657cf2SDmitri Tikhonov        switch (opt)
121e1657cf2SDmitri Tikhonov        {
122e1657cf2SDmitri Tikhonov        case 'n':
123e1657cf2SDmitri Tikhonov            n_iters = atoi(optarg);
124e1657cf2SDmitri Tikhonov            break;
125e1657cf2SDmitri Tikhonov        case 'i':
126e1657cf2SDmitri Tikhonov            qif_fd = open(optarg, O_RDONLY);
127e1657cf2SDmitri Tikhonov            if (qif_fd < 0)
128e1657cf2SDmitri Tikhonov            {
129e1657cf2SDmitri Tikhonov                perror("open");
130e1657cf2SDmitri Tikhonov                exit(EXIT_FAILURE);
131e1657cf2SDmitri Tikhonov            }
132e1657cf2SDmitri Tikhonov            if (0 != fstat(qif_fd, &st))
133e1657cf2SDmitri Tikhonov            {
134e1657cf2SDmitri Tikhonov                perror("fstat");
135e1657cf2SDmitri Tikhonov                exit(EXIT_FAILURE);
136e1657cf2SDmitri Tikhonov            }
137e1657cf2SDmitri Tikhonov            qif = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, qif_fd, 0);
138e1657cf2SDmitri Tikhonov            if (!qif)
139e1657cf2SDmitri Tikhonov            {
140e1657cf2SDmitri Tikhonov                perror("mmap");
141e1657cf2SDmitri Tikhonov                exit(EXIT_FAILURE);
142e1657cf2SDmitri Tikhonov            }
143e1657cf2SDmitri Tikhonov            break;
144e1657cf2SDmitri Tikhonov        case 'o':
145e1657cf2SDmitri Tikhonov            if (0 != strcmp(optarg, "-"))
146e1657cf2SDmitri Tikhonov            {
147e1657cf2SDmitri Tikhonov                out = fopen(optarg, "wb");
148e1657cf2SDmitri Tikhonov                if (!out)
149e1657cf2SDmitri Tikhonov                {
150e1657cf2SDmitri Tikhonov                    fprintf(stderr, "cannot open `%s' for writing: %s\n",
151e1657cf2SDmitri Tikhonov                                                optarg, strerror(errno));
152e1657cf2SDmitri Tikhonov                    exit(EXIT_FAILURE);
153e1657cf2SDmitri Tikhonov                }
154e1657cf2SDmitri Tikhonov            }
155e1657cf2SDmitri Tikhonov            break;
156e1657cf2SDmitri Tikhonov        case 'K':
157e1657cf2SDmitri Tikhonov            discard = 1;
158e1657cf2SDmitri Tikhonov            break;
15972061d40SDmitri Tikhonov        case 'H':
16072061d40SDmitri Tikhonov            use_history = 0;
16172061d40SDmitri Tikhonov            break;
162e1657cf2SDmitri Tikhonov        case 't':
163e1657cf2SDmitri Tikhonov            dyn_table_size = atoi(optarg);
164e1657cf2SDmitri Tikhonov            break;
165e1657cf2SDmitri Tikhonov        case 'h':
166e1657cf2SDmitri Tikhonov            usage(argv[0]);
167e1657cf2SDmitri Tikhonov            exit(EXIT_SUCCESS);
168e1657cf2SDmitri Tikhonov        case 'v':
169e1657cf2SDmitri Tikhonov            ++s_verbose;
170e1657cf2SDmitri Tikhonov            break;
171e1657cf2SDmitri Tikhonov        default:
172e1657cf2SDmitri Tikhonov            exit(EXIT_FAILURE);
173e1657cf2SDmitri Tikhonov        }
174e1657cf2SDmitri Tikhonov    }
175e1657cf2SDmitri Tikhonov
176e1657cf2SDmitri Tikhonov    if (!qif)
177e1657cf2SDmitri Tikhonov    {
178e1657cf2SDmitri Tikhonov        fprintf(stderr, "Please specify input QIF file using -i flag\n");
179e1657cf2SDmitri Tikhonov        exit(EXIT_FAILURE);
180e1657cf2SDmitri Tikhonov    }
181e1657cf2SDmitri Tikhonov
182e1657cf2SDmitri Tikhonov    const unsigned char *const begin = qif;
183e1657cf2SDmitri Tikhonov    const unsigned char *const end = begin + st.st_size;
184e1657cf2SDmitri Tikhonov    while (qif + 2 < end)
185e1657cf2SDmitri Tikhonov    {
186e1657cf2SDmitri Tikhonov        nlnl = memmem(qif, end - qif, "\n\n", 2);
187e1657cf2SDmitri Tikhonov        if (!nlnl)
188e1657cf2SDmitri Tikhonov            nlnl = end;
189e1657cf2SDmitri Tikhonov        hset = calloc(1, sizeof(*hset));
190e1657cf2SDmitri Tikhonov        if (!hset)
191e1657cf2SDmitri Tikhonov        {
192e1657cf2SDmitri Tikhonov            perror("malloc");
193e1657cf2SDmitri Tikhonov            exit(EXIT_FAILURE);
194e1657cf2SDmitri Tikhonov        }
195e1657cf2SDmitri Tikhonov        STAILQ_INSERT_TAIL(&header_sets, hset, next);
196e1657cf2SDmitri Tikhonov        p = qif;
197e1657cf2SDmitri Tikhonov        while (p < nlnl)
198e1657cf2SDmitri Tikhonov        {
199e1657cf2SDmitri Tikhonov            if (hset->n_headers >= MAX_HEADERS)
200e1657cf2SDmitri Tikhonov            {
201e1657cf2SDmitri Tikhonov                fprintf(stderr, "max headers > 32, off: %u",
202e1657cf2SDmitri Tikhonov                                            (unsigned) (p - begin));
203e1657cf2SDmitri Tikhonov                exit(EXIT_FAILURE);
204e1657cf2SDmitri Tikhonov            }
205e1657cf2SDmitri Tikhonov            tab = memmem(p, nlnl - p, "\t", 1);
206e1657cf2SDmitri Tikhonov            if (!tab)
207e1657cf2SDmitri Tikhonov            {
208e1657cf2SDmitri Tikhonov                fprintf(stderr, "tab not found, off: %u",
209e1657cf2SDmitri Tikhonov                                            (unsigned) (p - begin));
210e1657cf2SDmitri Tikhonov                exit(EXIT_FAILURE);
211e1657cf2SDmitri Tikhonov            }
212e1657cf2SDmitri Tikhonov            nl = memmem(tab + 1, nlnl - tab - 1, "\n", 1);
213e1657cf2SDmitri Tikhonov            if (!nl)
214e1657cf2SDmitri Tikhonov                nl = nlnl;
215e1657cf2SDmitri Tikhonov            hset->headers[ hset->n_headers ] = (struct header) {
216e1657cf2SDmitri Tikhonov                .name = (const char *) p,
217e1657cf2SDmitri Tikhonov                .val = (const char *) tab + 1,
218e1657cf2SDmitri Tikhonov                .name_len =  tab - p,
219e1657cf2SDmitri Tikhonov                .val_len = nl - tab - 1,
220e1657cf2SDmitri Tikhonov            };
221e1657cf2SDmitri Tikhonov            ++hset->n_headers;
222e1657cf2SDmitri Tikhonov            p = nl + 1;
223e1657cf2SDmitri Tikhonov        }
224e1657cf2SDmitri Tikhonov        qif = nlnl + 2;
225e1657cf2SDmitri Tikhonov    }
226e1657cf2SDmitri Tikhonov
227226eedebSDmitri Tikhonov    lsxpack_header_t hdr;
228e1657cf2SDmitri Tikhonov    for (n = 0; n < n_iters; ++n)
229e1657cf2SDmitri Tikhonov    {
230e1657cf2SDmitri Tikhonov        if (0 != lshpack_enc_init(&encoder))
231e1657cf2SDmitri Tikhonov        {
232e1657cf2SDmitri Tikhonov            perror("lshpack_enc_init");
233e1657cf2SDmitri Tikhonov            exit(EXIT_FAILURE);
234e1657cf2SDmitri Tikhonov        }
23572061d40SDmitri Tikhonov        (void) lshpack_enc_use_hist(&encoder, use_history);
236e1657cf2SDmitri Tikhonov
237e1657cf2SDmitri Tikhonov        STAILQ_FOREACH(hset, &header_sets, next)
238e1657cf2SDmitri Tikhonov        {
239e1657cf2SDmitri Tikhonov            for (i = 0; i < hset->n_headers; ++i)
240e1657cf2SDmitri Tikhonov            {
241e1657cf2SDmitri Tikhonov                header = &hset->headers[i];
242226eedebSDmitri Tikhonov                lsxpack_header_set_ptr(&hdr, header->name, header->name_len,
243226eedebSDmitri Tikhonov                                       header->val, header->val_len);
244226eedebSDmitri Tikhonov                s = lshpack_enc_encode(&encoder, buf, buf + sizeof(buf), &hdr);
245e1657cf2SDmitri Tikhonov                if (s <= buf)
246e1657cf2SDmitri Tikhonov                {
247e1657cf2SDmitri Tikhonov                    fprintf(stderr, "cannot encode\n");
248e1657cf2SDmitri Tikhonov                    exit(EXIT_FAILURE);
249e1657cf2SDmitri Tikhonov                }
250e1657cf2SDmitri Tikhonov                if (!discard)
251e1657cf2SDmitri Tikhonov                    (void) fwrite(buf, 1, s - buf, out);
252e1657cf2SDmitri Tikhonov            }
253e1657cf2SDmitri Tikhonov        }
254e1657cf2SDmitri Tikhonov
255e1657cf2SDmitri Tikhonov        lshpack_enc_set_max_capacity(&encoder, dyn_table_size);
256e1657cf2SDmitri Tikhonov        lshpack_enc_cleanup(&encoder);
257e1657cf2SDmitri Tikhonov    }
258e1657cf2SDmitri Tikhonov
259e1657cf2SDmitri Tikhonov    munmap((void *) begin, st.st_size);
2602a690864SDmitri Tikhonov    if (qif_fd >= 0)
2612a690864SDmitri Tikhonov        close(qif_fd);
262e1657cf2SDmitri Tikhonov    exit(EXIT_SUCCESS);
263e1657cf2SDmitri Tikhonov}
264