From e187cd9dae2ac03dc6bd1b2a1bf1299cecb35d96 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 14 Jul 2010 05:18:30 +0000 Subject: [PATCH] enhanced the red-black tree --- qse/include/qse/cmn/htb.h | 76 ++-- qse/include/qse/cmn/rbt.h | 404 +++++++++++++++++---- qse/lib/cmn/htb.c | 4 +- qse/lib/cmn/rbt.c | 733 ++++++++++++++++++++++++-------------- qse/samples/cmn/rbt.c | 43 +-- 5 files changed, 869 insertions(+), 391 deletions(-) diff --git a/qse/include/qse/cmn/htb.h b/qse/include/qse/cmn/htb.h index 6bf0fc85..a77bceab 100644 --- a/qse/include/qse/cmn/htb.h +++ b/qse/include/qse/cmn/htb.h @@ -1,5 +1,5 @@ /* - * $Id: htb.h 328 2010-07-08 06:58:44Z hyunghwan.chung $ + * $Id: htb.h 331 2010-07-13 11:18:30Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -52,8 +52,8 @@ typedef enum qse_htb_id_t qse_htb_id_t; */ typedef void* (*qse_htb_copier_t) ( qse_htb_t* htb /* hash table */, - void* dptr /* the pointer to a key or a value */, - qse_size_t dlen /* the length of a key or a value */ + void* dptr /* pointer to a key or a value */, + qse_size_t dlen /* length of a key or a value */ ); /** @@ -61,15 +61,15 @@ typedef void* (*qse_htb_copier_t) ( */ typedef void (*qse_htb_freeer_t) ( qse_htb_t* htb, /**< hash table */ - void* dptr, /**< the pointer to a key or a value */ - qse_size_t dlen /**< the length of a key or a value */ + void* dptr, /**< pointer to a key or a value */ + qse_size_t dlen /**< length of a key or a value */ ); /* key hasher */ typedef qse_size_t (*qse_htb_hasher_t) ( qse_htb_t* htb, /**< hash table */ - const void* kptr, /**< the pointer to a key */ - qse_size_t klen /**< the length of a key in bytes */ + const void* kptr, /**< pointer to a key */ + qse_size_t klen /**< length of a key in bytes */ ); /** @@ -82,10 +82,10 @@ typedef qse_size_t (*qse_htb_hasher_t) ( */ typedef int (*qse_htb_comper_t) ( qse_htb_t* htb, /**< hash table */ - const void* kptr1, /**< the pointer to a key */ - qse_size_t klen1, /**< the length of a key */ - const void* kptr2, /**< the pointer to a key */ - qse_size_t klen2 /**< the length of a key */ + const void* kptr1, /**< pointer to a key */ + qse_size_t klen1, /**< length of a key */ + const void* kptr2, /**< pointer to a key */ + qse_size_t klen2 /**< length of a key */ ); /** @@ -101,7 +101,7 @@ typedef void (*qse_htb_keeper_t) ( ); /** - * The qse_htb_sizer_T type defines a bucket size claculator that is called + * The qse_htb_sizer_t type defines a bucket size claculator that is called * when hash table should resize the bucket. The current bucket size +1 is passed * as the hint. */ @@ -123,15 +123,17 @@ typedef qse_htb_walk_t (*qse_htb_walker_t) ( * The qse_htb_pair_t type defines hash table pair. A pair is composed of a key * and a value. It maintains pointers to the beginning of a key and a value * plus their length. The length is scaled down with the scale factor - * specified in an owning htb. Use macros defined in the + * specified in an owning hash table. */ struct qse_htb_pair_t { - void* kptr; /**< the pointer to a key */ - qse_size_t klen; /**< the length of a key */ - void* vptr; /**< the pointer to a value */ - qse_size_t vlen; /**< the length of a value */ - qse_htb_pair_t* next; /**< the next pair under the same slot */ + void* kptr; /**< pointer to a key */ + qse_size_t klen; /**< length of a key */ + void* vptr; /**< pointer to a value */ + qse_size_t vlen; /**< length of a value */ + + /* management information below */ + qse_htb_pair_t* next; }; /** @@ -162,27 +164,16 @@ struct qse_htb_t #define QSE_HTB_COPIER_SIMPLE ((qse_htb_copier_t)1) #define QSE_HTB_COPIER_INLINE ((qse_htb_copier_t)2) -/****m* Common/QSE_HTB_SIZE - * NAME - * QSE_HTB_SIZE - get the number of pairs - * DESCRIPTION - * The QSE_HTB_SIZE() macro returns the number of pairs in hash table. - * SYNOPSIS +/** + * The QSE_HTB_SIZE() macro returns the number of pairs in a hash table. */ #define QSE_HTB_SIZE(m) ((m)->size) -/*****/ -/****m* Common/QSE_HTB_CAPA - * NAME - * QSE_HTB_CAPA - get the capacity of hash table - * - * DESCRIPTION - * The QSE_HTB_CAPA() macro returns the maximum number of pairs hash table can hold. - * - * SYNOPSIS +/** + * The QSE_HTB_CAPA() macro returns the maximum number of pairs that can be + * stored in a hash table without further reorganization. */ #define QSE_HTB_CAPA(m) ((m)->capa) -/*****/ #define QSE_HTB_KCOPIER(m) ((m)->copier[QSE_HTB_KEY]) #define QSE_HTB_VCOPIER(m) ((m)->copier[QSE_HTB_VAL]) @@ -285,15 +276,15 @@ int qse_htb_getscale ( void qse_htb_setscale ( qse_htb_t* htb, /**< hash table */ qse_htb_id_t id, /**< QSE_HTB_KEY or QSE_HTB_VAL */ - int scale /* scale factor in bytes */ + int scale /**< scale factor in bytes */ ); /** * The qse_htb_getcopier() function gets a data copier. */ qse_htb_copier_t qse_htb_getcopier ( - qse_htb_t* htb, - qse_htb_id_t id /**< QSE_HTB_KEY or QSE_HTB_VAL */ + qse_htb_t* htb, /**< hash table */ + qse_htb_id_t id /**< QSE_HTB_KEY or QSE_HTB_VAL */ ); /** @@ -312,7 +303,7 @@ void qse_htb_setcopier ( ); qse_htb_freeer_t qse_htb_getfreeer ( - qse_htb_t* htb, /**< htb */ + qse_htb_t* htb, /**< hash table */ qse_htb_id_t id /**< QSE_HTB_KEY or QSE_HTB_VAL */ ); @@ -321,18 +312,17 @@ qse_htb_freeer_t qse_htb_getfreeer ( * The @a freeer is called when a node containing the element is destroyed. */ void qse_htb_setfreeer ( - qse_htb_t* htb, /**< hash table */ - qse_htb_id_t id, /**< QSE_HTB_KEY or QSE_HTB_VAL */ + qse_htb_t* htb, /**< hash table */ + qse_htb_id_t id, /**< QSE_HTB_KEY or QSE_HTB_VAL */ qse_htb_freeer_t freeer /**< an element freeer */ ); - qse_htb_hasher_t qse_htb_gethasher ( - qse_htb_t* htb + qse_htb_t* htb ); void qse_htb_sethasher ( - qse_htb_t* htb, + qse_htb_t* htb, qse_htb_hasher_t hasher ); diff --git a/qse/include/qse/cmn/rbt.h b/qse/include/qse/cmn/rbt.h index 7ebc6e29..c7d7600d 100644 --- a/qse/include/qse/cmn/rbt.h +++ b/qse/include/qse/cmn/rbt.h @@ -24,6 +24,17 @@ #include #include +/**@file + * A red-black tree is a self-balancing binary search tree. + */ + +/* values that can be returned by qse_rbt_walker_t */ +enum qse_rbt_walk_t +{ + QSE_RBT_WALK_STOP = 0, + QSE_RBT_WALK_FORWARD = 1 +}; + enum qse_rbt_id_t { QSE_RBT_KEY = 0, @@ -31,110 +42,381 @@ enum qse_rbt_id_t }; typedef struct qse_rbt_t qse_rbt_t; -typedef struct qse_rbt_node_t qse_rbt_node_t; +typedef struct qse_rbt_pair_t qse_rbt_pair_t; +typedef enum qse_rbt_walk_t qse_rbt_walk_t; typedef enum qse_rbt_id_t qse_rbt_id_t; +/** + * The qse_rbt_copier_t type defines a pair contruction callback. + */ typedef void* (*qse_rbt_copier_t) ( - qse_rbt_t* rbt, /**< red-black tree */ - void* dptr, /**< the pointer to a key or a value */ - qse_size_t dlen /**< the length of a key or a value */ + qse_rbt_t* rbt /* red-black tree */, + void* dptr /* pointer to a key or a value */, + qse_size_t dlen /* length of a key or a value */ ); +/** + * The qse_rbt_freeer_t defines a key/value destruction callback. + */ typedef void (*qse_rbt_freeer_t) ( qse_rbt_t* rbt, /**< red-black tree */ - void* dptr, /**< the pointer to a key or a value */ - qse_size_t dlen /**< the length of a key or a value */ + void* dptr, /**< pointer to a key or a value */ + qse_size_t dlen /**< length of a key or a value */ ); +/** + * The qse_rbt_comper_t type defines a key comparator that is called when + * the rbt needs to compare keys. A rbt is created with a default comparator + * which performs bitwise comparison between two keys. + * + * The comparator should return 0 if the keys are the same and a non-zero + * integer otherwise. + */ typedef int (*qse_rbt_comper_t) ( - qse_rbt_t* rbt, /**< red-black tree */ - const void* kptr1, /**< the pointer to a key */ - qse_size_t klen1, /**< the length of a key */ - const void* kptr2, /**< the pointer to a key */ - qse_size_t klen2 /**< the length of a key */ + qse_rbt_t* rbt, /**< red-black tree */ + const void* kptr1, /**< key pointer */ + qse_size_t klen1, /**< key length */ + const void* kptr2, /**< key pointer */ + qse_size_t klen2 /**< key length */ ); -struct qse_rbt_node_t -{ - int key; - int value; - - enum - { - QSE_RBT_RED, - QSE_RBT_BLACK - } color; - - qse_rbt_node_t* parent; - qse_rbt_node_t* child[2]; /* left and right */ -}; - -struct qse_rbt_t -{ - QSE_DEFINE_COMMON_FIELDS (rbt) - - qse_rbt_node_t nil; /**< internal nil node */ - - qse_byte_t scale[2]; /**< scale factor */ - - qse_rbt_copier_t copier[2]; - qse_rbt_freeer_t freeer[2]; - qse_rbt_comper_t comper; - - qse_size_t size; /**< number of nodes */ - qse_rbt_node_t* root; -}; - -enum qse_rbt_walk_t -{ - QSE_RBT_WALK_STOP = 0, - QSE_RBT_WALK_FORWARD = 1 -}; - -typedef enum qse_rbt_walk_t qse_rbt_walk_t; +/** + * The qse_rbt_keeper_t type defines a value keeper that is called when + * a value is retained in the context that it should be destroyed because + * it is identical to a new value. Two values are identical if their beginning + * pointers and their lengths are equal. + */ +typedef void (*qse_rbt_keeper_t) ( + qse_rbt_t* rbt, /**< red-black tree */ + void* vptr, /**< value pointer */ + qse_size_t vlen /**< value length */ +); /** * The qse_rbt_walker_t defines a pair visitor. */ typedef qse_rbt_walk_t (*qse_rbt_walker_t) ( - qse_rbt_t* rbt, /**< tree */ - qse_rbt_node_t* node, /**< pointer to a node */ - void* ctx /**< pointer to user-defined context */ + qse_rbt_t* rbt, /**< rbt */ + qse_rbt_pair_t* pair, /**< pointer to a key/value pair */ + void* ctx /**< pointer to user-defined data */ ); +/** + * The qse_rbt_pair_t type defines red-black tree pair. A pair is composed + * of a key and a value. It maintains pointers to the beginning of a key and + * a value plus their length. The length is scaled down with the scale factor + * specified in an owning tree. Use macros defined in the + */ +struct qse_rbt_pair_t +{ + void* kptr; /**< key pointer */ + qse_size_t klen; /**< key length */ + void* vptr; /**< value pointer */ + qse_size_t vlen; /**< value length */ + + /* management information below */ + enum + { + QSE_RBT_RED, + QSE_RBT_BLACK + } color; + qse_rbt_pair_t* parent; + qse_rbt_pair_t* child[2]; /* left and right */ +}; + +/** + * The qse_rbt_t type defines a red-black tree. + */ +struct qse_rbt_t +{ + QSE_DEFINE_COMMON_FIELDS (rbt) + + qse_rbt_copier_t copier[2]; + qse_rbt_freeer_t freeer[2]; + qse_rbt_comper_t comper; /**< key comparator */ + qse_rbt_keeper_t keeper; /**< value keeper */ + + qse_byte_t scale[2]; /**< length scale */ + qse_byte_t factor; /**< load factor */ + qse_byte_t filler0; + + qse_rbt_pair_t nil; /**< internal nil node */ + + qse_size_t size; + qse_rbt_pair_t* root; +}; + #define QSE_RBT_COPIER_SIMPLE ((qse_rbt_copier_t)1) #define QSE_RBT_COPIER_INLINE ((qse_rbt_copier_t)2) +/** + * The QSE_RBT_SIZE() macro returns the number of pairs in red-black tree. + */ +#define QSE_RBT_SIZE(m) ((m)->size) + +#define QSE_RBT_KCOPIER(m) ((m)->copier[QSE_RBT_KEY]) +#define QSE_RBT_VCOPIER(m) ((m)->copier[QSE_RBT_VAL]) +#define QSE_RBT_KFREEER(m) ((m)->freeer[QSE_RBT_KEY]) +#define QSE_RBT_VFREEER(m) ((m)->freeer[QSE_RBT_VAL]) +#define QSE_RBT_COMPER(m) ((m)->comper) +#define QSE_RBT_KEEPER(m) ((m)->keeper) + +#define QSE_RBT_FACTOR(m) ((m)->factor) +#define QSE_RBT_KSCALE(m) ((m)->scale[QSE_RBT_KEY]) +#define QSE_RBT_VSCALE(m) ((m)->scale[QSE_RBT_VAL]) + +#define QSE_RBT_KPTR(p) ((p)->kptr) +#define QSE_RBT_KLEN(p) ((p)->klen) +#define QSE_RBT_VPTR(p) ((p)->vptr) +#define QSE_RBT_VLEN(p) ((p)->vlen) +#define QSE_RBT_NEXT(p) ((p)->next) + #ifdef __cplusplus extern "C" { #endif +QSE_DEFINE_COMMON_FUNCTIONS (rbt) + +/** + * The qse_rbt_open() function creates a red-black tree. + * @return qse_rbt_t pointer on success, QSE_NULL on failure. + */ qse_rbt_t* qse_rbt_open ( - qse_mmgr_t* mmgr, /**< memory manager */ - qse_size_t ext /**< size of extension area in bytes */ + qse_mmgr_t* mmgr, /**< memory manager */ + qse_size_t ext /**< extension size in bytes */ ); +/** + * The qse_rbt_close() function destroys a red-black tree. + */ void qse_rbt_close ( - qse_rbt_t* rbt /**< red-black tree */ + qse_rbt_t* rbt /**< red-black tree */ ); - +/** + * The qse_rbt_init() function initializes a red-black tree + */ qse_rbt_t* qse_rbt_init ( - qse_rbt_t* rbt, /**< red-black tree */ - qse_mmgr_t* mmgr /**< a memory manager */ + qse_rbt_t* rbt, /**< red-black tree */ + qse_mmgr_t* mmgr /**< memory manager */ ); +/** + * The qse_rbt_fini() funtion finalizes a red-black tree + */ void qse_rbt_fini ( - qse_rbt_t* rbt /**< red-black tree */ + qse_rbt_t* rbt /**< red-black tree */ ); +/** + * The qse_rbt_getsize() function gets the number of pairs in red-black tree. + */ +qse_size_t qse_rbt_getsize ( + qse_rbt_t* rbt /**< red-black tree */ +); + +/** + * The qse_rbt_getscale() function returns the scale factor + */ +int qse_rbt_getscale ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id /**< QSE_RBT_KEY or QSE_RBT_VAL */ +); + +/** + * The qse_rbt_setscale() function sets the scale factor of the length + * of a key and a value. A scale factor determines the actual length of + * a key and a value in bytes. A rbt is created with a scale factor of 1. + * The scale factor should be larger than 0 and less than 256. + * Note that it is a bad idea to change the scale factor while a red-black tree + * is not empty. + */ +void qse_rbt_setscale ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id, /**< QSE_RBT_KEY or QSE_RBT_VAL */ + int scale /**< scale factor in bytes */ +); + +/** + * The qse_rbt_getcopier() function gets a data copier. + */ +qse_rbt_copier_t qse_rbt_getcopier ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id /**< QSE_RBT_KEY or QSE_RBT_VAL */ +); + +/** + * The qse_rbt_setcopier() function specifies how to clone an element. + * A special copier QSE_RBT_COPIER_INLINE is provided. This copier enables + * you to copy the data inline to the internal node. No freeer is invoked + * when the node is freeed. + * + * You may set the copier to QSE_NULL to perform no special operation + * when the data pointer is rememebered. + */ +void qse_rbt_setcopier ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id, /**< QSE_RBT_KEY or QSE_RBT_VAL */ + qse_rbt_copier_t copier /**< element copier */ +); + +qse_rbt_freeer_t qse_rbt_getfreeer ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id /**< QSE_RBT_KEY or QSE_RBT_VAL */ +); + +/** + * The qse_rbt_setfreeer() function specifies how to destroy an element. + * The @a freeer is called when a node containing the element is destroyed. + */ +void qse_rbt_setfreeer ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_id_t id, /**< QSE_RBT_KEY or QSE_RBT_VAL */ + qse_rbt_freeer_t freeer /**< an element freeer */ +); + + +/** + * The qse_rbt_getcomper() function returns the key comparator. + */ +qse_rbt_comper_t qse_rbt_getcomper ( + qse_rbt_t* rbt /**< red-black tree */ +); + +/** + * The qse_rbt_setcomper() function changes the key comparator. + */ +void qse_rbt_setcomper ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_comper_t comper /**< comparator function pointer */ +); + +/** + * The qse_rbt_getkeeper() function returns the value retainer function + * that is called when you change the value of an existing key with the + * same value. + */ +qse_rbt_keeper_t qse_rbt_getkeeper ( + qse_rbt_t* rbt +); + +/** + * The qse_rbt_setkeeper() function changes the value retainer function. + */ +void qse_rbt_setkeeper ( + qse_rbt_t* rbt, + qse_rbt_keeper_t keeper +); + +/** + * The qse_rbt_search() function searches red-black tree to find a pair with a + * matching key. It returns the pointer to the pair found. If it fails + * to find one, it returns QSE_NULL. + * @return pointer to the pair with a maching key, + * or QSE_NULL if no match is found. + */ +qse_rbt_pair_t* qse_rbt_search ( + qse_rbt_t* rbt, /**< red-black tree */ + const void* kptr, /**< key pointer */ + qse_size_t klen /**< the size of the key */ +); + +/** + * The qse_rbt_upsert() function searches red-black tree for the pair with a + * matching key. If one is found, it updates the pair. Otherwise, it inserts + * a new pair with the key and value given. It returns the pointer to the + * pair updated or inserted. + * @return a pointer to the updated or inserted pair on success, + * QSE_NULL on failure. + */ +qse_rbt_pair_t* qse_rbt_upsert ( + qse_rbt_t* rbt, /**< red-black tree */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* vptr, /**< value pointer */ + qse_size_t vlen /**< value length */ +); + +/** + * The qse_rbt_ensert() function inserts a new pair with the key and the value + * given. If there exists a pair with the key given, the function returns + * the pair containing the key. + * @return pointer to a pair on success, QSE_NULL on failure. + */ +qse_rbt_pair_t* qse_rbt_ensert ( + qse_rbt_t* rbt, /**< red-black tree */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* vptr, /**< value pointer */ + qse_size_t vlen /**< value length */ +); + +/** + * The qse_rbt_insert() function inserts a new pair with the key and the value + * given. If there exists a pair with the key given, the function returns + * QSE_NULL without channging the value. + * @return pointer to the pair created on success, QSE_NULL on failure. + */ +qse_rbt_pair_t* qse_rbt_insert ( + qse_rbt_t* rbt, /**< red-black tree */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* vptr, /**< value pointer */ + qse_size_t vlen /**< value length */ +); + +/** + * The qse_rbt_update() function updates the value of an existing pair + * with a matching key. + * @return pointer to the pair on success, QSE_NULL on no matching pair + */ +qse_rbt_pair_t* qse_rbt_update ( + qse_rbt_t* rbt, /**< red-black tree */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* vptr, /**< value pointer */ + qse_size_t vlen /**< value length */ +); + +/** + * The qse_rbt_delete() function deletes a pair with a matching key + * @return 0 on success, -1 on failure + */ +int qse_rbt_delete ( + qse_rbt_t* rbt, /**< red-black tree */ + const void* kptr, /**< key pointer */ + qse_size_t klen /**< key size */ +); + +/* + * The qse_rbt_clear() function empties a red-black tree. + */ void qse_rbt_clear ( - qse_rbt_t* rbt /**< red-black tree */ + qse_rbt_t* rbt /**< red-black tree */ +); + +/** + * The qse_rbt_walk() function traverses a red-black tree in preorder + * from the leftmost child. + */ +void qse_rbt_walk ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_walker_t walker, /**< pointer to the function for each pair */ + void* ctx /**< pointer to user-specific data */ +); + +/** + * The qse_rbt_walk() function traverses a red-black tree in preorder + * from the rightmost child. + */ +void qse_rbt_rwalk ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_walker_t walker, /**< pointer to the function for each pair */ + void* ctx /**< pointer to user-specific data */ ); #ifdef __cplusplus } #endif - #endif diff --git a/qse/lib/cmn/htb.c b/qse/lib/cmn/htb.c index ffcbcbd3..9f6e42ae 100644 --- a/qse/lib/cmn/htb.c +++ b/qse/lib/cmn/htb.c @@ -1,5 +1,5 @@ /* - * $Id: htb.c 328 2010-07-08 06:58:44Z hyunghwan.chung $ + * $Id: htb.c 331 2010-07-13 11:18:30Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -73,7 +73,7 @@ static size_t hash_key (htb_t* htb, const void* kptr, size_t klen) return h ; } -static int comp_key (htb_t* htb, +static QSE_INLINE int comp_key (htb_t* htb, const void* kptr1, size_t klen1, const void* kptr2, size_t klen2) { diff --git a/qse/lib/cmn/rbt.c b/qse/lib/cmn/rbt.c index ce0f8767..22f3cf5c 100644 --- a/qse/lib/cmn/rbt.c +++ b/qse/lib/cmn/rbt.c @@ -21,34 +21,42 @@ #include #include "mem.h" -#define IS_NIL(rbt,x) ((x) == &((rbt)->nil)) - -#define KTOB(htb,len) ((len)*(htb)->scale[QSE_RBT_KEY]) - -#define LEFT 0 -#define RIGHT 1 - -#define left child[LEFT] -#define right child[RIGHT] - -#define rotate_left(rbt,pivot) rotate(rbt,pivot,1); -#define rotate_right(rbt,pivot) rotate(rbt,pivot,0); - QSE_IMPLEMENT_COMMON_FUNCTIONS (rbt) -static QSE_INLINE void init_node ( - qse_rbt_t* rbt, qse_rbt_node_t* node, int color, - int key, int value) -{ - QSE_MEMSET (node, 0, QSE_SIZEOF(*node)); +#define rbt_t qse_rbt_t +#define pair_t qse_rbt_pair_t +#define copier_t qse_rbt_copier_t +#define freeer_t qse_rbt_freeer_t +#define comper_t qse_rbt_comper_t +#define keeper_t qse_rbt_keeper_t +#define walker_t qse_rbt_walker_t - node->color = color; - node->right = &rbt->nil; - node->left = &rbt->nil; +#define KPTR(p) QSE_RBT_KPTR(p) +#define KLEN(p) QSE_RBT_KLEN(p) +#define VPTR(p) QSE_RBT_VPTR(p) +#define VLEN(p) QSE_RBT_VLEN(p) - node->key = key; - node->value = value; -} +#define SIZEOF(x) QSE_SIZEOF(x) +#define size_t qse_size_t +#define byte_t qse_byte_t +#define uint_t qse_uint_t +#define mmgr_t qse_mmgr_t + +#define KTOB(rbt,len) ((len)*(rbt)->scale[QSE_RBT_KEY]) +#define VTOB(rbt,len) ((len)*(rbt)->scale[QSE_RBT_VAL]) + +#define UPSERT 1 +#define UPDATE 2 +#define ENSERT 3 +#define INSERT 4 + +#define IS_NIL(rbt,x) ((x) == &((rbt)->nil)) +#define LEFT 0 +#define RIGHT 1 +#define left child[LEFT] +#define right child[RIGHT] +#define rotate_left(rbt,pivot) rotate(rbt,pivot,1); +#define rotate_right(rbt,pivot) rotate(rbt,pivot,0); static QSE_INLINE int comp_key ( qse_rbt_t* rbt, @@ -74,9 +82,85 @@ static QSE_INLINE int comp_key ( return n; } -qse_rbt_t* qse_rbt_open (qse_mmgr_t* mmgr, qse_size_t ext) +static pair_t* alloc_pair (rbt_t* rbt, + void* kptr, size_t klen, void* vptr, size_t vlen) { - qse_rbt_t* rbt; + pair_t* n; + + copier_t kcop = rbt->copier[QSE_RBT_KEY]; + copier_t vcop = rbt->copier[QSE_RBT_VAL]; + + size_t as = SIZEOF(pair_t); + if (kcop == QSE_RBT_COPIER_INLINE) as += KTOB(rbt,klen); + if (vcop == QSE_RBT_COPIER_INLINE) as += VTOB(rbt,vlen); + + n = (pair_t*) QSE_MMGR_ALLOC (rbt->mmgr, as); + if (n == QSE_NULL) return QSE_NULL; + + n->color = QSE_RBT_RED; + n->parent = QSE_NULL; + n->child[LEFT] = &rbt->nil; + n->child[RIGHT] = &rbt->nil; + + KLEN(n) = klen; + if (kcop == QSE_RBT_COPIER_SIMPLE) + { + KPTR(n) = kptr; + } + else if (kcop == QSE_RBT_COPIER_INLINE) + { + KPTR(n) = n + 1; + QSE_MEMCPY (KPTR(n), kptr, KTOB(rbt,klen)); + } + else + { + KPTR(n) = kcop (rbt, kptr, klen); + if (KPTR(n) == QSE_NULL) + { + QSE_MMGR_FREE (rbt->mmgr, n); + return QSE_NULL; + } + } + + VLEN(n) = vlen; + if (vcop == QSE_RBT_COPIER_SIMPLE) + { + VPTR(n) = vptr; + } + else if (vcop == QSE_RBT_COPIER_INLINE) + { + VPTR(n) = n + 1; + if (kcop == QSE_RBT_COPIER_INLINE) + VPTR(n) = (byte_t*)VPTR(n) + KTOB(rbt,klen); + QSE_MEMCPY (VPTR(n), vptr, VTOB(rbt,vlen)); + } + else + { + VPTR(n) = vcop (rbt, vptr, vlen); + if (VPTR(n) != QSE_NULL) + { + if (rbt->freeer[QSE_RBT_KEY] != QSE_NULL) + rbt->freeer[QSE_RBT_KEY] (rbt, KPTR(n), KLEN(n)); + QSE_MMGR_FREE (rbt->mmgr, n); + return QSE_NULL; + } + } + + return n; +} + +static void free_pair (rbt_t* rbt, pair_t* pair) +{ + if (rbt->freeer[QSE_RBT_KEY] != QSE_NULL) + rbt->freeer[QSE_RBT_KEY] (rbt, KPTR(pair), KLEN(pair)); + if (rbt->freeer[QSE_RBT_VAL] != QSE_NULL) + rbt->freeer[QSE_RBT_VAL] (rbt, VPTR(pair), VLEN(pair)); + QSE_MMGR_FREE (rbt->mmgr, pair); +} + +rbt_t* qse_rbt_open (mmgr_t* mmgr, size_t ext) +{ + rbt_t* rbt; if (mmgr == QSE_NULL) { @@ -88,7 +172,7 @@ qse_rbt_t* qse_rbt_open (qse_mmgr_t* mmgr, qse_size_t ext) if (mmgr == QSE_NULL) return QSE_NULL; } - rbt = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_rbt_t) + ext); + rbt = (rbt_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(rbt_t) + ext); if (rbt == QSE_NULL) return QSE_NULL; if (qse_rbt_init (rbt, mmgr) == QSE_NULL) @@ -100,179 +184,146 @@ qse_rbt_t* qse_rbt_open (qse_mmgr_t* mmgr, qse_size_t ext) return rbt; } -void qse_rbt_close (qse_rbt_t* rbt) +void qse_rbt_close (rbt_t* rbt) { qse_rbt_fini (rbt); QSE_MMGR_FREE (rbt->mmgr, rbt); } -qse_rbt_t* qse_rbt_init (qse_rbt_t* rbt, qse_mmgr_t* mmgr) +rbt_t* qse_rbt_init (rbt_t* rbt, mmgr_t* mmgr) { /* do not zero out the extension */ - QSE_MEMSET (rbt, 0, QSE_SIZEOF(*rbt)); - + QSE_MEMSET (rbt, 0, SIZEOF(*rbt)); rbt->mmgr = mmgr; - rbt->size = 0; rbt->scale[QSE_RBT_KEY] = 1; rbt->scale[QSE_RBT_VAL] = 1; + rbt->size = 0; rbt->comper = comp_key; rbt->copier[QSE_RBT_KEY] = QSE_RBT_COPIER_SIMPLE; rbt->copier[QSE_RBT_VAL] = QSE_RBT_COPIER_SIMPLE; - init_node (rbt, &rbt->nil, QSE_RBT_BLACK, 0, 0); + /* + rbt->freeer[QSE_RBT_KEY] = QSE_NULL; + rbt->freeer[QSE_RBT_VAL] = QSE_NULL; + rbt->keeper = QSE_NULL; + */ + + /* self-initializing nil */ + QSE_MEMSET(&rbt->nil, 0, QSE_SIZEOF(rbt->nil)); + rbt->nil.color = QSE_RBT_BLACK; + rbt->nil.left = &rbt->nil; + rbt->nil.right = &rbt->nil; + + /* root is set to nil initially */ rbt->root = &rbt->nil; + return rbt; } -void qse_rbt_fini (qse_rbt_t* rbt) +void qse_rbt_fini (rbt_t* rbt) { qse_rbt_clear (rbt); } -qse_rbt_node_t* qse_rbt_search (qse_rbt_t* rbt, int key) +int qse_rbt_getscale (rbt_t* rbt, qse_rbt_id_t id) { - /* TODO: enhance this. ues comper... etc */ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); + return rbt->scale[id]; +} - qse_rbt_node_t* node = rbt->root; +void qse_rbt_setscale (rbt_t* rbt, qse_rbt_id_t id, int scale) +{ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); - while (!IS_NIL(rbt,node)) + QSE_ASSERTX (scale > 0 && scale <= QSE_TYPE_MAX(qse_byte_t), + "The scale should be larger than 0 and less than or equal to the maximum value that the qse_byte_t type can hold"); + + if (scale <= 0) scale = 1; + if (scale > QSE_TYPE_MAX(qse_byte_t)) scale = QSE_TYPE_MAX(qse_byte_t); + + rbt->scale[id] = scale; +} + +copier_t qse_rbt_getcopier (rbt_t* rbt, qse_rbt_id_t id) +{ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); + return rbt->copier[id]; +} + +void qse_rbt_setcopier (rbt_t* rbt, qse_rbt_id_t id, copier_t copier) +{ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); + if (copier == QSE_NULL) copier = QSE_RBT_COPIER_SIMPLE; + rbt->copier[id] = copier; +} + +freeer_t qse_rbt_getfreeer (rbt_t* rbt, qse_rbt_id_t id) +{ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); + return rbt->freeer[id]; +} + +void qse_rbt_setfreeer (rbt_t* rbt, qse_rbt_id_t id, freeer_t freeer) +{ + QSE_ASSERTX (id == QSE_RBT_KEY || id == QSE_RBT_VAL, + "The ID should be either QSE_RBT_KEY or QSE_RBT_VAL"); + rbt->freeer[id] = freeer; +} + +comper_t qse_rbt_getcomper (rbt_t* rbt) +{ + return rbt->comper; +} + +void qse_rbt_setcomper (rbt_t* rbt, comper_t comper) +{ + if (comper == QSE_NULL) comper = comp_key; + rbt->comper = comper; +} + +keeper_t qse_rbt_getkeeper (rbt_t* rbt) +{ + return rbt->keeper; +} + +void qse_rbt_setkeeper (rbt_t* rbt, keeper_t keeper) +{ + rbt->keeper = keeper; +} + +size_t qse_rbt_getsize (rbt_t* rbt) +{ + return rbt->size; +} + +pair_t* qse_rbt_search (rbt_t* rbt, const void* kptr, size_t klen) +{ + pair_t* pair = rbt->root; + + while (!IS_NIL(rbt,pair)) { -#if 0 - int n = rbt->comper (rbt, kptr, klen, node->kptr, node->klen); - if (n == 0) return node; + int n = rbt->comper (rbt, kptr, klen, pair->kptr, pair->klen); + if (n == 0) return pair; - if (n > 0) node = node->right; - else /* if (n < 0) */ node = node->left; -#endif - if (key == node->key) return node; - - if (key > node->key) node = node->right; - else /* if (key < node->key) */ node = node->left; + if (n > 0) pair = pair->right; + else /* if (n < 0) */ pair = pair->left; } return QSE_NULL; } -#if 0 -static void rotate_left (qse_rbt_t* rbt, qse_rbt_node_t* pivot) -{ - /* move the pivot node down to the poistion of the pivot's original - * left child(x). move the pivot's right child(y) to the pivot's original - * position. as 'c1' is between 'y' and 'pivot', move it to the right - * of the new pivot position. - * - * parent parent - * | | (left or right?) | | - * pivot y - * / \ / \ - * x y =====> pivot c2 - * / \ / \ - * c1 c2 x c1 - * - * the actual implementation here resolves the pivot's relationship to - * its parent by comparaing pointers as it is not known if the pivot node - * is the left child or the right child of its parent, - */ - - qse_rbt_node_t* parent, * y, * c1; - - QSE_ASSERT (pivot != QSE_NULL && pivot->right != QSE_NULL); - - parent = pivot->parent; - y = pivot->right; - c1 = y->left; - - y->parent = parent; - if (parent) - { - if (parent->left == pivot) - { - parent->left = y; - } - else - { - QSE_ASSERT (parent->right == pivot); - parent->right = y; - } - } - else - { - QSE_ASSERT (rbt->root == pivot); - rbt->root = y; - } - - y->left = pivot; - if (!IS_NIL(rbt,pivot)) pivot->parent = y; - - pivot->right = c1; - if (!IS_NIL(rbt,c1)) c1->parent = pivot; -} - -static void rotate_right (qse_rbt_t* rbt, qse_rbt_node_t* pivot) -{ - /* move the pivot node down to the poistion of the pivot's original - * right child(y). move the pivot's left child(x) to the pivot's original - * position. as 'c2' is between 'x' and 'pivot', move it to the left - * of the new pivot position. - * - * parent parent - * | | (left or right?) | | - * pivot x - * / \ / \ - * x y =====> c1 pivot - * / \ / \ - * c1 c2 c2 y - * - * the actual implementation here resolves the pivot's relationship to - * its parent by comparaing pointers as it is not known if the pivot node - * is the left child or the right child of its parent, - */ - - qse_rbt_node_t* parent, * x, * c2; - - QSE_ASSERT (pivot != QSE_NULL); - - parent = pivot->parent; - x = pivot->left; - c2 = x->right; - - x->parent = parent; - if (parent) - { - if (parent->left == pivot) - { - /* pivot is the left child of its parent */ - parent->left = x; - } - else - { - /* pivot is the right child of its parent */ - QSE_ASSERT (parent->right == pivot); - parent->right = x; - } - } - else - { - /* pivot is the root node */ - QSE_ASSERT (rbt->root == pivot); - rbt->root = x; - } - - x->right = pivot; - if (!IS_NIL(rbt,pivot)) pivot->parent = x; - - pivot->left = c2; - if (!IS_NIL(rbt,c2)) c2->parent = pivot; -} -#endif - -static void rotate (qse_rbt_t* rbt, qse_rbt_node_t* pivot, int leftwise) +static void rotate (qse_rbt_t* rbt, qse_rbt_pair_t* pivot, int leftwise) { /* * == leftwise rotation - * move the pivot node down to the poistion of the pivot's original + * move the pivot pair down to the poistion of the pivot's original * left child(x). move the pivot's right child(y) to the pivot's original * position. as 'c1' is between 'y' and 'pivot', move it to the right * of the new pivot position. @@ -285,7 +336,7 @@ static void rotate (qse_rbt_t* rbt, qse_rbt_node_t* pivot, int leftwise) * c1 c2 x c1 * * == rightwise rotation - * move the pivot node down to the poistion of the pivot's original + * move the pivot pair down to the poistion of the pivot's original * right child(y). move the pivot's left child(x) to the pivot's original * position. as 'c2' is between 'x' and 'pivot', move it to the left * of the new pivot position. @@ -300,11 +351,11 @@ static void rotate (qse_rbt_t* rbt, qse_rbt_node_t* pivot, int leftwise) * * * the actual implementation here resolves the pivot's relationship to - * its parent by comparaing pointers as it is not known if the pivot node + * its parent by comparaing pointers as it is not known if the pivot pair * is the left child or the right child of its parent, */ - qse_rbt_node_t* parent, * z, * c; + qse_rbt_pair_t* parent, * z, * c; int cid1, cid2; QSE_ASSERT (pivot != QSE_NULL); @@ -352,14 +403,14 @@ static void rotate (qse_rbt_t* rbt, qse_rbt_node_t* pivot, int leftwise) if (!IS_NIL(rbt,c)) c->parent = pivot; } -static void adjust (qse_rbt_t* rbt, qse_rbt_node_t* node) +static void adjust (qse_rbt_t* rbt, qse_rbt_pair_t* pair) { - while (node != rbt->root) + while (pair != rbt->root) { - qse_rbt_node_t* tmp, * tmp2; + qse_rbt_pair_t* tmp, * tmp2; int leftwise; - qse_rbt_node_t* xpar = node->parent; + qse_rbt_pair_t* xpar = pair->parent; if (xpar->color == QSE_RBT_BLACK) break; QSE_ASSERT (xpar->parent != QSE_NULL); @@ -382,15 +433,15 @@ static void adjust (qse_rbt_t* rbt, qse_rbt_node_t* node) xpar->color = QSE_RBT_BLACK; tmp->color = QSE_RBT_BLACK; xpar->parent->color = QSE_RBT_RED; - node = xpar->parent; + pair = xpar->parent; } else { - if (node == tmp2) + if (pair == tmp2) { - node = xpar; - rotate (rbt, node, leftwise); - xpar = node->parent; + pair = xpar; + rotate (rbt, pair, leftwise); + xpar = pair->parent; } xpar->color = QSE_RBT_BLACK; @@ -400,60 +451,141 @@ static void adjust (qse_rbt_t* rbt, qse_rbt_node_t* node) } } -static qse_rbt_node_t* new_node (qse_rbt_t* rbt, int key, int value) +static pair_t* change_pair_val ( + rbt_t* rbt, pair_t* pair, void* vptr, size_t vlen) { - qse_rbt_node_t* node; + if (VPTR(pair) == vptr && VLEN(pair) == vlen) + { + /* if the old value and the new value are the same, + * it just calls the handler for this condition. + * No value replacement occurs. */ + if (rbt->keeper != QSE_NULL) + { + rbt->keeper (rbt, vptr, vlen); + } + } + else + { + copier_t vcop = rbt->copier[QSE_RBT_VAL]; + void* ovptr = VPTR(pair); + size_t ovlen = VLEN(pair); - node = QSE_MMGR_ALLOC (rbt->mmgr, QSE_SIZEOF(*node)); - if (node == QSE_NULL) return QSE_NULL; + /* place the new value according to the copier */ + if (vcop == QSE_RBT_COPIER_SIMPLE) + { + VPTR(pair) = vptr; + VLEN(pair) = vlen; + } + else if (vcop == QSE_RBT_COPIER_INLINE) + { + if (ovlen == vlen) + { + QSE_MEMCPY (VPTR(pair), vptr, VTOB(rbt,vlen)); + } + else + { + /* need to reconstruct the pair */ + pair_t* p = alloc_pair (rbt, + KPTR(pair), KLEN(pair), + vptr, vlen); + if (p == QSE_NULL) return QSE_NULL; - init_node (rbt, node, QSE_RBT_RED, key, value); - return node; + p->color = pair->color; + p->left = pair->left; + p->right = pair->right; + p->parent = pair->parent; + + if (pair->parent) + { + if (pair->parent->left == pair) + { + pair->parent->left = p; + } + else + { + QSE_ASSERT (parent->parent->right == pair); + pair->parent->right = p; + } + } + if (!IS_NIL(rbt,pair->left)) pair->left->parent = p; + if (!IS_NIL(rbt,pair->right)) pair->right->parent = p; + + free_pair (rbt, pair); + return p; + } + } + else + { + void* nvptr = vcop (rbt, vptr, vlen); + if (nvptr == QSE_NULL) return QSE_NULL; + VPTR(pair) = nvptr; + VLEN(pair) = vlen; + } + + /* free up the old value */ + if (rbt->freeer[QSE_RBT_VAL] != QSE_NULL) + { + rbt->freeer[QSE_RBT_VAL] (rbt, ovptr, ovlen); + } + } + + return pair; } -static void free_node (qse_rbt_t* rbt, qse_rbt_node_t* node) -{ - /* TODO: call destructor... */ - QSE_MMGR_FREE (rbt->mmgr, node); -} - -qse_rbt_node_t* qse_rbt_insert (qse_rbt_t* rbt, int key, int value) +static pair_t* insert ( + rbt_t* rbt, void* kptr, size_t klen, void* vptr, size_t vlen, int opt) { /* TODO: enhance this. ues comper... etc */ - qse_rbt_node_t* xcur = rbt->root; - qse_rbt_node_t* xpar = QSE_NULL; - qse_rbt_node_t* xnew; + qse_rbt_pair_t* xcur = rbt->root; + qse_rbt_pair_t* xpar = QSE_NULL; + qse_rbt_pair_t* xnew; while (!IS_NIL(rbt,xcur)) { - if (key == xcur->key) + int n = rbt->comper (rbt, kptr, klen, xcur->kptr, xcur->klen); + if (n == 0) { - /* TODO: handle various cases depending on insert types. + /* TODO: handle various cases depending on insert types. * return error. update value. */ - xcur->value = value; - return xcur; + switch (opt) + { + case UPSERT: + case UPDATE: + return change_pair_val (rbt, xcur, vptr, vlen); + + case ENSERT: + /* return existing pair */ + return xcur; + + case INSERT: + /* return failure */ + return QSE_NULL; + } } xpar = xcur; - if (key > xcur->key) xcur = xcur->right; - else /* if (key < xcur->key) */ xcur = xcur->left; + if (n > 0) xcur = xcur->right; + else /* if (n < 0) */ xcur = xcur->left; } - xnew = new_node (rbt, key, value); + if (opt == UPDATE) return QSE_NULL; + + xnew = alloc_pair (rbt, kptr, klen, vptr, vlen); if (xnew == QSE_NULL) return QSE_NULL; if (xpar == QSE_NULL) { - /* the tree contains no node */ + /* the tree contains no pair */ QSE_ASSERT (rbt->root == &rbt->nil); rbt->root = xnew; } else { /* perform normal binary insert */ - if (key > xpar->key) + int n = rbt->comper (rbt, kptr, klen, xpar->kptr, xpar->klen); + if (n > 0) { QSE_ASSERT (xpar->right == &rbt->nil); xpar->right = xnew; @@ -472,14 +604,39 @@ qse_rbt_node_t* qse_rbt_insert (qse_rbt_t* rbt, int key, int value) return xnew; } -static void adjust_for_delete ( - qse_rbt_t* rbt, qse_rbt_node_t* node, qse_rbt_node_t* par) +pair_t* qse_rbt_upsert ( + rbt_t* rbt, void* kptr, size_t klen, void* vptr, size_t vlen) { - while (node != rbt->root && node->color == QSE_RBT_BLACK) - { - qse_rbt_node_t* tmp; + return insert (rbt, kptr, klen, vptr, vlen, UPSERT); +} - if (node == par->left) +pair_t* qse_rbt_ensert ( + rbt_t* rbt, void* kptr, size_t klen, void* vptr, size_t vlen) +{ + return insert (rbt, kptr, klen, vptr, vlen, ENSERT); +} + +pair_t* qse_rbt_insert ( + rbt_t* rbt, void* kptr, size_t klen, void* vptr, size_t vlen) +{ + return insert (rbt, kptr, klen, vptr, vlen, INSERT); +} + + +pair_t* qse_rbt_update ( + rbt_t* rbt, void* kptr, size_t klen, void* vptr, size_t vlen) +{ + return insert (rbt, kptr, klen, vptr, vlen, UPDATE); +} + +static void adjust_for_delete ( + qse_rbt_t* rbt, qse_rbt_pair_t* pair, qse_rbt_pair_t* par) +{ + while (pair != rbt->root && pair->color == QSE_RBT_BLACK) + { + qse_rbt_pair_t* tmp; + + if (pair == par->left) { tmp = par->right; if (tmp->color == QSE_RBT_RED) @@ -494,8 +651,8 @@ static void adjust_for_delete ( tmp->right->color == QSE_RBT_BLACK) { if (!IS_NIL(rbt,tmp)) tmp->color = QSE_RBT_RED; - node = par; - par = node->parent; + pair = par; + par = pair->parent; } else { @@ -514,12 +671,12 @@ static void adjust_for_delete ( tmp->right->color = QSE_RBT_BLACK; rotate_left (rbt, par); - node = rbt->root; + pair = rbt->root; } } else { - QSE_ASSERT (node == par->right); + QSE_ASSERT (pair == par->right); tmp = par->left; if (tmp->color == QSE_RBT_RED) { @@ -533,8 +690,8 @@ static void adjust_for_delete ( tmp->right->color == QSE_RBT_BLACK) { if (!IS_NIL(rbt,tmp)) tmp->color = QSE_RBT_RED; - node = par; - par = node->parent; + pair = par; + par = pair->parent; } else { @@ -552,28 +709,28 @@ static void adjust_for_delete ( tmp->left->color = QSE_RBT_BLACK; rotate_right (rbt, par); - node = rbt->root; + pair = rbt->root; } } } - node->color = QSE_RBT_BLACK; + pair->color = QSE_RBT_BLACK; } -static void delete_node (qse_rbt_t* rbt, qse_rbt_node_t* node) +static void delete_pair (qse_rbt_t* rbt, qse_rbt_pair_t* pair) { - qse_rbt_node_t* x, * y, * par; + qse_rbt_pair_t* x, * y, * par; - QSE_ASSERT (node && !IS_NIL(rbt,node)); + QSE_ASSERT (pair && !IS_NIL(rbt,pair)); - if (IS_NIL(rbt,node->left) || IS_NIL(rbt,node->right)) + if (IS_NIL(rbt,pair->left) || IS_NIL(rbt,pair->right)) { - y = node; + y = pair; } else { /* find a successor with NIL as a child */ - y = node->right; + y = pair->right; while (!IS_NIL(rbt,y->left)) y = y->left; } @@ -594,12 +751,12 @@ static void delete_node (qse_rbt_t* rbt, qse_rbt_node_t* node) rbt->root = x; } - if (y == node) + if (y == pair) { if (y->color == QSE_RBT_BLACK && !IS_NIL(rbt,x)) adjust_for_delete (rbt, x, par); - free_node (rbt, y); + free_pair (rbt, y); } else { @@ -607,85 +764,133 @@ static void delete_node (qse_rbt_t* rbt, qse_rbt_node_t* node) adjust_for_delete (rbt, x, par); #if 1 - if (node->parent) + if (pair->parent) { - if (node->parent->left == node) node->parent->left = y; - if (node->parent->right == node) node->parent->right = y; + if (pair->parent->left == pair) pair->parent->left = y; + if (pair->parent->right == pair) pair->parent->right = y; } else { rbt->root = y; } - y->parent = node->parent; - y->left = node->left; - y->right = node->right; - y->color = node->color; + y->parent = pair->parent; + y->left = pair->left; + y->right = pair->right; + y->color = pair->color; - if (node->left->parent == node) node->left->parent = y; - if (node->right->parent == node) node->right->parent = y; + if (pair->left->parent == pair) pair->left->parent = y; + if (pair->right->parent == pair) pair->right->parent = y; #else - *y = *node; + *y = *pair; if (y->parent) { - if (y->parent->left == node) y->parent->left = y; - if (y->parent->right == node) y->parent->right = y; + if (y->parent->left == pair) y->parent->left = y; + if (y->parent->right == pair) y->parent->right = y; } else { rbt->root = y; } - if (y->left->parent == node) y->left->parent = y; - if (y->right->parent == node) y->right->parent = y; + if (y->left->parent == pair) y->left->parent = y; + if (y->right->parent == pair) y->right->parent = y; #endif - free_node (rbt, node); + free_pair (rbt, pair); } - /* TODO: update tally */ + rbt->size--; } -int qse_rbt_delete (qse_rbt_t* rbt, int key) +int qse_rbt_delete (qse_rbt_t* rbt, const void* kptr, size_t klen) { - qse_rbt_node_t* node; + qse_rbt_pair_t* pair; - node = qse_rbt_search (rbt, key); - if (node == QSE_NULL) return -1; + pair = qse_rbt_search (rbt, kptr, klen); + if (pair == QSE_NULL) return -1; - delete_node (rbt, node); + delete_pair (rbt, pair); return 0; } -void qse_rbt_clear (qse_rbt_t* rbt) +void qse_rbt_clear (rbt_t* rbt) { /* TODO: improve this */ - while (!IS_NIL(rbt,rbt->root)) delete_node (rbt, rbt->root); + while (!IS_NIL(rbt,rbt->root)) delete_pair (rbt, rbt->root); } -static qse_rbt_walk_t walk ( - qse_rbt_t* rbt, qse_rbt_walker_t walker, - void* ctx, qse_rbt_node_t* node) +static QSE_INLINE void walk (rbt_t* rbt, walker_t walker, void* ctx, int l, int r) { - if (!IS_NIL(rbt,node->left)) + qse_rbt_pair_t* xcur = rbt->root; + qse_rbt_pair_t* prev = rbt->root->parent; + + while (xcur && !IS_NIL(rbt,xcur)) { - if (walk (rbt, walker, ctx, node->left) == QSE_RBT_WALK_STOP) - return QSE_RBT_WALK_STOP; + if (prev == xcur->parent) + { + /* the previous node is the parent of the current node. + * it indicates that we're going down to the child[l] */ + if (!IS_NIL(rbt,xcur->child[l])) + { + /* go to the child[l] child */ + prev = xcur; + xcur = xcur->child[l]; + } + else + { + if (walker (rbt, xcur, ctx) == QSE_RBT_WALK_STOP) break; + + if (!IS_NIL(rbt,xcur->child[r])) + { + /* go down to the right node if exists */ + prev = xcur; + xcur = xcur->child[r]; + } + else + { + /* otherwise, move up to the parent */ + prev = xcur; + xcur = xcur->parent; + } + } + } + else if (prev == xcur->child[l]) + { + /* the left child has been already traversed */ + + if (walker (rbt, xcur, ctx) == QSE_RBT_WALK_STOP) break; + + if (!IS_NIL(rbt,xcur->child[r])) + { + /* go down to the right node if it exists */ + prev = xcur; + xcur = xcur->child[r]; + } + else + { + /* otherwise, move up to the parent */ + prev = xcur; + xcur = xcur->parent; + } + } + else + { + /* both the left child and the right child have beem traversed */ + QSE_ASSERT (prev == xcur->child[r]); + /* just move up to the parent */ + prev = xcur; + xcur = xcur->parent; + } } - - if (walker (rbt, node, ctx) == QSE_RBT_WALK_STOP) - return QSE_RBT_WALK_STOP; - - if (!IS_NIL(rbt,node->right)) - { - if (walk (rbt, walker, ctx, node->right) == QSE_RBT_WALK_STOP) - return QSE_RBT_WALK_STOP; - } - - return QSE_RBT_WALK_FORWARD; } -void qse_rbt_walk (qse_rbt_t* rbt, qse_rbt_walker_t walker, void* ctx) +void qse_rbt_walk (rbt_t* rbt, walker_t walker, void* ctx) { - walk (rbt, walker, ctx, rbt->root); + walk (rbt, walker, ctx, LEFT, RIGHT); +} + +void qse_rbt_rwalk (rbt_t* rbt, walker_t walker, void* ctx) +{ + walk (rbt, walker, ctx, RIGHT, LEFT); } diff --git a/qse/samples/cmn/rbt.c b/qse/samples/cmn/rbt.c index 38bf5a23..63292796 100644 --- a/qse/samples/cmn/rbt.c +++ b/qse/samples/cmn/rbt.c @@ -11,22 +11,16 @@ if (f() == -1) return -1; \ } while (0) -static qse_rbt_walk_t walk (qse_rbt_t* rbt, qse_rbt_node_t* node, void* ctx) +static qse_rbt_walk_t walk (qse_rbt_t* rbt, qse_rbt_pair_t* pair, void* ctx) { - qse_printf (QSE_T("key = %d, value = %d\n"), node->key, node->value); + qse_printf (QSE_T("key = %d, value = %d\n"), + *(int*)QSE_RBT_KPTR(pair), *(int*)QSE_RBT_VPTR(pair)); return QSE_RBT_WALK_FORWARD; } static int test1 () { qse_rbt_t* s1; - qse_char_t* x[] = - { - QSE_T("this is so good"), - QSE_T("what the fuck"), - QSE_T("do you like it?"), - QSE_T("oopsy!") - }; int i; s1 = qse_rbt_open (QSE_MMGR_GETDFL(), 0); @@ -36,52 +30,59 @@ static int test1 () return -1; } + qse_rbt_setcopier (s1, QSE_RBT_KEY, QSE_RBT_COPIER_INLINE); + qse_rbt_setcopier (s1, QSE_RBT_VAL, QSE_RBT_COPIER_INLINE); + /* + qse_rbt_setscale (s1, QSE_RBT_KEY, QSE_SIZEOF(int)); + qse_rbt_setscale (s1, QSE_RBT_VAL, QSE_SIZEOF(int)); + */ /* - qse_rbt_setcopier (s1, QSE_LDA_COPIER_INLINE); qse_rbt_setkeeper (s1, keeper1); - qse_rbt_setscale (s1, QSE_SIZEOF(qse_char_t)); */ for (i = 0; i < 20; i++) { + int x = i * 20; qse_printf (QSE_T("inserting at %d\n"), i); - qse_rbt_insert (s1, i, i * 20); + qse_rbt_insert (s1, &i, QSE_SIZEOF(i), &x, QSE_SIZEOF(x)); } - qse_rbt_walk (s1, walk, QSE_NULL); + qse_rbt_rwalk (s1, walk, QSE_NULL); for (i = 0; i < 20; i += 2) { qse_printf (QSE_T("deleting %d\n"), i); - qse_rbt_delete (s1, i); + qse_rbt_delete (s1, &i, QSE_SIZEOF(i)); } - qse_rbt_walk (s1, walk, QSE_NULL); + qse_rbt_rwalk (s1, walk, QSE_NULL); for (i = 0; i < 20; i++) { + int x = i * 20; qse_printf (QSE_T("inserting at %d\n"), i); - qse_rbt_insert (s1, i, i * 20); + qse_rbt_insert (s1, &i, QSE_SIZEOF(i), &x, QSE_SIZEOF(x)); } - qse_rbt_walk (s1, walk, QSE_NULL); + qse_rbt_rwalk (s1, walk, QSE_NULL); qse_rbt_clear (s1); for (i = 20; i > 0; i--) { + int x = i * 20; qse_printf (QSE_T("inserting at %d\n"), i); - qse_rbt_insert (s1, i, i * 20); + qse_rbt_insert (s1, &i, QSE_SIZEOF(i), &x, QSE_SIZEOF(x)); } - qse_rbt_walk (s1, walk, QSE_NULL); + qse_rbt_rwalk (s1, walk, QSE_NULL); for (i = 0; i < 20; i += 3) { qse_printf (QSE_T("deleting %d\n"), i); - qse_rbt_delete (s1, i); + qse_rbt_delete (s1, &i, QSE_SIZEOF(i)); } - qse_rbt_walk (s1, walk, QSE_NULL); + qse_rbt_rwalk (s1, walk, QSE_NULL); qse_rbt_close (s1); return 0;