Add linux kernel interval tree

This commit is contained in:
luwenpeng
2024-03-27 17:11:38 +08:00
parent eb281ab789
commit 814a0d739f
25 changed files with 2200 additions and 1665 deletions

1
deps/CMakeLists.txt vendored
View File

@@ -1,4 +1,5 @@
add_subdirectory(timeout)
add_subdirectory(dablooms)
add_subdirectory(toml)
add_subdirectory(rbtree)
add_subdirectory(interval_tree)

View File

@@ -1,4 +1,6 @@
add_library(interval_tree STATIC interval.cpp interval_list.cpp itree.cpp)
add_library(interval_tree STATIC interval_tree.cpp)
target_include_directories(interval_tree PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(interval_tree PUBLIC ${CMAKE_SOURCE_DIR}/deps/rbtree)
target_link_libraries(interval_tree rbtree)
add_subdirectory(test)

View File

@@ -1,71 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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 "interval.h"
#include <stdlib.h>
interval_t *interval_new(uint64_t low, uint64_t high, void *data, dup_f dup, rel_f rel)
{
interval_t *ri = (interval_t *)malloc(sizeof *ri);
if (ri == NULL)
{
return NULL;
}
ri->low = low;
ri->high = high;
ri->dup = dup;
ri->rel = rel;
ri->data = ri->dup(data);
return ri;
}
interval_t *interval_copy(const interval_t *i)
{
return interval_new(i->low, i->high, i->data, i->dup, i->rel);
}
void interval_delete(interval_t *i)
{
if (i != NULL)
{
if (i->data)
{
i->rel(i->data);
}
free(i);
}
}
int interval_overlap(const interval_t *i1, const interval_t *i2)
{
return i1->low <= i2->high && i2->low <= i1->high;
}
int interval_equal(const interval_t *i1, const interval_t *i2)
{
return i1->low == i2->low && i1->high == i2->high && i1->data == i2->data;
}

View File

@@ -1,59 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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 _INTERVAL_H_
#define _INTERVAL_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include <stddef.h>
#include <stdint.h>
/* User-defined item handling */
typedef void *(*dup_f)(void *p);
typedef void (*rel_f)(void *p);
typedef struct interval
{
uint64_t low, high; /* Interval boundaries, inclusive */
void *data; /* User-defined content */
dup_f dup; /* Clone an interval data item */
rel_f rel; /* Destroy an interval data item */
} interval_t;
/* Interval functions */
interval_t *interval_new(uint64_t low, uint64_t high, void *data, dup_f dup, rel_f rel);
interval_t *interval_copy(const interval_t *i);
void interval_delete(interval_t *i);
int interval_overlap(const interval_t *i1, const interval_t *i2);
int interval_equal(const interval_t *i1, const interval_t *i2);
#ifdef __cplusplus
}
#endif
#endif /* _INTERVAL_H_ */

View File

@@ -1,277 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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 "interval_list.h"
#include <stdlib.h>
#include <stdio.h>
#ifndef HEIGHT_LIMIT
#define HEIGHT_LIMIT 64
#endif
/* Note: doesn't own the interval, comes from the tree
which is responsible for deallocating the interval. */
typedef struct ilistnode
{
const interval_t *interval;
struct ilistnode *next;
} ilistnode_t;
struct ilist
{
ilistnode_t *head, *tail; /* Dummy nodes */
size_t size;
};
struct ilisttrav
{
ilist_t *list; /* Paired list */
ilistnode_t *it; /* Current node */
};
static ilistnode_t *ilistnode_new(const interval_t *i, ilistnode_t *next)
{
ilistnode_t *node = (ilistnode_t *)malloc(sizeof *node);
if (node != NULL)
{
node->interval = i; /* NOTE: doesn't own the interval, pointer aliasing */
node->next = next;
}
return node;
}
/*
Destroy a single node, assuming it's been unlinked.
Returns the next node specified by the link.
*/
ilistnode_t *ilistnode_delete(ilistnode_t *node)
{
ilistnode_t *next = NULL;
if (node != NULL)
{
/* Save a reference to the next node
since we're about to destroy this one */
next = node->next;
free(node);
}
return next;
}
ilist_t *ilist_new()
{
ilist_t *list = (ilist_t *)malloc(sizeof *list);
if (list != NULL)
{
ilistnode_t *tail = ilistnode_new(NULL, NULL);
if (tail == NULL)
{
free(list);
list = NULL;
}
else
{
list->tail = tail;
list->head = ilistnode_new(NULL, list->tail);
if (list->head == NULL)
{
free(list);
list = NULL;
}
list->size = 0;
}
}
return list;
}
void ilist_delete(ilist_t *list)
{
if (list == NULL)
{
return;
}
ilistnode_t *it = list->head;
while (it != list->tail)
{
it = ilistnode_delete(it);
}
ilistnode_delete(it); /* Delete tail */
free(list);
}
size_t ilist_size(ilist_t *list)
{
return list->size;
}
static ilistnode_t *insert_before(ilist_t *, ilistnode_t *, interval_t *);
static ilistnode_t *insert_after(ilist_t *list, ilistnode_t *pos, interval_t *i)
{
ilistnode_t *node = NULL;
if (list != NULL && pos != NULL)
{
if (pos != list->tail)
{
node = ilistnode_new(i, pos->next);
if (node != NULL)
{
pos->next = node;
++list->size;
}
}
else
{
node = insert_before(list, pos, i);
}
}
return node;
}
static ilistnode_t *insert_before(ilist_t *list, ilistnode_t *pos, interval_t *i)
{
ilistnode_t *node = NULL;
if (list != NULL && pos != NULL)
{
if (pos != list->head)
{
/* Find the previous node */
ilistnode_t *it = list->head;
while (it->next != pos)
{
it = it->next;
}
/* Create a new node and set the new link */
node = ilistnode_new(i, it->next);
if (node != NULL)
{
it->next = node;
++list->size;
}
}
else
{
node = insert_after(list, pos, i);
}
}
return node;
}
int ilist_append(ilist_t *list, interval_t *i)
{
ilistnode_t *node = insert_before(list, list->tail, i);
if (node != NULL)
{
return 1;
}
return 0;
}
ilisttrav_t *ilisttrav_new(ilist_t *list)
{
if (list == NULL)
{
return NULL;
}
ilisttrav_t *trav = (ilisttrav_t *)malloc(sizeof(ilisttrav_t));
if (trav != NULL)
{
trav->list = list;
}
return trav;
}
void ilisttrav_delete(ilisttrav_t *trav)
{
free(trav);
}
const interval_t *ilisttrav_first(ilisttrav_t *trav)
{
if (trav->list == NULL)
{
return NULL;
}
trav->it = trav->list->head->next;
return trav->it == NULL || trav->it == trav->list->tail ? NULL : trav->it->interval;
}
const interval_t *ilisttrav_last(ilisttrav_t *trav)
{
if (trav->list == NULL)
{
return NULL;
}
trav->it = trav->list->head;
while (trav->it->next != trav->list->tail)
{
trav->it = trav->it->next;
}
return trav->it == trav->list->head || trav->it == NULL ? NULL : trav->it->interval;
}
const interval_t *ilisttrav_next(ilisttrav_t *trav)
{
if (trav == NULL)
{
return NULL;
}
trav->it = trav->it->next;
return trav->it == NULL || trav->it == trav->list->tail ? NULL : trav->it->interval;
}
/* Very inefficient, need a doubly linked list to properly support this */
const interval_t *ilisttrav_prev(ilisttrav_t *trav)
{
if (trav == NULL)
{
return NULL;
}
ilistnode_t *it = trav->list->head;
while (it->next != trav->it)
{
it = it->next;
}
trav->it = it;
return trav->it == trav->list->head || trav->it == NULL ? NULL : trav->it->interval;
}

View File

@@ -1,57 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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 _INTERVAL_LIST_H_
#define _INTERVAL_LIST_H_
#ifdef __cplusplus
extern "C"
{
#endif
#include "interval.h"
/* Declarations for an interval list, opaque types */
typedef struct ilist ilist_t;
typedef struct ilisttrav ilisttrav_t;
/* Interval list functions */
ilist_t *ilist_new();
void ilist_delete(ilist_t *list);
size_t ilist_size(ilist_t *list);
int ilist_append(ilist_t *list, interval_t *i);
/* Interval list traversal functions */
ilisttrav_t *ilisttrav_new(ilist_t *list);
void ilisttrav_delete(ilisttrav_t *trav);
const interval_t *ilisttrav_first(ilisttrav_t *trav);
const interval_t *ilisttrav_last(ilisttrav_t *trav);
const interval_t *ilisttrav_next(ilisttrav_t *trav);
const interval_t *ilisttrav_prev(ilisttrav_t *trav);
#ifdef __cplusplus
}
#endif
#endif /* _INTERVAL_LIST_H_ */

10
deps/interval_tree/interval_tree.cpp vendored Normal file
View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "interval_tree.h"
#include "interval_tree_generic.h"
#define START(node) ((node)->start)
#define LAST(node) ((node)->last)
INTERVAL_TREE_DEFINE(struct interval_tree_node, rb,
uint64_t, __subtree_last,
START, LAST, , interval_tree)

33
deps/interval_tree/interval_tree.h vendored Normal file
View File

@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_INTERVAL_TREE_H
#define _LINUX_INTERVAL_TREE_H
#include "rbtree.h"
#include <stdint.h>
struct interval_tree_node
{
struct rb_node rb;
uint64_t start; /* Start of interval */
uint64_t last; /* Last location _in_ interval */
uint64_t __subtree_last;
void *user_data;
};
extern void
interval_tree_insert(struct interval_tree_node *node,
struct rb_root_cached *root);
extern void
interval_tree_remove(struct interval_tree_node *node,
struct rb_root_cached *root);
extern struct interval_tree_node *
interval_tree_iter_first(struct rb_root_cached *root,
uint64_t start, uint64_t last);
extern struct interval_tree_node *
interval_tree_iter_next(struct interval_tree_node *node,
uint64_t start, uint64_t last);
#endif /* _LINUX_INTERVAL_TREE_H */

View File

@@ -0,0 +1,197 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Interval Trees
(C) 2012 Michel Lespinasse <walken@google.com>
include/linux/interval_tree_generic.h
*/
#include "rbtree_augmented.h"
/*
* Template for implementing interval trees
*
* ITSTRUCT: struct type of the interval tree nodes
* ITRB: name of struct rb_node field within ITSTRUCT
* ITTYPE: type of the interval endpoints
* ITSUBTREE: name of ITTYPE field within ITSTRUCT holding last-in-subtree
* ITSTART(n): start endpoint of ITSTRUCT node n
* ITLAST(n): last endpoint of ITSTRUCT node n
* ITSTATIC: 'static' or empty
* ITPREFIX: prefix to use for the inline tree definitions
*
* Note - before using this, please consider if generic version
* (interval_tree.h) would work for you...
*/
#define INTERVAL_TREE_DEFINE(ITSTRUCT, ITRB, ITTYPE, ITSUBTREE, \
ITSTART, ITLAST, ITSTATIC, ITPREFIX) \
\
/* Callbacks for augmented rbtree insert and remove */ \
\
RB_DECLARE_CALLBACKS_MAX(static, ITPREFIX##_augment, \
ITSTRUCT, ITRB, ITTYPE, ITSUBTREE, ITLAST) \
\
/* Insert / remove interval nodes from the tree */ \
\
ITSTATIC void ITPREFIX##_insert(ITSTRUCT *node, \
struct rb_root_cached *root) \
{ \
struct rb_node **link = &root->rb_root.rb_node, *rb_parent = NULL; \
ITTYPE start = ITSTART(node), last = ITLAST(node); \
ITSTRUCT *parent; \
bool leftmost = true; \
\
while (*link) \
{ \
rb_parent = *link; \
parent = rb_entry(rb_parent, ITSTRUCT, ITRB); \
if (parent->ITSUBTREE < last) \
parent->ITSUBTREE = last; \
if (start < ITSTART(parent)) \
link = &parent->ITRB.rb_left; \
else \
{ \
link = &parent->ITRB.rb_right; \
leftmost = false; \
} \
} \
\
node->ITSUBTREE = last; \
rb_link_node(&node->ITRB, rb_parent, link); \
rb_insert_augmented_cached(&node->ITRB, root, \
leftmost, &ITPREFIX##_augment); \
} \
\
ITSTATIC void ITPREFIX##_remove(ITSTRUCT *node, \
struct rb_root_cached *root) \
{ \
rb_erase_augmented_cached(&node->ITRB, root, &ITPREFIX##_augment); \
} \
\
/* \
* Iterate over intervals intersecting [start;last] \
* \
* Note that a node's interval intersects [start;last] iff: \
* Cond1: ITSTART(node) <= last \
* and \
* Cond2: start <= ITLAST(node) \
*/ \
\
static ITSTRUCT * \
ITPREFIX##_subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last) \
{ \
while (true) \
{ \
/* \
* Loop invariant: start <= node->ITSUBTREE \
* (Cond2 is satisfied by one of the subtree nodes) \
*/ \
if (node->ITRB.rb_left) \
{ \
ITSTRUCT *left = rb_entry(node->ITRB.rb_left, \
ITSTRUCT, ITRB); \
if (start <= left->ITSUBTREE) \
{ \
/* \
* Some nodes in left subtree satisfy Cond2. \
* Iterate to find the leftmost such node N. \
* If it also satisfies Cond1, that's the \
* match we are looking for. Otherwise, there \
* is no matching interval as nodes to the \
* right of N can't satisfy Cond1 either. \
*/ \
node = left; \
continue; \
} \
} \
if (ITSTART(node) <= last) \
{ /* Cond1 */ \
if (start <= ITLAST(node)) /* Cond2 */ \
return node; /* node is leftmost match */ \
if (node->ITRB.rb_right) \
{ \
node = rb_entry(node->ITRB.rb_right, \
ITSTRUCT, ITRB); \
if (start <= node->ITSUBTREE) \
continue; \
} \
} \
return NULL; /* No match */ \
} \
} \
\
ITSTATIC ITSTRUCT * \
ITPREFIX##_iter_first(struct rb_root_cached *root, \
ITTYPE start, ITTYPE last) \
{ \
ITSTRUCT *node, *leftmost; \
\
if (!root->rb_root.rb_node) \
return NULL; \
\
/* \
* Fastpath range intersection/overlap between A: [a0, a1] and \
* B: [b0, b1] is given by: \
* \
* a0 <= b1 && b0 <= a1 \
* \
* ... where A holds the lock range and B holds the smallest \
* 'start' and largest 'last' in the tree. For the later, we \
* rely on the root node, which by augmented interval tree \
* property, holds the largest value in its last-in-subtree. \
* This allows mitigating some of the tree walk overhead for \
* for non-intersecting ranges, maintained and consulted in O(1). \
*/ \
node = rb_entry(root->rb_root.rb_node, ITSTRUCT, ITRB); \
if (node->ITSUBTREE < start) \
return NULL; \
\
leftmost = rb_entry(root->rb_leftmost, ITSTRUCT, ITRB); \
if (ITSTART(leftmost) > last) \
return NULL; \
\
return ITPREFIX##_subtree_search(node, start, last); \
} \
\
ITSTATIC ITSTRUCT * \
ITPREFIX##_iter_next(ITSTRUCT *node, ITTYPE start, ITTYPE last) \
{ \
struct rb_node *rb = node->ITRB.rb_right, *prev; \
\
while (true) \
{ \
/* \
* Loop invariants: \
* Cond1: ITSTART(node) <= last \
* rb == node->ITRB.rb_right \
* \
* First, search right subtree if suitable \
*/ \
if (rb) \
{ \
ITSTRUCT *right = rb_entry(rb, ITSTRUCT, ITRB); \
if (start <= right->ITSUBTREE) \
return ITPREFIX##_subtree_search(right, \
start, last); \
} \
\
/* Move up the tree until we come from a node's left child */ \
do \
{ \
rb = rb_parent(&node->ITRB); \
if (!rb) \
return NULL; \
prev = &node->ITRB; \
node = rb_entry(rb, ITSTRUCT, ITRB); \
rb = node->ITRB.rb_right; \
} while (prev == rb); \
\
/* Check if the node intersects [start;last] */ \
if (last < ITSTART(node)) /* !Cond1 */ \
return NULL; \
else if (start <= ITLAST(node)) /* Cond2 */ \
return node; \
} \
}

View File

@@ -1,628 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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.
*/
/*
Interval Tree library
This is an adaptation of the AVL balanced tree C library
created by Julienne Walker which can be found here:
http://www.eternallyconfuzzled.com/Libraries.aspx
*/
#include "itree.h"
#include <stdlib.h>
#ifndef HEIGHT_LIMIT
#define HEIGHT_LIMIT 64 /* Tallest allowable tree */
#endif
typedef struct itreenode
{
int balance; /* Balance factor */
uint64_t max; /* Maximum high value in the subtree rooted at this node */
interval_t *interval; /* The interval the node represents */
struct itreenode *link[2]; /* Left (0) and right (1) links */
} itreenode_t;
struct itree
{
itreenode_t *root; /* Top of the tree */
dup_f dup; /* Clone an interval data item (user-defined) */
rel_f rel; /* Destroy an interval data item (user-defined) */
size_t size; /* Number of items (user-defined) */
};
struct itreetrav
{
itree_t *tree; /* Paired tree */
itreenode_t *it; /* Current node */
itreenode_t *path[HEIGHT_LIMIT]; /* Traversal path */
size_t top; /* Top of stack */
};
/* Two way single rotation */
#define single(root, dir) \
do \
{ \
itreenode_t *save = root->link[!dir]; \
root->link[!dir] = save->link[dir]; \
save->link[dir] = root; \
root = save; \
} while (0)
/* Two way double rotation */
#define double(root, dir) \
do \
{ \
itreenode_t *save = root->link[!dir]->link[dir]; \
root->link[!dir]->link[dir] = save->link[!dir]; \
save->link[!dir] = root->link[!dir]; \
root->link[!dir] = save; \
save = root->link[!dir]; \
root->link[!dir] = save->link[dir]; \
save->link[dir] = root; \
root = save; \
} while (0)
/* Adjust balance before double rotation */
#define adjust_balance(root, dir, bal) \
do \
{ \
itreenode_t *n = root->link[dir]; \
itreenode_t *nn = n->link[!dir]; \
if (nn->balance == 0) \
{ \
root->balance = n->balance = 0; \
} \
else if (nn->balance == bal) \
{ \
root->balance = -bal; \
n->balance = 0; \
} \
else \
{ /* nn->balance == -bal */ \
root->balance = 0; \
n->balance = bal; \
} \
nn->balance = 0; \
} while (0)
/* Rebalance after insertion */
#define insert_balance(root, dir) \
do \
{ \
itreenode_t *n = root->link[dir]; \
int bal = dir == 0 ? -1 : +1; \
if (n->balance == bal) \
{ \
root->balance = n->balance = 0; \
single(root, !dir); \
} \
else \
{ /* n->balance == -bal */ \
adjust_balance(root, dir, bal); \
double(root, !dir); \
} \
} while (0)
/* Rebalance after deletion */
#define remove_balance(root, dir, done) \
do \
{ \
itreenode_t *n = root->link[!dir]; \
int bal = dir == 0 ? -1 : +1; \
if (n->balance == -bal) \
{ \
root->balance = n->balance = 0; \
single(root, dir); \
} \
else if (n->balance == bal) \
{ \
adjust_balance(root, !dir, -bal); \
double(root, dir); \
} \
else \
{ /* n->balance == 0 */ \
root->balance = -bal; \
n->balance = bal; \
single(root, dir); \
done = 1; \
} \
} while (0)
static itreenode_t *new_node(itree_t *tree, interval_t *i)
{
itreenode_t *rn = (itreenode_t *)malloc(sizeof *rn);
if (rn == NULL)
{
return NULL;
}
rn->interval = (interval_t *)malloc(sizeof(interval_t));
if (rn->interval == NULL)
{
return NULL;
}
rn->interval->low = i->low;
rn->interval->high = i->high;
rn->interval->data = tree->dup(i->data);
rn->balance = 0;
rn->max = i->high;
rn->link[0] = rn->link[1] = NULL;
return rn;
}
itree_t *itree_new(dup_f dup, rel_f rel)
{
itree_t *rt = (itree_t *)malloc(sizeof *rt);
if (rt == NULL)
{
return NULL;
}
rt->root = NULL;
rt->dup = dup;
rt->rel = rel;
rt->size = 0;
return rt;
}
void itree_delete(itree_t *tree)
{
itreenode_t *it = tree->root;
itreenode_t *save;
/* Destruction by rotation */
while (it != NULL)
{
if (it->link[0] == NULL)
{
/* Remove node */
save = it->link[1];
tree->rel(it->interval->data);
free(it->interval);
free(it);
}
else
{
/* Rotate right */
save = it->link[0];
it->link[0] = save->link[1];
save->link[1] = it;
}
it = save;
}
free(tree);
}
interval_t *itree_find(itree_t *tree, interval_t *interval)
{
itreenode_t *it = tree->root;
while (it != NULL)
{
/* int cmp = tree->cmp ( it->data, data ); */
/* if ( cmp == 0 ) */
/* break; */
/* it = it->link[cmp < 0]; */
if (interval_overlap(it->interval, interval))
{
break;
}
it = it->link[it->link[0] == NULL || it->link[0]->max < interval->low];
}
return it == NULL ? NULL : it->interval;
}
void search(itreenode_t *node, interval_t *interval, ilist_t *results)
{
/*
* If interval is to the right of the rightmost point of any interval
* in this node and all its children, there won't be any matches
*/
if (node == NULL || interval->low > node->max)
{
return;
}
/* search the left subtree */
if (node->link[0] != NULL && node->link[0]->max >= interval->low)
{
search(node->link[0], interval, results);
}
/* search this node */
if (interval_overlap(node->interval, interval))
{
ilist_append(results, node->interval);
}
/*
* if interval is to the left of the start of this interval
* it can't be in any child to the right
*/
if (interval->high < node->interval->low)
{
return;
}
/* search the right subtree */
search(node->link[1], interval, results);
}
ilist_t *itree_findall(itree_t *tree, interval_t *interval)
{
ilist_t *results = ilist_new();
if (results != NULL)
{
/* empty tree case */
if (tree->root == NULL)
{
return results;
}
search(tree->root, interval, results);
}
return results;
}
int itree_insert(itree_t *tree, interval_t *interval)
{
/* Empty tree case */
if (tree->root == NULL)
{
tree->root = new_node(tree, interval);
if (tree->root == NULL)
{
return 0;
}
}
else
{
itreenode_t head = {0}; /* Temporary tree root */
itreenode_t *s, *t; /* Place to rebalance and parent */
itreenode_t *p, *q; /* Iterator and save pointer to the newly inserted node */
int dir;
/* Set up false root to ease maintenance */
t = &head;
t->link[1] = tree->root;
/* Search down the tree, saving rebalance points */
for (s = p = t->link[1];; p = q)
{
/* Duplicates admitted, placed in the right subtree */
dir = p->interval->low <= interval->low; /* tree->cmp ( p->data, data ) < 0; */
q = p->link[dir];
p->max = p->max < interval->high ? interval->high : p->max; /* Update ancestor's max if needed */
if (q == NULL)
{
break;
}
if (q->balance != 0)
{
t = p;
s = q;
}
}
p->link[dir] = q = new_node(tree, interval);
if (q == NULL)
{
return 0; /* TODO: should rollback to previous ancestors' max values */
}
/* Update balance factors */
for (p = s; p != q; p = p->link[dir])
{
dir = p->interval->low <= interval->low; /* tree->cmp ( p->data, data ) < 0; */
p->balance += dir == 0 ? -1 : +1;
}
q = s; /* Save rebalance point for parent fix */
/* Rebalance if necessary */
if (abs(s->balance) > 1)
{
dir = s->interval->low <= interval->low; /* tree->cmp ( s->data, data ) < 0; */
insert_balance(s, dir);
}
/* Fix parent */
if (q == head.link[1])
{
tree->root = s;
}
else
{
t->link[q == t->link[1]] = s;
}
}
++tree->size;
return 1;
}
int itree_remove(itree_t *tree, interval_t *interval)
{
if (tree->root != NULL)
{
itreenode_t *it, *up[HEIGHT_LIMIT];
int upd[HEIGHT_LIMIT], top = 0, top_max;
int done = 0;
it = tree->root;
/* Search down tree and save path */
for (;;)
{
if (it == NULL)
{
return 0;
}
else if (interval_equal(it->interval, interval))
{
break;
}
/* Push direction and node onto stack */
upd[top] = it->interval->low <= interval->low; /* tree->cmp ( it->data, data ) < 0; */
up[top++] = it;
it = it->link[upd[top - 1]];
}
/* Remove the node */
if (it->link[0] == NULL || it->link[1] == NULL)
{
/* Which child is not null? */
int dir = it->link[0] == NULL;
/* Fix parent */
if (top != 0)
{
up[top - 1]->link[upd[top - 1]] = it->link[dir];
}
else
{
tree->root = it->link[dir];
}
tree->rel(it->interval->data);
free(it->interval);
free(it);
}
else
{
/* Find the inorder successor */
itreenode_t *heir = it->link[1];
void *save;
/* Save this path too */
upd[top] = 1;
up[top++] = it;
while (heir->link[0] != NULL)
{
upd[top] = 0;
up[top++] = heir;
heir = heir->link[0];
}
/* Swap data */
save = it->interval;
it->interval = heir->interval;
heir->interval = (interval_t *)save;
/* Unlink successor and fix parent */
up[top - 1]->link[up[top - 1] == it] = heir->link[1];
tree->rel(heir->interval->data);
free(heir->interval);
free(heir);
}
/* Update max: walk back up the search path and bubbles up to root */
top_max = top;
while (--top_max >= 0)
{
itreenode_t *left = up[top_max]->link[0], *right = up[top_max]->link[1];
if (left != NULL && right != NULL)
{
uint64_t left_right_max = left->max < right->max ? right->max : left->max;
up[top_max]->max = up[top_max]->interval->high < left_right_max ? left_right_max : up[top_max]->interval->high;
}
else if (left != NULL && right == NULL)
{
up[top_max]->max = up[top_max]->interval->high < left->max ? left->max : up[top_max]->interval->high;
}
else if (left == NULL && right != NULL)
{
up[top_max]->max = up[top_max]->interval->high < right->max ? right->max : up[top_max]->interval->high;
}
else
{
up[top_max]->max = up[top_max]->interval->high;
}
}
/* Walk back up the search path */
while (--top >= 0 && !done)
{
/* Update balance factors */
up[top]->balance += upd[top] != 0 ? -1 : +1;
/* Terminate or rebalance as necessary */
if (abs(up[top]->balance) == 1)
{
break;
}
else if (abs(up[top]->balance) > 1)
{
remove_balance(up[top], upd[top], done);
/* Fix parent */
if (top != 0)
{
up[top - 1]->link[upd[top - 1]] = up[top];
}
else
{
tree->root = up[0];
}
}
}
--tree->size;
}
return 1;
}
size_t itree_size(itree_t *tree)
{
return tree->size;
}
itreetrav_t *itreetnew(void)
{
return (itreetrav_t *)malloc(sizeof(itreetrav_t));
}
void itreetdelete(itreetrav_t *trav)
{
free(trav);
}
/*
First step in traversal,
handles min and max
*/
static interval_t *start(itreetrav_t *trav, itree_t *tree, int dir)
{
trav->tree = tree;
trav->it = tree->root;
trav->top = 0;
/* Build a path to work with */
if (trav->it != NULL)
{
while (trav->it->link[dir] != NULL)
{
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[dir];
}
}
#ifdef DEBUG
if (trav->it)
printf("[%.1f, %.1f] (%d) (%.1f)\n", trav->it->interval->low, trav->it->interval->high, *(int *)trav->it->interval->data, trav->it->max);
#endif
return trav->it == NULL ? NULL : trav->it->interval;
}
/*
Subsequent traversal steps,
handles ascending and descending
*/
static interval_t *move(itreetrav_t *trav, int dir)
{
if (trav->it->link[dir] != NULL)
{
/* Continue down this branch */
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[dir];
while (trav->it->link[!dir] != NULL)
{
trav->path[trav->top++] = trav->it;
trav->it = trav->it->link[!dir];
}
}
else
{
/* Move to the next branch */
itreenode_t *last;
do
{
if (trav->top == 0)
{
trav->it = NULL;
{
break;
}
}
last = trav->it;
trav->it = trav->path[--trav->top];
} while (last == trav->it->link[dir]);
}
#ifdef DEBUG
if (trav->it)
printf("[%.1f, %.1f] (%d) (%.1f)\n", trav->it->interval->low, trav->it->interval->high, *(int *)trav->it->interval->data, trav->it->max);
#endif
return trav->it == NULL ? NULL : trav->it->interval;
}
interval_t *itreetfirst(itreetrav_t *trav, itree_t *tree)
{
return start(trav, tree, 0); /* Min value */
}
interval_t *itreetlast(itreetrav_t *trav, itree_t *tree)
{
return start(trav, tree, 1); /* Max value */
}
interval_t *itreetnext(itreetrav_t *trav)
{
return move(trav, 1); /* Toward larger items */
}
interval_t *itreetprev(itreetrav_t *trav)
{
return move(trav, 0); /* Toward smaller items */
}

View File

@@ -1,75 +0,0 @@
/*
* Libitree: an interval tree library in C
*
* Copyright (C) 2018 Alessandro Vullo
*
* 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 _INTERVAL_TREE_H_
#define _INTERVAL_TREE_H_
/*
Interval Tree library
This is an adaptation of the AVL balanced tree C library
created by Julienne Walker which can be found here:
http://www.eternallyconfuzzled.com/Libraries.aspx
*/
#ifdef __cplusplus
extern "C"
{
#endif
#include "interval.h"
#include "interval_list.h"
/* Opaque types */
typedef struct itree itree_t;
typedef struct itreetrav itreetrav_t;
/* User-defined interval data item handling */
typedef void *(*dup_f)(void *p);
typedef void (*rel_f)(void *p);
/* Interval tree functions */
itree_t *itree_new(dup_f dup, rel_f rel);
void itree_delete(itree_t *tree);
interval_t *itree_find(itree_t *tree, interval_t *interval);
ilist_t *itree_findall(itree_t *tree, interval_t *interval);
int itree_insert(itree_t *tree, interval_t *interval);
int itree_remove(itree_t *tree, interval_t *interval);
size_t itree_size(itree_t *tree);
/* Tree traversal functions */
itreetrav_t *itreetnew(void);
void itreetdelete(itreetrav_t *trav);
interval_t *itreetfirst(itreetrav_t *trav, itree_t *tree);
interval_t *itreetlast(itreetrav_t *trav, itree_t *tree);
interval_t *itreetnext(itreetrav_t *trav);
interval_t *itreetprev(itreetrav_t *trav);
#ifdef __cplusplus
}
#endif
#endif /* _INTERVAL_TREE_H_ */

View File

@@ -1,298 +1,178 @@
#include <gtest/gtest.h>
#include "itree.h"
#include "interval_tree.h"
void *my_dup(void *p)
struct interval_tree_node *node_new(uint64_t start, uint64_t last, void *user_data)
{
return p;
uint64_t size = last - start + 1;
struct interval_tree_node *node = (struct interval_tree_node *)calloc(1, sizeof(struct interval_tree_node) + size);
if (node == nullptr)
{
return nullptr;
}
node->start = start;
node->last = last;
node->user_data = (char *)node + sizeof(struct interval_tree_node);
memcpy(node->user_data, user_data, size);
printf("new node: %p, start: %lu, last: %lu, user_data: %s\n", node, node->start, node->last, (char *)node->user_data);
return node;
}
void my_rel(void *p)
void node_free(struct interval_tree_node *node)
{
if (node)
{
printf("free node: %p, start: %lu, last: %lu, user_data: %s\n", node, node->start, node->last, (char *)node->user_data);
free(node);
node = NULL;
}
}
// find overlap
struct range
{
uint64_t start;
uint64_t last;
};
#if 1
TEST(INTERVAL_TREE, FIND)
{
itree_t *tree;
interval_t *result;
interval_t query;
interval_t segment;
void *data = (void *)"Hello";
// new
tree = itree_new(my_dup, my_rel);
EXPECT_TRUE(tree != nullptr);
EXPECT_TRUE(itree_size(tree) == 0);
struct rb_root_cached root = RB_ROOT_CACHED;
struct interval_tree_node *node;
// insert
segment = {
.low = 5,
.high = 9,
.data = data,
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 1);
// find
query = {
.low = 5,
.high = 5,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
// find
query = {
.low = 6,
.high = 8,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
// find
query = {
.low = 5,
.high = 9,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
// find
query = {
.low = 9,
.high = 9,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
// find
query = {
.low = 1,
.high = 14,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
node = node_new(5, 10, (void *)"Hello");
interval_tree_insert(node, &root);
// not found
query = {
.low = 1,
.high = 4,
struct range ranges_not_found[] = {
{0, 4},
{11, 15},
};
result = itree_find(tree, &query);
EXPECT_TRUE(result == nullptr);
for (size_t i = 0; i < sizeof(ranges_not_found) / sizeof(ranges_not_found[0]); i++)
{
node = interval_tree_iter_first(&root, ranges_not_found[i].start, ranges_not_found[i].last);
EXPECT_TRUE(node == nullptr);
}
// not found
query = {
.low = 10,
.high = 14,
// found
struct range ranges_found[] = {
// overlap interval
{0, 5},
{1, 6},
{2, 7},
{3, 8},
{4, 9},
{5, 10},
{6, 11},
{7, 12},
{8, 13},
{9, 14},
{10, 15},
// overlap point
{5, 5},
{6, 6},
{7, 7},
{8, 8},
{9, 9},
{10, 10},
// overlap interval
{5, 10},
{4, 11},
};
result = itree_find(tree, &query);
itree_delete(tree);
}
#endif
#if 1
TEST(INTERVAL_TREE, DELETE)
{
itree_t *tree;
interval_t query;
interval_t segment;
void *data = (void *)"Hello";
// new
tree = itree_new(my_dup, my_rel);
EXPECT_TRUE(tree != nullptr);
EXPECT_TRUE(itree_size(tree) == 0);
// insert
segment = {
.low = 5,
.high = 9,
.data = data,
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 1);
// delete
query = {
.low = 5,
.high = 5,
};
EXPECT_TRUE(itree_remove(tree, &query) == 0);
EXPECT_TRUE(itree_size(tree) == 1);
// delete
query = {
.low = 9,
.high = 9,
.data = data,
};
EXPECT_TRUE(itree_remove(tree, &query) == 0);
EXPECT_TRUE(itree_size(tree) == 1);
// delete
query = {
.low = 1,
.high = 20,
.data = data,
};
EXPECT_TRUE(itree_remove(tree, &query) == 0);
EXPECT_TRUE(itree_size(tree) == 1);
// delete
query = {
.low = 5,
.high = 9,
.data = data,
};
EXPECT_TRUE(itree_remove(tree, &query) == 1);
EXPECT_TRUE(itree_size(tree) == 0);
itree_delete(tree);
}
#endif
#if 1
TEST(INTERVAL_TREE, REPEAT1)
{
itree_t *tree;
interval_t segment;
interval_t query;
void *data1 = (void *)"Hello";
void *data2 = (void *)"World";
// new
tree = itree_new(my_dup, my_rel);
EXPECT_TRUE(tree != nullptr);
EXPECT_TRUE(itree_size(tree) == 0);
// insert
segment = {
.low = 5,
.high = 9,
.data = data1,
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 1);
// insert
segment = {
.low = 5,
.high = 9,
.data = data2,
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 2);
for (size_t i = 0; i < sizeof(ranges_found) / sizeof(ranges_found[0]); i++)
{
node = interval_tree_iter_first(&root, ranges_found[i].start, ranges_found[i].last);
EXPECT_TRUE(node != nullptr);
EXPECT_TRUE(node->start == 5);
EXPECT_TRUE(node->last == 10);
EXPECT_STREQ((const char *)node->user_data, "Hello");
}
// remove
query = {
.low = 5,
.high = 9,
.data = data1,
};
EXPECT_TRUE(itree_remove(tree, &query) == 1);
EXPECT_TRUE(itree_size(tree) == 1);
query = {
.low = 5,
.high = 9,
.data = data2,
};
EXPECT_TRUE(itree_remove(tree, &query) == 1);
EXPECT_TRUE(itree_size(tree) == 0);
interval_tree_remove(node, &root);
node_free(node);
itree_delete(tree);
// find
node = interval_tree_iter_first(&root, 5, 10);
EXPECT_TRUE(node == nullptr);
}
#endif
#if 1 // repeat, return the fist one
TEST(INTERVAL_TREE, REPEAT2)
#if 1
TEST(INTERVAL_TREE, REPEAT)
{
itree_t *tree;
ilist_t *list;
ilisttrav_t *trav;
interval_t *result;
interval_t query;
interval_t segment;
// new
tree = itree_new(my_dup, my_rel);
EXPECT_TRUE(tree != nullptr);
EXPECT_TRUE(itree_size(tree) == 0);
struct rb_root_cached root = RB_ROOT_CACHED;
struct interval_tree_node *node;
// insert
segment = {
.low = 5,
.high = 9,
.data = (void *)"Hello",
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 1);
node = node_new(5, 10, (void *)"Hello");
interval_tree_insert(node, &root);
// insert repeat
node = node_new(5, 10, (void *)"World");
interval_tree_insert(node, &root);
node = interval_tree_iter_first(&root, 5, 10);
EXPECT_TRUE(node != nullptr);
EXPECT_TRUE(node->start == 5);
EXPECT_TRUE(node->last == 10);
EXPECT_STREQ((const char *)node->user_data, "Hello");
node = interval_tree_iter_next(node, 5, 10);
EXPECT_TRUE(node != nullptr);
EXPECT_TRUE(node->start == 5);
EXPECT_TRUE(node->last == 10);
EXPECT_STREQ((const char *)node->user_data, "World");
node = interval_tree_iter_next(node, 5, 10);
EXPECT_TRUE(node == nullptr);
// remove all
while ((node = interval_tree_iter_first(&root, 5, 10)) != nullptr)
{
interval_tree_remove(node, &root);
node_free(node);
}
}
#endif
#if 1
TEST(INTERVAL_TREE, OVERLAP)
{
struct rb_root_cached root = RB_ROOT_CACHED;
struct interval_tree_node *node;
// insert
segment = {
.low = 5,
.high = 9,
.data = (void *)"World",
};
EXPECT_TRUE(itree_insert(tree, &segment) == 1);
EXPECT_TRUE(itree_size(tree) == 2);
node = node_new(5, 10, (void *)"Hello");
interval_tree_insert(node, &root);
// find
query = {
.low = 5,
.high = 9,
};
result = itree_find(tree, &query);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
// insert repeat
node = node_new(7, 12, (void *)"World");
interval_tree_insert(node, &root);
// find all, free the repeat
query = {
.low = 5,
.high = 9,
};
list = itree_findall(tree, &query);
EXPECT_TRUE(list != nullptr);
EXPECT_TRUE(ilist_size(list) == 2);
trav = ilisttrav_new(list);
EXPECT_TRUE(trav != nullptr);
node = interval_tree_iter_first(&root, 5, 10);
EXPECT_TRUE(node != nullptr);
EXPECT_TRUE(node->start == 5);
EXPECT_TRUE(node->last == 10);
EXPECT_STREQ((const char *)node->user_data, "Hello");
result = (interval_t *)ilisttrav_first(trav);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "Hello");
node = interval_tree_iter_next(node, 5, 10);
EXPECT_TRUE(node != nullptr);
EXPECT_TRUE(node->start == 7);
EXPECT_TRUE(node->last == 12);
EXPECT_STREQ((const char *)node->user_data, "World");
result = (interval_t *)ilisttrav_next(trav);
EXPECT_TRUE(result != nullptr);
EXPECT_TRUE(result->low == 5);
EXPECT_TRUE(result->high == 9);
EXPECT_STREQ((const char *)result->data, "World");
node = interval_tree_iter_next(node, 5, 10);
EXPECT_TRUE(node == nullptr);
ilisttrav_delete(trav);
ilist_delete(list);
itree_delete(tree);
// remove all
while ((node = interval_tree_iter_first(&root, 5, 10)) != nullptr)
{
interval_tree_remove(node, &root);
node_free(node);
}
}
#endif

356
deps/list/list.h vendored Normal file
View File

@@ -0,0 +1,356 @@
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2010 Francisco Jerez <currojerez@riseup.net>
*
* 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 (including the next
* paragraph) 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.
*
*/
/* Modified by Ben Skeggs <bskeggs@redhat.com> to match kernel list APIs */
#ifndef _XORG_LIST_H_
#define _XORG_LIST_H_
/**
* @file Classic doubly-link circular list implementation.
* For real usage examples of the linked list, see the file test/list.c
*
* Example:
* We need to keep a list of struct foo in the parent struct bar, i.e. what
* we want is something like this.
*
* struct bar {
* ...
* struct foo *list_of_foos; -----> struct foo {}, struct foo {}, struct foo{}
* ...
* }
*
* We need one list head in bar and a list element in all list_of_foos (both are of
* data type 'struct list_head').
*
* struct bar {
* ...
* struct list_head list_of_foos;
* ...
* }
*
* struct foo {
* ...
* struct list_head entry;
* ...
* }
*
* Now we initialize the list head:
*
* struct bar bar;
* ...
* INIT_LIST_HEAD(&bar.list_of_foos);
*
* Then we create the first element and add it to this list:
*
* struct foo *foo = malloc(...);
* ....
* list_add(&foo->entry, &bar.list_of_foos);
*
* Repeat the above for each element you want to add to the list. Deleting
* works with the element itself.
* list_del(&foo->entry);
* free(foo);
*
* Note: calling list_del(&bar.list_of_foos) will set bar.list_of_foos to an empty
* list again.
*
* Looping through the list requires a 'struct foo' as iterator and the
* name of the field the subnodes use.
*
* struct foo *iterator;
* list_for_each_entry(iterator, &bar.list_of_foos, entry) {
* if (iterator->something == ...)
* ...
* }
*
* Note: You must not call list_del() on the iterator if you continue the
* loop. You need to run the safe for-each loop instead:
*
* struct foo *iterator, *next;
* list_for_each_entry_safe(iterator, next, &bar.list_of_foos, entry) {
* if (...)
* list_del(&iterator->entry);
* }
*
*/
/**
* The linkage struct for list nodes. This struct must be part of your
* to-be-linked struct. struct list_head is required for both the head of the
* list and for each list node.
*
* Position and name of the struct list_head field is irrelevant.
* There are no requirements that elements of a list are of the same type.
* There are no requirements for a list head, any struct list_head can be a list
* head.
*/
struct list_head
{
struct list_head *next, *prev;
};
/**
* Initialize the list as an empty list.
*
* Example:
* INIT_LIST_HEAD(&bar->list_of_foos);
*
* @param The list to initialized.
*/
#define LIST_HEAD_INIT(name) \
{ \
&(name), &(name) \
}
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
static inline void
INIT_LIST_HEAD(struct list_head *list)
{
list->next = list->prev = list;
}
static inline void
__list_add(struct list_head *entry,
struct list_head *prev, struct list_head *next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
/**
* Insert a new element after the given list head. The new element does not
* need to be initialised as empty list.
* The list changes from:
* head → some element → ...
* to
* head → new element → older element → ...
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head, head->next);
}
/**
* Append a new element to the end of the list given with this list head.
*
* The list changes from:
* head → some element → ... → lastelement
* to
* head → some element → ... → lastelement → new element
*
* Example:
* struct foo *newfoo = malloc(...);
* list_add_tail(&newfoo->entry, &bar->list_of_foos);
*
* @param entry The new element to prepend to the list.
* @param head The existing list.
*/
static inline void
list_add_tail(struct list_head *entry, struct list_head *head)
{
__list_add(entry, head->prev, head);
}
static inline void
__list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* Remove the element from the list it is in. Using this function will reset
* the pointers to/from this element so it is removed from the list. It does
* NOT free the element itself or manipulate it otherwise.
*
* Using list_del on a pure list head (like in the example at the top of
* this file) will NOT remove the first element from
* the list but rather reset the list as empty list.
*
* Example:
* list_del(&foo->entry);
*
* @param entry The element to remove.
*/
static inline void
list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
static inline void
list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}
/**
* Check if the list is empty.
*
* Example:
* list_empty(&bar->list_of_foos);
*
* @return True if the list contains one or more elements or False otherwise.
*/
static inline bool
list_empty(struct list_head *head)
{
return head->next == head;
}
/**
* Returns a pointer to the container of this list element.
*
* Example:
* struct foo* f;
* f = container_of(&foo->entry, struct foo, entry);
* assert(f == foo);
*
* @param ptr Pointer to the struct list_head.
* @param type Data type of the list element.
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the data struct containing the list head.
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *)&((type *)0)->member)
#endif
/**
* Alias of container_of
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* Retrieve the first list entry for the given list pointer.
*
* Example:
* struct foo *first;
* first = list_first_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the first list element.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* Retrieve the last list entry for the given listpointer.
*
* Example:
* struct foo *first;
* first = list_last_entry(&bar->list_of_foos, struct foo, list_of_foos);
*
* @param ptr The list head
* @param type Data type of the list element to retrieve
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the last list element.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
#define __container_of(ptr, sample, member) \
(void *)container_of((ptr), typeof(*(sample)), member)
/**
* Loop through the list given by head and set pos to struct in the list.
*
* Example:
* struct foo *iterator;
* list_for_each_entry(iterator, &bar->list_of_foos, entry) {
* [modify iterator]
* }
*
* This macro is not safe for node deletion. Use list_for_each_entry_safe
* instead.
*
* @param pos Iterator variable of the type of the list elements.
* @param head List head
* @param member Member name of the struct list_head in the list elements.
*
*/
#define list_for_each_entry(pos, head, member) \
for (pos = __container_of((head)->next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
/**
* Loop through the list, keeping a backup pointer to the element. This
* macro allows for the deletion of a list element while looping through the
* list.
*
* See list_for_each_entry for more details.
*/
#define list_for_each_entry_safe(pos, tmp, head, member) \
for (pos = __container_of((head)->next, pos, member), \
tmp = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = tmp, tmp = __container_of(pos->member.next, tmp, member))
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = __container_of((head)->prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_continue(pos, head, member) \
for (pos = __container_of(pos->member.next, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#define list_for_each_entry_continue_reverse(pos, head, member) \
for (pos = __container_of(pos->member.prev, pos, member); \
&pos->member != (head); \
pos = __container_of(pos->member.prev, pos, member))
#define list_for_each_entry_from(pos, head, member) \
for (; \
&pos->member != (head); \
pos = __container_of(pos->member.next, pos, member))
#endif

2
deps/rbtree/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,2 @@
add_library(rbtree STATIC rbtree.cpp)
target_include_directories(rbtree PUBLIC ${CMAKE_CURRENT_LIST_DIR})

624
deps/rbtree/rbtree.cpp vendored Normal file
View File

@@ -0,0 +1,624 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
(C) 2012 Michel Lespinasse <walken@google.com>
linux/lib/rbtree.c
*/
#include "rbtree_augmented.h"
/*
* red-black trees properties: https://en.wikipedia.org/wiki/Rbtree
*
* 1) A node is either red or black
* 2) The root is black
* 3) All leaves (NULL) are black
* 4) Both children of every red node are black
* 5) Every simple path from root to leaves contains the same number
* of black nodes.
*
* 4 and 5 give the O(log n) guarantee, since 4 implies you cannot have two
* consecutive red nodes in a path and every red node is therefore followed by
* a black. So if B is the number of black nodes on every simple path (as per
* 5), then the longest possible path due to 4 is 2B.
*
* We shall indicate color with case, where black nodes are uppercase and red
* nodes will be lowercase. Unknown color nodes shall be drawn as red within
* parentheses and have some accompanying text comment.
*/
/*
* Notes on lockless lookups:
*
* All stores to the tree structure (rb_left and rb_right) must be done using
* WRITE_ONCE(). And we must not inadvertently cause (temporary) loops in the
* tree structure as seen in program order.
*
* These two requirements will allow lockless iteration of the tree -- not
* correct iteration mind you, tree rotations are not atomic so a lookup might
* miss entire subtrees.
*
* But they do guarantee that any such traversal will only see valid elements
* and that it will indeed complete -- does not get stuck in a loop.
*
* It also guarantees that if the lookup returns an element it is the 'correct'
* one. But not returning an element does _NOT_ mean it's not present.
*
* NOTE:
*
* Stores to __rb_parent_color are not important for simple lookups so those
* are left undone as of now. Nor did I check for loops involving parent
* pointers.
*/
#define likely(expr) __builtin_expect((expr), 1)
#define unlikely(expr) __builtin_expect((expr), 0)
static inline void rb_set_black(struct rb_node *rb)
{
rb->__rb_parent_color |= RB_BLACK;
}
static inline struct rb_node *rb_red_parent(struct rb_node *red)
{
return (struct rb_node *)red->__rb_parent_color;
}
/*
* Helper function for rotations:
* - old's parent and color get assigned to new
* - old gets assigned new as a parent and 'color' as a color.
*/
static inline void
__rb_rotate_set_parents(struct rb_node *old, struct rb_node *new_node,
struct rb_root *root, int color)
{
struct rb_node *parent = rb_parent(old);
new_node->__rb_parent_color = old->__rb_parent_color;
rb_set_parent_color(old, new_node, color);
__rb_change_child(old, new_node, parent, root);
}
static inline void
__rb_insert(struct rb_node *node, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node))
{
struct rb_node *parent = rb_red_parent(node), *gparent, *tmp;
while (true)
{
/*
* Loop invariant: node is red.
*/
if (unlikely(!parent))
{
/*
* The inserted node is root. Either this is the
* first node, or we recursed at Case 1 below and
* are no longer violating 4).
*/
rb_set_parent_color(node, NULL, RB_BLACK);
break;
}
/*
* If there is a black parent, we are done.
* Otherwise, take some corrective action as,
* per 4), we don't want a red root or two
* consecutive red nodes.
*/
if (rb_is_black(parent))
break;
gparent = rb_red_parent(parent);
tmp = gparent->rb_right;
if (parent != tmp)
{ /* parent == gparent->rb_left */
if (tmp && rb_is_red(tmp))
{
/*
* Case 1 - node's uncle is red (color flips).
*
* G g
* / \ / \
* p u --> P U
* / /
* n n
*
* However, since g's parent might be red, and
* 4) does not allow this, we need to recurse
* at g.
*/
rb_set_parent_color(tmp, gparent, RB_BLACK);
rb_set_parent_color(parent, gparent, RB_BLACK);
node = gparent;
parent = rb_parent(node);
rb_set_parent_color(node, parent, RB_RED);
continue;
}
tmp = parent->rb_right;
if (node == tmp)
{
/*
* Case 2 - node's uncle is black and node is
* the parent's right child (left rotate at parent).
*
* G G
* / \ / \
* p U --> n U
* \ /
* n p
*
* This still leaves us in violation of 4), the
* continuation into Case 3 will fix that.
*/
tmp = node->rb_left;
parent->rb_right = tmp;
node->rb_left = parent;
if (tmp)
rb_set_parent_color(tmp, parent,
RB_BLACK);
rb_set_parent_color(parent, node, RB_RED);
augment_rotate(parent, node);
parent = node;
tmp = node->rb_right;
}
/*
* Case 3 - node's uncle is black and node is
* the parent's left child (right rotate at gparent).
*
* G P
* / \ / \
* p U --> n g
* / \
* n U
*/
gparent->rb_left = tmp; /* == parent->rb_right */
parent->rb_right = gparent;
if (tmp)
rb_set_parent_color(tmp, gparent, RB_BLACK);
__rb_rotate_set_parents(gparent, parent, root, RB_RED);
augment_rotate(gparent, parent);
break;
}
else
{
tmp = gparent->rb_left;
if (tmp && rb_is_red(tmp))
{
/* Case 1 - color flips */
rb_set_parent_color(tmp, gparent, RB_BLACK);
rb_set_parent_color(parent, gparent, RB_BLACK);
node = gparent;
parent = rb_parent(node);
rb_set_parent_color(node, parent, RB_RED);
continue;
}
tmp = parent->rb_left;
if (node == tmp)
{
/* Case 2 - right rotate at parent */
tmp = node->rb_right;
parent->rb_left = tmp;
node->rb_right = parent;
if (tmp)
rb_set_parent_color(tmp, parent,
RB_BLACK);
rb_set_parent_color(parent, node, RB_RED);
augment_rotate(parent, node);
parent = node;
tmp = node->rb_left;
}
/* Case 3 - left rotate at gparent */
gparent->rb_right = tmp; /* == parent->rb_left */
parent->rb_left = gparent;
if (tmp)
rb_set_parent_color(tmp, gparent, RB_BLACK);
__rb_rotate_set_parents(gparent, parent, root, RB_RED);
augment_rotate(gparent, parent);
break;
}
}
}
/*
* Inline version for rb_erase() use - we want to be able to inline
* and eliminate the dummy_rotate callback there
*/
static inline void
____rb_erase_color(struct rb_node *parent, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node))
{
struct rb_node *node = NULL, *sibling, *tmp1, *tmp2;
while (true)
{
/*
* Loop invariants:
* - node is black (or NULL on first iteration)
* - node is not the root (parent is not NULL)
* - All leaf paths going through parent and node have a
* black node count that is 1 lower than other leaf paths.
*/
sibling = parent->rb_right;
if (node != sibling)
{ /* node == parent->rb_left */
if (rb_is_red(sibling))
{
/*
* Case 1 - left rotate at parent
*
* P S
* / \ / \
* N s --> p Sr
* / \ / \
* Sl Sr N Sl
*/
tmp1 = sibling->rb_left;
parent->rb_right = tmp1;
sibling->rb_left = parent;
rb_set_parent_color(tmp1, parent, RB_BLACK);
__rb_rotate_set_parents(parent, sibling, root,
RB_RED);
augment_rotate(parent, sibling);
sibling = tmp1;
}
tmp1 = sibling->rb_right;
if (!tmp1 || rb_is_black(tmp1))
{
tmp2 = sibling->rb_left;
if (!tmp2 || rb_is_black(tmp2))
{
/*
* Case 2 - sibling color flip
* (p could be either color here)
*
* (p) (p)
* / \ / \
* N S --> N s
* / \ / \
* Sl Sr Sl Sr
*
* This leaves us violating 5) which
* can be fixed by flipping p to black
* if it was red, or by recursing at p.
* p is red when coming from Case 1.
*/
rb_set_parent_color(sibling, parent,
RB_RED);
if (rb_is_red(parent))
rb_set_black(parent);
else
{
node = parent;
parent = rb_parent(node);
if (parent)
continue;
}
break;
}
/*
* Case 3 - right rotate at sibling
* (p could be either color here)
*
* (p) (p)
* / \ / \
* N S --> N sl
* / \ \
* sl Sr S
* \
* Sr
*
* Note: p might be red, and then both
* p and sl are red after rotation(which
* breaks property 4). This is fixed in
* Case 4 (in __rb_rotate_set_parents()
* which set sl the color of p
* and set p RB_BLACK)
*
* (p) (sl)
* / \ / \
* N sl --> P S
* \ / \
* S N Sr
* \
* Sr
*/
tmp1 = tmp2->rb_right;
sibling->rb_left = tmp1;
tmp2->rb_right = sibling;
parent->rb_right = tmp2;
if (tmp1)
rb_set_parent_color(tmp1, sibling,
RB_BLACK);
augment_rotate(sibling, tmp2);
tmp1 = sibling;
sibling = tmp2;
}
/*
* Case 4 - left rotate at parent + color flips
* (p and sl could be either color here.
* After rotation, p becomes black, s acquires
* p's color, and sl keeps its color)
*
* (p) (s)
* / \ / \
* N S --> P Sr
* / \ / \
* (sl) sr N (sl)
*/
tmp2 = sibling->rb_left;
parent->rb_right = tmp2;
sibling->rb_left = parent;
rb_set_parent_color(tmp1, sibling, RB_BLACK);
if (tmp2)
rb_set_parent(tmp2, parent);
__rb_rotate_set_parents(parent, sibling, root,
RB_BLACK);
augment_rotate(parent, sibling);
break;
}
else
{
sibling = parent->rb_left;
if (rb_is_red(sibling))
{
/* Case 1 - right rotate at parent */
tmp1 = sibling->rb_right;
parent->rb_left = tmp1;
sibling->rb_right = parent;
rb_set_parent_color(tmp1, parent, RB_BLACK);
__rb_rotate_set_parents(parent, sibling, root,
RB_RED);
augment_rotate(parent, sibling);
sibling = tmp1;
}
tmp1 = sibling->rb_left;
if (!tmp1 || rb_is_black(tmp1))
{
tmp2 = sibling->rb_right;
if (!tmp2 || rb_is_black(tmp2))
{
/* Case 2 - sibling color flip */
rb_set_parent_color(sibling, parent,
RB_RED);
if (rb_is_red(parent))
rb_set_black(parent);
else
{
node = parent;
parent = rb_parent(node);
if (parent)
continue;
}
break;
}
/* Case 3 - left rotate at sibling */
tmp1 = tmp2->rb_left;
sibling->rb_right = tmp1;
tmp2->rb_left = sibling;
parent->rb_left = tmp2;
if (tmp1)
rb_set_parent_color(tmp1, sibling,
RB_BLACK);
augment_rotate(sibling, tmp2);
tmp1 = sibling;
sibling = tmp2;
}
/* Case 4 - right rotate at parent + color flips */
tmp2 = sibling->rb_right;
parent->rb_left = tmp2;
sibling->rb_right = parent;
rb_set_parent_color(tmp1, sibling, RB_BLACK);
if (tmp2)
rb_set_parent(tmp2, parent);
__rb_rotate_set_parents(parent, sibling, root,
RB_BLACK);
augment_rotate(parent, sibling);
break;
}
}
}
/* Non-inline version for rb_erase_augmented() use */
void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node))
{
____rb_erase_color(parent, root, augment_rotate);
}
/*
* Non-augmented rbtree manipulation functions.
*
* We use dummy augmented callbacks here, and have the compiler optimize them
* out of the rb_insert_color() and rb_erase() function definitions.
*/
static inline void dummy_propagate(struct rb_node *node, struct rb_node *stop) {}
static inline void dummy_copy(struct rb_node *old, struct rb_node *new_node) {}
static inline void dummy_rotate(struct rb_node *old, struct rb_node *new_node) {}
static const struct rb_augment_callbacks dummy_callbacks = {
.propagate = dummy_propagate,
.copy = dummy_copy,
.rotate = dummy_rotate};
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
__rb_insert(node, root, dummy_rotate);
}
void rb_erase(struct rb_node *node, struct rb_root *root)
{
struct rb_node *rebalance;
rebalance = __rb_erase_augmented(node, root, &dummy_callbacks);
if (rebalance)
____rb_erase_color(rebalance, root, dummy_rotate);
}
/*
* Augmented rbtree manipulation functions.
*
* This instantiates the same inline functions as in the non-augmented
* case, but this time with user-defined callbacks.
*/
void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node))
{
__rb_insert(node, root, augment_rotate);
}
/*
* This function returns the first node (in sort order) of the tree.
*/
struct rb_node *rb_first(const struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return NULL;
while (n->rb_left)
n = n->rb_left;
return n;
}
struct rb_node *rb_last(const struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return NULL;
while (n->rb_right)
n = n->rb_right;
return n;
}
struct rb_node *rb_next(const struct rb_node *node)
{
struct rb_node *parent;
if (RB_EMPTY_NODE(node))
return NULL;
/*
* If we have a right-hand child, go down and then left as far
* as we can.
*/
if (node->rb_right)
{
node = node->rb_right;
while (node->rb_left)
node = node->rb_left;
return (struct rb_node *)node;
}
/*
* No right-hand children. Everything down and left is smaller than us,
* so any 'next' node must be in the general direction of our parent.
* Go up the tree; any time the ancestor is a right-hand child of its
* parent, keep going up. First time it's a left-hand child of its
* parent, said parent is our 'next' node.
*/
while ((parent = rb_parent(node)) && node == parent->rb_right)
node = parent;
return parent;
}
struct rb_node *rb_prev(const struct rb_node *node)
{
struct rb_node *parent;
if (RB_EMPTY_NODE(node))
return NULL;
/*
* If we have a left-hand child, go down and then right as far
* as we can.
*/
if (node->rb_left)
{
node = node->rb_left;
while (node->rb_right)
node = node->rb_right;
return (struct rb_node *)node;
}
/*
* No left-hand children. Go up till we find an ancestor which
* is a right-hand child of its parent.
*/
while ((parent = rb_parent(node)) && node == parent->rb_left)
node = parent;
return parent;
}
void rb_replace_node(struct rb_node *victim, struct rb_node *new_node,
struct rb_root *root)
{
struct rb_node *parent = rb_parent(victim);
/* Copy the pointers/colour from the victim to the replacement */
*new_node = *victim;
/* Set the surrounding nodes to point to the replacement */
if (victim->rb_left)
rb_set_parent(victim->rb_left, new_node);
if (victim->rb_right)
rb_set_parent(victim->rb_right, new_node);
__rb_change_child(victim, new_node, parent, root);
}
static struct rb_node *rb_left_deepest_node(const struct rb_node *node)
{
for (;;)
{
if (node->rb_left)
node = node->rb_left;
else if (node->rb_right)
node = node->rb_right;
else
return (struct rb_node *)node;
}
}
struct rb_node *rb_next_postorder(const struct rb_node *node)
{
const struct rb_node *parent;
if (!node)
return NULL;
parent = rb_parent(node);
/* If we're sitting on node, we've already seen our children */
if (parent && node == parent->rb_left && parent->rb_right)
{
/* If we are the parent's left node, go to the parent's right
* node then all the way down to the left */
return rb_left_deepest_node(parent->rb_right);
}
else
/* Otherwise we are the parent's right node, and the parent
* should be next */
return (struct rb_node *)parent;
}
struct rb_node *rb_first_postorder(const struct rb_root *root)
{
if (!root->rb_node)
return NULL;
return rb_left_deepest_node(root->rb_node);
}

383
deps/rbtree/rbtree.h vendored Normal file
View File

@@ -0,0 +1,383 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
This will avoid us to use callbacks and to drop drammatically performances.
I know it's not the cleaner way, but in C (not in C++) to get
performances and genericity...
See Documentation/core-api/rbtree.rst for documentation and samples.
*/
#ifndef _LINUX_RBTREE_H
#define _LINUX_RBTREE_H
#include <stdlib.h>
struct rb_node
{
unsigned long __rb_parent_color;
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */
struct rb_root
{
struct rb_node *rb_node;
};
/*
* Leftmost-cached rbtrees.
*
* We do not cache the rightmost node based on footprint
* size vs number of potential users that could benefit
* from O(1) rb_last(). Just not worth it, users that want
* this feature can always implement the logic explicitly.
* Furthermore, users that want to cache both pointers may
* find it a bit asymmetric, but that's ok.
*/
struct rb_root_cached
{
struct rb_root rb_root;
struct rb_node *rb_leftmost;
};
#define RB_ROOT \
(struct rb_root) { NULL, }
#define RB_ROOT_CACHED \
(struct rb_root_cached) { { \
NULL, \
}, \
NULL }
/**
* Returns a pointer to the container of this list element.
*
* Example:
* struct foo* f;
* f = container_of(&foo->entry, struct foo, entry);
* assert(f == foo);
*
* @param ptr Pointer to the struct list_head.
* @param type Data type of the list element.
* @param member Member name of the struct list_head field in the list element.
* @return A pointer to the data struct containing the list head.
*/
#ifndef container_of
#define container_of(ptr, type, member) \
(type *)((char *)(ptr) - (char *)&((type *)0)->member)
#endif
#define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3))
#define rb_entry(ptr, type, member) container_of(ptr, type, member)
#define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL)
/* 'empty' nodes are nodes that are known not to be inserted in an rbtree */
#define RB_EMPTY_NODE(node) \
((node)->__rb_parent_color == (unsigned long)(node))
#define RB_CLEAR_NODE(node) \
((node)->__rb_parent_color = (unsigned long)(node))
extern void rb_insert_color(struct rb_node *, struct rb_root *);
extern void rb_erase(struct rb_node *, struct rb_root *);
/* Find logical next and previous nodes in a tree */
extern struct rb_node *rb_next(const struct rb_node *);
extern struct rb_node *rb_prev(const struct rb_node *);
extern struct rb_node *rb_first(const struct rb_root *);
extern struct rb_node *rb_last(const struct rb_root *);
/* Postorder iteration - always visit the parent after its children */
extern struct rb_node *rb_first_postorder(const struct rb_root *);
extern struct rb_node *rb_next_postorder(const struct rb_node *);
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new_node,
struct rb_root *root);
static inline void rb_link_node(struct rb_node *node, struct rb_node *parent,
struct rb_node **rb_link)
{
node->__rb_parent_color = (unsigned long)parent;
node->rb_left = node->rb_right = NULL;
*rb_link = node;
}
#define rb_entry_safe(ptr, type, member) \
({ \
typeof(ptr) ____ptr = (ptr); \
____ptr ? rb_entry(____ptr, type, member) : NULL; \
})
/**
* rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of
* given type allowing the backing memory of @pos to be invalidated
*
* @pos: the 'type *' to use as a loop cursor.
* @n: another 'type *' to use as temporary storage
* @root: 'rb_root *' of the rbtree.
* @field: the name of the rb_node field within 'type'.
*
* rbtree_postorder_for_each_entry_safe() provides a similar guarantee as
* list_for_each_entry_safe() and allows the iteration to continue independent
* of changes to @pos by the body of the loop.
*
* Note, however, that it cannot handle other modifications that re-order the
* rbtree it is iterating over. This includes calling rb_erase() on @pos, as
* rb_erase() may rebalance the tree, causing us to miss some nodes.
*/
#define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \
for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \
pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \
typeof(*pos), field); 1; }); \
pos = n)
/* Same as rb_first(), but O(1) */
#define rb_first_cached(root) (root)->rb_leftmost
static inline void rb_insert_color_cached(struct rb_node *node,
struct rb_root_cached *root,
bool leftmost)
{
if (leftmost)
root->rb_leftmost = node;
rb_insert_color(node, &root->rb_root);
}
static inline struct rb_node *
rb_erase_cached(struct rb_node *node, struct rb_root_cached *root)
{
struct rb_node *leftmost = NULL;
if (root->rb_leftmost == node)
leftmost = root->rb_leftmost = rb_next(node);
rb_erase(node, &root->rb_root);
return leftmost;
}
static inline void rb_replace_node_cached(struct rb_node *victim,
struct rb_node *new_node,
struct rb_root_cached *root)
{
if (root->rb_leftmost == victim)
root->rb_leftmost = new_node;
rb_replace_node(victim, new_node, &root->rb_root);
}
/*
* The below helper functions use 2 operators with 3 different
* calling conventions. The operators are related like:
*
* comp(a->key,b) < 0 := less(a,b)
* comp(a->key,b) > 0 := less(b,a)
* comp(a->key,b) == 0 := !less(a,b) && !less(b,a)
*
* If these operators define a partial order on the elements we make no
* guarantee on which of the elements matching the key is found. See
* rb_find().
*
* The reason for this is to allow the find() interface without requiring an
* on-stack dummy object, which might not be feasible due to object size.
*/
/**
* rb_add_cached() - insert @node into the leftmost cached tree @tree
* @node: node to insert
* @tree: leftmost cached tree to insert @node into
* @less: operator defining the (partial) node order
*
* Returns @node when it is the new leftmost, or NULL.
*/
static inline struct rb_node *
rb_add_cached(struct rb_node *node, struct rb_root_cached *tree,
bool (*less)(struct rb_node *, const struct rb_node *))
{
struct rb_node **link = &tree->rb_root.rb_node;
struct rb_node *parent = NULL;
bool leftmost = true;
while (*link)
{
parent = *link;
if (less(node, parent))
{
link = &parent->rb_left;
}
else
{
link = &parent->rb_right;
leftmost = false;
}
}
rb_link_node(node, parent, link);
rb_insert_color_cached(node, tree, leftmost);
return leftmost ? node : NULL;
}
/**
* rb_add() - insert @node into @tree
* @node: node to insert
* @tree: tree to insert @node into
* @less: operator defining the (partial) node order
*/
static inline void
rb_add(struct rb_node *node, struct rb_root *tree,
bool (*less)(struct rb_node *, const struct rb_node *))
{
struct rb_node **link = &tree->rb_node;
struct rb_node *parent = NULL;
while (*link)
{
parent = *link;
if (less(node, parent))
link = &parent->rb_left;
else
link = &parent->rb_right;
}
rb_link_node(node, parent, link);
rb_insert_color(node, tree);
}
/**
* rb_find_add() - find equivalent @node in @tree, or add @node
* @node: node to look-for / insert
* @tree: tree to search / modify
* @cmp: operator defining the node order
*
* Returns the rb_node matching @node, or NULL when no match is found and @node
* is inserted.
*/
static inline struct rb_node *
rb_find_add(struct rb_node *node, struct rb_root *tree,
int (*cmp)(struct rb_node *, const struct rb_node *))
{
struct rb_node **link = &tree->rb_node;
struct rb_node *parent = NULL;
int c;
while (*link)
{
parent = *link;
c = cmp(node, parent);
if (c < 0)
link = &parent->rb_left;
else if (c > 0)
link = &parent->rb_right;
else
return parent;
}
rb_link_node(node, parent, link);
rb_insert_color(node, tree);
return NULL;
}
/**
* rb_find() - find @key in tree @tree
* @key: key to match
* @tree: tree to search
* @cmp: operator defining the node order
*
* Returns the rb_node matching @key or NULL.
*/
static inline struct rb_node *
rb_find(const void *key, const struct rb_root *tree,
int (*cmp)(const void *key, const struct rb_node *))
{
struct rb_node *node = tree->rb_node;
while (node)
{
int c = cmp(key, node);
if (c < 0)
node = node->rb_left;
else if (c > 0)
node = node->rb_right;
else
return node;
}
return NULL;
}
/**
* rb_find_first() - find the first @key in @tree
* @key: key to match
* @tree: tree to search
* @cmp: operator defining node order
*
* Returns the leftmost node matching @key, or NULL.
*/
static inline struct rb_node *
rb_find_first(const void *key, const struct rb_root *tree,
int (*cmp)(const void *key, const struct rb_node *))
{
struct rb_node *node = tree->rb_node;
struct rb_node *match = NULL;
while (node)
{
int c = cmp(key, node);
if (c <= 0)
{
if (!c)
match = node;
node = node->rb_left;
}
else if (c > 0)
{
node = node->rb_right;
}
}
return match;
}
/**
* rb_next_match() - find the next @key in @tree
* @key: key to match
* @tree: tree to search
* @cmp: operator defining node order
*
* Returns the next node matching @key, or NULL.
*/
static inline struct rb_node *
rb_next_match(const void *key, struct rb_node *node,
int (*cmp)(const void *key, const struct rb_node *))
{
node = rb_next(node);
if (node && cmp(key, node))
node = NULL;
return node;
}
/**
* rb_for_each() - iterates a subtree matching @key
* @node: iterator
* @key: key to match
* @tree: tree to search
* @cmp: operator defining node order
*/
#define rb_for_each(node, key, tree, cmp) \
for ((node) = rb_find_first((key), (tree), (cmp)); \
(node); (node) = rb_next_match((key), (node), (cmp)))
#endif /* _LINUX_RBTREE_H */

320
deps/rbtree/rbtree_augmented.h vendored Normal file
View File

@@ -0,0 +1,320 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
(C) 2012 Michel Lespinasse <walken@google.com>
linux/include/linux/rbtree_augmented.h
*/
#ifndef _LINUX_RBTREE_AUGMENTED_H
#define _LINUX_RBTREE_AUGMENTED_H
#include "rbtree.h"
/*
* Please note - only struct rb_augment_callbacks and the prototypes for
* rb_insert_augmented() and rb_erase_augmented() are intended to be public.
* The rest are implementation details you are not expected to depend on.
*
* See Documentation/core-api/rbtree.rst for documentation and samples.
*/
struct rb_augment_callbacks
{
void (*propagate)(struct rb_node *node, struct rb_node *stop);
void (*copy)(struct rb_node *old, struct rb_node *new_node);
void (*rotate)(struct rb_node *old, struct rb_node *new_node);
};
extern void __rb_insert_augmented(struct rb_node *node, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node));
/*
* Fixup the rbtree and update the augmented information when rebalancing.
*
* On insertion, the user must update the augmented information on the path
* leading to the inserted node, then call rb_link_node() as usual and
* rb_insert_augmented() instead of the usual rb_insert_color() call.
* If rb_insert_augmented() rebalances the rbtree, it will callback into
* a user provided function to update the augmented information on the
* affected subtrees.
*/
static inline void
rb_insert_augmented(struct rb_node *node, struct rb_root *root,
const struct rb_augment_callbacks *augment)
{
__rb_insert_augmented(node, root, augment->rotate);
}
static inline void
rb_insert_augmented_cached(struct rb_node *node,
struct rb_root_cached *root, bool newleft,
const struct rb_augment_callbacks *augment)
{
if (newleft)
root->rb_leftmost = node;
rb_insert_augmented(node, &root->rb_root, augment);
}
/*
* Template for declaring augmented rbtree callbacks (generic case)
*
* RBSTATIC: 'static' or empty
* RBNAME: name of the rb_augment_callbacks structure
* RBSTRUCT: struct type of the tree nodes
* RBFIELD: name of struct rb_node field within RBSTRUCT
* RBAUGMENTED: name of field within RBSTRUCT holding data for subtree
* RBCOMPUTE: name of function that recomputes the RBAUGMENTED data
*/
#define RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \
RBSTRUCT, RBFIELD, RBAUGMENTED, RBCOMPUTE) \
static inline void \
RBNAME##_propagate(struct rb_node *rb, struct rb_node *stop) \
{ \
while (rb != stop) \
{ \
RBSTRUCT *node = rb_entry(rb, RBSTRUCT, RBFIELD); \
if (RBCOMPUTE(node, true)) \
break; \
rb = rb_parent(&node->RBFIELD); \
} \
} \
static inline void \
RBNAME##_copy(struct rb_node *rb_old, struct rb_node *rb_new) \
{ \
RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD); \
RBSTRUCT *new_node = rb_entry(rb_new, RBSTRUCT, RBFIELD); \
new_node->RBAUGMENTED = old->RBAUGMENTED; \
} \
static void \
RBNAME##_rotate(struct rb_node *rb_old, struct rb_node *rb_new) \
{ \
RBSTRUCT *old = rb_entry(rb_old, RBSTRUCT, RBFIELD); \
RBSTRUCT *new_node = rb_entry(rb_new, RBSTRUCT, RBFIELD); \
new_node->RBAUGMENTED = old->RBAUGMENTED; \
RBCOMPUTE(old, false); \
} \
RBSTATIC const struct rb_augment_callbacks RBNAME = { \
.propagate = RBNAME##_propagate, \
.copy = RBNAME##_copy, \
.rotate = RBNAME##_rotate};
/*
* Template for declaring augmented rbtree callbacks,
* computing RBAUGMENTED scalar as max(RBCOMPUTE(node)) for all subtree nodes.
*
* RBSTATIC: 'static' or empty
* RBNAME: name of the rb_augment_callbacks structure
* RBSTRUCT: struct type of the tree nodes
* RBFIELD: name of struct rb_node field within RBSTRUCT
* RBTYPE: type of the RBAUGMENTED field
* RBAUGMENTED: name of RBTYPE field within RBSTRUCT holding data for subtree
* RBCOMPUTE: name of function that returns the per-node RBTYPE scalar
*/
#define RB_DECLARE_CALLBACKS_MAX(RBSTATIC, RBNAME, RBSTRUCT, RBFIELD, \
RBTYPE, RBAUGMENTED, RBCOMPUTE) \
static inline bool RBNAME##_compute_max(RBSTRUCT *node, bool exit) \
{ \
RBSTRUCT *child; \
RBTYPE max = RBCOMPUTE(node); \
if (node->RBFIELD.rb_left) \
{ \
child = rb_entry(node->RBFIELD.rb_left, RBSTRUCT, RBFIELD); \
if (child->RBAUGMENTED > max) \
max = child->RBAUGMENTED; \
} \
if (node->RBFIELD.rb_right) \
{ \
child = rb_entry(node->RBFIELD.rb_right, RBSTRUCT, RBFIELD); \
if (child->RBAUGMENTED > max) \
max = child->RBAUGMENTED; \
} \
if (exit && node->RBAUGMENTED == max) \
return true; \
node->RBAUGMENTED = max; \
return false; \
} \
RB_DECLARE_CALLBACKS(RBSTATIC, RBNAME, \
RBSTRUCT, RBFIELD, RBAUGMENTED, RBNAME##_compute_max)
#define RB_RED 0
#define RB_BLACK 1
#define __rb_parent(pc) ((struct rb_node *)(pc & ~3))
#define __rb_color(pc) ((pc) & 1)
#define __rb_is_black(pc) __rb_color(pc)
#define __rb_is_red(pc) (!__rb_color(pc))
#define rb_color(rb) __rb_color((rb)->__rb_parent_color)
#define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color)
#define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color)
static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
{
rb->__rb_parent_color = rb_color(rb) | (unsigned long)p;
}
static inline void rb_set_parent_color(struct rb_node *rb,
struct rb_node *p, int color)
{
rb->__rb_parent_color = (unsigned long)p | color;
}
static inline void
__rb_change_child(struct rb_node *old, struct rb_node *new_node,
struct rb_node *parent, struct rb_root *root)
{
if (parent)
{
if (parent->rb_left == old)
parent->rb_left = new_node;
else
parent->rb_right = new_node;
}
else
root->rb_node = new_node;
}
extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root,
void (*augment_rotate)(struct rb_node *old, struct rb_node *new_node));
static inline struct rb_node *
__rb_erase_augmented(struct rb_node *node, struct rb_root *root,
const struct rb_augment_callbacks *augment)
{
struct rb_node *child = node->rb_right;
struct rb_node *tmp = node->rb_left;
struct rb_node *parent, *rebalance;
unsigned long pc;
if (!tmp)
{
/*
* Case 1: node to erase has no more than 1 child (easy!)
*
* Note that if there is one child it must be red due to 5)
* and node must be black due to 4). We adjust colors locally
* so as to bypass __rb_erase_color() later on.
*/
pc = node->__rb_parent_color;
parent = __rb_parent(pc);
__rb_change_child(node, child, parent, root);
if (child)
{
child->__rb_parent_color = pc;
rebalance = NULL;
}
else
rebalance = __rb_is_black(pc) ? parent : NULL;
tmp = parent;
}
else if (!child)
{
/* Still case 1, but this time the child is node->rb_left */
tmp->__rb_parent_color = pc = node->__rb_parent_color;
parent = __rb_parent(pc);
__rb_change_child(node, tmp, parent, root);
rebalance = NULL;
tmp = parent;
}
else
{
struct rb_node *successor = child, *child2;
tmp = child->rb_left;
if (!tmp)
{
/*
* Case 2: node's successor is its right child
*
* (n) (s)
* / \ / \
* (x) (s) -> (x) (c)
* \
* (c)
*/
parent = successor;
child2 = successor->rb_right;
augment->copy(node, successor);
}
else
{
/*
* Case 3: node's successor is leftmost under
* node's right child subtree
*
* (n) (s)
* / \ / \
* (x) (y) -> (x) (y)
* / /
* (p) (p)
* / /
* (s) (c)
* \
* (c)
*/
do
{
parent = successor;
successor = tmp;
tmp = tmp->rb_left;
} while (tmp);
child2 = successor->rb_right;
parent->rb_left = child2;
successor->rb_right = child;
rb_set_parent(child, successor);
augment->copy(node, successor);
augment->propagate(parent, successor);
}
tmp = node->rb_left;
successor->rb_left = tmp;
rb_set_parent(tmp, successor);
pc = node->__rb_parent_color;
tmp = __rb_parent(pc);
__rb_change_child(node, successor, tmp, root);
if (child2)
{
rb_set_parent_color(child2, parent, RB_BLACK);
rebalance = NULL;
}
else
{
rebalance = rb_is_black(successor) ? parent : NULL;
}
successor->__rb_parent_color = pc;
tmp = successor;
}
augment->propagate(tmp, NULL);
return rebalance;
}
static inline void
rb_erase_augmented(struct rb_node *node, struct rb_root *root,
const struct rb_augment_callbacks *augment)
{
struct rb_node *rebalance = __rb_erase_augmented(node, root, augment);
if (rebalance)
__rb_erase_color(rebalance, root, augment->rotate);
}
static inline void
rb_erase_augmented_cached(struct rb_node *node, struct rb_root_cached *root,
const struct rb_augment_callbacks *augment)
{
if (root->rb_leftmost == node)
root->rb_leftmost = rb_next(node);
rb_erase_augmented(node, &root->rb_root, augment);
}
#endif /* _LINUX_RBTREE_AUGMENTED_H */

View File

@@ -826,6 +826,11 @@ static inline const char *parse_mpls(struct packet *pkt, const char *data, uint1
{
next_proto = MPLS_NEXT_PROTO_MPLS;
}
if (unlikely(hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_MPLS);
return data;
}
SET_LAYER(pkt, layer, LAYER_TYPE_MPLS, hdr_len, data, len);
switch (next_proto)
@@ -863,6 +868,11 @@ static inline const char *parse_ipv4(struct packet *pkt, const char *data, uint1
struct ip *hdr = (struct ip *)data;
uint8_t next_proto = ipv4_hdr_get_proto(hdr);
uint16_t hdr_len = ipv4_hdr_get_hdr_len(hdr);
if (unlikely(hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_IPV4);
return data;
}
SET_LAYER(pkt, layer, LAYER_TYPE_IPV4, hdr_len, data, len);
// ip fragmented
@@ -913,7 +923,7 @@ static inline const char *parse_gre(struct packet *pkt, const char *data, uint16
#define GRE_PRO_PPP (0x880B)
uint16_t hdr_len = get_gre_hdr_len(data, len);
if (unlikely(hdr_len == 0))
if (unlikely(hdr_len == 0 || hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_GRE);
return data;
@@ -989,6 +999,11 @@ static inline const char *parse_tcp(struct packet *pkt, const char *data, uint16
return data;
}
uint16_t hdr_len = tcp_hdr_get_hdr_len((struct tcphdr *)data);
if (unlikely(hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_TCP);
return data;
}
SET_LAYER(pkt, layer, LAYER_TYPE_TCP, hdr_len, data, len);
return layer->pld_ptr;
@@ -1024,7 +1039,7 @@ static inline const char *parse_vxlan(struct packet *pkt, const char *data, uint
static inline const char *parse_gtpv1_u(struct packet *pkt, const char *data, uint16_t len)
{
uint16_t hdr_len = get_gtp_hdr_len(data, len);
if (unlikely(hdr_len == 0))
if (unlikely(hdr_len == 0 || hdr_len > len))
{
PACKET_LOG_DATA_INSUFFICIENCY(LAYER_TYPE_GTPV1_U);
return data;

View File

@@ -98,6 +98,12 @@ static void *dumpfile_thread_cycle(void *arg)
file_scan(handle->dumpfile_dir, dumpfile_handle, arg);
while (ATOMIC_READ(&handle->io_thread_need_exit) == 0)
{
PACKET_IO_LOG_STATE("dumpfile io thread waiting");
sleep(1);
}
PACKET_IO_LOG_STATE("dumpfile io thread is exiting");
ATOMIC_SET(&handle->io_thread_is_runing, 0);

View File

@@ -527,6 +527,7 @@ static struct session *session_manager_new_tcp_session(struct session_manager *m
return NULL;
}
mgr->stat.tcp_sess.nr_sess_used++;
SESSION_LOG_DEBUG("session %lu, c2s reassembler %p, s2c reassembler %p", session_get_id(sess), sess->c2s_reassembly, sess->s2c_reassembly);
enum session_dir dir = tcp_hdr_get_ack_flag(hdr) ? SESSION_DIR_S2C : SESSION_DIR_C2S;
enum session_state next_state = session_transition_run(SESSION_STATE_INIT, TCP_SYN);

View File

@@ -159,7 +159,7 @@ void session_transition_log(struct session *sess, enum session_state curr_state,
char reason[128] = {0};
tuple6_to_str(session_get0_key(sess), buff, sizeof(buff));
session_inputs_to_str(inputs, reason, sizeof(reason));
SESSION_TRANSITION_LOG_DEBUG("%s session %lu %s (%s) %s -> %s",
session_type_to_str(session_get_type(sess)), session_get_id(sess), buff, reason,
session_state_to_str(curr_state), session_state_to_str(next_state));
SESSION_TRANSITION_LOG_INFO("%s session %lu %s (%s) %s -> %s",
session_type_to_str(session_get_type(sess)), session_get_id(sess), buff, reason,
session_state_to_str(curr_state), session_state_to_str(next_state));
}

View File

@@ -9,7 +9,7 @@ extern "C"
#include "log.h"
#include "session.h"
#define SESSION_TRANSITION_LOG_DEBUG(format, ...) LOG_DEBUG("session transition", format, ##__VA_ARGS__)
#define SESSION_TRANSITION_LOG_INFO(format, ...) LOG_INFO("session transition", format, ##__VA_ARGS__)
enum session_inputs
{

View File

@@ -95,14 +95,18 @@ void plugin_manager_dispatch(void *plugin_mgr, struct session *sess, const struc
if (session_get_type(sess) == SESSION_TYPE_TCP)
{
payload = session_peek_tcp_payload(sess, &len);
if (payload && len > 0)
do
{
hex_dump(payload, len);
}
session_consume_tcp_payload(sess, len);
payload = session_peek_tcp_payload(sess, &len);
if (payload && len > 0)
{
hex_dump(payload, len);
}
session_consume_tcp_payload(sess, len);
} while (payload && len > 0);
}
session_set0_cur_pkt(sess, NULL);
session_set_cur_dir(sess, SESSION_DIR_NONE);
printf("<= plugin dispatch session\n");
}

View File

@@ -1,5 +1,6 @@
add_library(tcp_reassembly tcp_reassembly.cpp)
target_include_directories(tcp_reassembly PUBLIC ${CMAKE_CURRENT_LIST_DIR})
target_include_directories(tcp_reassembly PUBLIC ${CMAKE_SOURCE_DIR}/deps/list)
target_link_libraries(tcp_reassembly interval_tree log)
add_subdirectory(test)

View File

@@ -2,36 +2,27 @@
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "list.h"
#include "tcp_reassembly.h"
#include "itree.h"
#include "interval_tree.h"
struct segment
{
struct tcp_reassembly *assy;
struct segment *next;
struct segment *prev;
uint64_t id;
struct interval_tree_node tree_node;
struct list_head list_node;
uint64_t time;
uint32_t offset;
uint32_t len;
uint64_t id;
char *payload; // Flexible array member
};
struct segment_list
{
struct segment *head; // del segment from head
struct segment *tail; // add segment to tail
};
struct tcp_reassembly
{
struct tcp_reassembly_options opts;
struct tcp_reassembly_stat stat;
struct segment_list list;
struct itree *itree;
struct rb_root_cached tree_root;
struct list_head list_root;
uint64_t exp_seq;
};
@@ -45,73 +36,6 @@ static inline bool before(uint32_t seq1, uint32_t seq2)
return (int32_t)(seq1 - seq2) < 0;
}
static inline void segment_list_add(struct segment_list *list, struct segment *seg)
{
if (list->head == NULL)
{
list->head = seg;
}
else
{
list->tail->next = seg;
seg->prev = list->tail;
}
list->tail = seg;
}
static inline void segment_list_del(struct segment_list *list, struct segment *seg)
{
if (list->head == seg)
{
list->head = seg->next;
}
if (list->tail == seg)
{
list->tail = seg->prev;
}
if (seg->prev)
{
seg->prev->next = seg->next;
}
if (seg->next)
{
seg->next->prev = seg->prev;
}
}
static inline struct segment *segment_list_get_oldest(struct segment_list *list)
{
return list->head;
}
static inline struct segment *segment_new(uint32_t len)
{
return (struct segment *)calloc(1, sizeof(struct segment) + len);
}
static inline void *segment_dup(void *p)
{
return p;
}
static inline void segment_free(void *p)
{
struct segment *seg = NULL;
struct tcp_reassembly *assy = NULL;
if (p)
{
seg = (struct segment *)p;
assy = seg->assy;
segment_list_del(&assy->list, seg);
assy->stat.curr_bytes -= seg->len;
assy->stat.curr_segments--;
free(seg);
}
}
struct tcp_reassembly *tcp_reassembly_new(struct tcp_reassembly_options *opts)
{
struct tcp_reassembly *assy = NULL;
@@ -121,35 +45,30 @@ struct tcp_reassembly *tcp_reassembly_new(struct tcp_reassembly_options *opts)
{
return NULL;
}
memcpy(&assy->opts, opts, sizeof(struct tcp_reassembly_options));
if (!assy->opts.enable)
{
return assy;
}
assy->itree = itree_new(segment_dup, segment_free);
if (assy->itree == NULL)
{
goto error_out;
}
assy->tree_root = RB_ROOT_CACHED;
INIT_LIST_HEAD(&assy->list_root);
return assy;
error_out:
tcp_reassembly_free(assy);
return NULL;
}
void tcp_reassembly_free(struct tcp_reassembly *assy)
{
struct segment *seg = NULL;
struct interval_tree_node *tree_node = NULL;
if (assy)
{
if (assy->itree)
while ((tree_node = interval_tree_iter_first(&assy->tree_root, 0, UINT64_MAX)))
{
itree_delete(assy->itree);
seg = container_of(tree_node, struct segment, tree_node);
interval_tree_remove(&seg->tree_node, &assy->tree_root);
list_del(&seg->list_node);
free(seg);
seg = NULL;
}
free(assy);
assy = NULL;
}
}
@@ -171,28 +90,30 @@ void tcp_reassembly_expire(struct tcp_reassembly *assy, uint64_t now)
return;
}
uint64_t high;
interval_t expire;
uint64_t len;
struct segment *seg = NULL;
while ((seg = segment_list_get_oldest(&assy->list)) != NULL)
while (!list_empty(&assy->list_root))
{
seg = list_first_entry(&assy->list_root, struct segment, list_node);
if (seg->time + assy->opts.max_timeout > now)
{
break;
}
high = (uint64_t)seg->offset + (uint64_t)seg->len - 1;
expire = {
.low = seg->offset,
.high = high,
.data = seg,
};
assy->stat.timeout_discard_segments++;
assy->stat.timeout_discard_bytes += seg->len;
TCP_REASSEMBLE_DEBUG("reassembler %p expire segment %p [%lu, %lu] (time: %lu, now: %lu)", assy, seg, seg->offset, high, seg->time, now);
len = seg->tree_node.last - seg->tree_node.start + 1;
itree_remove(assy->itree, &expire);
assy->stat.timeout_discard_segments++;
assy->stat.timeout_discard_bytes += len;
assy->stat.curr_segments--;
assy->stat.curr_bytes -= len;
TCP_REASSEMBLE_DEBUG("reassembler %p expire segment %p [%lu, %lu] (time: %lu, now: %lu)", assy, seg, seg->tree_node.start, seg->tree_node.last, seg->time, now);
interval_tree_remove(&seg->tree_node, &assy->tree_root);
list_del(&seg->list_node);
free(seg);
seg = NULL;
}
}
@@ -205,8 +126,6 @@ void tcp_reassembly_insert(struct tcp_reassembly *assy, uint32_t offset, const c
uint64_t low = (uint64_t)offset;
uint64_t high = (uint64_t)offset + (uint64_t)len - 1; // from uint32_t to uint64_t, so no overflow
struct segment *seg = NULL;
interval_t insert;
assy->stat.insert_segments++;
assy->stat.insert_bytes += len;
@@ -235,35 +154,29 @@ void tcp_reassembly_insert(struct tcp_reassembly *assy, uint32_t offset, const c
return;
}
seg = segment_new(len);
struct segment *seg = (struct segment *)calloc(1, sizeof(struct segment) + len);
if (seg == NULL)
{
assy->stat.overload_bypass_segments++;
assy->stat.overload_bypass_bytes += len;
TCP_REASSEMBLE_DEBUG("reassembler %p insert [%lu, %lu] failed, calloc segment failed", assy, low, high);
return;
}
seg->assy = assy;
seg->id = assy->stat.insert_segments;
seg->tree_node.start = low;
seg->tree_node.last = high;
seg->time = now;
seg->offset = offset;
seg->len = len;
seg->id = assy->stat.insert_segments;
seg->payload = (char *)seg + sizeof(struct segment);
memcpy(seg->payload, payload, len);
insert = {
.low = low,
.high = high,
.data = seg,
};
if (itree_insert(assy->itree, &insert) == 0)
{
free(seg);
return;
}
TCP_REASSEMBLE_DEBUG("reassembler %p insert segment %p [%lu, %lu]", assy, seg, insert.low, insert.high);
segment_list_add(&assy->list, seg);
list_add_tail(&seg->list_node, &assy->list_root);
interval_tree_insert(&seg->tree_node, &assy->tree_root);
TCP_REASSEMBLE_DEBUG("reassembler %p insert segment %p [%lu, %lu]", assy, seg, low, high);
assy->stat.curr_segments++;
assy->stat.curr_bytes += seg->len;
assy->stat.curr_bytes += len;
}
const char *tcp_reassembly_peek(struct tcp_reassembly *assy, uint32_t *len)
@@ -275,89 +188,49 @@ const char *tcp_reassembly_peek(struct tcp_reassembly *assy, uint32_t *len)
return NULL;
}
int count = 0;
interval_t peek;
uint64_t overlap = 0;
uint64_t min_id = UINT64_MAX;
uint64_t id = UINT64_MAX;
struct segment *seg = NULL;
ilist_t *list = NULL;
ilisttrav_t *trav = NULL;
interval_t *query = NULL;
interval_t *oldest = NULL;
struct interval_tree_node *tree_node = NULL;
struct interval_tree_node *oldest_node = NULL;
tree_node = interval_tree_iter_first(&assy->tree_root, assy->exp_seq, assy->exp_seq);
while (tree_node)
{
seg = container_of(tree_node, struct segment, tree_node);
if (seg->id < id)
{
id = seg->id;
oldest_node = tree_node;
}
tree_node = interval_tree_iter_next(tree_node, assy->exp_seq, assy->exp_seq);
}
peek = {
.low = assy->exp_seq,
.high = assy->exp_seq,
};
list = itree_findall(assy->itree, &peek);
if (list == NULL)
if (oldest_node == NULL)
{
return NULL;
}
count = ilist_size(list);
trav = ilisttrav_new(list);
for (int i = 0; i < count; i++)
uint64_t payload_len = oldest_node->last - oldest_node->start + 1;
seg = container_of(oldest_node, struct segment, tree_node);
if (oldest_node->start < assy->exp_seq)
{
if (i == 0)
{
query = (interval_t *)ilisttrav_first(trav);
}
else
{
query = (interval_t *)ilisttrav_next(trav);
}
seg = (struct segment *)query->data;
if (seg->id < min_id)
{
min_id = seg->id;
oldest = query;
}
}
ilisttrav_delete(trav);
ilist_delete(list);
if (oldest == NULL)
{
return NULL;
}
seg = (struct segment *)oldest->data;
if (seg->offset < assy->exp_seq)
{
overlap = assy->exp_seq - seg->offset;
*len = seg->len - overlap;
TCP_REASSEMBLE_DEBUG("reassembler %p peek [%lu, +∞], found segment %p [%lu, %lu] (left overlap: %lu)", assy, assy->exp_seq, seg, oldest->low, oldest->high, overlap);
uint64_t overlap = assy->exp_seq - oldest_node->start;
*len = (uint16_t)(payload_len - overlap);
TCP_REASSEMBLE_DEBUG("reassembler %p peek [%lu, +∞], found segment %p [%lu, %lu] (left overlap: %lu)", assy, assy->exp_seq, seg, oldest_node->start, oldest_node->last, overlap);
return seg->payload + overlap;
}
TCP_REASSEMBLE_DEBUG("reassembler %p peek [%lu, +∞], found segment %p [%lu, %lu]", assy, assy->exp_seq, seg, oldest->low, oldest->high);
TCP_REASSEMBLE_DEBUG("reassembler %p peek [%lu, +∞], found segment %p [%lu, %lu]", assy, assy->exp_seq, seg, oldest_node->start, oldest_node->last);
*len = seg->len;
*len = (uint16_t)payload_len;
return seg->payload;
}
void tcp_reassembly_consume(struct tcp_reassembly *assy, uint32_t len)
{
if (!assy->opts.enable)
if (!assy->opts.enable || len == 0)
{
return;
}
if (len == 0)
{
return;
}
int count;
uint64_t old_exp_seq;
uint64_t new_exp_seq;
interval_t consume;
ilist_t *list = NULL;
interval_t *del = NULL;
ilisttrav_t *trav = NULL;
struct segment *seg = NULL;
/*
* https://www.ietf.org/rfc/rfc0793.txt
*
@@ -376,52 +249,46 @@ void tcp_reassembly_consume(struct tcp_reassembly *assy, uint32_t len)
* seq range: [0, 4294967295]
* seq range: [0, UINT32_MAX]
*/
old_exp_seq = assy->exp_seq;
uint64_t old_exp_seq = assy->exp_seq;
assy->exp_seq += len;
if (assy->exp_seq > UINT32_MAX)
{
assy->exp_seq = assy->exp_seq % 4294967296;
}
new_exp_seq = assy->exp_seq;
uint64_t new_exp_seq = assy->exp_seq;
TCP_REASSEMBLE_DEBUG("reassembler %p consume [%lu, %lu], update expect seq %lu -> %lu", assy, old_exp_seq, old_exp_seq + len - 1, old_exp_seq, new_exp_seq);
consume =
{
.low = old_exp_seq,
.high = old_exp_seq + len - 1,
};
list = itree_findall(assy->itree, &consume);
if (list == NULL)
{
return;
}
assy->stat.consume_segments++;
assy->stat.consume_bytes += len;
count = ilist_size(list);
trav = ilisttrav_new(list);
for (int i = 0; i < count; i++)
struct interval_tree_node *node = interval_tree_iter_first(&assy->tree_root, old_exp_seq, old_exp_seq + len - 1);
while (node)
{
if (i == 0)
if (before(node->last, new_exp_seq))
{
del = (interval_t *)ilisttrav_first(trav);
struct segment *seg = container_of(node, struct segment, tree_node);
uint32_t len = node->last - node->start + 1;
assy->stat.remove_segments++;
assy->stat.remove_bytes += len;
assy->stat.curr_segments--;
assy->stat.curr_bytes -= len;
TCP_REASSEMBLE_DEBUG("reassembler %p consume [%lu, %lu], delete segment %p [%lu, %lu]", assy, old_exp_seq, old_exp_seq + len - 1, node, node->start, node->last);
interval_tree_remove(node, &assy->tree_root);
list_del(&seg->list_node);
free(seg);
node = interval_tree_iter_first(&assy->tree_root, old_exp_seq, old_exp_seq + len - 1);
}
else
{
del = (interval_t *)ilisttrav_next(trav);
}
if (del && before(del->high, new_exp_seq))
{
seg = (struct segment *)del->data;
assy->stat.remove_segments++;
assy->stat.remove_bytes += seg->len;
TCP_REASSEMBLE_DEBUG("reassembler %p consume [%lu, %lu], delete segment %p [%lu, %lu]", assy, old_exp_seq, old_exp_seq + len - 1, seg, del->low, del->high);
itree_remove(assy->itree, del);
node = interval_tree_iter_next(node, old_exp_seq, old_exp_seq + len - 1);
}
}
ilisttrav_delete(trav);
ilist_delete(list);
}
struct tcp_reassembly_stat *tcp_reassembly_get_stat(struct tcp_reassembly *assy)