varnish-cache/bin/varnishd/hpack/vhp_table.c
0
/*-
1
 * Copyright (c) 2016 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Martin Blix Grydeland <martin@varnish-software.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * Layout:
30
 *
31
 * buf [
32
 *    <x bytes name index n - 1> <x bytes value index n - 1>
33
 *    <x bytes name index n - 2> <x bytes value index n - 2>
34
 *    ...
35
 *    <x bytes name index 0> <x bytes value index 0>
36
 *
37
 *    (padding bytes for pointer alignment)
38
 *
39
 *    <struct vht_entry index 0>
40
 *    <struct vht_entry index 1>
41
 *    ...
42
 *    <struct vht_entry index n - 1>
43
 * ]
44
 *
45
 */
46
47
#include "config.h"
48
49
#include <stdio.h>
50
#include <stdlib.h>
51
#include <stdarg.h>
52
#include <string.h>
53
#include <stdint.h>
54
#include <limits.h>
55
56
#include "vdef.h"
57
#include "miniobj.h"
58
#include "vas.h"
59
60
#include "hpack/vhp.h"
61
62
#define VHT_STATIC_MAX 61
63
64
struct vht_static {
65
        const char *name;
66
        unsigned namelen;
67
        const char *value;
68
        unsigned valuelen;
69
};
70
71
static const struct vht_static static_table[] = {
72
#define HPS(NUM, NAME, VAL)                     \
73
        { NAME, sizeof NAME - 1, VAL, sizeof VAL - 1 },
74
#include "tbl/vhp_static.h"
75
};
76
77
#define TBLSIZE(tbl) ((tbl)->size + (tbl)->n * VHT_ENTRY_SIZE)
78
#define ENTRIES(buf, bufsize, n)                                        \
79
        (((struct vht_entry *)((uintptr_t)(buf) + bufsize)) - (n))
80
#define TBLENTRIES(tbl) ENTRIES((tbl)->buf, (tbl)->bufsize, (tbl)->n)
81
#define TBLENTRY(tbl, i) (&TBLENTRIES(tbl)[(i)])
82
#define ENTRYLEN(e) ((e)->namelen + (e)->valuelen)
83
#define ENTRYSIZE(e) (ENTRYLEN(e) + VHT_ENTRY_SIZE)
84
85
/****************************************************************************/
86
/* Internal interface */
87
88
static void
89 4200
vht_newentry(struct vht_table *tbl)
90
{
91
        struct vht_entry *e;
92
93 4200
        assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE);
94 4200
        tbl->n++;
95 4200
        e = TBLENTRY(tbl, 0);
96 4200
        INIT_OBJ(e, VHT_ENTRY_MAGIC);
97 4200
        e->offset = tbl->size;
98 4200
}
99
100
/* Trim elements from the end until the table size is less than max. */
101
static void
102 62480
vht_trim(struct vht_table *tbl, ssize_t max)
103
{
104
        unsigned u, v;
105
        int i;
106
        struct vht_entry *e;
107
108 62480
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
109
110 62480
        if (max < 0)
111 0
                max = 0;
112 62480
        if (TBLSIZE(tbl) <= max)
113 60600
                return;
114
115 1880
        u = v = 0;
116 9800
        for (i = tbl->n - 1; i >= 0; i--) {
117 7920
                e = TBLENTRY(tbl, i);
118 7920
                CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
119 7920
                if (TBLSIZE(tbl) - (u + v * VHT_ENTRY_SIZE) > max) {
120
                        /* Trim entry */
121 1920
                        assert(e->offset == u);
122 1920
                        u += ENTRYLEN(e);
123 1920
                        v++;
124 1920
                        FINI_OBJ(e);
125 1920
                } else {
126
                        /* Fixup offset */
127 6000
                        assert(e->offset >= u);
128 6000
                        e->offset -= u;
129
                }
130 7920
        }
131 1880
        assert(v <= tbl->n);
132
133 1880
        memmove(tbl->buf, tbl->buf + u, tbl->size - u);
134 1880
        memmove(TBLENTRY(tbl, v), TBLENTRY(tbl, 0), (tbl->n - v) * sizeof *e);
135 1880
        tbl->n -= v;
136 1880
        tbl->size -= u;
137 62480
}
138
139
/* Append len bytes from buf to entry 0 name. Asserts if no space. */
140
static void
141 6240
vht_appendname(struct vht_table *tbl, const char *buf, size_t len)
142
{
143
        struct vht_entry *e;
144
145 6240
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
146 6240
        e = TBLENTRY(tbl, 0);
147 6240
        CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
148 6240
        AZ(e->valuelen);        /* Name needs to be set before value */
149 6240
        assert(TBLSIZE(tbl) + len <= tbl->maxsize);
150 6240
        assert(e->offset + e->namelen == tbl->size);
151 6240
        memcpy(tbl->buf + tbl->size, buf, len);
152 6240
        e->namelen += len;
153 6240
        tbl->size += len;
154 6240
}
155
156
/* Append len bytes from buf to entry 0 value. Asserts if no space. */
157
static void
158 45200
vht_appendvalue(struct vht_table *tbl, const char *buf, size_t len)
159
{
160
        struct vht_entry *e;
161
162 45200
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
163 45200
        e = TBLENTRY(tbl, 0);
164 45200
        CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
165 45200
        assert(TBLSIZE(tbl) + len <= tbl->maxsize);
166 45200
        assert(e->offset + e->namelen + e->valuelen == tbl->size);
167 45200
        memcpy(tbl->buf + tbl->size, buf, len);
168 45200
        e->valuelen += len;
169 45200
        tbl->size += len;
170 45200
}
171
172
/****************************************************************************/
173
/* Public interface */
174
175
void
176 4200
VHT_NewEntry(struct vht_table *tbl)
177
{
178
179 4200
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
180 4200
        assert(tbl->maxsize <= tbl->protomax);
181 4200
        vht_trim(tbl, tbl->maxsize - VHT_ENTRY_SIZE);
182 4200
        if (tbl->maxsize - TBLSIZE(tbl) < VHT_ENTRY_SIZE) {
183
                /* Maxsize less than one entry */
184 80
                assert(tbl->maxsize < VHT_ENTRY_SIZE);
185 80
                return;
186
        }
187 4120
        vht_newentry(tbl);
188 4200
}
189
190
int
191 3560
VHT_NewEntry_Indexed(struct vht_table *tbl, unsigned idx)
192
{
193
        struct vht_entry *e, *e2;
194
        unsigned l, l2, lentry, lname, u;
195
        uint8_t tmp[48];
196
197
        /* Referenced name insertion. This has to be done carefully
198
           because the referenced name may be evicted as the result of the
199
           insertion (RFC 7541 section 4.4). */
200
201 3560
        assert(sizeof tmp >= VHT_ENTRY_SIZE);
202
203 3560
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
204 3560
        assert(tbl->maxsize <= tbl->protomax);
205
206 3560
        if (idx == 0)
207 0
                return (-1);
208
209 3560
        if (idx <= VHT_STATIC_MAX) {
210
                /* Static table reference */
211 3360
                VHT_NewEntry(tbl);
212 6720
                VHT_AppendName(tbl, static_table[idx - 1].name,
213 3360
                    static_table[idx - 1].namelen);
214 3360
                return (0);
215
        }
216 200
        idx -= VHT_STATIC_MAX + 1;
217
218 200
        if (idx >= tbl->n)
219 0
                return (-1);    /* No such index */
220 200
        assert(tbl->maxsize >= VHT_ENTRY_SIZE);
221
222 200
        e = TBLENTRY(tbl, idx);
223 200
        CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
224
225
        /* Count how many elements we can safely evict to make space
226
           without evicting the referenced entry. */
227 200
        l = 0;
228 200
        u = 0;
229 280
        while (tbl->n - 1 - u > idx &&
230 40
            tbl->maxsize - TBLSIZE(tbl) + l < VHT_ENTRY_SIZE + e->namelen) {
231 40
                e2 = TBLENTRY(tbl, tbl->n - 1 - u);
232 40
                CHECK_OBJ_NOTNULL(e2, VHT_ENTRY_MAGIC);
233 40
                l += ENTRYSIZE(e2);
234 40
                u++;
235
        }
236 200
        vht_trim(tbl, TBLSIZE(tbl) - l);
237 200
        e += u;
238 200
        assert(e == TBLENTRY(tbl, idx));
239
240 200
        if (tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + e->namelen) {
241
                /* New entry with name fits */
242 80
                vht_newentry(tbl);
243 80
                idx++;
244 80
                assert(e == TBLENTRY(tbl, idx));
245 80
                CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
246 80
                vht_appendname(tbl, tbl->buf + e->offset, e->namelen);
247 80
                return (0);
248
        }
249
250
        /* The tricky case: The referenced name will be evicted as a
251
           result of the insertion. Move the referenced element data to
252
           the end of the buffer through a local buffer. */
253
254
        /* Remove the referenced element from the entry list */
255 120
        assert(idx == tbl->n - 1);
256 120
        assert(e->offset == 0);
257 120
        lname = e->namelen;
258 120
        lentry = ENTRYLEN(e);
259 120
        FINI_OBJ(e);
260 120
        memmove(TBLENTRY(tbl, 1), TBLENTRY(tbl, 0), (tbl->n - 1) * sizeof *e);
261 120
        tbl->n--;
262
263
        /* Shift the referenced element last using a temporary buffer. */
264 120
        l = 0;
265 280
        while (l < lentry) {
266 160
                l2 = lentry - l;
267 160
                if (l2 > sizeof tmp)
268 40
                        l2 = sizeof tmp;
269 160
                memcpy(tmp, tbl->buf, l2);
270 160
                memmove(tbl->buf, tbl->buf + l2, tbl->size - l2);
271 160
                memcpy(tbl->buf + tbl->size - l2, tmp, l2);
272 160
                l += l2;
273
        }
274 120
        assert(l == lentry);
275 120
        tbl->size -= lentry;
276
277
        /* Fix up the existing element offsets */
278 240
        for (u = 0; u < tbl->n; u++) {
279 120
                e = TBLENTRY(tbl, u);
280 120
                CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
281 120
                assert(e->offset >= lentry);
282 120
                e->offset -= lentry;
283 120
                assert(e->offset + ENTRYLEN(e) <= tbl->size);
284 120
        }
285
286
        /* Insert the new entry with the name now present at the end of
287
           the buffer. */
288 120
        assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + lname);
289 120
        tbl->n++;
290 120
        e = TBLENTRY(tbl, 0);
291 120
        INIT_OBJ(e, VHT_ENTRY_MAGIC);
292 120
        e->offset = tbl->size;
293 120
        e->namelen = lname;
294 120
        tbl->size += lname;
295
296 120
        return (0);
297 3560
}
298
299
void
300 6160
VHT_AppendName(struct vht_table *tbl, const char *buf, ssize_t len)
301
{
302
303 6160
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
304 6160
        assert(tbl->maxsize <= tbl->protomax);
305 6160
        if (len == 0)
306 0
                return;
307 6160
        AN(buf);
308 6160
        if (len < 0)
309 200
                len = strlen(buf);
310 6160
        vht_trim(tbl, tbl->maxsize - len);
311 6160
        if (tbl->n == 0)
312
                /* Max size exceeded */
313 0
                return;
314 6160
        vht_appendname(tbl, buf, len);
315 6160
}
316
317
void
318 45240
VHT_AppendValue(struct vht_table *tbl, const char *buf, ssize_t len)
319
{
320
321 45240
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
322 45240
        assert(tbl->maxsize <= tbl->protomax);
323 45240
        if (len == 0)
324 0
                return;
325 45240
        AN(buf);
326 45240
        if (len < 0)
327 400
                len = strlen(buf);
328 45240
        vht_trim(tbl, tbl->maxsize - len);
329 45240
        if (tbl->n == 0)
330
                /* Max size exceeded */
331 40
                return;
332 45200
        vht_appendvalue(tbl, buf, len);
333 45240
}
334
335
int
336 160
VHT_SetMaxTableSize(struct vht_table *tbl, size_t maxsize)
337
{
338
339 160
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
340 160
        assert(tbl->maxsize <= tbl->protomax);
341 160
        if (maxsize > tbl->protomax)
342 40
                return (-1);
343 120
        vht_trim(tbl, maxsize);
344 120
        assert(TBLSIZE(tbl) <= maxsize);
345 120
        tbl->maxsize = maxsize;
346 120
        return (0);
347 160
}
348
349
int
350 6560
VHT_SetProtoMax(struct vht_table *tbl, size_t protomax)
351
{
352
        size_t bufsize;
353
        char *buf;
354
355 6560
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
356 6560
        assert(protomax <= UINT_MAX);
357 6560
        assert(tbl->maxsize <= tbl->protomax);
358
359 6560
        if (protomax == tbl->protomax)
360 0
                return (0);
361
362 6560
        if (tbl->maxsize > protomax)
363 40
                tbl->maxsize = protomax;
364 6560
        vht_trim(tbl, tbl->maxsize);
365 6560
        assert(TBLSIZE(tbl) <= tbl->maxsize);
366
367 6560
        bufsize = PRNDUP(protomax);
368 6560
        if (bufsize == tbl->bufsize) {
369 0
                tbl->protomax = protomax;
370 0
                return (0);
371
        }
372
373 6560
        buf = malloc(bufsize);
374 6560
        if (buf == NULL)
375 0
                return (-1);
376
377 6560
        if (tbl->buf != NULL) {
378 80
                memcpy(buf, tbl->buf, tbl->size);
379 160
                memcpy(ENTRIES(buf, bufsize, tbl->n), TBLENTRIES(tbl),
380 80
                    sizeof (struct vht_entry) * tbl->n);
381 80
                free(tbl->buf);
382 80
        }
383 6560
        tbl->buf = buf;
384 6560
        tbl->bufsize = bufsize;
385 6560
        tbl->protomax = protomax;
386 6560
        return (0);
387 6560
}
388
389
const char *
390 41560
VHT_LookupName(const struct vht_table *tbl, unsigned idx, size_t *plen)
391
{
392
        struct vht_entry *e;
393
394 41560
        AN(plen);
395 41560
        *plen = 0;
396
397 41560
        if (idx == 0) {
398 40
                return (NULL);
399
        }
400 41520
        if (idx <= VHT_STATIC_MAX) {
401 29760
                *plen = static_table[idx - 1].namelen;
402 29760
                return (static_table[idx - 1].name);
403
        }
404
405 11760
        if (tbl == NULL)
406 0
                return (NULL);
407 11760
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
408
409 11760
        idx -= VHT_STATIC_MAX + 1;
410 11760
        if (idx >= tbl->n)
411 0
                return (NULL);
412
413 11760
        e = TBLENTRY(tbl, idx);
414 11760
        CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
415 11760
        assert(e->offset + e->namelen <= tbl->size);
416 11760
        *plen = e->namelen;
417 11760
        return (tbl->buf + e->offset);
418 41560
}
419
420
const char *
421 28640
VHT_LookupValue(const struct vht_table *tbl, unsigned idx, size_t *plen)
422
{
423
        struct vht_entry *e;
424
425 28640
        AN(plen);
426 28640
        *plen = 0;
427
428 28640
        if (idx == 0) {
429 40
                return (NULL);
430
        }
431 28600
        if (idx <= VHT_STATIC_MAX) {
432 8400
                *plen = static_table[idx - 1].valuelen;
433 8400
                return (static_table[idx - 1].value);
434
        }
435
436 20200
        if (tbl == NULL)
437 0
                return (NULL);
438 20200
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
439
440 20200
        idx -= VHT_STATIC_MAX + 1;
441 20200
        if (idx >= tbl->n)
442 0
                return (NULL);
443
444 20200
        e = TBLENTRY(tbl, idx);
445 20200
        CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
446 20200
        assert(e->offset + e->namelen + e->valuelen <= tbl->size);
447 20200
        *plen = e->valuelen;
448 20200
        return (tbl->buf + e->offset + e->namelen);
449 28640
}
450
451
int
452 6480
VHT_Init(struct vht_table *tbl, size_t protomax)
453
{
454
        int r;
455
456 6480
        assert(sizeof (struct vht_entry) <= VHT_ENTRY_SIZE);
457
458 6480
        AN(tbl);
459 6480
        if (protomax > UINT_MAX)
460 0
                return (-1);
461 6480
        INIT_OBJ(tbl, VHT_TABLE_MAGIC);
462 6480
        r = VHT_SetProtoMax(tbl, protomax);
463 6480
        if (r) {
464 0
                FINI_OBJ(tbl);
465 0
                return (r);
466
        }
467 6480
        tbl->maxsize = tbl->protomax;
468 6480
        return (0);
469 6480
}
470
471
void
472 6195
VHT_Fini(struct vht_table *tbl)
473
{
474
475 6195
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
476 6195
        free(tbl->buf);
477 6195
        memset(tbl, 0, sizeof *tbl);
478 6195
}
479
480
/****************************************************************************/
481
/* Internal interface */
482
483
#ifdef TABLE_TEST_DRIVER
484
485
#define VHT_DYNAMIC (VHT_STATIC_MAX + 1)
486
487
static int verbose = 0;
488
489
static int
490 880
vht_matchtable(struct vht_table *tbl, ...)
491
{
492
        va_list ap;
493
        unsigned u;
494
        int r;
495
        const char *a, *b;
496
        const struct vht_entry *e;
497
498 880
        CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
499
500 880
        va_start(ap, tbl);
501 880
        r = 0;
502 2040
        for (u = 0; u < tbl->n; u++) {
503 1160
                a = NULL;
504 1160
                b = NULL;
505 1160
                if (!r) {
506 1160
                        a = va_arg(ap, const char *);
507 1160
                        if (a == NULL) {
508 0
                                printf("Too many elements in table\n");
509 0
                                r = -1;
510 0
                        } else {
511 1160
                                b = va_arg(ap, const char *);
512 1160
                                AN(b);
513
                        }
514 1160
                }
515
516 1160
                e = TBLENTRY(tbl, u);
517 1160
                CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
518
519 1160
                if (a) {
520 1160
                        AN(b);
521 1160
                        if (e->namelen != strlen(a) ||
522 1160
                            strncmp(a, tbl->buf + e->offset, e->namelen))
523 0
                                r = -1;
524 1160
                        if (e->valuelen != strlen(b) ||
525 2320
                            strncmp(b, tbl->buf + e->offset + e->namelen,
526 1160
                             e->valuelen))
527 0
                                r = -1;
528 1160
                }
529
530 1160
                if (verbose || r)
531 0
                        printf("%2u: @%03u (\"%.*s\", \"%.*s\")",
532 0
                            u, e->offset, (int)e->namelen, tbl->buf + e->offset,
533 0
                            (int)e->valuelen, tbl->buf + e->offset +e->namelen);
534
535 1160
                if (a && (verbose || r)) {
536 0
                        AN(b);
537 0
                        printf(" %s (\"%s\", \"%s\")", (r ? "!=" : "=="), a, b);
538 0
                }
539
540 1160
                if (verbose || r)
541 0
                        printf("\n");
542 1160
        }
543 880
        if (!r) {
544 880
                a = va_arg(ap, const char *);
545 880
                if (a != NULL) {
546 0
                        printf("Missing elements in table\n");
547 0
                        r = -1;
548 0
                }
549 880
        }
550 880
        va_end(ap);
551
552 880
        if (verbose || r)
553 0
                printf("n=%d, size=%u, tblsz=%u, max=%u, pmax=%u, bufsz=%u\n",
554 0
                    tbl->n, tbl->size, TBLSIZE(tbl), tbl->maxsize,
555 0
                    tbl->protomax, tbl->bufsize);
556
557 880
        return (r);
558
}
559
560
static void
561 40
test_1(void)
562
{
563
        /* Static table */
564
565
        const char *p;
566
        size_t l;
567
568 40
        if (verbose)
569 0
                printf("Test 1:\n");
570
571
        /* 1: ':authority' -> '' */
572 40
        p = VHT_LookupName(NULL, 1, &l);
573 40
        assert(l == strlen(":authority"));
574 40
        AZ(strncmp(p, ":authority", strlen(":authority")));
575 40
        p = VHT_LookupValue(NULL, 1, &l);
576 40
        AN(p);
577 40
        AZ(l);
578
579
        /* 5: ':path' -> '/index.html' */
580 40
        p = VHT_LookupValue(NULL, 5, &l);
581 40
        assert(l == strlen("/index.html"));
582 40
        AZ(strncmp(p, "/index.html", strlen("/index.html")));
583
584
        /* 61: 'www-authenticate' -> '' */
585 40
        p = VHT_LookupName(NULL, 61, &l);
586 40
        assert(l == strlen("www-authenticate"));
587 40
        AZ(strncmp(p, "www-authenticate", strlen("www-authenticate")));
588 40
        p = VHT_LookupValue(NULL, 61, &l);
589 40
        AN(p);
590 40
        AZ(l);
591
592
        /* Test zero index */
593 40
        AZ(VHT_LookupName(NULL, 0, &l));
594 40
        AZ(l);
595 40
        AZ(VHT_LookupValue(NULL, 0, &l));
596 40
        AZ(l);
597
598 40
        printf("Test 1 finished successfully\n");
599 40
        if (verbose)
600 0
                printf("\n");
601 40
}
602
603
static void
604 40
test_2(void)
605
{
606
        /* Test filling and overflow */
607
608
        struct vht_table tbl[1];
609
610 40
        if (verbose)
611 0
                printf("Test 2:\n");
612
613 40
        AZ(VHT_Init(tbl, VHT_ENTRY_SIZE + 10));
614
615 40
        VHT_NewEntry(tbl);
616 40
        VHT_AppendName(tbl, "12345", -1);
617 40
        VHT_AppendValue(tbl, "abcde", -1);
618 40
        assert(TBLSIZE(tbl) == VHT_ENTRY_SIZE + 10);
619
        /* 0: '12345' -> 'abcde' */
620 40
        AZ(vht_matchtable(tbl, "12345", "abcde", NULL));
621
622 40
        VHT_AppendValue(tbl, "f", -1);
623 40
        AZ(vht_matchtable(tbl, NULL));
624
625 40
        VHT_NewEntry(tbl);
626 40
        AZ(vht_matchtable(tbl, "", "", NULL));
627
628 40
        VHT_Fini(tbl);
629 40
        AZ(tbl->buf);
630
631 40
        printf("Test 2 finished successfully\n");
632 40
        if (verbose)
633 0
                printf("\n");
634 40
}
635
636
static void
637 40
test_3(void)
638
{
639
        /* Test change in proto max size and dynamic max size */
640
641
        struct vht_table tbl[1];
642
643 40
        if (verbose)
644 0
                printf("Test 3:\n");
645
646 40
        AZ(VHT_Init(tbl, 4096));
647
648 40
        VHT_NewEntry(tbl);
649 40
        VHT_AppendName(tbl, "a", -1);
650 40
        VHT_AppendValue(tbl, "12345", -1);
651 40
        VHT_NewEntry(tbl);
652 40
        VHT_AppendName(tbl, "b", -1);
653 40
        VHT_AppendValue(tbl, "67890", -1);
654 40
        VHT_NewEntry(tbl);
655 40
        VHT_AppendName(tbl, "c", -1);
656 40
        VHT_AppendValue(tbl, "abcde", -1);
657 40
        AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", "a", "12345", NULL));
658
659
        /* Buffer reallocation */
660 40
        AZ(VHT_SetProtoMax(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2));
661 40
        AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", NULL));
662
663
        /* Increase table size beyond protomax */
664 40
        assert(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 + 1) == -1);
665
666
        /* Decrease by one */
667 40
        AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 - 1));
668 40
        AZ(vht_matchtable(tbl, "c", "abcde", NULL));
669
670
        /* Increase by one back to protomax */
671 40
        AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2));
672 40
        AZ(vht_matchtable(tbl, "c", "abcde", NULL));
673
674
        /* Add entry */
675 40
        VHT_NewEntry(tbl);
676 40
        VHT_AppendName(tbl, "d", -1);
677 40
        VHT_AppendValue(tbl, "ABCDE", -1);
678 40
        AZ(vht_matchtable(tbl, "d", "ABCDE", "c", "abcde", NULL));
679
680
        /* Set to zero */
681 40
        AZ(VHT_SetMaxTableSize(tbl, 0));
682 40
        AZ(vht_matchtable(tbl, NULL));
683 40
        VHT_NewEntry(tbl);
684 40
        AZ(vht_matchtable(tbl, NULL));
685
686
        /* Set protomax to zero */
687 40
        AZ(VHT_SetProtoMax(tbl, 0));
688 40
        AZ(vht_matchtable(tbl, NULL));
689 40
        VHT_NewEntry(tbl);
690 40
        AZ(vht_matchtable(tbl, NULL));
691
692 40
        VHT_Fini(tbl);
693
694 40
        printf("Test 3 finished successfully\n");
695 40
        if (verbose)
696 0
                printf("\n");
697 40
}
698
699
static void
700 40
test_4(void)
701
{
702
        /* Referenced name new entry */
703
704
        struct vht_table tbl[1];
705
        static const char longname[] =
706
            "1234567890"
707
            "1234567890"
708
            "1234567890"
709
            "1234567890"
710
            "1234567890"
711
            "1";        /* 51 bytes + VHT_ENTRY_SIZE == 83 */
712
713 40
        if (verbose)
714 0
                printf("Test 4:\n");
715
716 40
        AZ(VHT_Init(tbl, VHT_ENTRY_SIZE * 2 + 10 * 2)); /* 84 bytes */
717
718
        /* New entry indexed from static table */
719 40
        AZ(VHT_NewEntry_Indexed(tbl, 4));
720 40
        VHT_AppendValue(tbl, "12345", -1);
721 40
        AZ(vht_matchtable(tbl, ":path", "12345", NULL));
722
723
        /* New entry indexed from dynamic table */
724 40
        AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
725 40
        VHT_AppendValue(tbl, "abcde", -1);
726 40
        AZ(vht_matchtable(tbl, ":path", "abcde", ":path", "12345", NULL));
727 40
        AZ(tbl->maxsize - TBLSIZE(tbl)); /* No space left */
728
729
        /* New entry indexed from dynamic table, no overlap eviction */
730 40
        AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
731 40
        VHT_AppendValue(tbl, "ABCDE", -1);
732 40
        AZ(vht_matchtable(tbl, ":path", "ABCDE", ":path", "abcde", NULL));
733
734
        /* New entry indexed from dynamic table, overlap eviction */
735 40
        AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 1));
736 40
        AZ(vht_matchtable(tbl, ":path", "", ":path", "ABCDE", NULL));
737
738
        /* New entry indexed from dynamic table, overlap eviction with
739
           overlap larger than the copy buffer size */
740 40
        VHT_NewEntry(tbl);
741 40
        VHT_AppendName(tbl, longname, strlen(longname));
742 40
        AZ(vht_matchtable(tbl, longname, "", NULL));
743 40
        AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
744 40
        VHT_AppendValue(tbl, "2", -1);
745 40
        AZ(vht_matchtable(tbl, longname, "2", NULL));
746
747 40
        VHT_Fini(tbl);
748 40
        printf("Test 4 finished successfully\n");
749 40
        if (verbose)
750 0
                printf("\n");
751 40
}
752
753
static void
754 40
test_5(void)
755
{
756
        struct vht_table tbl[1];
757
        char buf_a[3];
758
        char buf_b[2];
759
        int i;
760
761 40
        if (verbose)
762 0
                printf("Test 5:\n");
763
764 40
        assert(sizeof buf_a > 0);
765 120
        for (i = 0; i < sizeof buf_a - 1; i++)
766 80
                buf_a[i] = 'a';
767 40
        buf_a[i++] = '\0';
768
769 40
        assert(sizeof buf_b > 0);
770 80
        for (i = 0; i < sizeof buf_b - 1; i++)
771 40
                buf_b[i] = 'b';
772 40
        buf_b[i++] = '\0';
773
774 40
        AZ(VHT_Init(tbl,
775
                3 * ((sizeof buf_a - 1)+(sizeof buf_b - 1)+VHT_ENTRY_SIZE)));
776
777 40
        VHT_NewEntry(tbl);
778 40
        VHT_AppendName(tbl, buf_a, sizeof buf_a - 1);
779 40
        VHT_AppendValue(tbl, buf_b, sizeof buf_b - 1);
780 40
        AZ(vht_matchtable(tbl, buf_a, buf_b, NULL));
781
782 40
        VHT_NewEntry(tbl);
783 40
        VHT_AppendName(tbl, buf_a, sizeof buf_a - 1);
784 40
        VHT_AppendValue(tbl, buf_b, sizeof buf_b - 1);
785 40
        AZ(vht_matchtable(tbl, buf_a, buf_b, buf_a, buf_b, NULL));
786
787 40
        VHT_NewEntry(tbl);
788 40
        VHT_AppendName(tbl, buf_a, sizeof buf_a - 1);
789 40
        VHT_AppendValue(tbl, buf_b, sizeof buf_b - 1);
790 40
        AZ(vht_matchtable(tbl, buf_a, buf_b, buf_a, buf_b, buf_a, buf_b, NULL));
791
792 40
        AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 2));
793 40
        VHT_AppendValue(tbl, buf_b, sizeof buf_b - 1);
794 40
        AZ(vht_matchtable(tbl, buf_a, buf_b, buf_a, buf_b, buf_a, buf_b, NULL));
795
796 40
        VHT_Fini(tbl);
797 40
        printf("Test 5 finished successfully\n");
798 40
        if (verbose)
799 0
                printf("\n");
800 40
}
801
802
int
803 40
main(int argc, char **argv)
804
{
805
806 40
        if (argc == 2 && !strcmp(argv[1], "-v"))
807 0
                verbose = 1;
808 40
        else if (argc != 1) {
809 0
                fprintf(stderr, "Usage: %s [-v]\n", argv[0]);
810 0
                return (1);
811
        }
812
813 40
        if (verbose) {
814 0
                printf("sizeof (struct vht_table) == %zu\n",
815
                    sizeof (struct vht_table));
816 0
                printf("sizeof (struct vht_entry) == %zu\n",
817
                    sizeof (struct vht_entry));
818 0
                printf("\n");
819 0
        }
820
821 40
        test_1();
822 40
        test_2();
823 40
        test_3();
824 40
        test_4();
825 40
        test_5();
826
827 40
        return (0);
828 40
}
829
830
#endif  /* TABLE_TEST_DRIVER */