forked from osmocom/wireshark
Introduces augmented interval trees
Interval trees (wmem_itree_t) are implemented as an extension of wmem_tree with a guint64-based range as the key. This is useful for instance in MPTCP analysis, to look for packets matching a range defined by a mapping across TCP subflows. Change-Id: Iea706d44fe975e390a4191ad0257ef37d5c71525 Reviewed-on: https://code.wireshark.org/review/11714 Reviewed-by: Evan Huus <eapache@gmail.com> Petri-Dish: Evan Huus <eapache@gmail.com> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Guy Harris <guy@alum.mit.edu>
This commit is contained in:
parent
9601a4f724
commit
bd08ab920d
|
@ -1545,6 +1545,7 @@ set(WMEM_FILES
|
||||||
wmem/wmem_allocator_block_fast.c
|
wmem/wmem_allocator_block_fast.c
|
||||||
wmem/wmem_allocator_simple.c
|
wmem/wmem_allocator_simple.c
|
||||||
wmem/wmem_allocator_strict.c
|
wmem/wmem_allocator_strict.c
|
||||||
|
wmem/wmem_interval_tree.c
|
||||||
wmem/wmem_list.c
|
wmem/wmem_list.c
|
||||||
wmem/wmem_map.c
|
wmem/wmem_map.c
|
||||||
wmem/wmem_miscutl.c
|
wmem/wmem_miscutl.c
|
||||||
|
|
|
@ -35,7 +35,8 @@ LIBWMEM_SRC = \
|
||||||
wmem_stack.c \
|
wmem_stack.c \
|
||||||
wmem_strbuf.c \
|
wmem_strbuf.c \
|
||||||
wmem_strutl.c \
|
wmem_strutl.c \
|
||||||
wmem_tree.c \
|
wmem_tree.c \
|
||||||
|
wmem_interval_tree.c \
|
||||||
wmem_user_cb.c
|
wmem_user_cb.c
|
||||||
|
|
||||||
LIBWMEM_INCLUDES = \
|
LIBWMEM_INCLUDES = \
|
||||||
|
@ -56,7 +57,9 @@ LIBWMEM_INCLUDES = \
|
||||||
wmem_stack.h \
|
wmem_stack.h \
|
||||||
wmem_strbuf.h \
|
wmem_strbuf.h \
|
||||||
wmem_strutl.h \
|
wmem_strutl.h \
|
||||||
wmem_tree.h \
|
wmem_tree.h \
|
||||||
|
wmem_tree-int.h \
|
||||||
|
wmem_interval_tree.h \
|
||||||
wmem_user_cb.h \
|
wmem_user_cb.h \
|
||||||
wmem_user_cb_int.h
|
wmem_user_cb_int.h
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "wmem_strbuf.h"
|
#include "wmem_strbuf.h"
|
||||||
#include "wmem_strutl.h"
|
#include "wmem_strutl.h"
|
||||||
#include "wmem_tree.h"
|
#include "wmem_tree.h"
|
||||||
|
#include "wmem_interval_tree.h"
|
||||||
#include "wmem_user_cb.h"
|
#include "wmem_user_cb.h"
|
||||||
|
|
||||||
#endif /* __WMEM_H__ */
|
#endif /* __WMEM_H__ */
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
/* wmem_interval_tree.c
|
||||||
|
* Implements an augmented interval tree
|
||||||
|
* Based on the red-black tree implementation in epan/wmem.*
|
||||||
|
* Copyright 2015, Matthieu coudron <matthieu.coudron@lip6.fr>
|
||||||
|
*
|
||||||
|
* Wireshark - Network traffic analyzer
|
||||||
|
* By Gerald Combs <gerald@wireshark.org>
|
||||||
|
* Copyright 1998 Gerald Combs
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "wmem_core.h"
|
||||||
|
#include "wmem_tree-int.h"
|
||||||
|
#include "wmem_strutl.h"
|
||||||
|
#include "wmem_interval_tree.h"
|
||||||
|
#include "wmem_user_cb.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_range(const void *value)
|
||||||
|
{
|
||||||
|
wmem_range_t *range = (wmem_range_t *)value;
|
||||||
|
if(!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf("Range: low=%lu high=%lu max_edge=%lu\n", range->low, range->high, range->max_edge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In an augmented interval tree, each node saves the maximum edge of its child subtrees
|
||||||
|
* This function compares the children max_edge with the current max_edge
|
||||||
|
* and propagates any change to the parent nodes.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
update_max_edge(wmem_tree_node_t *node)
|
||||||
|
{
|
||||||
|
wmem_range_t *range;
|
||||||
|
wmem_range_t *range_l;
|
||||||
|
wmem_range_t *range_r;
|
||||||
|
guint64 maxEdge = 0;
|
||||||
|
|
||||||
|
if(!node) {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
range = (wmem_range_t *)node->key;
|
||||||
|
|
||||||
|
range_l = (node->left) ? (wmem_range_t *) (node->left->key) : NULL;
|
||||||
|
range_r = (node->right) ? (wmem_range_t *) (node->right->key) : NULL;
|
||||||
|
|
||||||
|
maxEdge = range->max_edge;
|
||||||
|
|
||||||
|
if(range_r) {
|
||||||
|
maxEdge = MAX(maxEdge, range_r->max_edge) ;
|
||||||
|
}
|
||||||
|
if(range_l) {
|
||||||
|
maxEdge = MAX(maxEdge, range_l->max_edge) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* a change was made, update the parent nodes */
|
||||||
|
if(range->max_edge != maxEdge) {
|
||||||
|
range->max_edge = maxEdge;
|
||||||
|
update_max_edge(node->parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
wmem_itree_range_overlap(const wmem_range_t *r1, const wmem_range_t *r2)
|
||||||
|
{
|
||||||
|
return (r1->low <= r2->high && r2->low <= r1->high);
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem_itree_t *
|
||||||
|
wmem_itree_new(wmem_allocator_t *allocator)
|
||||||
|
{
|
||||||
|
wmem_itree_t *tree = wmem_tree_new(allocator);
|
||||||
|
tree->post_rotation_cb = &update_max_edge;
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
wmem_itree_is_empty(wmem_itree_t *tree)
|
||||||
|
{
|
||||||
|
return wmem_tree_is_empty(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
wmem_tree_compare_ranges(const wmem_range_t *ra, const wmem_range_t *rb)
|
||||||
|
{
|
||||||
|
if( ra->low == rb->low) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if(ra->low < rb->low) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
wmem_itree_insert(wmem_itree_t *tree, const guint64 low, const guint64 high, void *data)
|
||||||
|
{
|
||||||
|
wmem_tree_node_t *node;
|
||||||
|
wmem_range_t *range = (wmem_range_t *)wmem_new(tree->allocator, wmem_range_t);
|
||||||
|
|
||||||
|
g_assert(low <= high);
|
||||||
|
range->low = low;
|
||||||
|
range->high = high;
|
||||||
|
range->max_edge = high;
|
||||||
|
|
||||||
|
node = wmem_tree_insert(tree, range, data, (compare_func)wmem_tree_compare_ranges);
|
||||||
|
|
||||||
|
/* Even If no rotations, still a need to update max_edge */
|
||||||
|
update_max_edge(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
wmem_itree_find_intervals_in_subtree(wmem_tree_node_t *node, wmem_range_t requested, wmem_list_t *results)
|
||||||
|
{
|
||||||
|
const wmem_range_t* current;
|
||||||
|
|
||||||
|
if(!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
current = (wmem_range_t*)node->key;
|
||||||
|
|
||||||
|
/* there is no child that can possibly match */
|
||||||
|
if(requested.low > current->max_edge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(wmem_itree_range_overlap(current, &requested)) {
|
||||||
|
wmem_list_prepend(results, node->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem_itree_find_intervals_in_subtree(node->left, requested, results);
|
||||||
|
wmem_itree_find_intervals_in_subtree(node->right, requested, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem_list_t *
|
||||||
|
wmem_itree_find_intervals(wmem_itree_t *tree, wmem_allocator_t *allocator, guint64 low, guint64 high)
|
||||||
|
{
|
||||||
|
wmem_list_t *results = NULL;
|
||||||
|
wmem_range_t requested;
|
||||||
|
requested.low = low;
|
||||||
|
requested.high = high;
|
||||||
|
results = wmem_list_new(allocator);
|
||||||
|
|
||||||
|
wmem_itree_find_intervals_in_subtree(tree->root, requested, results);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
wmem_print_itree(wmem_tree_t *tree)
|
||||||
|
{
|
||||||
|
wmem_print_tree(tree, &print_range, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||||
|
*
|
||||||
|
* Local variables:
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* tab-width: 8
|
||||||
|
* indent-tabs-mode: nil
|
||||||
|
* End:
|
||||||
|
*
|
||||||
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||||
|
* :indentSize=4:tabSize=8:noTabs=true:
|
||||||
|
*/
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* wmem_interval_tree.h
|
||||||
|
* Definitions for the Wireshark Memory Manager Red-Black Tree
|
||||||
|
* Based on the red-black tree implementation in epan/emem.*
|
||||||
|
* Copyright 2015, Matthieu coudron <matthieu.coudron@lip6.fr>
|
||||||
|
*
|
||||||
|
* Wireshark - Network traffic analyzer
|
||||||
|
* By Gerald Combs <gerald@wireshark.org>
|
||||||
|
* Copyright 1998 Gerald Combs
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#ifndef __WMEM_INTERVAL_TREE_H__
|
||||||
|
#define __WMEM_INTERVAL_TREE_H__
|
||||||
|
|
||||||
|
#include "wmem_core.h"
|
||||||
|
#include "wmem_tree.h"
|
||||||
|
#include "wmem_list.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
/** @addtogroup wmem
|
||||||
|
* @{
|
||||||
|
* @defgroup wmem-interval-tree Interval Tree
|
||||||
|
*
|
||||||
|
* http://www.geeksforgeeks.org/interval-tree/
|
||||||
|
* The idea is to augment a self-balancing Binary Search Tree (BST) like Red Black Tree, AVL Tree, etc ...
|
||||||
|
* to maintain a set of intervals so that all operations can be done in O(Logn) time.
|
||||||
|
* @{
|
||||||
|
* Following wikipedia's convention this is an augmented tree rather then an interval tree
|
||||||
|
* http://www.wikiwand.com/en/Interval_tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct _wmem_tree_t;
|
||||||
|
typedef struct _wmem_tree_t wmem_itree_t;
|
||||||
|
|
||||||
|
struct _wmem_range_t {
|
||||||
|
guint64 low; /* low is used as the key in the binary tree */
|
||||||
|
guint64 high; /* Max value of the range */
|
||||||
|
guint64 max_edge; /* max value among subtrees */
|
||||||
|
};
|
||||||
|
|
||||||
|
WS_DLL_PUBLIC
|
||||||
|
wmem_itree_t *
|
||||||
|
wmem_itree_new(wmem_allocator_t *allocator)
|
||||||
|
G_GNUC_MALLOC;
|
||||||
|
|
||||||
|
|
||||||
|
/** Returns true if the tree is empty (has no nodes). */
|
||||||
|
WS_DLL_PUBLIC
|
||||||
|
gboolean
|
||||||
|
wmem_itree_is_empty(wmem_itree_t *tree);
|
||||||
|
|
||||||
|
|
||||||
|
/** Inserts a range low-high indexed by "low" in O(log(n)).
|
||||||
|
* As in wmem_tree, if a key "low" already exists, it will be overwritten with the new data
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
WS_DLL_PUBLIC
|
||||||
|
void
|
||||||
|
wmem_itree_insert(wmem_itree_t *tree, const guint64 low, const guint64 high, void *data);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save results in a wmem_list with the scope passed as a parameter.
|
||||||
|
* wmem_list_t is always allocated even if there is no result
|
||||||
|
*/
|
||||||
|
WS_DLL_PUBLIC
|
||||||
|
wmem_list_t *
|
||||||
|
wmem_itree_find_intervals(wmem_itree_t *tree, wmem_allocator_t *allocator, guint64 low, guint64 high);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print ranges along the tree
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
wmem_print_itree(wmem_itree_t *tree);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* __WMEM_INTERVAL_TREE_H__ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||||
|
*
|
||||||
|
* Local variables:
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* tab-width: 8
|
||||||
|
* indent-tabs-mode: nil
|
||||||
|
* End:
|
||||||
|
*
|
||||||
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||||
|
* :indentSize=4:tabSize=8:noTabs=true:
|
||||||
|
*/
|
|
@ -27,6 +27,7 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include "wmem.h"
|
#include "wmem.h"
|
||||||
|
#include "wmem_tree-int.h"
|
||||||
#include "wmem_allocator.h"
|
#include "wmem_allocator.h"
|
||||||
#include "wmem_allocator_block.h"
|
#include "wmem_allocator_block.h"
|
||||||
#include "wmem_allocator_block_fast.h"
|
#include "wmem_allocator_block_fast.h"
|
||||||
|
@ -982,6 +983,109 @@ wmem_test_tree(void)
|
||||||
wmem_destroy_allocator(allocator);
|
wmem_destroy_allocator(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* to be used as userdata in the callback wmem_test_itree_check_overlap_cb*/
|
||||||
|
typedef struct wmem_test_itree_user_data {
|
||||||
|
wmem_range_t range;
|
||||||
|
guint counter;
|
||||||
|
} wmem_test_itree_user_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
/* increase userData counter in case the range match the userdata range */
|
||||||
|
static gboolean
|
||||||
|
wmem_test_itree_check_overlap_cb (const void *key, void *value _U_, void *userData)
|
||||||
|
{
|
||||||
|
const wmem_range_t *ckey = (const wmem_range_t *)key;
|
||||||
|
struct wmem_test_itree_user_data * d = (struct wmem_test_itree_user_data *)userData;
|
||||||
|
g_assert(key);
|
||||||
|
g_assert(d);
|
||||||
|
|
||||||
|
if(wmem_itree_range_overlap(ckey, &d->range)) {
|
||||||
|
d->counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
wmem_test_overlap(guint64 low, guint64 high, guint64 lowbis, guint64 highbis)
|
||||||
|
{
|
||||||
|
wmem_range_t r1 = {low, high, 0};
|
||||||
|
wmem_range_t r2 = {lowbis, highbis, 0};
|
||||||
|
return wmem_itree_range_overlap(&r1, &r2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wmem_test_itree(void)
|
||||||
|
{
|
||||||
|
wmem_allocator_t *allocator, *extra_allocator;
|
||||||
|
wmem_itree_t *tree;
|
||||||
|
int i = 0;
|
||||||
|
gint32 max_rand = 0;
|
||||||
|
wmem_test_itree_user_data_t userData;
|
||||||
|
|
||||||
|
allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
|
||||||
|
extra_allocator = wmem_allocator_new(WMEM_ALLOCATOR_STRICT);
|
||||||
|
|
||||||
|
tree = wmem_itree_new(allocator);
|
||||||
|
g_assert(tree);
|
||||||
|
g_assert(wmem_itree_is_empty(tree));
|
||||||
|
|
||||||
|
wmem_free_all(allocator);
|
||||||
|
|
||||||
|
/* make sure that wmem_test_overlap is correct (well it's no proof but...)*/
|
||||||
|
g_assert(wmem_test_overlap(0, 10, 0, 4));
|
||||||
|
g_assert(wmem_test_overlap(0, 10, 9, 14));
|
||||||
|
g_assert(wmem_test_overlap(5, 10, 3, 8));
|
||||||
|
g_assert(wmem_test_overlap(5, 10, 1, 12));
|
||||||
|
g_assert(!wmem_test_overlap(0, 10, 11, 12));
|
||||||
|
|
||||||
|
/* Generate a reference range, then fill an itree with random ranges,
|
||||||
|
then we count greedily the number of overlapping ranges and compare
|
||||||
|
the result with the optimized result
|
||||||
|
*/
|
||||||
|
|
||||||
|
userData.counter = 0;
|
||||||
|
|
||||||
|
tree = wmem_itree_new(allocator);
|
||||||
|
wmem_range_t range, r2;
|
||||||
|
|
||||||
|
/* even though keys are uint64_t, we use G_MAXINT32 as a max because of the type returned by
|
||||||
|
g_test_rand_int_range.
|
||||||
|
*/
|
||||||
|
max_rand = G_MAXINT32;
|
||||||
|
r2.max_edge = range.max_edge = 0;
|
||||||
|
range.low = g_test_rand_int_range(0, max_rand);
|
||||||
|
range.high = g_test_rand_int_range( (gint32)range.low, (gint32)max_rand);
|
||||||
|
userData.range = range;
|
||||||
|
|
||||||
|
for (i=0; i<CONTAINER_ITERS; i++) {
|
||||||
|
|
||||||
|
wmem_list_t *results = NULL;
|
||||||
|
|
||||||
|
/* reset the search */
|
||||||
|
userData.counter = 0;
|
||||||
|
r2.low = (guint64)g_test_rand_int_range(0, 100);
|
||||||
|
r2.high = (guint64)g_test_rand_int_range( (gint32)r2.low, 100);
|
||||||
|
|
||||||
|
wmem_itree_insert(tree, r2.low, r2.high, GINT_TO_POINTER(i));
|
||||||
|
|
||||||
|
/* greedy search */
|
||||||
|
wmem_tree_foreach(tree, wmem_test_itree_check_overlap_cb, &userData);
|
||||||
|
|
||||||
|
/* Optimized search */
|
||||||
|
results = wmem_itree_find_intervals(tree, allocator, range.low, range.high);
|
||||||
|
|
||||||
|
/* keep it as a loop instead of wmem_list_count in case one */
|
||||||
|
g_assert(wmem_list_count(results) == userData.counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
wmem_destroy_allocator(extra_allocator);
|
||||||
|
wmem_destroy_allocator(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -1007,6 +1111,7 @@ main(int argc, char **argv)
|
||||||
g_test_add_func("/wmem/datastruct/stack", wmem_test_stack);
|
g_test_add_func("/wmem/datastruct/stack", wmem_test_stack);
|
||||||
g_test_add_func("/wmem/datastruct/strbuf", wmem_test_strbuf);
|
g_test_add_func("/wmem/datastruct/strbuf", wmem_test_strbuf);
|
||||||
g_test_add_func("/wmem/datastruct/tree", wmem_test_tree);
|
g_test_add_func("/wmem/datastruct/tree", wmem_test_tree);
|
||||||
|
g_test_add_func("/wmem/datastruct/itree", wmem_test_itree);
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/* wmem_tree_internals.h
|
||||||
|
* Definitions for the Wireshark Memory Manager Red-Black Tree
|
||||||
|
* Copyright 2013, Evan Huus <eapache@gmail.com>
|
||||||
|
*
|
||||||
|
* Wireshark - Network traffic analyzer
|
||||||
|
* By Gerald Combs <gerald@wireshark.org>
|
||||||
|
* Copyright 1998 Gerald Combs
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __WMEM_TREE_INT_H__
|
||||||
|
#define __WMEM_TREE_INT_H__
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
typedef enum _wmem_node_color_t {
|
||||||
|
WMEM_NODE_COLOR_RED,
|
||||||
|
WMEM_NODE_COLOR_BLACK
|
||||||
|
} wmem_node_color_t;
|
||||||
|
|
||||||
|
|
||||||
|
struct _wmem_tree_node_t {
|
||||||
|
struct _wmem_tree_node_t *parent;
|
||||||
|
struct _wmem_tree_node_t *left;
|
||||||
|
struct _wmem_tree_node_t *right;
|
||||||
|
|
||||||
|
const void *key;
|
||||||
|
void *data;
|
||||||
|
|
||||||
|
wmem_node_color_t color;
|
||||||
|
gboolean is_subtree;
|
||||||
|
gboolean is_removed;
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _wmem_tree_node_t wmem_tree_node_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _wmem_itree_node_t wmem_itree_node_t;
|
||||||
|
|
||||||
|
struct _wmem_tree_t {
|
||||||
|
wmem_allocator_t *master;
|
||||||
|
wmem_allocator_t *allocator;
|
||||||
|
wmem_tree_node_t *root;
|
||||||
|
guint master_cb_id;
|
||||||
|
guint slave_cb_id;
|
||||||
|
|
||||||
|
void (*post_rotation_cb)(wmem_tree_node_t *);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct _wmem_tree_t wmem_tree_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef int (*compare_func)(const void *a, const void *b);
|
||||||
|
|
||||||
|
wmem_tree_node_t *
|
||||||
|
wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cmp);
|
||||||
|
|
||||||
|
typedef struct _wmem_range_t wmem_range_t;
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
wmem_itree_range_overlap(const wmem_range_t *r1, const wmem_range_t *r2);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* __WMEM_TREE__INTERNALS_H__ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||||
|
*
|
||||||
|
* Local variables:
|
||||||
|
* c-basic-offset: 4
|
||||||
|
* tab-width: 8
|
||||||
|
* indent-tabs-mode: nil
|
||||||
|
* End:
|
||||||
|
*
|
||||||
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
||||||
|
* :indentSize=4:tabSize=8:noTabs=true:
|
||||||
|
*/
|
|
@ -31,37 +31,11 @@
|
||||||
#include "wmem_core.h"
|
#include "wmem_core.h"
|
||||||
#include "wmem_strutl.h"
|
#include "wmem_strutl.h"
|
||||||
#include "wmem_tree.h"
|
#include "wmem_tree.h"
|
||||||
|
#include "wmem_tree-int.h"
|
||||||
#include "wmem_user_cb.h"
|
#include "wmem_user_cb.h"
|
||||||
|
|
||||||
typedef enum _wmem_node_color_t {
|
|
||||||
WMEM_NODE_COLOR_RED,
|
|
||||||
WMEM_NODE_COLOR_BLACK
|
|
||||||
} wmem_node_color_t;
|
|
||||||
|
|
||||||
struct _wmem_tree_node_t {
|
|
||||||
struct _wmem_tree_node_t *parent;
|
|
||||||
struct _wmem_tree_node_t *left;
|
|
||||||
struct _wmem_tree_node_t *right;
|
|
||||||
|
|
||||||
const void *key;
|
|
||||||
void *data;
|
|
||||||
|
|
||||||
wmem_node_color_t color;
|
|
||||||
gboolean is_subtree;
|
|
||||||
gboolean is_removed;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _wmem_tree_node_t wmem_tree_node_t;
|
|
||||||
|
|
||||||
struct _wmem_tree_t {
|
|
||||||
wmem_allocator_t *master;
|
|
||||||
wmem_allocator_t *allocator;
|
|
||||||
wmem_tree_node_t *root;
|
|
||||||
guint master_cb_id;
|
|
||||||
guint slave_cb_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int (*compare_func)(const void *a, const void *b);
|
|
||||||
|
|
||||||
static wmem_tree_node_t *
|
static wmem_tree_node_t *
|
||||||
node_uncle(wmem_tree_node_t *node)
|
node_uncle(wmem_tree_node_t *node)
|
||||||
|
@ -111,6 +85,10 @@ rotate_left(wmem_tree_t *tree, wmem_tree_node_t *node)
|
||||||
node->right->parent = node;
|
node->right->parent = node;
|
||||||
}
|
}
|
||||||
node->parent->left = node;
|
node->parent->left = node;
|
||||||
|
|
||||||
|
if (tree->post_rotation_cb) {
|
||||||
|
tree->post_rotation_cb (node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -135,6 +113,11 @@ rotate_right(wmem_tree_t *tree, wmem_tree_node_t *node)
|
||||||
node->left->parent = node;
|
node->left->parent = node;
|
||||||
}
|
}
|
||||||
node->parent->right = node;
|
node->parent->right = node;
|
||||||
|
|
||||||
|
|
||||||
|
if (tree->post_rotation_cb) {
|
||||||
|
tree->post_rotation_cb (node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -232,7 +215,7 @@ wmem_tree_new(wmem_allocator_t *allocator)
|
||||||
tree->master = allocator;
|
tree->master = allocator;
|
||||||
tree->allocator = allocator;
|
tree->allocator = allocator;
|
||||||
tree->root = NULL;
|
tree->root = NULL;
|
||||||
|
tree->post_rotation_cb = NULL;
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +255,7 @@ wmem_tree_new_autoreset(wmem_allocator_t *master, wmem_allocator_t *slave)
|
||||||
tree->master = master;
|
tree->master = master;
|
||||||
tree->allocator = slave;
|
tree->allocator = slave;
|
||||||
tree->root = NULL;
|
tree->root = NULL;
|
||||||
|
tree->post_rotation_cb = NULL;
|
||||||
|
|
||||||
tree->master_cb_id = wmem_register_callback(master, wmem_tree_destroy_cb,
|
tree->master_cb_id = wmem_register_callback(master, wmem_tree_destroy_cb,
|
||||||
tree);
|
tree);
|
||||||
|
@ -310,8 +294,13 @@ create_node(wmem_allocator_t *allocator, wmem_tree_node_t *parent, const void *k
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CREATE_DATA(TRANSFORM, DATA) ((TRANSFORM) ? (TRANSFORM)(DATA) : (DATA))
|
#define CREATE_DATA(TRANSFORM, DATA) ((TRANSFORM) ? (TRANSFORM)(DATA) : (DATA))
|
||||||
static void *
|
|
||||||
lookup_or_insert32(wmem_tree_t *tree, guint32 key,
|
|
||||||
|
/**
|
||||||
|
* return inserted node
|
||||||
|
*/
|
||||||
|
static wmem_tree_node_t *
|
||||||
|
lookup_or_insert32_node(wmem_tree_t *tree, guint32 key,
|
||||||
void*(*func)(void*), void* data, gboolean is_subtree, gboolean replace)
|
void*(*func)(void*), void* data, gboolean is_subtree, gboolean replace)
|
||||||
{
|
{
|
||||||
wmem_tree_node_t *node = tree->root;
|
wmem_tree_node_t *node = tree->root;
|
||||||
|
@ -322,7 +311,7 @@ lookup_or_insert32(wmem_tree_t *tree, guint32 key,
|
||||||
new_node = create_node(tree->allocator, NULL, GUINT_TO_POINTER(key),
|
new_node = create_node(tree->allocator, NULL, GUINT_TO_POINTER(key),
|
||||||
CREATE_DATA(func, data), WMEM_NODE_COLOR_BLACK, is_subtree);
|
CREATE_DATA(func, data), WMEM_NODE_COLOR_BLACK, is_subtree);
|
||||||
tree->root = new_node;
|
tree->root = new_node;
|
||||||
return new_node->data;
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* it was not the new root so walk the tree until we find where to
|
/* it was not the new root so walk the tree until we find where to
|
||||||
|
@ -334,7 +323,7 @@ lookup_or_insert32(wmem_tree_t *tree, guint32 key,
|
||||||
if (replace) {
|
if (replace) {
|
||||||
node->data = CREATE_DATA(func, data);
|
node->data = CREATE_DATA(func, data);
|
||||||
}
|
}
|
||||||
return node->data;
|
return node;
|
||||||
}
|
}
|
||||||
else if (key < GPOINTER_TO_UINT(node->key)) {
|
else if (key < GPOINTER_TO_UINT(node->key)) {
|
||||||
if (node->left) {
|
if (node->left) {
|
||||||
|
@ -365,7 +354,16 @@ lookup_or_insert32(wmem_tree_t *tree, guint32 key,
|
||||||
/* node will now point to the newly created node */
|
/* node will now point to the newly created node */
|
||||||
rb_insert_case1(tree, new_node);
|
rb_insert_case1(tree, new_node);
|
||||||
|
|
||||||
return new_node->data;
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *
|
||||||
|
lookup_or_insert32(wmem_tree_t *tree, guint32 key,
|
||||||
|
void*(*func)(void*), void* data, gboolean is_subtree, gboolean replace)
|
||||||
|
{
|
||||||
|
wmem_tree_node_t *node = lookup_or_insert32_node(tree, key, func, data, is_subtree, replace);
|
||||||
|
return node->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
@ -395,7 +393,7 @@ wmem_tree_lookup(wmem_tree_t *tree, const void *key, compare_func cmp)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
wmem_tree_node_t *
|
||||||
wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cmp)
|
wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cmp)
|
||||||
{
|
{
|
||||||
wmem_tree_node_t *node = tree->root;
|
wmem_tree_node_t *node = tree->root;
|
||||||
|
@ -405,7 +403,7 @@ wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cm
|
||||||
if (!node) {
|
if (!node) {
|
||||||
tree->root = create_node(tree->allocator, node, key,
|
tree->root = create_node(tree->allocator, node, key,
|
||||||
data, WMEM_NODE_COLOR_BLACK, FALSE);
|
data, WMEM_NODE_COLOR_BLACK, FALSE);
|
||||||
return;
|
return tree->root;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* it was not the new root so walk the tree until we find where to
|
/* it was not the new root so walk the tree until we find where to
|
||||||
|
@ -416,7 +414,7 @@ wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cm
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
node->data = data;
|
node->data = data;
|
||||||
node->is_removed = data ? FALSE : TRUE;
|
node->is_removed = data ? FALSE : TRUE;
|
||||||
return;
|
return node;
|
||||||
}
|
}
|
||||||
else if (result < 0) {
|
else if (result < 0) {
|
||||||
if (node->left) {
|
if (node->left) {
|
||||||
|
@ -443,6 +441,8 @@ wmem_tree_insert(wmem_tree_t *tree, const void *key, void *data, compare_func cm
|
||||||
|
|
||||||
/* node will now point to the newly created node */
|
/* node will now point to the newly created node */
|
||||||
rb_insert_case1(tree, new_node);
|
rb_insert_case1(tree, new_node);
|
||||||
|
|
||||||
|
return new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -700,59 +700,71 @@ wmem_tree_foreach(wmem_tree_t* tree, wmem_foreach_func callback,
|
||||||
return wmem_tree_foreach_nodes(tree->root, callback, user_data);
|
return wmem_tree_foreach_nodes(tree->root, callback, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wmem_print_subtree(wmem_tree_t *tree, guint32 level);
|
static void wmem_print_subtree(wmem_tree_t *tree, guint32 level, wmem_printer_func key_printer, wmem_printer_func data_printer);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wmem_tree_print_nodes(const char *prefix, wmem_tree_node_t *node, guint32 level)
|
wmem_print_indent(guint32 level) {
|
||||||
{
|
|
||||||
guint32 i;
|
guint32 i;
|
||||||
|
|
||||||
if (!node)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (i=0; i<level; i++) {
|
for (i=0; i<level; i++) {
|
||||||
printf(" ");
|
printf(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%sNODE:%p parent:%p left:%p right:%p colour:%s key:%u %s:%p\n",
|
|
||||||
prefix,
|
|
||||||
(void *)node, (void *)node->parent,
|
|
||||||
(void *)node->left, (void *)node->right,
|
|
||||||
node->color?"Black":"Red", GPOINTER_TO_UINT(node->key),
|
|
||||||
node->is_subtree?"tree":"data", node->data);
|
|
||||||
if (node->left)
|
|
||||||
wmem_tree_print_nodes("L-", node->left, level+1);
|
|
||||||
if (node->right)
|
|
||||||
wmem_tree_print_nodes("R-", node->right, level+1);
|
|
||||||
|
|
||||||
if (node->is_subtree)
|
|
||||||
wmem_print_subtree((wmem_tree_t *)node->data, level+1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wmem_print_subtree(wmem_tree_t *tree, guint32 level)
|
wmem_tree_print_nodes(const char *prefix, wmem_tree_node_t *node, guint32 level,
|
||||||
|
wmem_printer_func key_printer, wmem_printer_func data_printer)
|
||||||
{
|
{
|
||||||
guint32 i;
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wmem_print_indent(level);
|
||||||
|
|
||||||
|
printf("%sNODE:%p parent:%p left:%p right:%p colour:%s key:%p %s:%p\n",
|
||||||
|
prefix,
|
||||||
|
(void *)node, (void *)node->parent,
|
||||||
|
(void *)node->left, (void *)node->right,
|
||||||
|
node->color?"Black":"Red", node->key,
|
||||||
|
node->is_subtree?"tree":"data", node->data);
|
||||||
|
if(key_printer) {
|
||||||
|
wmem_print_indent(level);
|
||||||
|
key_printer(node->key);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if(data_printer) {
|
||||||
|
wmem_print_indent(level);
|
||||||
|
data_printer(node->data);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->left)
|
||||||
|
wmem_tree_print_nodes("L-", node->left, level+1, key_printer, data_printer);
|
||||||
|
if (node->right)
|
||||||
|
wmem_tree_print_nodes("R-", node->right, level+1, key_printer, data_printer);
|
||||||
|
|
||||||
|
if (node->is_subtree)
|
||||||
|
wmem_print_subtree((wmem_tree_t *)node->data, level+1, key_printer, data_printer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
wmem_print_subtree(wmem_tree_t *tree, guint32 level, wmem_printer_func key_printer, wmem_printer_func data_printer)
|
||||||
|
{
|
||||||
if (!tree)
|
if (!tree)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (i=0; i<level; i++) {
|
wmem_print_indent(level);
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("WMEM tree:%p root:%p\n", (void *)tree, (void *)tree->root);
|
printf("WMEM tree:%p root:%p\n", (void *)tree, (void *)tree->root);
|
||||||
if (tree->root) {
|
if (tree->root) {
|
||||||
wmem_tree_print_nodes("Root-", tree->root, level);
|
wmem_tree_print_nodes("Root-", tree->root, level, key_printer, data_printer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
wmem_print_tree(wmem_tree_t *tree)
|
wmem_print_tree(wmem_tree_t *tree, wmem_printer_func key_printer, wmem_printer_func data_printer)
|
||||||
{
|
{
|
||||||
wmem_print_subtree(tree, 0);
|
wmem_print_subtree(tree, 0, key_printer, data_printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
* Editor modelines - http://www.wireshark.org/tools/modelines.html
|
||||||
*
|
*
|
||||||
|
|
|
@ -206,6 +206,11 @@ wmem_tree_lookup32_array_le(wmem_tree_t *tree, wmem_tree_key_t *key);
|
||||||
*/
|
*/
|
||||||
typedef gboolean (*wmem_foreach_func)(const void *key, void *value, void *userdata);
|
typedef gboolean (*wmem_foreach_func)(const void *key, void *value, void *userdata);
|
||||||
|
|
||||||
|
|
||||||
|
/** Function type to print key/data of nodes in wmem_print_tree_verbose */
|
||||||
|
typedef void (*wmem_printer_func)(const void *data);
|
||||||
|
|
||||||
|
|
||||||
/** Traverse the tree and call callback(value, userdata) for each value found.
|
/** Traverse the tree and call callback(value, userdata) for each value found.
|
||||||
* Returns TRUE if the traversal was ended prematurely by the callback.
|
* Returns TRUE if the traversal was ended prematurely by the callback.
|
||||||
*/
|
*/
|
||||||
|
@ -214,9 +219,10 @@ gboolean
|
||||||
wmem_tree_foreach(wmem_tree_t* tree, wmem_foreach_func callback,
|
wmem_tree_foreach(wmem_tree_t* tree, wmem_foreach_func callback,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
/** Prints the structure of the tree to stdout. Primarily for debugging. */
|
|
||||||
|
/* Accepts callbacks to print the key and/or data (both printers can be null) */
|
||||||
void
|
void
|
||||||
wmem_print_tree(wmem_tree_t *tree);
|
wmem_print_tree(wmem_tree_t *tree, wmem_printer_func key_printer, wmem_printer_func data_printer);
|
||||||
|
|
||||||
/** @}
|
/** @}
|
||||||
* @} */
|
* @} */
|
||||||
|
|
Loading…
Reference in New Issue