From f9eed6c05ec6ff4f7540e9df7beb62d53ca90664 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com> Date: Sat, 28 Apr 2018 18:06:13 +0900 Subject: [PATCH] Improve perf when getting scattered stream data in certain pattern --- lib/CMakeLists.txt | 1 + lib/Makefile.am | 4 +- lib/ngtcp2_psl.c | 494 ++++++++++++++++++++++++++++++++++++++++ lib/ngtcp2_psl.h | 217 ++++++++++++++++++ lib/ngtcp2_rob.c | 272 +++++++++++++--------- lib/ngtcp2_rob.h | 20 +- tests/CMakeLists.txt | 1 + tests/Makefile.am | 2 + tests/main.c | 3 + tests/ngtcp2_psl_test.c | 131 +++++++++++ tests/ngtcp2_psl_test.h | 34 +++ tests/ngtcp2_rob_test.c | 226 ++++++++++++++++-- tests/ngtcp2_rob_test.h | 1 + 13 files changed, 1256 insertions(+), 150 deletions(-) create mode 100644 lib/ngtcp2_psl.c create mode 100644 lib/ngtcp2_psl.h create mode 100644 tests/ngtcp2_psl_test.c create mode 100644 tests/ngtcp2_psl_test.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8aa2fe0a..275d02c9 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -53,6 +53,7 @@ set(ngtcp2_SOURCES ngtcp2_ringbuf.c ngtcp2_log.c ngtcp2_cid.c + ngtcp2_psl.c ) # Public shared library diff --git a/lib/Makefile.am b/lib/Makefile.am index ea74ec49..02da35ca 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -52,7 +52,8 @@ OBJECTS = \ ngtcp2_gaptr.c \ ngtcp2_ringbuf.c \ ngtcp2_log.c \ - ngtcp2_cid.c + ngtcp2_cid.c \ + ngtcp2_psl.c HFILES = \ ngtcp2_pkt.h \ @@ -76,6 +77,7 @@ HFILES = \ ngtcp2_ringbuf.h \ ngtcp2_log.h \ ngtcp2_cid.h \ + ngtcp2_psl.h \ ngtcp2_macro.h libngtcp2_la_SOURCES = $(HFILES) $(OBJECTS) diff --git a/lib/ngtcp2_psl.c b/lib/ngtcp2_psl.c new file mode 100644 index 00000000..7414df5c --- /dev/null +++ b/lib/ngtcp2_psl.c @@ -0,0 +1,494 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_psl.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" + +int ngtcp2_psl_init(ngtcp2_psl *psl, ngtcp2_mem *mem) { + ngtcp2_psl_blk *head; + + psl->mem = mem; + psl->head = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_psl_blk)); + if (!psl->head) { + return NGTCP2_ERR_NOMEM; + } + + head = psl->head; + + head->next = NULL; + head->n = 1; + head->leaf = 1; + head->nodes[0].range.begin = UINT64_MAX; + head->nodes[0].range.end = UINT64_MAX; + head->nodes[0].data = NULL; + + return 0; +} + +/* + * free_blk frees |blk| recursively. + */ +static void free_blk(ngtcp2_psl_blk *blk, ngtcp2_mem *mem) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + free_blk(blk->nodes[i].blk, mem); + } + } + + ngtcp2_mem_free(mem, blk); +} + +void ngtcp2_psl_free(ngtcp2_psl *psl) { + if (!psl) { + return; + } + + free_blk(psl->head, psl->mem); +} + +/* + * psl_split_blk splits |blk| into 2 ngtcp2_psl_blk objects. The new + * ngtcp2_psl_blk is always the "right" block. + * + * It returns the pointer to the ngtcp2_psl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static ngtcp2_psl_blk *psl_split_blk(ngtcp2_psl *psl, ngtcp2_psl_blk *blk) { + ngtcp2_psl_blk *rblk; + + rblk = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, &blk->nodes[blk->n - rblk->n], + sizeof(ngtcp2_psl_node) * rblk->n); + + blk->n -= rblk->n; + + return rblk; +} + +/* + * psl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_split_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, size_t i) { + ngtcp2_psl_blk *lblk = blk->nodes[i].blk, *rblk; + + assert(blk->n < NGTCP2_PSL_NBLK); + + rblk = psl_split_blk(psl, lblk); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + memmove(&blk->nodes[i + 2], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + blk->nodes[i + 1].blk = rblk; + + ++blk->n; + + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + blk->nodes[i + 1].range = rblk->nodes[rblk->n - 1].range; + + return 0; +} + +/* + * psl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int psl_split_head(ngtcp2_psl *psl) { + ngtcp2_psl_blk *rblk = NULL, *lblk, *nhead = NULL; + + rblk = psl_split_blk(psl, psl->head); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + lblk = psl->head; + + nhead = ngtcp2_mem_malloc(psl->mem, sizeof(ngtcp2_psl_blk)); + if (nhead == NULL) { + ngtcp2_mem_free(psl->mem, rblk); + return NGTCP2_ERR_NOMEM; + } + nhead->next = NULL; + nhead->n = 2; + nhead->leaf = 0; + + nhead->nodes[0].range = lblk->nodes[lblk->n - 1].range; + nhead->nodes[0].blk = lblk; + nhead->nodes[1].range = rblk->nodes[rblk->n - 1].range; + nhead->nodes[1].blk = rblk; + + psl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose range is |range| with the + * associated |data| at the index of |i|. This function assumes that + * the number of nodes contained by |blk| is strictly less than + * NGTCP2_PSL_NBLK. + */ +static void insert_node(ngtcp2_psl_blk *blk, size_t i, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_node *node; + + assert(blk->n < NGTCP2_PSL_NBLK); + + memmove(&blk->nodes[i + 1], &blk->nodes[i], + sizeof(ngtcp2_psl_node) * (blk->n - i)); + + node = &blk->nodes[i]; + node->range = *range; + node->data = data; + + ++blk->n; +} + +static int range_intersect(const ngtcp2_range *a, const ngtcp2_range *b) { + return ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end); +} + +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + int rv; + + if (blk->n + 1 == NGTCP2_PSL_NBLK) { + rv = psl_split_head(psl); + if (rv != 0) { + return rv; + } + blk = psl->head; + } + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + assert(!range_intersect(&node->range, range)); + + if (blk->leaf) { + insert_node(blk, i, range, data); + if (it) { + ngtcp2_psl_it_init(it, blk, i); + } + return 0; + } + + if (node->blk->n + 1 == NGTCP2_PSL_NBLK) { + rv = psl_split_node(psl, blk, i); + if (rv != 0) { + return rv; + } + if (node->range.begin < range->begin) { + node = &blk->nodes[i + 1]; + } + } + + blk = node->blk; + } +} + +/* + * remove_node removes the node included in |blk| at the index of |i|. + */ +static void remove_node(ngtcp2_psl_blk *blk, size_t i) { + memmove(&blk->nodes[i], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * psl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |psl| by 1. + * + * This function returns the pointer to the merged block. + */ +static ngtcp2_psl_blk *psl_merge_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, + size_t i) { + ngtcp2_psl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = blk->nodes[i].blk; + rblk = blk->nodes[i + 1].blk; + + assert(lblk->n + rblk->n < NGTCP2_PSL_NBLK); + + memcpy(&lblk->nodes[lblk->n], &rblk->nodes[0], + sizeof(ngtcp2_psl_node) * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + + ngtcp2_mem_free(psl->mem, rblk); + + if (psl->head == blk && blk->n == 2) { + ngtcp2_mem_free(psl->mem, psl->head); + psl->head = lblk; + } else { + remove_node(blk, i + 1); + blk->nodes[i].range = lblk->nodes[lblk->n - 1].range; + } + + return lblk; +} + +/* + * psl_relocate_node moves the node located at the index of |i| in + * |blk| to the next block. + * + * It returns the index of the block in |blk| where the node is moved. + */ +static size_t psl_relocate_node(ngtcp2_psl *psl, ngtcp2_psl_blk *blk, + size_t i) { + ngtcp2_psl_node *node = &blk->nodes[i]; + ngtcp2_psl_node *rnode = &blk->nodes[i + 1]; + + assert(blk->n > i + 1); + + memmove(&rnode->blk->nodes[1], &rnode->blk->nodes[0], + sizeof(ngtcp2_psl_node) * rnode->blk->n); + + rnode->blk->nodes[0] = node->blk->nodes[node->blk->n - 1]; + ++rnode->blk->n; + + --node->blk->n; + + if (node->blk->n == 0) { + ngtcp2_mem_free(psl->mem, node->blk); + memmove(&blk->nodes[i], &blk->nodes[i + 1], + sizeof(ngtcp2_psl_node) * (blk->n - (i + 1))); + --blk->n; + return i; + } + + node->range = node->blk->nodes[node->blk->n - 1].range; + + return i + 1; +} + +ngtcp2_psl_it ngtcp2_psl_remove(ngtcp2_psl *psl, const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head, *lblk, *rblk; + ngtcp2_psl_node *node; + size_t i, j; + ngtcp2_psl_it it; + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin; + ++i, ++node) + ; + + if (blk->leaf) { + assert(i < blk->n); + remove_node(blk, i); + if (blk->n == i) { + ngtcp2_psl_it_init(&it, blk->next, 0); + } else { + ngtcp2_psl_it_init(&it, blk, i); + } + return it; + } + + if (ngtcp2_range_equal(&node->range, range)) { + i = psl_relocate_node(psl, blk, i); + node = &blk->nodes[i]; + } + + if (blk->n >= 2 && blk->n < NGTCP2_PSL_NBLK / 2) { + j = i == 0 ? 0 : i - 1; + + lblk = blk->nodes[j].blk; + rblk = blk->nodes[j + 1].blk; + + assert(lblk->n); + assert(rblk->n); + + if (lblk->n + rblk->n < NGTCP2_PSL_NBLK) { + blk = psl_merge_node(psl, blk, j); + } else { + blk = node->blk; + } + } else { + blk = node->blk; + } + } +} + +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < range->begin && + !range_intersect(&node->range, range); + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + ngtcp2_psl_it it = {blk, i}; + return it; + } + + blk = node->blk; + } +} + +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range) { + ngtcp2_psl_blk *blk = psl->head; + ngtcp2_psl_node *node; + size_t i; + + assert(old_range->begin <= new_range->begin); + assert(new_range->end <= old_range->end); + + for (;;) { + for (i = 0, node = &blk->nodes[i]; node->range.begin < old_range->begin; + ++i, node = &blk->nodes[i]) + ; + + if (blk->leaf) { + assert(ngtcp2_range_equal(&node->range, old_range)); + node->range = *new_range; + return; + } + + if (ngtcp2_range_equal(&node->range, old_range)) { + node->range = *new_range; + } else { + assert(!range_intersect(&node->range, old_range)); + } + + blk = node->blk; + } +} + +static void psl_print(ngtcp2_psl *psl, const ngtcp2_psl_blk *blk, + size_t level) { + size_t i; + + fprintf(stderr, "LV=%zu n=%zu\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + fprintf(stderr, " [%" PRIu64 ", %" PRIu64 ")", blk->nodes[i].range.begin, + blk->nodes[i].range.end); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + psl_print(psl, blk->nodes[i].blk, level + 1); + } +} + +void ngtcp2_psl_print(ngtcp2_psl *psl) { psl_print(psl, psl->head, 0); } + +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl) { + const ngtcp2_psl_blk *blk = psl->head; + + for (;;) { + if (blk->leaf) { + ngtcp2_psl_it it = {blk, 0}; + return it; + } + blk = blk->nodes[0].blk; + } +} + +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, + size_t i) { + it->blk = blk; + it->i = i; +} + +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it) { + return it->blk->nodes[it->i].data; +} + +void ngtcp2_psl_it_next(ngtcp2_psl_it *it) { + assert(!ngtcp2_psl_it_end(it)); + + if (++it->i == it->blk->n) { + it->blk = it->blk->next; + it->i = 0; + } +} + +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it) { + ngtcp2_range end = {UINT64_MAX, UINT64_MAX}; + return ngtcp2_range_equal(&end, &it->blk->nodes[it->i].range); +} + +const ngtcp2_range *ngtcp2_psl_it_range(const ngtcp2_psl_it *it) { + return &it->blk->nodes[it->i].range; +} diff --git a/lib/ngtcp2_psl.h b/lib/ngtcp2_psl.h new file mode 100644 index 00000000..4a900932 --- /dev/null +++ b/lib/ngtcp2_psl.h @@ -0,0 +1,217 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PSL_H +#define NGTCP2_PSL_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_range.h" + +/* + * Skip List implementation inspired by + * https://github.com/jabr/olio/blob/master/skiplist.c + * + * Removed rebalancing because it destroys the invariant that the + * range of node must be the same as the range of its last direct + * descendant. It seems that the original code also fails to maintain + * it when deleting a node. This implementation fixes these issues. + */ + +/* NGTCP2_PSL_NBLK is the maximum number of nodes which a single block + can contain. It contains normally one less nodes because we have + to allocate one empty slot when deleting the last node. */ +#define NGTCP2_PSL_NBLK 16 + +struct ngtcp2_psl_node; +typedef struct ngtcp2_psl_node ngtcp2_psl_node; + +struct ngtcp2_psl_blk; +typedef struct ngtcp2_psl_blk ngtcp2_psl_blk; + +/* + * ngtcp2_psl_node is a node which contains either ngtcp2_psl_blk or + * opaque data. If a node is an internal node, it contains + * ngtcp2_psl_blk. Otherwise, it has data. The invariant is that the + * range of internal node dictates the maximum range in its + * descendants, and the corresponding leaf node must exist. + */ +struct ngtcp2_psl_node { + ngtcp2_range range; + union { + ngtcp2_psl_blk *blk; + void *data; + }; +}; + +/* + * ngtcp2_psl_blk contains ngtcp2_psl_node objects. + */ +struct ngtcp2_psl_blk { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_psl_blk *next; + /* n is the number of nodes this object contains in nodes. */ + size_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + int leaf; + ngtcp2_psl_node nodes[NGTCP2_PSL_NBLK]; +}; + +struct ngtcp2_psl_it; +typedef struct ngtcp2_psl_it ngtcp2_psl_it; + +/* + * ngtcp2_psl_it is a forward iterator to iterate nodes. + */ +struct ngtcp2_psl_it { + const ngtcp2_psl_blk *blk; + size_t i; +}; + +struct ngtcp2_psl; +typedef struct ngtcp2_psl ngtcp2_psl; + +/* + * ngtcp2_psl is a deterministic paged skip list. + */ +struct ngtcp2_psl { + /* head points to the root block. */ + ngtcp2_psl_blk *head; + ngtcp2_mem *mem; +}; + +/* + * ngtcp2_psl_init initializes |psl|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_init(ngtcp2_psl *psl, ngtcp2_mem *mem); + +/* + * ngtcp2_psl_free frees resources allocated for |psl|. If |psl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |psl| itself. + */ +void ngtcp2_psl_free(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_insert inserts |range| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function assumes that the existing ranges do not intersect + * with |range|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_psl_insert(ngtcp2_psl *psl, ngtcp2_psl_it *it, + const ngtcp2_range *range, void *data); + +/* + * ngtcp2_psl_remove removes the |range| from |psl|. It assumes such + * the range is included in |psl|. + * + * This function returns the iterator which points to the node which + * is located at the right next of the removed node. + */ +ngtcp2_psl_it ngtcp2_psl_remove(ngtcp2_psl *psl, const ngtcp2_range *range); + +/* + * ngtcp2_psl_update_range replaces the range of nodes which has + * |old_range| with |new_range|. |old_range| must include + * |new_range|. + */ +void ngtcp2_psl_update_range(ngtcp2_psl *psl, const ngtcp2_range *old_range, + const ngtcp2_range *new_range); + +/* + * ngtcp2_psl_lower_bound returns the iterator which points to the + * first node whose range intersects with |range|. If there is no + * such node, it returns the iterator which satisfies + * ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_lower_bound(ngtcp2_psl *psl, + const ngtcp2_range *range); + +/* + * ngtcp2_psl_begin returns the iterator which points to the first + * node. If there is no node in |psl|, it returns the iterator which + * satisfies ngtcp2_psl_it_end(it) != 0. + */ +ngtcp2_psl_it ngtcp2_psl_begin(const ngtcp2_psl *psl); + +/* + * ngtcp2_psl_print prints its internal state in stderr. This + * function should be used for the debugging purpose only. + */ +void ngtcp2_psl_print(ngtcp2_psl *psl); + +/* + * ngtcp2_psl_it_init initializes |it|. + */ +void ngtcp2_psl_it_init(ngtcp2_psl_it *it, const ngtcp2_psl_blk *blk, size_t i); + +/* + * ngtcp2_psl_it_get returns the data associated to the node which + * |it| points to. If this function is called when + * ngtcp2_psl_it_end(it) returns nonzero, it returns NULL. + */ +void *ngtcp2_psl_it_get(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_next advances the iterator by one. It is undefined + * if this function is called when ngtcp2_psl_it_end(it) returns + * nonzero. + */ +void ngtcp2_psl_it_next(ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +int ngtcp2_psl_it_end(const ngtcp2_psl_it *it); + +/* + * ngtcp2_psl_range returns the range of the node which |it| points + * to. It is OK to call this function when ngtcp2_psl_it_end(it) + * returns nonzero. In this case, this function returns {UINT64_MAX, + * UINT64_MAX}. + */ +const ngtcp2_range *ngtcp2_psl_it_range(const ngtcp2_psl_it *it); + +#endif /* NGTCP2_PSL_H */ diff --git a/lib/ngtcp2_rob.c b/lib/ngtcp2_rob.c index 6d83c2c6..18cab144 100644 --- a/lib/ngtcp2_rob.c +++ b/lib/ngtcp2_rob.c @@ -36,8 +36,8 @@ int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, return NGTCP2_ERR_NOMEM; } - ngtcp2_range_init(&(*pg)->range, begin, end); - (*pg)->next = NULL; + (*pg)->range.begin = begin; + (*pg)->range.end = end; return 0; } @@ -53,10 +53,10 @@ int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, return NGTCP2_ERR_NOMEM; } + (*pd)->range.begin = offset; + (*pd)->range.end = offset + chunk; (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data); (*pd)->end = (*pd)->begin + chunk; - (*pd)->offset = offset; - (*pd)->next = NULL; return 0; } @@ -67,85 +67,96 @@ void ngtcp2_rob_data_del(ngtcp2_rob_data *d, ngtcp2_mem *mem) { int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, ngtcp2_mem *mem) { int rv; + ngtcp2_rob_gap *g; - rv = ngtcp2_rob_gap_new(&rob->gap, 0, UINT64_MAX, mem); + rv = ngtcp2_psl_init(&rob->gappsl, mem); if (rv != 0) { - return rv; + goto fail_gappsl_psl_init; + } + + rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); + if (rv != 0) { + goto fail_rob_gap_new; + } + + rv = ngtcp2_psl_insert(&rob->gappsl, NULL, &g->range, g); + if (rv != 0) { + goto fail_gappsl_psl_insert; + } + + rv = ngtcp2_psl_init(&rob->datapsl, mem); + if (rv != 0) { + goto fail_datapsl_psl_init; } - rob->data = NULL; rob->chunk = chunk; rob->mem = mem; return 0; + +fail_datapsl_psl_init: +fail_gappsl_psl_insert: + ngtcp2_rob_gap_del(g, mem); +fail_rob_gap_new: + ngtcp2_psl_free(&rob->gappsl); +fail_gappsl_psl_init: + return rv; } void ngtcp2_rob_free(ngtcp2_rob *rob) { - ngtcp2_rob_gap *g, *ng; - ngtcp2_rob_data *d, *nd; + static const ngtcp2_range r = {0, 0}; + ngtcp2_psl_it it; if (rob == NULL) { return; } - for (g = rob->gap; g;) { - ng = g->next; - ngtcp2_rob_gap_del(g, rob->mem); - g = ng; + for (it = ngtcp2_psl_lower_bound(&rob->datapsl, &r); !ngtcp2_psl_it_end(&it); + ngtcp2_psl_it_next(&it)) { + ngtcp2_rob_data_del(ngtcp2_psl_it_get(&it), rob->mem); } - for (d = rob->data; d;) { - nd = d->next; - ngtcp2_rob_data_del(d, rob->mem); - d = nd; - } -} - -static void insert_gap(ngtcp2_rob_gap **pg, ngtcp2_rob_gap *g) { - g->next = (*pg)->next; - (*pg)->next = g; -} -static void remove_gap(ngtcp2_rob_gap **pg, ngtcp2_mem *mem) { - ngtcp2_rob_gap *g = *pg; - *pg = g->next; - ngtcp2_rob_gap_del(g, mem); -} + for (it = ngtcp2_psl_lower_bound(&rob->gappsl, &r); !ngtcp2_psl_it_end(&it); + ngtcp2_psl_it_next(&it)) { + ngtcp2_rob_gap_del(ngtcp2_psl_it_get(&it), rob->mem); + } -static void remove_data(ngtcp2_rob_data **pd, ngtcp2_mem *mem) { - ngtcp2_rob_data *d = *pd; - *pd = d->next; - ngtcp2_rob_data_del(d, mem); + ngtcp2_psl_free(&rob->datapsl); + ngtcp2_psl_free(&rob->gappsl); } -static int rob_write_data(ngtcp2_rob *rob, ngtcp2_rob_data **pd, - uint64_t offset, const uint8_t *data, size_t len) { +static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t len) { size_t n; int rv; - ngtcp2_rob_data *nd; + ngtcp2_rob_data *d; + ngtcp2_range range = {offset, offset + len}; + ngtcp2_psl_it it; - for (;;) { - if (*pd == NULL || offset < (*pd)->offset) { - rv = ngtcp2_rob_data_new(&nd, (offset / rob->chunk) * rob->chunk, + for (it = ngtcp2_psl_lower_bound(&rob->datapsl, &range); len; + ngtcp2_psl_it_next(&it)) { + d = ngtcp2_psl_it_get(&it); + + if (d == NULL || offset < d->range.begin) { + rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk, rob->chunk, rob->mem); if (rv != 0) { return rv; } - /* insert before *pd */ - nd->next = *pd; - *pd = nd; - } else if ((*pd)->offset + rob->chunk < offset) { - pd = &(*pd)->next; - continue; + + rv = ngtcp2_psl_insert(&rob->datapsl, &it, &d->range, d); + if (rv != 0) { + ngtcp2_rob_data_del(d, rob->mem); + return rv; + } + } else if (d->range.begin + rob->chunk < offset) { + assert(0); } - n = ngtcp2_min(len, (*pd)->offset + rob->chunk - offset); - memcpy((*pd)->begin + (offset - (*pd)->offset), data, n); + n = ngtcp2_min(len, d->range.begin + rob->chunk - offset); + memcpy(d->begin + (offset - d->range.begin), data, n); offset += n; data += n; len -= n; - if (len == 0) { - return 0; - } - pd = &(*pd)->next; } return 0; @@ -154,111 +165,150 @@ static int rob_write_data(ngtcp2_rob *rob, ngtcp2_rob_data **pd, int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, size_t datalen) { int rv; - ngtcp2_rob_gap **pg; + ngtcp2_rob_gap *g; ngtcp2_range m, l, r, q = {offset, offset + datalen}; - ngtcp2_rob_data **pd = &rob->data; - - for (pg = &rob->gap; *pg;) { - m = ngtcp2_range_intersect(&q, &(*pg)->range); - if (ngtcp2_range_len(&m)) { - if (ngtcp2_range_equal(&(*pg)->range, &m)) { - remove_gap(pg, rob->mem); - rv = rob_write_data(rob, pd, m.begin, data + (m.begin - offset), - ngtcp2_range_len(&m)); + ngtcp2_psl_it it; + + it = ngtcp2_psl_lower_bound(&rob->gappsl, &q); + + for (; !ngtcp2_psl_it_end(&it);) { + g = ngtcp2_psl_it_get(&it); + + m = ngtcp2_range_intersect(&q, &g->range); + if (!ngtcp2_range_len(&m)) { + break; + } + if (ngtcp2_range_equal(&g->range, &m)) { + it = ngtcp2_psl_remove(&rob->gappsl, &g->range); + ngtcp2_rob_gap_del(g, rob->mem); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; + } + + continue; + } + ngtcp2_range_cut(&l, &r, &g->range, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_psl_update_range(&rob->gappsl, &g->range, &l); + g->range = l; + + if (ngtcp2_range_len(&r)) { + ngtcp2_rob_gap *ng; + rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); if (rv != 0) { return rv; } - continue; - } - ngtcp2_range_cut(&l, &r, &(*pg)->range, &m); - if (ngtcp2_range_len(&l)) { - (*pg)->range = l; - - if (ngtcp2_range_len(&r)) { - ngtcp2_rob_gap *ng; - rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); - if (rv != 0) { - return rv; - } - insert_gap(pg, ng); - pg = &((*pg)->next); + rv = ngtcp2_psl_insert(&rob->gappsl, &it, &ng->range, ng); + if (rv != 0) { + ngtcp2_rob_gap_del(ng, rob->mem); + return rv; } - } else if (ngtcp2_range_len(&r)) { - (*pg)->range = r; - } - rv = rob_write_data(rob, pd, m.begin, data + (m.begin - offset), - ngtcp2_range_len(&m)); - if (rv != 0) { - return rv; } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_psl_update_range(&rob->gappsl, &g->range, &r); + g->range = r; } - if (ngtcp2_range_not_after(&q, &(*pg)->range)) { - break; + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; } - pg = &((*pg)->next); + ngtcp2_psl_it_next(&it); } return 0; } void ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { - ngtcp2_rob_gap **pg; - ngtcp2_rob_data **pd; + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_psl_it it; + + it = ngtcp2_psl_begin(&rob->gappsl); - for (pg = &rob->gap; *pg;) { - if (offset <= (*pg)->range.begin) { + for (; !ngtcp2_psl_it_end(&it);) { + g = ngtcp2_psl_it_get(&it); + if (offset <= g->range.begin) { break; } - if (offset < (*pg)->range.end) { - (*pg)->range.begin = offset; + if (offset < g->range.end) { + ngtcp2_range r = {offset, g->range.end}; + ngtcp2_psl_update_range(&rob->gappsl, &g->range, &r); + g->range.begin = offset; break; } - remove_gap(pg, rob->mem); + it = ngtcp2_psl_remove(&rob->gappsl, &g->range); + ngtcp2_rob_gap_del(g, rob->mem); } - for (pd = &rob->data; *pd;) { - if (offset <= (*pd)->offset) { - return; - } - if (offset < (*pd)->offset + rob->chunk) { + it = ngtcp2_psl_begin(&rob->datapsl); + + for (; !ngtcp2_psl_it_end(&it);) { + d = ngtcp2_psl_it_get(&it); + if (offset < d->range.begin + rob->chunk) { return; } - remove_data(pd, rob->mem); + it = ngtcp2_psl_remove(&rob->datapsl, &d->range); + ngtcp2_rob_data_del(d, rob->mem); } } size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, uint64_t offset) { - ngtcp2_rob_gap *g = rob->gap; - ngtcp2_rob_data *d = rob->data; + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_psl_it it; + + it = ngtcp2_psl_begin(&rob->gappsl); + if (ngtcp2_psl_it_end(&it)) { + return 0; + } + + g = ngtcp2_psl_it_get(&it); if (g->range.begin <= offset) { return 0; } + it = ngtcp2_psl_begin(&rob->datapsl); + d = ngtcp2_psl_it_get(&it); + assert(d); - assert(d->offset <= offset); - assert(offset < d->offset + rob->chunk); + assert(d->range.begin <= offset); + assert(offset < d->range.begin + rob->chunk); - *pdest = d->begin + (offset - d->offset); + *pdest = d->begin + (offset - d->range.begin); - return ngtcp2_min(g->range.begin, d->offset + rob->chunk) - offset; + return ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) - offset; } void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { - ngtcp2_rob_data **pd = &rob->data; + ngtcp2_psl_it it; + ngtcp2_rob_data *d; - assert(*pd); + it = ngtcp2_psl_begin(&rob->datapsl); + d = ngtcp2_psl_it_get(&it); + + assert(d); - if (offset + len < (*pd)->offset + rob->chunk) { + if (offset + len < d->range.begin + rob->chunk) { return; } - remove_data(pd, rob->mem); + ngtcp2_psl_remove(&rob->datapsl, &d->range); + ngtcp2_rob_data_del(d, rob->mem); } uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) { - if (rob->gap) { - return rob->gap->range.begin; + ngtcp2_psl_it it = ngtcp2_psl_begin(&rob->gappsl); + ngtcp2_rob_gap *g; + + if (ngtcp2_psl_it_end(&it)) { + return UINT64_MAX; } - return UINT64_MAX; + + g = ngtcp2_psl_it_get(&it); + + return g->range.begin; } diff --git a/lib/ngtcp2_rob.h b/lib/ngtcp2_rob.h index 42600a4b..c514c8c0 100644 --- a/lib/ngtcp2_rob.h +++ b/lib/ngtcp2_rob.h @@ -33,6 +33,7 @@ #include "ngtcp2_mem.h" #include "ngtcp2_range.h" +#include "ngtcp2_psl.h" struct ngtcp2_rob_gap; typedef struct ngtcp2_rob_gap ngtcp2_rob_gap; @@ -42,10 +43,6 @@ typedef struct ngtcp2_rob_gap ngtcp2_rob_gap; * data that is not received yet. */ struct ngtcp2_rob_gap { - /* next points to the next gap. This singly linked list is ordered - by range.begin in the increasing order, and they never - overlap. */ - ngtcp2_rob_gap *next; /* range is the range of this gap. */ ngtcp2_range range; }; @@ -79,15 +76,12 @@ typedef struct ngtcp2_rob_data ngtcp2_rob_data; * ngtcp2_rob_data holds the buffered stream data. */ struct ngtcp2_rob_data { - /* next points to the next data. This singly linked list is ordered - by offset in the increasing order, and they never overlap. */ - ngtcp2_rob_data *next; + /* range is the range of this gap. */ + ngtcp2_range range; /* begin points to the buffer. */ uint8_t *begin; /* end points to the one beyond of the last byte of the buffer */ uint8_t *end; - /* offset is a stream offset of begin. */ - uint64_t offset; }; /* @@ -119,12 +113,12 @@ void ngtcp2_rob_data_del(ngtcp2_rob_data *d, ngtcp2_mem *mem); * received in out of order. */ typedef struct { - /* gap maintains the range of offset which is not received + /* gappsl maintains the range of offset which is not received yet. Initially, its range is [0, UINT64_MAX). */ - ngtcp2_rob_gap *gap; - /* data maintains the list of buffers which store received data + ngtcp2_psl gappsl; + /* datapsl maintains the list of buffers which store received data ordered by stream offset. */ - ngtcp2_rob_data *data; + ngtcp2_psl datapsl; /* mem is custom memory allocator */ ngtcp2_mem *mem; /* chunk is the size of each buffer in data field */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a61b6b7d..32265e0f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,6 +44,7 @@ if(HAVE_CUNIT) ngtcp2_ringbuf_test.c ngtcp2_conv_test.c ngtcp2_test_helper.c + ngtcp2_psl_test.c ) add_executable(main EXCLUDE_FROM_ALL diff --git a/tests/Makefile.am b/tests/Makefile.am index 8e581a2d..b9aeeae2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -39,6 +39,7 @@ OBJECTS = \ ngtcp2_conn_test.c \ ngtcp2_ringbuf_test.c \ ngtcp2_conv_test.c \ + ngtcp2_psl_test.c \ ngtcp2_test_helper.c HFILES= \ ngtcp2_pkt_test.h \ @@ -52,6 +53,7 @@ HFILES= \ ngtcp2_conn_test.h \ ngtcp2_ringbuf_test.h \ ngtcp2_conv_test.h \ + ngtcp2_psl_test.h \ ngtcp2_test_helper.h main_SOURCES = $(HFILES) $(OBJECTS) diff --git a/tests/main.c b/tests/main.c index ed29e636..071233ea 100644 --- a/tests/main.c +++ b/tests/main.c @@ -41,6 +41,7 @@ #include "ngtcp2_conn_test.h" #include "ngtcp2_ringbuf_test.h" #include "ngtcp2_conv_test.h" +#include "ngtcp2_psl_test.h" static int init_suite1(void) { return 0; } @@ -125,7 +126,9 @@ int main() { !CU_add_test(pSuite, "range_intersect", test_ngtcp2_range_intersect) || !CU_add_test(pSuite, "range_cut", test_ngtcp2_range_cut) || !CU_add_test(pSuite, "range_not_after", test_ngtcp2_range_not_after) || + !CU_add_test(pSuite, "psl_insert", test_ngtcp2_psl_insert) || !CU_add_test(pSuite, "rob_push", test_ngtcp2_rob_push) || + !CU_add_test(pSuite, "rob_push_random", test_ngtcp2_rob_push_random) || !CU_add_test(pSuite, "rob_data_at", test_ngtcp2_rob_data_at) || !CU_add_test(pSuite, "rob_remove_prefix", test_ngtcp2_rob_remove_prefix) || diff --git a/tests/ngtcp2_psl_test.c b/tests/ngtcp2_psl_test.c new file mode 100644 index 00000000..48702aa2 --- /dev/null +++ b/tests/ngtcp2_psl_test.c @@ -0,0 +1,131 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_psl_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_psl.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_psl_insert(void) { + static const ngtcp2_range keys[] = { + {10, 11}, {3, 4}, {8, 9}, {11, 12}, {16, 17}, {12, 13}, + {1, 2}, {5, 6}, {4, 5}, {0, 1}, {13, 14}, {7, 8}, + {9, 10}, {2, 3}, {14, 15}, {6, 7}, {15, 16}}; + ngtcp2_psl psl; + ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + const ngtcp2_range *pr; + ngtcp2_range r; + ngtcp2_psl_it it; + + ngtcp2_psl_init(&psl, mem); + + for (i = 0; i < arraylen(keys); ++i) { + ngtcp2_psl_insert(&psl, NULL, &keys[i], NULL); + it = ngtcp2_psl_lower_bound(&psl, &keys[i]); + + CU_ASSERT(ngtcp2_range_equal(&keys[i], ngtcp2_psl_it_range(&it))); + } + + for (i = 0; i < arraylen(keys); ++i) { + ngtcp2_psl_remove(&psl, &keys[i]); + it = ngtcp2_psl_lower_bound(&psl, &keys[i]); + pr = ngtcp2_psl_it_range(&it); + + CU_ASSERT(keys[i].end <= pr->begin); + } + + ngtcp2_psl_free(&psl); + + /* check the case that the right end range is removed */ + ngtcp2_psl_init(&psl, mem); + + for (i = 0; i < 16; ++i) { + ngtcp2_range_init(&r, i, i + 1); + ngtcp2_psl_insert(&psl, NULL, &r, NULL); + } + + /* Removing [7, 8) requires relocation */ + ngtcp2_range_init(&r, 7, 8); + it = ngtcp2_psl_remove(&psl, &r); + pr = ngtcp2_psl_it_range(&it); + + CU_ASSERT(8 == pr->begin); + CU_ASSERT(9 == pr->end); + + it = ngtcp2_psl_lower_bound(&psl, &r); + pr = ngtcp2_psl_it_range(&it); + + CU_ASSERT(8 == pr->begin); + CU_ASSERT(9 == pr->end); + + pr = &psl.head->nodes[0].range; + + CU_ASSERT(6 == pr->begin); + CU_ASSERT(7 == pr->end); + + ngtcp2_psl_free(&psl); + + /* check merge node (head) */ + ngtcp2_psl_init(&psl, mem); + + for (i = 0; i < 15; ++i) { + ngtcp2_range_init(&r, i, i + 1); + ngtcp2_psl_insert(&psl, NULL, &r, NULL); + } + + /* Removing these 2 nodes kicks merging 2 nodes under head */ + ngtcp2_range_init(&r, 6, 7); + ngtcp2_psl_remove(&psl, &r); + + ngtcp2_range_init(&r, 5, 6); + ngtcp2_psl_remove(&psl, &r); + + CU_ASSERT(14 == psl.head->n); + + ngtcp2_psl_free(&psl); + + /* check merge node (non head) */ + ngtcp2_psl_init(&psl, mem); + + for (i = 0; i < 15 + 8; ++i) { + ngtcp2_range_init(&r, i, i + 1); + ngtcp2_psl_insert(&psl, NULL, &r, NULL); + } + + /* Removing these 2 nodes kicks merging 2 nodes */ + ngtcp2_range_init(&r, 6, 7); + ngtcp2_psl_remove(&psl, &r); + + ngtcp2_range_init(&r, 5, 6); + ngtcp2_psl_remove(&psl, &r); + + CU_ASSERT(2 == psl.head->n); + CU_ASSERT(14 == psl.head->nodes[0].blk->n); + CU_ASSERT(8 == psl.head->nodes[1].blk->n); + + ngtcp2_psl_free(&psl); +} diff --git a/tests/ngtcp2_psl_test.h b/tests/ngtcp2_psl_test.h new file mode 100644 index 00000000..6e491dae --- /dev/null +++ b/tests/ngtcp2_psl_test.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PSL_TEST_H +#define NGTCP2_PSL_TEST_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_psl_insert(void); + +#endif /* NGTCP2_PSL_TEST_H */ diff --git a/tests/ngtcp2_rob_test.c b/tests/ngtcp2_rob_test.c index dc419966..61731042 100644 --- a/tests/ngtcp2_rob_test.c +++ b/tests/ngtcp2_rob_test.c @@ -29,6 +29,7 @@ #include "ngtcp2_rob.h" #include "ngtcp2_test_helper.h" #include "ngtcp2_mem.h" +#include "ngtcp2_macro.h" void test_ngtcp2_rob_push(void) { ngtcp2_mem *mem = ngtcp2_mem_default(); @@ -36,6 +37,7 @@ void test_ngtcp2_rob_push(void) { int rv; uint8_t data[256]; ngtcp2_rob_gap *g; + ngtcp2_psl_it it; /* Check range overlapping */ ngtcp2_rob_init(&rob, 64, mem); @@ -44,27 +46,34 @@ void test_ngtcp2_rob_push(void) { CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(0 == g->range.begin); CU_ASSERT(34567 == g->range.end); - g = g->next; + ngtcp2_psl_it_next(&it); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(34567 + 145 == g->range.begin); CU_ASSERT(UINT64_MAX == g->range.end); - CU_ASSERT(NULL == g->next); + + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); rv = ngtcp2_rob_push(&rob, 34565, data, 1); CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(0 == g->range.begin); CU_ASSERT(34565 == g->range.end); - g = g->next; + ngtcp2_psl_it_next(&it); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(34566 == g->range.begin); CU_ASSERT(34567 == g->range.end); @@ -73,12 +82,14 @@ void test_ngtcp2_rob_push(void) { CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(0 == g->range.begin); CU_ASSERT(34563 == g->range.end); - g = g->next; + ngtcp2_psl_it_next(&it); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(34564 == g->range.begin); CU_ASSERT(34565 == g->range.end); @@ -87,16 +98,21 @@ void test_ngtcp2_rob_push(void) { CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(0 == g->range.begin); CU_ASSERT(34561 == g->range.end); - g = g->next; + ngtcp2_psl_it_next(&it); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(34567 + 145 == g->range.begin); CU_ASSERT(UINT64_MAX == g->range.end); - CU_ASSERT(NULL == g->next); + + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); ngtcp2_rob_free(&rob); @@ -107,11 +123,15 @@ void test_ngtcp2_rob_push(void) { CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(123 == g->range.begin); CU_ASSERT(UINT64_MAX == g->range.end); - CU_ASSERT(NULL == g->next); + + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); ngtcp2_rob_free(&rob); @@ -122,11 +142,137 @@ void test_ngtcp2_rob_push(void) { CU_ASSERT(0 == rv); - g = rob.gap; + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); CU_ASSERT(0 == g->range.begin); CU_ASSERT(UINT64_MAX - 123 == g->range.end); - CU_ASSERT(NULL == g->next); + + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); + + ngtcp2_rob_free(&rob); +} + +static ngtcp2_range randkeys[] = { + {25996, 26260}, {9431, 9555}, {9113, 9417}, {2992, 3408}, + {35761, 36019}, {38891, 39113}, {30074, 30325}, {9525, 9953}, + {31708, 31944}, {24554, 24864}, {13097, 13472}, {47253, 47400}, + {18424, 18742}, {4618, 4889}, {40871, 41076}, {17642, 18068}, + {47496, 47588}, {1226, 1283}, {17904, 18248}, {9221, 9488}, + {8621, 8773}, {27912, 28344}, {5878, 6121}, {37336, 37545}, + {15403, 15557}, {29314, 29450}, {2342, 2595}, {34000, 34356}, + {46428, 46828}, {40624, 40703}, {47014, 47319}, {13353, 13635}, + {14466, 14682}, {22446, 22654}, {10035, 10140}, {1005, 1410}, + {3741, 4133}, {45734, 46053}, {7954, 8214}, {32666, 32796}, + {45236, 45531}, {32100, 32501}, {25466, 25850}, {2845, 3179}, + {23525, 23991}, {46367, 46459}, {37712, 38164}, {8506, 8680}, + {31702, 31752}, {33364, 33825}, {14284, 14614}, {22928, 23344}, + {29058, 29155}, {36639, 37014}, {29133, 29445}, {31071, 31478}, + {40074, 40370}, {1263, 1383}, {7908, 8181}, {40426, 40716}, + {4830, 5053}, {38241, 38645}, {51197, 51401}, {36180, 36301}, + {14920, 15262}, {5707, 5882}, {32697, 32948}, {42324, 42791}, + {1543, 1732}, {11037, 11395}, {36534, 36707}, {26093, 26322}, + {41862, 42213}, {1373, 1745}, {31322, 31706}, {45474, 45851}, + {19333, 19701}, {49172, 49524}, {10641, 10932}, {17459, 17630}, + {5560, 5936}, {7657, 7988}, {3300, 3357}, {2496, 2600}, + {46018, 46173}, {43127, 43239}, {48949, 49036}, {45094, 45412}, + {8405, 8738}, {8687, 9168}, {41405, 41759}, {22014, 22474}, + {16097, 16426}, {29611, 29931}, {46054, 46250}, {26305, 26545}, + {13696, 13964}, {26899, 26981}, {30797, 30936}, {34125, 34235}, + {50016, 50058}, {46775, 47005}, {4891, 5106}, {12720, 12994}, + {44623, 44967}, {33597, 34060}, {50796, 51295}, {18862, 19242}, + {36166, 36249}, {22237, 22583}, {18188, 18586}, {21376, 21447}, + {49563, 49800}, {10121, 10272}, {39156, 39275}, {17609, 17866}, + {47609, 47829}, {34311, 34631}, {2144, 2433}, {34692, 34824}, + {8309, 8476}, {26969, 27447}, {40651, 40952}, {11906, 12116}, + {22467, 22864}, {35535, 35941}, {33061, 33259}, {21006, 21364}, + {15212, 15504}, {6954, 7356}, {6126, 6405}, {29268, 29514}, + {35221, 35505}, {4163, 4350}, {17374, 17519}, {16170, 16511}, + {37142, 37440}, {6288, 6556}, {27795, 28092}, {35381, 35476}, + {1186, 1455}, {39834, 40197}, {3471, 3906}, {46871, 47242}, + {40258, 40406}, {0, 306}, {31852, 32133}, {23314, 23408}, + {37494, 37625}, {48742, 48990}, {37616, 37905}, {18615, 18991}, + {2561, 2921}, {47767, 48139}, {39616, 39792}, {44791, 45046}, + {2770, 3067}, {16697, 17083}, {9216, 9427}, {37661, 37774}, + {14666, 14976}, {31547, 31819}, {36052, 36356}, {34989, 35285}, + {1651, 2028}, {36264, 36515}, {10257, 10551}, {24381, 24628}, + {28428, 28726}, {4242, 4576}, {44972, 45107}, {12970, 13213}, + {19539, 19828}, {42541, 42763}, {20349, 20630}, {20138, 20418}, + {10884, 11138}, {2717, 2908}, {8292, 8399}, {712, 1101}, + {44451, 44741}, {28660, 28946}, {40955, 41253}, {29424, 29864}, + {14177, 14446}, {30219, 30632}, {24757, 25012}, {47991, 48306}, + {42054, 42252}, {3984, 4419}, {42304, 42506}, {7160, 7543}, + {2004, 2152}, {9777, 10105}, {15724, 16008}, {11263, 11573}, + {15066, 15239}, {12108, 12336}, {17138, 17570}, {30472, 30714}, + {41197, 41294}, {24294, 24496}, {17371, 17514}, {11426, 11749}, + {25223, 25474}, {18083, 18345}, {27611, 27919}, {8116, 8261}, + {40317, 40373}, {46652, 47026}, {18082, 18151}, {19808, 19970}, + {46627, 46885}, {11646, 11789}, {1498, 1687}, {35907, 36081}, + {36340, 36593}, {1255, 1311}, {43485, 43551}, {6586, 6895}, + {10331, 10467}, {26803, 26998}, {14007, 14360}, {35951, 36120}, + {37327, 37592}, {35419, 35724}, {50379, 50514}, {37251, 37489}, + {27313, 27752}, {27502, 27845}, {36608, 36732}, {41751, 42057}, + {19118, 19267}, {16529, 16926}, {49794, 50066}, {37378, 37699}, + {7440, 7552}, {10418, 10650}, {50184, 50635}, {44350, 44579}, + {8178, 8502}, {33838, 34017}, {11582, 11864}, {11756, 11785}, + {42136, 42328}, {39404, 39545}, {13924, 14209}, {29411, 29627}, + {10836, 11139}, {40332, 40598}, {26097, 26561}, {5422, 5512}, + {30687, 30849}, {4399, 4726}, {50679, 50762}, {41224, 41439}, + {46023, 46129}, {22690, 23010}, {37920, 38085}, {25885, 26249}, + {51047, 51185}, {21508, 21904}, {6731, 7010}, {38144, 38493}, + {47648, 47886}, {120, 603}, {49964, 50182}, {43503, 43765}, + {24092, 24436}, {19204, 19509}, {19668, 19930}, {6815, 6963}, + {10552, 10775}, {949, 1239}, {36976, 37348}, {34806, 34901}, + {19939, 20308}, {42245, 42329}, {42700, 43067}, {13821, 14054}, + {28109, 28331}, {32929, 33212}, {23736, 24036}, {31969, 32240}, + {12326, 12612}, {5999, 6132}, {42871, 43283}, {33204, 33496}, + {5757, 5991}, {46826, 46927}, {4994, 5278}, {47371, 47713}, + {20886, 21106}, {38457, 38794}, {48451, 48789}, {34146, 34343}, + {45911, 46248}, {48215, 48615}, {43970, 44131}, {30886, 31216}, + {50135, 50292}, {3726, 3854}, {39041, 39408}, {48617, 48756}, + {46205, 46590}, {39766, 39923}, {20835, 21106}, {43716, 44066}, + {45665, 45789}, {12549, 12755}, {23366, 23752}, {17864, 17942}, + {28288, 28528}, {2744, 2941}, {49355, 49605}, {34527, 34816}, + {23092, 23447}, {5832, 5912}, {21146, 21478}, {30784, 30884}, + {28221, 28469}, {34944, 35047}, {23956, 24126}, {7538, 7890}, + {32496, 32803}, {16404, 16607}, {37968, 38277}, {7399, 7574}, + {28605, 28842}, {50454, 50851}, {20581, 20845}, {21395, 21705}, + {50726, 50871}, {11953, 12278}, {533, 822}, {5298, 5658}, + {48707, 48914}, {21760, 22223}, {1889, 2146}, {6409, 6842}, + {44094, 44473}, {18003, 18336}, {41550, 41926}, {50042, 50136}, + {38646, 38835}, {5425, 5693}, {48967, 49383}, {376, 596}, + {47514, 47704}, {43238, 43663}, {25440, 25655}, {25652, 26050}, + {16909, 17232}, {41312, 41490}, {5909, 6049}, {3153, 3523}, + {27877, 28046}, {26715, 26810}, {10031, 10108}, {32282, 32620}, + {8934, 9219}, {5133, 5493}, {26666, 26787}, {45324, 45630}, + {34880, 35008}, {20823, 20920}, {39571, 39704}, {15523, 15869}, + {4360, 4637}, {46199, 46384}, {35991, 36242}, {46852, 46931}, + {39218, 39644}, {11785, 12029}, {27225, 27366}, {29820, 30097}, + {36778, 37072}, {9871, 10255}, {51065, 51208}, {38775, 39102}, + {39446, 39712}, {33856, 34083}, {28853, 29289}, {526, 666}, + {37510, 37697}, {13455, 13855}, {25648, 25691}, {10694, 11041}, + {26441, 26889}, {18821, 19058}, {3357, 3590}, {15915, 16276}, + {37706, 37934}, {24970, 25281}, {43951, 44124}, {35874, 36128}, +}; + +void test_ngtcp2_rob_push_random(void) { + ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_rob rob; + int rv; + uint8_t data[512]; + size_t i; + + ngtcp2_rob_init(&rob, 1024 * 1024, mem); + for (i = 0; i < arraylen(randkeys); ++i) { + rv = ngtcp2_rob_push(&rob, randkeys[i].begin, &data[0], + ngtcp2_range_len(&randkeys[i])); + + CU_ASSERT(0 == rv); + } + + CU_ASSERT(51401 == ngtcp2_rob_first_gap_offset(&rob)); ngtcp2_rob_free(&rob); } @@ -140,6 +286,8 @@ void test_ngtcp2_rob_data_at(void) { const uint8_t *p; size_t len; ngtcp2_rob_data *d; + ngtcp2_psl_it it; + ngtcp2_rob_gap *g; for (i = 0; i < sizeof(data); ++i) { data[i] = (uint8_t)i; @@ -247,14 +395,20 @@ void test_ngtcp2_rob_data_at(void) { CU_ASSERT(0 == rv); - d = rob.data->next; + it = ngtcp2_psl_begin(&rob.datapsl); + ngtcp2_psl_it_next(&it); + d = ngtcp2_psl_it_get(&it); + + CU_ASSERT(16 == d->range.begin); - CU_ASSERT(16 == d->offset); + ngtcp2_psl_it_next(&it); + d = ngtcp2_psl_it_get(&it); - d = d->next; + CU_ASSERT(32 == d->range.begin); - CU_ASSERT(32 == d->offset); - CU_ASSERT(NULL == d->next); + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); ngtcp2_rob_free(&rob); @@ -281,8 +435,14 @@ void test_ngtcp2_rob_data_at(void) { ngtcp2_rob_pop(&rob, i * 16, len); } - CU_ASSERT(256 == rob.gap->range.begin); - CU_ASSERT(NULL == rob.data); + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); + + CU_ASSERT(256 == g->range.begin); + + it = ngtcp2_psl_begin(&rob.datapsl); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); ngtcp2_rob_free(&rob); @@ -340,6 +500,9 @@ void test_ngtcp2_rob_data_at(void) { void test_ngtcp2_rob_remove_prefix(void) { ngtcp2_mem *mem = ngtcp2_mem_default(); ngtcp2_rob rob; + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_psl_it it; uint8_t data[256]; int rv; @@ -352,8 +515,15 @@ void test_ngtcp2_rob_remove_prefix(void) { ngtcp2_rob_remove_prefix(&rob, 33); - CU_ASSERT(33 == rob.gap->range.begin); - CU_ASSERT(32 == rob.data->offset); + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); + + CU_ASSERT(33 == g->range.begin); + + it = ngtcp2_psl_begin(&rob.datapsl); + d = ngtcp2_psl_it_get(&it); + + CU_ASSERT(32 == d->range.begin); ngtcp2_rob_free(&rob); @@ -370,8 +540,14 @@ void test_ngtcp2_rob_remove_prefix(void) { ngtcp2_rob_remove_prefix(&rob, 16); - CU_ASSERT(16 == rob.gap->range.begin); - CU_ASSERT(NULL == rob.gap->next); + it = ngtcp2_psl_begin(&rob.gappsl); + g = ngtcp2_psl_it_get(&it); + + CU_ASSERT(16 == g->range.begin); + + ngtcp2_psl_it_next(&it); + + CU_ASSERT(ngtcp2_psl_it_end(&it)); ngtcp2_rob_free(&rob); } diff --git a/tests/ngtcp2_rob_test.h b/tests/ngtcp2_rob_test.h index e59be5ac..3f7325aa 100644 --- a/tests/ngtcp2_rob_test.h +++ b/tests/ngtcp2_rob_test.h @@ -30,6 +30,7 @@ #endif /* HAVE_CONFIG_H */ void test_ngtcp2_rob_push(void); +void test_ngtcp2_rob_push_random(void); void test_ngtcp2_rob_data_at(void); void test_ngtcp2_rob_remove_prefix(void); -- GitLab