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