diff --git a/qse/include/qse/cmn/htb.h b/qse/include/qse/cmn/htb.h index db389006..f8523848 100644 --- a/qse/include/qse/cmn/htb.h +++ b/qse/include/qse/cmn/htb.h @@ -1,5 +1,5 @@ /* - * $Id: htb.h 363 2010-10-27 12:54:37Z hyunghwan.chung $ + * $Id: htb.h 364 2010-10-28 13:09:53Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -56,6 +56,9 @@ typedef enum qse_htb_id_t qse_htb_id_t; /** * The qse_htb_copier_t type defines a pair contruction callback. + * A special copier #QSE_HTB_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. */ typedef void* (*qse_htb_copier_t) ( qse_htb_t* htb /* hash table */, @@ -65,6 +68,7 @@ typedef void* (*qse_htb_copier_t) ( /** * The qse_htb_freeer_t defines a key/value destruction callback + * The freeer is called when a node containing the element is destroyed. */ typedef void (*qse_htb_freeer_t) ( qse_htb_t* htb, /**< hash table */ @@ -108,8 +112,8 @@ typedef void (*qse_htb_keeper_t) ( /** * 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. + * when hash table should resize the bucket. The current bucket size + 1 is + * passed as the hint. */ typedef qse_size_t (*qse_htb_sizer_t) ( qse_htb_t* htb, /**< htb */ @@ -163,15 +167,6 @@ struct qse_htb_t const qse_htb_mancbs_t* mancbs; -#if 0 - qse_htb_copier_t copier[2]; - qse_htb_freeer_t freeer[2]; - qse_htb_hasher_t hasher; /**< key hasher */ - qse_htb_comper_t comper; /**< key comparator */ - qse_htb_keeper_t keeper; /**< value keeper */ - qse_htb_sizer_t sizer; /**< bucket capacity recalculator */ -#endif - qse_byte_t scale[2]; /**< length scale */ qse_byte_t factor; /**< load factor in percentage */ @@ -313,87 +308,6 @@ void qse_htb_setmancbs ( const qse_htb_mancbs_t* mancbs ); -#if 0 -/** - * The qse_htb_getcopier() function gets a data copier. - */ -qse_htb_copier_t qse_htb_getcopier ( - qse_htb_t* htb, /**< hash table */ - qse_htb_id_t id /**< QSE_HTB_KEY or QSE_HTB_VAL */ -); - -/** - * The qse_htb_setcopier() function specifies how to clone an element. - * A special copier QSE_HTB_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_htb_setcopier ( - qse_htb_t* htb, /**< hash table */ - qse_htb_id_t id, /**< QSE_HTB_KEY or QSE_HTB_VAL */ - qse_htb_copier_t copier /**< element copier */ -); - -/** - * The qse_htb_getfreeer() function returns a custom element destroyer. - */ -qse_htb_freeer_t qse_htb_getfreeer ( - qse_htb_t* htb, /**< hash table */ - qse_htb_id_t id /**< QSE_HTB_KEY or QSE_HTB_VAL */ -); - -/** - * The qse_htb_setfreeer() function specifies how to destroy an element. - * 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_freeer_t freeer /**< an element freeer */ -); - -qse_htb_hasher_t qse_htb_gethasher ( - qse_htb_t* htb -); - -void qse_htb_sethasher ( - qse_htb_t* htb, - qse_htb_hasher_t hasher -); - -qse_htb_comper_t qse_htb_getcomper ( - qse_htb_t* htb -); - -void qse_htb_setcomper ( - qse_htb_t* htb, - qse_htb_comper_t comper -); - -qse_htb_keeper_t qse_htb_getkeeper ( - qse_htb_t* htb -); - -void qse_htb_setkeeper ( - qse_htb_t* htb, - qse_htb_keeper_t keeper -); - -qse_htb_sizer_t qse_htb_getsizer ( - qse_htb_t* htb -); - -/* the sizer function is passed hash table object and htb->capa + 1 */ -void qse_htb_setsizer ( - qse_htb_t* htb, - qse_htb_sizer_t sizer -); - -#endif - /** * The qse_htb_search() function searches a hash table to find a pair with a * matching key. It returns the pointer to the pair found. If it fails @@ -464,6 +378,22 @@ qse_htb_pair_t* qse_htb_update ( qse_size_t vlen /**< the length of the value */ ); +typedef qse_htb_pair_t* (*qse_htb_cbserter_t) ( + qse_htb_t* htb, + qse_htb_pair_t* pair, + void* kptr, + qse_size_t klen, + void* ctx +); + +qse_htb_pair_t* qse_htb_cbsert ( + qse_htb_t* htb, + void* kptr, + qse_size_t klen, + qse_htb_cbserter_t cbserter, + void* ctx +); + /** * The qse_htb_delete() function deletes a pair with a matching key * @return 0 on success, -1 on failure @@ -509,6 +439,28 @@ qse_htb_pair_t* qse_htb_getnextpair ( qse_size_t* buckno /**< bucket number */ ); +/** + * The qse_htb_allocpair() function allocates a pair for a key and a value + * given. But it does not chain the pair allocated into the hash table @a htb. + * Use this function at your own risk. + */ +qse_htb_pair_t* qse_htb_allocpair ( + qse_htb_t* htb, + void* kptr, + qse_size_t klen, + void* vptr, + qse_size_t vlen +); + +/** + * The qse_htb_freepair() function destroys a pair. But it does not detach + * the pair destroyed from the hash table @a htb. Use this function at your + * own risk. + */ +void qse_htb_freepair ( + qse_htb_t* htb, + qse_htb_pair_t* pair +); qse_size_t qse_htb_dflhash ( qse_htb_t* htb, diff --git a/qse/lib/cmn/htb.c b/qse/lib/cmn/htb.c index aa39d991..34e5cd51 100644 --- a/qse/lib/cmn/htb.c +++ b/qse/lib/cmn/htb.c @@ -1,5 +1,5 @@ /* - * $Id: htb.c 363 2010-10-27 12:54:37Z hyunghwan.chung $ + * $Id: htb.c 364 2010-10-28 13:09:53Z hyunghwan.chung $ * Copyright 2006-2009 Chung, Hyung-Hwan. This file is part of QSE. @@ -23,15 +23,16 @@ 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 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 KPTR(p) QSE_HTB_KPTR(p) #define KLEN(p) QSE_HTB_KLEN(p) @@ -47,14 +48,8 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (htb) #define KTOB(htb,len) ((len)*(htb)->scale[QSE_HTB_KEY]) #define VTOB(htb,len) ((len)*(htb)->scale[QSE_HTB_VAL]) -#define UPSERT 1 -#define UPDATE 2 -#define ENSERT 3 -#define INSERT 4 - - -static pair_t* alloc_pair (htb_t* htb, - void* kptr, size_t klen, void* vptr, size_t vlen) +QSE_INLINE pair_t* qse_htb_allocpair ( + htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen) { pair_t* n; copier_t kcop = htb->mancbs->copier[QSE_HTB_KEY]; @@ -116,7 +111,7 @@ static pair_t* alloc_pair (htb_t* htb, return n; } -static void free_pair (htb_t* htb, pair_t* pair) +QSE_INLINE void qse_htb_freepair (htb_t* htb, pair_t* pair) { if (htb->mancbs->freeer[QSE_HTB_KEY] != QSE_NULL) htb->mancbs->freeer[QSE_HTB_KEY] (htb, KPTR(pair), KLEN(pair)); @@ -125,7 +120,7 @@ static void free_pair (htb_t* htb, pair_t* pair) QSE_MMGR_FREE (htb->mmgr, pair); } -static pair_t* change_pair_val ( +static QSE_INLINE pair_t* change_pair_val ( htb_t* htb, pair_t* pair, void* vptr, size_t vlen) { if (VPTR(pair) == vptr && VLEN(pair) == vlen) @@ -159,11 +154,11 @@ static pair_t* change_pair_val ( else { /* need to reconstruct the pair */ - pair_t* p = alloc_pair (htb, + pair_t* p = qse_htb_allocpair (htb, KPTR(pair), KLEN(pair), vptr, vlen); if (p == QSE_NULL) return QSE_NULL; - free_pair (htb, pair); + qse_htb_freepair (htb, pair); return p; } } @@ -332,79 +327,6 @@ void qse_htb_setmancbs (htb_t* htb, const qse_htb_mancbs_t* mancbs) htb->mancbs = mancbs; } -#if 0 -copier_t qse_htb_getcopier (htb_t* htb, qse_htb_id_t id) -{ - QSE_ASSERTX (id == QSE_HTB_KEY || id == QSE_HTB_VAL, - "The ID should be either QSE_HTB_KEY or QSE_HTB_VAL"); - return htb->copier[id]; -} - -void qse_htb_setcopier (htb_t* htb, qse_htb_id_t id, copier_t copier) -{ - QSE_ASSERTX (id == QSE_HTB_KEY || id == QSE_HTB_VAL, - "The ID should be either QSE_HTB_KEY or QSE_HTB_VAL"); - if (copier == QSE_NULL) copier = QSE_HTB_COPIER_SIMPLE; - htb->copier[id] = copier; -} - -freeer_t qse_htb_getfreeer (htb_t* htb, qse_htb_id_t id) -{ - QSE_ASSERTX (id == QSE_HTB_KEY || id == QSE_HTB_VAL, - "The ID should be either QSE_HTB_KEY or QSE_HTB_VAL"); - return htb->freeer[id]; -} - -void qse_htb_setfreeer (htb_t* htb, qse_htb_id_t id, freeer_t freeer) -{ - QSE_ASSERTX (id == QSE_HTB_KEY || id == QSE_HTB_VAL, - "The ID should be either QSE_HTB_KEY or QSE_HTB_VAL"); - htb->freeer[id] = freeer; -} - -hasher_t qse_htb_gethasher (htb_t* htb) -{ - return htb->hasher; -} - -void qse_htb_sethasher (htb_t* htb, hasher_t hasher) -{ - if (hasher == QSE_NULL) hasher = hash_key; - htb->hasher = hasher; -} - -comper_t qse_htb_getcomper (htb_t* htb) -{ - return htb->comper; -} - -void qse_htb_setcomper (htb_t* htb, comper_t comper) -{ - if (comper == QSE_NULL) comper = comp_key; - htb->comper = comper; -} - -keeper_t qse_htb_getkeeper (htb_t* htb) -{ - return htb->keeper; -} - -void qse_htb_setkeeper (htb_t* htb, keeper_t keeper) -{ - htb->keeper = keeper; -} - -sizer_t qse_htb_getsizer (htb_t* htb) -{ - return htb->sizer; -} - -void qse_htb_setsizer (htb_t* htb, sizer_t sizer) -{ - htb->sizer = sizer; -} -#endif - size_t qse_htb_getsize (htb_t* htb) { return htb->size; @@ -498,6 +420,12 @@ static QSE_INLINE_ALWAYS int reorganize (htb_t* htb) return 0; } +/* insert options */ +#define UPSERT 1 +#define UPDATE 2 +#define ENSERT 3 +#define INSERT 4 + static pair_t* insert ( htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen, int opt) { @@ -522,17 +450,18 @@ static pair_t* insert ( p = change_pair_val (htb, pair, vptr, vlen); if (p == QSE_NULL) { - /* error in change the value */ + /* error in changing the value */ return QSE_NULL; } if (p != pair) { - /* pair reallocated. - * relink it */ + /* old pair destroyed. new pair reallocated. + * relink to include the new pair but to drop + * the old pair. */ if (prev == QSE_NULL) htb->bucket[hc] = p; else NEXT(prev) = p; - NEXT(p) = next; + NEXT(p) = next; } return p; @@ -554,7 +483,9 @@ static pair_t* insert ( if (htb->threshold > 0 && htb->size >= htb->threshold) { - if (reorganize(htb) == 0) /* ignore the error */ + /* ingore reorganization error as it simply means + * more bucket collision and performance penalty. */ + if (reorganize(htb) == 0) { hc = htb->mancbs->hasher(htb,kptr,klen) % htb->capa; } @@ -562,7 +493,7 @@ static pair_t* insert ( QSE_ASSERT (pair == QSE_NULL); - pair = alloc_pair (htb, kptr, klen, vptr, vlen); + pair = qse_htb_allocpair (htb, kptr, klen, vptr, vlen); if (pair == QSE_NULL) return QSE_NULL; /* error */ NEXT(pair) = htb->bucket[hc]; @@ -597,6 +528,68 @@ pair_t* qse_htb_update ( return insert (htb, kptr, klen, vptr, vlen, UPDATE); } +pair_t* qse_htb_cbsert ( + htb_t* htb, void* kptr, size_t klen, cbserter_t cbserter, void* ctx) +{ + pair_t* pair, * p, * prev, * next; + size_t hc; + + hc = htb->mancbs->hasher(htb,kptr,klen) % htb->capa; + pair = htb->bucket[hc]; + prev = QSE_NULL; + + while (pair != QSE_NULL) + { + next = NEXT(pair); + + if (htb->mancbs->comper (htb, KPTR(pair), KLEN(pair), kptr, klen) == 0) + { + /* found a pair with a matching key */ + p = cbserter (htb, pair, kptr, klen, ctx); + if (p == QSE_NULL) + { + /* error returned by the callback function */ + return QSE_NULL; + } + if (p != pair) + { + /* old pair destroyed. new pair reallocated. + * relink to include the new pair but to drop + * the old pair. */ + if (prev == QSE_NULL) + htb->bucket[hc] = p; + else NEXT(prev) = p; + NEXT(p) = next; + } + return p; + } + + prev = pair; + pair = next; + } + + if (htb->threshold > 0 && htb->size >= htb->threshold) + { + /* ingore reorganization error as it simply means + * more bucket collision and performance penalty. */ + if (reorganize(htb) == 0) + { + hc = htb->mancbs->hasher(htb,kptr,klen) % htb->capa; + } + } + + QSE_ASSERT (pair == QSE_NULL); + + pair = cbserter (htb, QSE_NULL, kptr, klen, ctx); + if (pair == QSE_NULL) return QSE_NULL; /* error */ + + NEXT(pair) = htb->bucket[hc]; + htb->bucket[hc] = pair; + htb->size++; + + return pair; /* new key added */ +} + int qse_htb_delete (htb_t* htb, const void* kptr, size_t klen) { pair_t* pair, * prev; @@ -614,7 +607,7 @@ int qse_htb_delete (htb_t* htb, const void* kptr, size_t klen) htb->bucket[hc] = NEXT(pair); else NEXT(prev) = NEXT(pair); - free_pair (htb, pair); + qse_htb_freepair (htb, pair); htb->size--; return 0; @@ -639,7 +632,7 @@ void qse_htb_clear (htb_t* htb) while (pair != QSE_NULL) { next = NEXT(pair); - free_pair (htb, pair); + qse_htb_freepair (htb, pair); htb->size--; pair = next; } diff --git a/qse/samples/cmn/htb.c b/qse/samples/cmn/htb.c index 603f99bb..b06ea52a 100644 --- a/qse/samples/cmn/htb.c +++ b/qse/samples/cmn/htb.c @@ -10,6 +10,23 @@ if (f() == -1) return -1; \ } while (0) +static qse_htb_mancbs_t mancbs1 = +{ + { + QSE_HTB_COPIER_INLINE, + QSE_HTB_COPIER_INLINE + }, + { + QSE_HTB_FREEER_DEFAULT, + QSE_HTB_FREEER_DEFAULT + }, + QSE_HTB_HASHER_DEFAULT, + QSE_HTB_COMPER_DEFAULT, + QSE_HTB_KEEPER_DEFAULT, + QSE_HTB_SIZER_DEFAULT +}; + + static int test1_build (qse_htb_t* s1) { int i; @@ -109,15 +126,13 @@ static int test1 () { qse_htb_t* s1; - s1 = qse_htb_open (QSE_MMGR_GETDFL(), 0, 5, 70); + s1 = qse_htb_open (QSE_MMGR_GETDFL(), 0, 5, 70, 1, 1); if (s1 == QSE_NULL) { qse_printf (QSE_T("cannot open a map\n")); return -1; } - - qse_htb_setcopier (s1, QSE_HTB_KEY, QSE_HTB_COPIER_INLINE); - qse_htb_setcopier (s1, QSE_HTB_VAL, QSE_HTB_COPIER_INLINE); + qse_htb_setmancbs (s1, &mancbs1); if (test1_build(s1) == -1) { @@ -154,17 +169,22 @@ static int test2 () QSE_T("in a given directory") }; - s1 = qse_htb_open (QSE_MMGR_GETDFL(), 0, 1, 70); + qse_char_t* vals[] = + { + QSE_T("what the hell is this"), + QSE_T("oh my goddess"), + QSE_T("hello mr monkey"), + QSE_T("is this good?") + }; + + s1 = qse_htb_open (QSE_MMGR_GETDFL(), 0, 1, 70, + QSE_SIZEOF(qse_char_t), QSE_SIZEOF(qse_char_t)); if (s1 == QSE_NULL) { qse_printf (QSE_T("cannot open a map\n")); return -1; } - - qse_htb_setscale (s1, QSE_HTB_KEY, QSE_SIZEOF(qse_char_t)); - qse_htb_setscale (s1, QSE_HTB_VAL, QSE_SIZEOF(qse_char_t)); - qse_htb_setcopier (s1, QSE_HTB_KEY, QSE_HTB_COPIER_INLINE); - qse_htb_setcopier (s1, QSE_HTB_VAL, QSE_HTB_COPIER_INLINE); + qse_htb_setmancbs (s1, &mancbs1); for (i = 0; i < QSE_COUNTOF(keys); i++) { @@ -185,10 +205,28 @@ static int test2 () qse_htb_walk (s1, print_map_pair, QSE_NULL); + for (i = 0; i < QSE_COUNTOF(keys); i++) + { + qse_printf (QSE_T("updating a key [%s] and a value [%s]: "), keys[i], vals[i]); + if (qse_htb_update (s1, + keys[i], qse_strlen(keys[i]), + vals[i], qse_strlen(vals[i])) == QSE_NULL) + { + qse_printf (QSE_T("[FAILED]\n")); + } + else + { + qse_printf (QSE_T("[OK]\n")); + } + } + qse_htb_walk (s1, print_map_pair, QSE_NULL); + + qse_htb_close (s1); return 0; } +#if 0 static int comp_key (qse_htb_t* map, const void* kptr1, qse_size_t klen1, const void* kptr2, qse_size_t klen2) @@ -296,12 +334,15 @@ static int test4 () qse_htb_close (s1); return 0; } +#endif int main () { R (test1); R (test2); +#if 0 R (test3); R (test4); +#endif return 0; }