From 1c82886941cad2103f43629548d4769dd8a42550 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 31 Oct 2010 06:49:18 +0000 Subject: [PATCH] added qse_rbt_cbsert() --- qse/include/qse/cmn/fma.h | 2 +- qse/include/qse/cmn/htb.h | 253 +++++++++++++++++++++++--------------- qse/include/qse/cmn/map.h | 6 + qse/include/qse/cmn/rbt.h | 205 +++++++++++++++++++++++++++++- qse/include/qse/cmn/str.h | 4 +- qse/include/qse/cmn/xma.h | 2 +- qse/lib/cmn/htb.c | 34 ++--- qse/lib/cmn/rbt.c | 124 +++++++++++++++++-- qse/samples/cmn/rbt.c | 105 +++++++++++++++- 9 files changed, 601 insertions(+), 134 deletions(-) diff --git a/qse/include/qse/cmn/fma.h b/qse/include/qse/cmn/fma.h index e8e0c143..a53c049c 100644 --- a/qse/include/qse/cmn/fma.h +++ b/qse/include/qse/cmn/fma.h @@ -271,7 +271,7 @@ void* qse_fma_alloc ( * the @a size requested is not greater than the fixed block size of the memory * allocator @a fma, it allocates a block of the fixed size; If the block * @a blk is not #QSE_NULL and the @a size requested is not greater than the - * fixed block size of the memory allocator @a fma, it returns the block @blk. + * fixed block size of the memory allocator @a fma, it returns the block @a blk. * * @return block pointer on success, #QSE_NULL on failure */ diff --git a/qse/include/qse/cmn/htb.h b/qse/include/qse/cmn/htb.h index c626169d..468ddee6 100644 --- a/qse/include/qse/cmn/htb.h +++ b/qse/include/qse/cmn/htb.h @@ -1,5 +1,5 @@ /* - * $Id: htb.h 365 2010-10-29 13:54:36Z hyunghwan.chung $ + * $Id: htb.h 366 2010-10-30 12:49:18Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -25,8 +25,44 @@ #include /**@file - * A hash table maintains buckets for key/value pairs with the same key hash - * chained under the same bucket. + * This file provides a hash table encapsulated in the #qse_htb_t type that + * maintains buckets for key/value pairs with the same key hash chained under + * the same bucket. Its interface is very close to #qse_rbt_t. + * + * This sample code adds a series of keys and values and print them + * in the randome order. + * @code + * #include + * #include + * #include + * + * static qse_htb_walk_t walk (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx) + * { + * qse_printf (QSE_T("key = %d, value = %d\n"), + * *(int*)QSE_HTB_KPTR(pair), *(int*)QSE_HTB_VPTR(pair)); + * return QSE_HTB_WALK_FORWARD; + * } + * + * int main () + * { + * qse_htb_t* s1; + * int i; + * + * s1 = qse_htb_open (QSE_MMGR_GETDFL(), 0, 30, 75, 1, 1); // error handling skipped + * qse_htb_setmancbs (s1, qse_htb_mancbs(QSE_HTB_MANCBS_INLINE_COPIERS)); + * + * for (i = 0; i < 20; i++) + * { + * int x = i * 20; + * qse_htb_insert (s1, &i, QSE_SIZEOF(i), &x, QSE_SIZEOF(x)); // eror handling skipped + * } + * + * qse_htb_walk (s1, walk, QSE_NULL); + * + * qse_htb_close (s1); + * return 0; + * } + * @endcode */ typedef struct qse_htb_t qse_htb_t; @@ -132,6 +168,25 @@ typedef qse_htb_walk_t (*qse_htb_walker_t) ( void* ctx /**< pointer to user-defined data */ ); +/** + * The qse_htb_cbserter_t type defines a callback function for qse_htb_cbsert(). + * The qse_htb_cbserter() function calls it to allocate a new pair for the + * key pointed to by @a kptr of the length @a klen and the callback context + * @a ctx. The second parameter @a pair is passed the pointer to the existing + * pair for the key or #QSE_NULL in case of no existing key. The callback + * must return a pointer to a new or a reallocated pair. When reallocating the + * existing pair, this callback must destroy the existing pair and return the + * newly reallocated pair. It must return #QSE_NULL for failure. + */ +typedef qse_htb_pair_t* (*qse_htb_cbserter_t) ( + qse_htb_t* htb, /**< hash table */ + qse_htb_pair_t* pair, /**< pair pointer */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* ctx /**< callback context */ +); + + /** * 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 @@ -161,11 +216,19 @@ struct qse_htb_mancbs_t qse_htb_hasher_t hasher; /**< key hasher */ }; +/** + * The qse_htb_mancbs_kind_t type defines the type of predefined + * callback set for pair manipulation. + */ enum qse_htb_mancbs_kind_t { + /** store the key and the value pointer */ QSE_HTB_MANCBS_DEFAULT, + /** copy both key and value into the pair */ QSE_HTB_MANCBS_INLINE_COPIERS, + /** copy the key into the pair but store the value pointer */ QSE_HTB_MANCBS_INLINE_KEY_COPIER, + /** copy the value into the pair but store the key pointer */ QSE_HTB_MANCBS_INLINE_VALUE_COPIER }; @@ -190,7 +253,17 @@ struct qse_htb_t qse_htb_pair_t** bucket; }; + +/** + * The QSE_HTB_COPIER_SIMPLE macros defines a copier that remembers the + * pointer and length of data in a pair. + **/ #define QSE_HTB_COPIER_SIMPLE ((qse_htb_copier_t)1) + +/** + * The QSE_HTB_COPIER_INLINE macros defines a copier that copies data into + * a pair. + **/ #define QSE_HTB_COPIER_INLINE ((qse_htb_copier_t)2) #define QSE_HTB_COPIER_DEFAULT (QSE_HTB_COPIER_SIMPLE) @@ -227,6 +300,10 @@ extern "C" { QSE_DEFINE_COMMON_FUNCTIONS (htb) +/** + * The qse_htb_mancbs() functions returns a predefined callback set for + * pair manipulation. + */ const qse_htb_mancbs_t* qse_htb_mancbs ( qse_htb_mancbs_kind_t kind ); @@ -380,24 +457,6 @@ qse_htb_pair_t* qse_htb_update ( qse_size_t vlen /**< value length */ ); -/** - * The qse_htb_cbserter_t type defines a callback function for qse_htb_cbsert(). - * The qse_htb_cbserter() function calls it to allocate a new pair for the - * key pointed to by @a kptr of the length @a klen and the callback context - * @a ctx. The second parameter @a pair is passed the pointer to the existing - * pair for the key or #QSE_NULL in case of no existing key. The callback - * must return a pointer to a new or a reallocated pair. When reallocating the - * existing pair, this callback must destroy the existing pair and return the - * newly reallocated pair. It must return #QSE_NULL for failure. - */ -typedef qse_htb_pair_t* (*qse_htb_cbserter_t) ( - qse_htb_t* htb, /**< hash table */ - qse_htb_pair_t* pair, /**< pair pointer */ - void* kptr, /**< key pointer */ - qse_size_t klen, /**< key length */ - void* ctx /**< callback context */ -); - /** * The qse_htb_cbsert() function inserts a key/value pair by delegating pair * allocation to a callback function. Depending on the callback function, @@ -407,82 +466,82 @@ typedef qse_htb_pair_t* (*qse_htb_cbserter_t) ( * existing value delimited by a comma if the key is found. * * @code - * qse_htb_walk_t print_map_pair (qse_htb_t* map, qse_htb_pair_t* pair, void* ctx) - * { - * qse_printf (QSE_T("%.*s[%d] => %.*s[%d]\n"), - * (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)QSE_HTB_KLEN(pair), - * (int)QSE_HTB_VLEN(pair), QSE_HTB_VPTR(pair), (int)QSE_HTB_VLEN(pair)); - * return QSE_HTB_WALK_FORWARD; - * } - * - * qse_htb_pair_t* cbserter ( - * qse_htb_t* htb, qse_htb_pair_t* pair, - * void* kptr, qse_size_t klen, void* ctx) - * { - * qse_xstr_t* v = (qse_xstr_t*)ctx; - * if (pair == QSE_NULL) - * { - * // no existing key for the key - * return qse_htb_allocpair (htb, kptr, klen, v->ptr, v->len); - * } - * else - * { - * // a pair with the key exists. - * // in this sample, i will append the new value to the old value - * // separated by a comma - * qse_htb_pair_t* new_pair; - * qse_char_t comma = QSE_T(','); - * qse_byte_t* vptr; - * - * // allocate a new pair, but without filling the actual value. - * // note vptr is given QSE_NULL for that purpose - * new_pair = qse_htb_allocpair ( - * htb, kptr, klen, QSE_NULL, pair->vlen + 1 + v->len); - * if (new_pair == QSE_NULL) return QSE_NULL; - * - * // fill in the value space - * vptr = new_pair->vptr; - * qse_memcpy (vptr, pair->vptr, pair->vlen*QSE_SIZEOF(qse_char_t)); - * vptr += pair->vlen*QSE_SIZEOF(qse_char_t); - * qse_memcpy (vptr, &comma, QSE_SIZEOF(qse_char_t)); - * vptr += QSE_SIZEOF(qse_char_t); - * qse_memcpy (vptr, v->ptr, v->len*QSE_SIZEOF(qse_char_t)); - * - * // this callback requires the old pair to be destroyed - * qse_htb_freepair (htb, pair); - * - * // return the new pair - * return new_pair; - * } - * } - * - * int main () - * { - * qse_htb_t* s1; - * int i; - * qse_char_t* keys[] = { QSE_T("one"), QSE_T("two"), QSE_T("three") }; - * qse_char_t* vals[] = { QSE_T("1"), QSE_T("2"), QSE_T("3"), QSE_T("4"), QSE_T("5") }; - * - * s1 = qse_htb_open ( - * QSE_MMGR_GETDFL(), 0, 10, 70, - * QSE_SIZEOF(qse_char_t), QSE_SIZEOF(qse_char_t) - * ); // note error check is skipped - * qse_htb_setmancbs (s1, &mancbs1); - * - * for (i = 0; i < QSE_COUNTOF(vals); i++) - * { - * qse_xstr_t ctx; - * ctx.ptr = vals[i]; ctx.len = qse_strlen(vals[i]); - * qse_htb_cbsert (s1, - * keys[i%QSE_COUNTOF(keys)], qse_strlen(keys[i%QSE_COUNTOF(keys)]), - * cbserter, &ctx - * ); // note error check is skipped - * } - * qse_htb_walk (s1, print_map_pair, QSE_NULL); - * - * qse_htb_close (s1); - * return 0; - * } + * qse_htb_walk_t print_map_pair (qse_htb_t* map, qse_htb_pair_t* pair, void* ctx) + * { + * qse_printf (QSE_T("%.*s[%d] => %.*s[%d]\n"), + * (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)QSE_HTB_KLEN(pair), + * (int)QSE_HTB_VLEN(pair), QSE_HTB_VPTR(pair), (int)QSE_HTB_VLEN(pair)); + * return QSE_HTB_WALK_FORWARD; + * } + * + * qse_htb_pair_t* cbserter ( + * qse_htb_t* htb, qse_htb_pair_t* pair, + * void* kptr, qse_size_t klen, void* ctx) + * { + * qse_xstr_t* v = (qse_xstr_t*)ctx; + * if (pair == QSE_NULL) + * { + * // no existing key for the key + * return qse_htb_allocpair (htb, kptr, klen, v->ptr, v->len); + * } + * else + * { + * // a pair with the key exists. + * // in this sample, i will append the new value to the old value + * // separated by a comma + * qse_htb_pair_t* new_pair; + * qse_char_t comma = QSE_T(','); + * qse_byte_t* vptr; + * + * // allocate a new pair, but without filling the actual value. + * // note vptr is given QSE_NULL for that purpose + * new_pair = qse_htb_allocpair ( + * htb, kptr, klen, QSE_NULL, pair->vlen + 1 + v->len); + * if (new_pair == QSE_NULL) return QSE_NULL; + * + * // fill in the value space + * vptr = new_pair->vptr; + * qse_memcpy (vptr, pair->vptr, pair->vlen*QSE_SIZEOF(qse_char_t)); + * vptr += pair->vlen*QSE_SIZEOF(qse_char_t); + * qse_memcpy (vptr, &comma, QSE_SIZEOF(qse_char_t)); + * vptr += QSE_SIZEOF(qse_char_t); + * qse_memcpy (vptr, v->ptr, v->len*QSE_SIZEOF(qse_char_t)); + * + * // this callback requires the old pair to be destroyed + * qse_htb_freepair (htb, pair); + * + * // return the new pair + * return new_pair; + * } + * } + * + * int main () + * { + * qse_htb_t* s1; + * int i; + * qse_char_t* keys[] = { QSE_T("one"), QSE_T("two"), QSE_T("three") }; + * qse_char_t* vals[] = { QSE_T("1"), QSE_T("2"), QSE_T("3"), QSE_T("4"), QSE_T("5") }; + * + * s1 = qse_htb_open ( + * QSE_MMGR_GETDFL(), 0, 10, 70, + * QSE_SIZEOF(qse_char_t), QSE_SIZEOF(qse_char_t) + * ); // note error check is skipped + * qse_htb_setmancbs (s1, &mancbs1); + * + * for (i = 0; i < QSE_COUNTOF(vals); i++) + * { + * qse_xstr_t ctx; + * ctx.ptr = vals[i]; ctx.len = qse_strlen(vals[i]); + * qse_htb_cbsert (s1, + * keys[i%QSE_COUNTOF(keys)], qse_strlen(keys[i%QSE_COUNTOF(keys)]), + * cbserter, &ctx + * ); // note error check is skipped + * } + * qse_htb_walk (s1, print_map_pair, QSE_NULL); + * + * qse_htb_close (s1); + * return 0; + * } * @endcode */ qse_htb_pair_t* qse_htb_cbsert ( diff --git a/qse/include/qse/cmn/map.h b/qse/include/qse/cmn/map.h index 8ec21276..a6d7029a 100644 --- a/qse/include/qse/cmn/map.h +++ b/qse/include/qse/cmn/map.h @@ -47,6 +47,7 @@ # define qse_map_ensert(map,kptr,klen,vptr,vlen) qse_htb_ensert(map,kptr,klen,vptr,vlen) # define qse_map_insert(map,kptr,klen,vptr,vlen) qse_htb_insert(map,kptr,klen,vptr,vlen) # define qse_map_update(map,kptr,klen,vptr,vlen) qse_htb_update(map,kptr,klen,vptr,vlen) +# define qse_map_cbsert(map,kptr,klen,cb,ctx) qse_htb_cbsert(map,kptr,klen,cb,ctx) # define qse_map_delete(map,kptr,klen) qse_htb_delete(map,kptr,klen) # define qse_map_clear(map) qse_htb_clear(map) # define qse_map_walk(map,walker,ctx) qse_htb_walk(map,walker,ctx) @@ -59,6 +60,8 @@ # define qse_map_t qse_htb_t # define qse_map_pair_t qse_htb_pair_t # define qse_map_mancbs_t qse_htb_mancbs_t +# define qse_map_cbserter_t qse_htb_cbserter_t +# define qse_map_walker_t qse_htb_walker_t # define QSE_MAP_COPIER_SIMPLE QSE_HTB_COPIER_SIMPLE # define QSE_MAP_COPIER_INLINE QSE_HTB_COPIER_INLINE # define QSE_MAP_COPIER_DEFAULT QSE_HTB_COPIER_DEFAULT @@ -100,6 +103,7 @@ # define qse_map_ensert(map,kptr,klen,vptr,vlen) qse_rbt_ensert(map,kptr,klen,vptr,vlen) # define qse_map_insert(map,kptr,klen,vptr,vlen) qse_rbt_insert(map,kptr,klen,vptr,vlen) # define qse_map_update(map,kptr,klen,vptr,vlen) qse_rbt_update(map,kptr,klen,vptr,vlen) +# define qse_map_cbsert(map,kptr,klen,cb,ctx) qse_rbt_cbsert(map,kptr,klen,cb,ctx) # define qse_map_delete(map,kptr,klen) qse_rbt_delete(map,kptr,klen) # define qse_map_clear(map) qse_rbt_clear(map) # define qse_map_walk(map,walker,ctx) qse_rbt_walk(map,walker,ctx) @@ -112,6 +116,8 @@ # define qse_map_t qse_rbt_t # define qse_map_pair_t qse_rbt_pair_t # define qse_map_mancbs_t qse_rbt_mancbs_t +# define qse_map_cbserter_t qse_rbt_cbserter_t +# define qse_map_walker_t qse_rbt_walker_t # define QSE_MAP_COPIER_SIMPLE QSE_RBT_COPIER_SIMPLE # define QSE_MAP_COPIER_INLINE QSE_RBT_COPIER_INLINE # define QSE_MAP_COPIER_DEFAULT QSE_RBT_COPIER_DEFAULT diff --git a/qse/include/qse/cmn/rbt.h b/qse/include/qse/cmn/rbt.h index 4cc98055..330d2e67 100644 --- a/qse/include/qse/cmn/rbt.h +++ b/qse/include/qse/cmn/rbt.h @@ -25,7 +25,44 @@ #include /**@file - * A red-black tree is a self-balancing binary search tree. + * This file provides a red-black tree encapsulated in the #qse_rbt_t type that + * implements a self-balancing binary search tree.Its interface is very close + * to #qse_htb_t. + * + * This sample code adds a series of keys and values and print them + * in descending key order. + * @code + * #include + * #include + * #include + * + * 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"), + * *(int*)QSE_RBT_KPTR(pair), *(int*)QSE_RBT_VPTR(pair)); + * return QSE_RBT_WALK_FORWARD; + * } + * + * int main () + * { + * qse_rbt_t* s1; + * int i; + * + * s1 = qse_rbt_open (QSE_MMGR_GETDFL(), 0, 1, 1); // error handling skipped + * qse_rbt_setmancbs (s1, qse_rbt_mancbs(QSE_RBT_MANCBS_INLINE_COPIERS)); + * + * for (i = 0; i < 20; i++) + * { + * int x = i * 20; + * qse_rbt_insert (s1, &i, QSE_SIZEOF(i), &x, QSE_SIZEOF(x)); // eror handling skipped + * } + * + * qse_rbt_rwalk (s1, walk, QSE_NULL); + * + * qse_rbt_close (s1); + * return 0; + * } + * @endcode */ typedef struct qse_rbt_t qse_rbt_t; @@ -102,11 +139,29 @@ typedef void (*qse_rbt_keeper_t) ( * The qse_rbt_walker_t defines a pair visitor. */ typedef qse_rbt_walk_t (*qse_rbt_walker_t) ( - qse_rbt_t* rbt, /**< rbt */ + qse_rbt_t* rbt, /**< red-black tree */ qse_rbt_pair_t* pair, /**< pointer to a key/value pair */ void* ctx /**< pointer to user-defined data */ ); +/** + * The qse_rbt_cbserter_t type defines a callback function for qse_rbt_cbsert(). + * The qse_rbt_cbserter() function calls it to allocate a new pair for the + * key pointed to by @a kptr of the length @a klen and the callback context + * @a ctx. The second parameter @a pair is passed the pointer to the existing + * pair for the key or #QSE_NULL in case of no existing key. The callback + * must return a pointer to a new or a reallocated pair. When reallocating the + * existing pair, this callback must destroy the existing pair and return the + * newly reallocated pair. It must return #QSE_NULL for failure. + */ +typedef qse_rbt_pair_t* (*qse_rbt_cbserter_t) ( + qse_rbt_t* rbt, /**< red-black tree */ + qse_rbt_pair_t* pair, /**< pair pointer */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + void* ctx /**< callback context */ +); + /** * 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 @@ -132,6 +187,10 @@ struct qse_rbt_pair_t typedef struct qse_rbt_mancbs_t qse_rbt_mancbs_t; +/** + * The qse_rbt_mancbs_t type defines callback function sets for key/value + * pair manipulation. + */ struct qse_rbt_mancbs_t { qse_rbt_copier_t copier[2]; /**< key and value copier */ @@ -140,11 +199,19 @@ struct qse_rbt_mancbs_t qse_rbt_keeper_t keeper; /**< value keeper */ }; +/** + * The qse_rbt_mancbs_kind_t type defines the type of predefined + * callback set for pair manipulation. + */ enum qse_rbt_mancbs_kind_t { + /** store the key and the value pointer */ QSE_RBT_MANCBS_DEFAULT, + /** copy both key and value into the pair */ QSE_RBT_MANCBS_INLINE_COPIERS, + /** copy the key into the pair but store the value pointer */ QSE_RBT_MANCBS_INLINE_KEY_COPIER, + /** copy the value into the pair but store the key pointer */ QSE_RBT_MANCBS_INLINE_VALUE_COPIER }; @@ -203,6 +270,10 @@ extern "C" { QSE_DEFINE_COMMON_FUNCTIONS (rbt) +/** + * The qse_rbt_mancbs() functions returns a predefined callback set for + * pair manipulation. + */ const qse_rbt_mancbs_t* qse_rbt_mancbs ( qse_rbt_mancbs_kind_t kind ); @@ -251,7 +322,7 @@ const qse_rbt_mancbs_t* qse_rbt_getmancbs ( /** * The qse_rbt_setmancbs() function sets internal manipulation callback - * functions for data construction, destruction, resizing, hashing, etc. + * functions for data construction, destruction, comparison, etc. */ void qse_rbt_setmancbs ( qse_rbt_t* rbt, /**< red-black tree */ @@ -335,6 +406,101 @@ qse_rbt_pair_t* qse_rbt_update ( qse_size_t vlen /**< value length */ ); +/** + * The qse_rbt_cbsert() function inserts a key/value pair by delegating pair + * allocation to a callback function. Depending on the callback function, + * it may behave like qse_rbt_insert(), qse_rbt_upsert(), qse_rbt_update(), + * qse_rbt_ensert(), or totally differently. The sample code below inserts + * a new pair if the key is not found and appends the new value to the + * existing value delimited by a comma if the key is found. + * + * @code + * qse_rbt_walk_t print_map_pair (qse_rbt_t* map, qse_rbt_pair_t* pair, void* ctx) + * { + * qse_printf (QSE_T("%.*s[%d] => %.*s[%d]\n"), + * (int)QSE_RBT_KLEN(pair), QSE_RBT_KPTR(pair), (int)QSE_RBT_KLEN(pair), + * (int)QSE_RBT_VLEN(pair), QSE_RBT_VPTR(pair), (int)QSE_RBT_VLEN(pair)); + * return QSE_RBT_WALK_FORWARD; + * } + * + * qse_rbt_pair_t* cbserter ( + * qse_rbt_t* rbt, qse_rbt_pair_t* pair, + * void* kptr, qse_size_t klen, void* ctx) + * { + * qse_xstr_t* v = (qse_xstr_t*)ctx; + * if (pair == QSE_NULL) + * { + * // no existing key for the key + * return qse_rbt_allocpair (rbt, kptr, klen, v->ptr, v->len); + * } + * else + * { + * // a pair with the key exists. + * // in this sample, i will append the new value to the old value + * // separated by a comma + * qse_rbt_pair_t* new_pair; + * qse_char_t comma = QSE_T(','); + * qse_byte_t* vptr; + * + * // allocate a new pair, but without filling the actual value. + * // note vptr is given QSE_NULL for that purpose + * new_pair = qse_rbt_allocpair ( + * rbt, kptr, klen, QSE_NULL, pair->vlen + 1 + v->len); + * if (new_pair == QSE_NULL) return QSE_NULL; + * + * // fill in the value space + * vptr = new_pair->vptr; + * qse_memcpy (vptr, pair->vptr, pair->vlen*QSE_SIZEOF(qse_char_t)); + * vptr += pair->vlen*QSE_SIZEOF(qse_char_t); + * qse_memcpy (vptr, &comma, QSE_SIZEOF(qse_char_t)); + * vptr += QSE_SIZEOF(qse_char_t); + * qse_memcpy (vptr, v->ptr, v->len*QSE_SIZEOF(qse_char_t)); + * + * // this callback requires the old pair to be destroyed + * qse_rbt_freepair (rbt, pair); + * + * // return the new pair + * return new_pair; + * } + * } + * + * int main () + * { + * qse_rbt_t* s1; + * int i; + * qse_char_t* keys[] = { QSE_T("one"), QSE_T("two"), QSE_T("three") }; + * qse_char_t* vals[] = { QSE_T("1"), QSE_T("2"), QSE_T("3"), QSE_T("4"), QSE_T("5") }; + * + * s1 = qse_rbt_open ( + * QSE_MMGR_GETDFL(), 0, + * QSE_SIZEOF(qse_char_t), QSE_SIZEOF(qse_char_t) + * ); // note error check is skipped + * qse_rbt_setmancbs (s1, &mancbs1); + * + * for (i = 0; i < QSE_COUNTOF(vals); i++) + * { + * qse_xstr_t ctx; + * ctx.ptr = vals[i]; ctx.len = qse_strlen(vals[i]); + * qse_rbt_cbsert (s1, + * keys[i%QSE_COUNTOF(keys)], qse_strlen(keys[i%QSE_COUNTOF(keys)]), + * cbserter, &ctx + * ); // note error check is skipped + * } + * qse_rbt_walk (s1, print_map_pair, QSE_NULL); + * + * qse_rbt_close (s1); + * return 0; + * } + * @endcode + */ +qse_rbt_pair_t* qse_rbt_cbsert ( + qse_rbt_t* rbt, /**< red-black tree */ + void* kptr, /**< key pointer */ + qse_size_t klen, /**< key length */ + qse_rbt_cbserter_t cbserter, /**< callback function */ + void* ctx /**< callback context */ +); + /** * The qse_rbt_delete() function deletes a pair with a matching key * @return 0 on success, -1 on failure @@ -372,6 +538,39 @@ void qse_rbt_rwalk ( void* ctx /**< pointer to user-specific data */ ); +/** + * The qse_rbt_allocpair() function allocates a pair for a key and a value + * given. But it does not chain the pair allocated into the red-black tree @a rbt. + * Use this function at your own risk. + * + * Take note of he following special behavior when the copier is + * #QSE_RBT_COPIER_INLINE. + * - If @a kptr is #QSE_NULL, the key space of the size @a klen is reserved but + * not propagated with any data. + * - If @a vptr is #QSE_NULL, the value space of the size @a vlen is reserved + * but not propagated with any data. + */ +qse_rbt_pair_t* qse_rbt_allocpair ( + qse_rbt_t* rbt, + void* kptr, + qse_size_t klen, + void* vptr, + qse_size_t vlen +); + +/** + * The qse_rbt_freepair() function destroys a pair. But it does not detach + * the pair destroyed from the red-black tree @a rbt. Use this function at your + * own risk. + */ +void qse_rbt_freepair ( + qse_rbt_t* rbt, + qse_rbt_pair_t* pair +); + +/** + * The qse_rbt_dflcomp() function defines the default key comparator. + */ int qse_rbt_dflcomp ( qse_rbt_t* rbt, const void* kptr1, diff --git a/qse/include/qse/cmn/str.h b/qse/include/qse/cmn/str.h index 38dfb397..03a1ec7d 100644 --- a/qse/include/qse/cmn/str.h +++ b/qse/include/qse/cmn/str.h @@ -1,5 +1,5 @@ /* - * $Id: str.h 356 2010-09-07 12:29:25Z hyunghwan.chung $ + * $Id: str.h 366 2010-10-30 12:49:18Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -229,7 +229,7 @@ qse_size_t qse_strfcpy ( /** * The qse_strfncpy() function formats a string by position. - * It differs from qse_strfcpy() in that @str is an array of the + * It differs from qse_strfcpy() in that @a str is an array of the * #qse_cstr_t type. * @sa qse_strfcpy, qse_strxfcpy, qse_strxfncpy */ diff --git a/qse/include/qse/cmn/xma.h b/qse/include/qse/cmn/xma.h index 46160c9e..33abf9bc 100644 --- a/qse/include/qse/cmn/xma.h +++ b/qse/include/qse/cmn/xma.h @@ -140,7 +140,7 @@ void qse_xma_close ( * The qse_xma_init() initializes a memory allocator. If you have the qse_xma_t * structure statically declared or already allocated, you may pass the pointer * to this function instead of calling qse_xma_open(). It obtains a memory zone - * of @a zonesize bytes with the memory manager @mmgr. Unlike qse_xma_open(), + * of @a zonesize bytes with the memory manager @a mmgr. Unlike qse_xma_open(), * it does not accept the extension size, thus not creating an extention area. * @return @a xma on success, #QSE_NULL on failure */ diff --git a/qse/lib/cmn/htb.c b/qse/lib/cmn/htb.c index e7fa4f84..efc6b371 100644 --- a/qse/lib/cmn/htb.c +++ b/qse/lib/cmn/htb.c @@ -1,5 +1,5 @@ /* - * $Id: htb.c 365 2010-10-29 13:54:36Z hyunghwan.chung $ + * $Id: htb.c 366 2010-10-30 12:49:18Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -23,16 +23,18 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (htb) -#define htb_t qse_htb_t -#define pair_t qse_htb_pair_t -#define copier_t qse_htb_copier_t -#define freeer_t qse_htb_freeer_t -#define hasher_t qse_htb_hasher_t -#define comper_t qse_htb_comper_t -#define keeper_t qse_htb_keeper_t -#define sizer_t qse_htb_sizer_t -#define walker_t qse_htb_walker_t -#define cbserter_t qse_htb_cbserter_t +#define htb_t qse_htb_t +#define pair_t qse_htb_pair_t +#define copier_t qse_htb_copier_t +#define freeer_t qse_htb_freeer_t +#define hasher_t qse_htb_hasher_t +#define comper_t qse_htb_comper_t +#define keeper_t qse_htb_keeper_t +#define sizer_t qse_htb_sizer_t +#define walker_t qse_htb_walker_t +#define cbserter_t qse_htb_cbserter_t +#define mancbs_t qse_htb_mancbs_t +#define mancbs_kind_t qse_htb_mancbs_kind_t #define KPTR(p) QSE_HTB_KPTR(p) #define KLEN(p) QSE_HTB_KLEN(p) @@ -153,7 +155,7 @@ static QSE_INLINE pair_t* change_pair_val ( { if (ovlen == vlen) { - QSE_MEMCPY (VPTR(pair), vptr, VTOB(htb,vlen)); + if (vptr) QSE_MEMCPY (VPTR(pair), vptr, VTOB(htb,vlen)); } else { @@ -185,7 +187,7 @@ static QSE_INLINE pair_t* change_pair_val ( return pair; } -static qse_htb_mancbs_t mancbs[] = +static mancbs_t mancbs[] = { { { @@ -248,7 +250,7 @@ static qse_htb_mancbs_t mancbs[] = } }; -const qse_htb_mancbs_t* qse_htb_mancbs (qse_htb_mancbs_kind_t kind) +const mancbs_t* qse_htb_mancbs (mancbs_kind_t kind) { return &mancbs[kind]; }; @@ -330,12 +332,12 @@ void qse_htb_fini (htb_t* htb) QSE_MMGR_FREE (htb->mmgr, htb->bucket); } -const qse_htb_mancbs_t* qse_htb_getmancbs (htb_t* htb) +const mancbs_t* qse_htb_getmancbs (htb_t* htb) { return htb->mancbs; } -void qse_htb_setmancbs (htb_t* htb, const qse_htb_mancbs_t* mancbs) +void qse_htb_setmancbs (htb_t* htb, const mancbs_t* mancbs) { QSE_ASSERT (mancbs != QSE_NULL); htb->mancbs = mancbs; diff --git a/qse/lib/cmn/rbt.c b/qse/lib/cmn/rbt.c index 50e04fa4..e5440707 100644 --- a/qse/lib/cmn/rbt.c +++ b/qse/lib/cmn/rbt.c @@ -23,14 +23,17 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (rbt) -#define rbt_t qse_rbt_t -#define pair_t qse_rbt_pair_t -#define id_t qse_rbt_id_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 +#define rbt_t qse_rbt_t +#define pair_t qse_rbt_pair_t +#define id_t qse_rbt_id_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 +#define cbserter_t qse_rbt_cbserter_t +#define mancbs_t qse_rbt_mancbs_t +#define mancbs_kind_t qse_rbt_mancbs_kind_t #define KPTR(p) QSE_RBT_KPTR(p) #define KLEN(p) QSE_RBT_KLEN(p) @@ -134,7 +137,7 @@ QSE_INLINE void qse_rbt_freepair (rbt_t* rbt, pair_t* pair) QSE_MMGR_FREE (rbt->mmgr, pair); } -static qse_rbt_mancbs_t mancbs[] = +static mancbs_t mancbs[] = { { { @@ -189,7 +192,7 @@ static qse_rbt_mancbs_t mancbs[] = } }; -const qse_rbt_mancbs_t* qse_rbt_mancbs (qse_rbt_mancbs_kind_t kind) +const mancbs_t* qse_rbt_mancbs (mancbs_kind_t kind) { return &mancbs[kind]; }; @@ -257,12 +260,12 @@ void qse_rbt_fini (rbt_t* rbt) qse_rbt_clear (rbt); } -const qse_rbt_mancbs_t* qse_rbt_getmancbs (rbt_t* rbt) +const mancbs_t* qse_rbt_getmancbs (rbt_t* rbt) { return rbt->mancbs; } -void qse_rbt_setmancbs (rbt_t* rbt, const qse_rbt_mancbs_t* mancbs) +void qse_rbt_setmancbs (rbt_t* rbt, const mancbs_t* mancbs) { QSE_ASSERT (mancbs != QSE_NULL); rbt->mancbs = mancbs; @@ -450,7 +453,7 @@ static pair_t* change_pair_val ( { if (ovlen == vlen) { - QSE_MEMCPY (VPTR(pair), vptr, VTOB(rbt,vlen)); + if (vptr) QSE_MEMCPY (VPTR(pair), vptr, VTOB(rbt,vlen)); } else { @@ -597,6 +600,101 @@ pair_t* qse_rbt_update ( return insert (rbt, kptr, klen, vptr, vlen, UPDATE); } +pair_t* qse_rbt_cbsert ( + rbt_t* rbt, void* kptr, size_t klen, cbserter_t cbserter, void* ctx) +{ + pair_t* xcur = rbt->root; + pair_t* xpar = QSE_NULL; + pair_t* xnew; + + while (!IS_NIL(rbt,xcur)) + { + int n = rbt->mancbs->comper (rbt, kptr, klen, xcur->kptr, xcur->klen); + if (n == 0) + { + /* back up the contents of the current pair + * in case it is reallocated */ + pair_t tmp = *xcur; + + /* call the callback function to manipulate the pair */ + xnew = cbserter (rbt, xcur, kptr, klen, ctx); + if (xnew == QSE_NULL) + { + /* error returned by the callback function */ + return QSE_NULL; + } + + if (xnew != xcur) + { + /* the current pair has been reallocated, which implicitly + * means the previous contents were wiped out. so the contents + * backed up will be used for restoration/migration */ + + xnew->color = tmp.color; + xnew->left = tmp.left; + xnew->right = tmp.right; + xnew->parent = tmp.parent; + + if (tmp.parent) + { + if (tmp.parent->left == xcur) + { + tmp.parent->left = xnew; + } + else + { + QSE_ASSERT (tmp.parent->right == xcur); + tmp.parent->right = xnew; + } + } + if (!IS_NIL(rbt,tmp.left)) tmp.left->parent = xnew; + if (!IS_NIL(rbt,tmp.right)) tmp.right->parent = xnew; + + if (xcur == rbt->root) rbt->root = xnew; + } + + return xnew; + } + + xpar = xcur; + + if (n > 0) xcur = xcur->right; + else /* if (n < 0) */ xcur = xcur->left; + } + + xnew = cbserter (rbt, QSE_NULL, kptr, klen, ctx); + if (xnew == QSE_NULL) return QSE_NULL; + + if (xpar == QSE_NULL) + { + /* the tree contains no pair */ + QSE_ASSERT (rbt->root == &rbt->nil); + rbt->root = xnew; + } + else + { + /* perform normal binary insert */ + int n = rbt->mancbs->comper (rbt, kptr, klen, xpar->kptr, xpar->klen); + if (n > 0) + { + QSE_ASSERT (xpar->right == &rbt->nil); + xpar->right = xnew; + } + else + { + QSE_ASSERT (xpar->left == &rbt->nil); + xpar->left = xnew; + } + + xnew->parent = xpar; + adjust (rbt, xnew); + } + + rbt->root->color = QSE_RBT_BLACK; + return xnew; +} + + static void adjust_for_delete (rbt_t* rbt, pair_t* pair, pair_t* par) { while (pair != rbt->root && pair->color == QSE_RBT_BLACK) diff --git a/qse/samples/cmn/rbt.c b/qse/samples/cmn/rbt.c index 5347fbcd..5cdbdd6e 100644 --- a/qse/samples/cmn/rbt.c +++ b/qse/samples/cmn/rbt.c @@ -109,17 +109,120 @@ static int test2 () qse_printf (QSE_T("-------------------\n")); qse_rbt_upsert (s1, QSE_T("hello"), 5, QSE_T("dr. gorilla"), 11); - qse_rbt_upsert (s1, QSE_T("thinkpad"), 8, QSE_T("x61 rocks"), 9); + qse_rbt_upsert (s1, QSE_T("thinkpad"), 8, QSE_T("x61 rocks on"), 13); qse_rbt_ensert (s1, QSE_T("vista"), 5, QSE_T("microsoft"), 9); + qse_rbt_update (s1, QSE_T("world"), 5, QSE_T("baseball classic"), 16); qse_rbt_rwalk (s1, walk2, QSE_NULL); qse_rbt_close (s1); return 0; } +qse_rbt_walk_t print_map_pair (qse_rbt_t* map, qse_rbt_pair_t* pair, void* arg) +{ + qse_printf (QSE_T("%.*s[%d] => %.*s[%d]\n"), + (int)QSE_RBT_KLEN(pair), QSE_RBT_KPTR(pair), (int)QSE_RBT_KLEN(pair), + (int)QSE_RBT_VLEN(pair), QSE_RBT_VPTR(pair), (int)QSE_RBT_VLEN(pair)); + + return QSE_RBT_WALK_FORWARD; +} + +static qse_rbt_pair_t* test5_cbserter ( + qse_rbt_t* rbt, qse_rbt_pair_t* pair, + void* kptr, qse_size_t klen, void* ctx) +{ + qse_xstr_t* v = (qse_xstr_t*)ctx; + if (pair == QSE_NULL) + { + /* no existing key for the key */ + return qse_rbt_allocpair (rbt, kptr, klen, v->ptr, v->len); + } + else + { + /* a pair with the key exists. + * in this sample, i will append the new value to the old value + * separated by a comma */ + + qse_rbt_pair_t* new_pair; + qse_char_t comma = QSE_T(','); + qse_byte_t* vptr; + + /* allocate a new pair, but without filling the actual value. + * note vptr is given QSE_NULL for that purpose */ + new_pair = qse_rbt_allocpair ( + rbt, kptr, klen, QSE_NULL, pair->vlen + 1 + v->len); + if (new_pair == QSE_NULL) return QSE_NULL; + + /* fill in the value space */ + vptr = new_pair->vptr; + qse_memcpy (vptr, pair->vptr, pair->vlen*QSE_SIZEOF(qse_char_t)); + vptr += pair->vlen*QSE_SIZEOF(qse_char_t); + qse_memcpy (vptr, &comma, QSE_SIZEOF(qse_char_t)); + vptr += QSE_SIZEOF(qse_char_t); + qse_memcpy (vptr, v->ptr, v->len*QSE_SIZEOF(qse_char_t)); + + /* this callback requires the old pair to be destroyed */ + qse_rbt_freepair (rbt, pair); + + /* return the new pair */ + return new_pair; + } +} + +static int test5 () +{ + qse_rbt_t* s1; + int i; + + qse_char_t* keys[] = + { + QSE_T("one"), QSE_T("two"), QSE_T("three") + }; + qse_char_t* vals[] = + { + QSE_T("1"), QSE_T("2"), QSE_T("3"), QSE_T("4"), QSE_T("5"), + }; + + s1 = qse_rbt_open (QSE_MMGR_GETDFL(), 0, + QSE_SIZEOF(qse_char_t), QSE_SIZEOF(qse_char_t)); + if (s1 == QSE_NULL) + { + qse_printf (QSE_T("cannot open a hash table\n")); + return -1; + } + qse_rbt_setmancbs (s1, qse_rbt_mancbs(QSE_RBT_MANCBS_INLINE_COPIERS)); + + for (i = 0; i < QSE_COUNTOF(vals); i++) + { + qse_xstr_t ctx; + + qse_printf (QSE_T("setting a key [%s] and a value [%s]: "), keys[i%QSE_COUNTOF(keys)], vals[i]); + + ctx.ptr = vals[i]; + ctx.len = qse_strlen(vals[i]); + if (qse_rbt_cbsert (s1, + keys[i%QSE_COUNTOF(keys)], + qse_strlen(keys[i%QSE_COUNTOF(keys)]), + test5_cbserter, &ctx) == QSE_NULL) + { + qse_printf (QSE_T("[FAILED]\n")); + } + else + { + qse_printf (QSE_T("[OK]\n")); + } + } + qse_rbt_walk (s1, print_map_pair, QSE_NULL); + + + qse_rbt_close (s1); + return 0; +} + int main () { R (test1); R (test2); + R (test5); return 0; }