From c9bbd3c9936f9a83aa0dc4858863936c264790ea Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 11 Dec 2017 08:48:06 +0000 Subject: [PATCH] added qse_htl_yanknode(), qse_htl_upyank(). added qse_raddic_deletevendorbyname(), qse_raddic_deletevendorbyid() --- qse/include/qse/cmn/htl.h | 45 +++++++++++------ qse/include/qse/rad/raddic.h | 30 +++++++---- qse/lib/cmn/htl.c | 48 ++++++++++++++---- qse/lib/rad/raddic.c | 93 +++++++++++++++++++++++++++++++--- qse/samples/rad/raddic01.c | 96 ++++++++++++++++++++++++++++++++++-- 5 files changed, 267 insertions(+), 45 deletions(-) diff --git a/qse/include/qse/cmn/htl.h b/qse/include/qse/cmn/htl.h index 1e0ce31c..db458f57 100644 --- a/qse/include/qse/cmn/htl.h +++ b/qse/include/qse/cmn/htl.h @@ -197,10 +197,10 @@ static QSE_INLINE void qse_htl_setcopier (qse_htl_t* htl, qse_htl_copier_t _copi /** * The qse_htl_search() function searches a hash table to find a - * matching datum. It returns the index to the slot of an internal array - * where the matching datum is found. + * matching item. It returns the index to the slot of an internal array + * where the matching item is found. * \return slot index if a match if found, - * #QSE_HTL_NIL if no match is found. + * #QSE_NULL if no match is found. */ QSE_EXPORT qse_htl_node_t* qse_htl_search ( qse_htl_t* htl, /**< hash table */ @@ -215,10 +215,9 @@ QSE_EXPORT qse_htl_node_t* qse_htl_heterosearch ( ); /** - * The qse_htl_insert() function inserts a new datum. It fails if it finds - * an existing datum. - * \return slot index where the new datum is inserted on success, - * #QSE_HTL_NIL on failure. + * The qse_htl_insert() function inserts a new item. It fails if it finds + * an existing item. + * \return a node pointer on success, #QSE_NULL on failure. */ QSE_EXPORT qse_htl_node_t* qse_htl_insert ( qse_htl_t* htl, /**< hash table */ @@ -226,9 +225,9 @@ QSE_EXPORT qse_htl_node_t* qse_htl_insert ( ); /** - * The qse_htl_upsert() function inserts a new datum if it finds no matching - * datum or updates an exsting datum if finds a matching datum. - * \return slot index where the datum is inserted or updated. + * The qse_htl_upsert() function inserts a new item if it finds no matching + * item or updates an exsting item if finds a matching item. + * \return node pointer on success, #QSE_NULL on failure. */ QSE_EXPORT qse_htl_node_t* qse_htl_upsert ( qse_htl_t* htl, /**< hash table */ @@ -236,16 +235,25 @@ QSE_EXPORT qse_htl_node_t* qse_htl_upsert ( ); /** - * The qse_htl_update() function updates an existing datum. It fails if it finds - * no existing datum. - * \return slot index where an existing datum is updated on success, - * #QSE_HTL_NIL on failure. + * The qse_htl_update() function updates an existing item. It fails if it finds + * no existing item. + * \return a node pointer on success, #QSE_NULL on failure. */ QSE_EXPORT qse_htl_node_t* qse_htl_update ( qse_htl_t* htl, /**< hash table */ void* data /**< data pointer */ ); +/** + * The qse_htl_upyank() function updates an existing dataum without disposing + * the old data. + */ +QSE_EXPORT qse_htl_node_t* qse_htl_upyank ( + qse_htl_t* htl, /**< hash table */ + void* data, /**< data pointer */ + void** oldata /**< old data pointer */ +); + /** * The qse_htl_ensert() function inserts a new item if one is not found. * It returns an existing item otherwise. @@ -256,8 +264,8 @@ QSE_EXPORT qse_htl_node_t* qse_htl_ensert ( ); /** - * The qse_htl_delete() function deletes an existing datum. It fails if it finds - * no existing datum. + * The qse_htl_delete() function deletes an existing item. It fails if it finds + * no existing item. * \return 0 on success, -1 on failure */ QSE_EXPORT int qse_htl_delete ( @@ -270,6 +278,11 @@ QSE_EXPORT void* qse_htl_yank ( void* data ); +QSE_EXPORT qse_htl_node_t* qse_htl_yanknode ( + qse_htl_t* ht, + void* data +); + /** * The qse_htl_clear() functions deletes all data items. */ diff --git a/qse/include/qse/rad/raddic.h b/qse/include/qse/rad/raddic.h index 5b517556..27e1906e 100644 --- a/qse/include/qse/rad/raddic.h +++ b/qse/include/qse/rad/raddic.h @@ -76,21 +76,23 @@ typedef struct qse_raddic_attr_t qse_raddic_attr_t; struct qse_raddic_value_t { - int attr; - int value; - qse_char_t name[1]; + int attr; + int value; + qse_char_t name[1]; }; typedef struct qse_raddic_value_t qse_raddic_value_t; +typedef struct qse_raddic_vendor_t qse_raddic_vendor_t; struct qse_raddic_vendor_t { - int vendorpec; - int type; - int length; - int flags; - qse_char_t name[1]; + int vendorpec; + int type; + int length; + int flags; + qse_raddic_vendor_t* nextv; /* next vendor with same details except the name */ + qse_char_t name[1]; }; -typedef struct qse_raddic_vendor_t qse_raddic_vendor_t; + typedef struct qse_raddic_t qse_raddic_t; @@ -123,6 +125,16 @@ QSE_EXPORT qse_raddic_vendor_t* qse_raddic_addvendor ( int value ); +QSE_EXPORT int qse_raddic_deletevendorbyname ( + qse_raddic_t* dic, + const qse_char_t* name +); + +QSE_EXPORT int qse_raddic_deletevendorbyvalue ( + qse_raddic_t* dic, + int value +); + #if defined(__cplusplus) } #endif diff --git a/qse/lib/cmn/htl.c b/qse/lib/cmn/htl.c index 15c18617..196803e1 100644 --- a/qse/lib/cmn/htl.c +++ b/qse/lib/cmn/htl.c @@ -578,9 +578,30 @@ qse_htl_node_t* qse_htl_update (qse_htl_t* ht, void* data) return node; } +qse_htl_node_t* qse_htl_upyank (qse_htl_t* ht, void* data, void** olddata) +{ + qse_htl_node_t* node; + void* datap, * olddatap; + + node = qse_htl_search(ht, data); + if (!node) return QSE_NULL; + + if (ht->copier) + { + datap = ht->copier (ht, data); + if (!datap) return QSE_NULL; + } + else datap = data; + + *olddata = node->data; + node->data = datap; + + return node; +} + qse_htl_node_t* qse_htl_ensert (qse_htl_t* ht, void* data) { - qse_htl_node_t *node; + qse_htl_node_t* node; node = qse_htl_search(ht, data); if (!node) node = qse_htl_insert(ht, data); @@ -588,19 +609,14 @@ qse_htl_node_t* qse_htl_ensert (qse_htl_t* ht, void* data) return node; } -/* - * Yank an entry from the hash table, without freeing the data. - */ -void* qse_htl_yank (qse_htl_t* ht, void* data) + +qse_htl_node_t* qse_htl_yanknode (qse_htl_t* ht, void* data) { qse_uint32_t key; qse_uint32_t entry; qse_uint32_t reversed; - void *old; qse_htl_node_t* node; - if (!ht) return QSE_NULL; - key = ht->hasher(ht, data); entry = key & ht->mask; reversed = reverse(key); @@ -613,9 +629,23 @@ void* qse_htl_yank (qse_htl_t* ht, void* data) list_delete(ht, &ht->buckets[entry], node); ht->num_elements--; + return node; +} + + +/* + * Yank an entry from the hash table, without freeing the data. + */ +void* qse_htl_yank (qse_htl_t* ht, void* data) +{ + qse_htl_node_t* node; + void* old; + + node = qse_htl_yanknode (ht, data); + if (!node) return QSE_NULL; + old = node->data; QSE_MMGR_FREE (ht->mmgr, node); - return old; } diff --git a/qse/lib/rad/raddic.c b/qse/lib/rad/raddic.c index 846d56a4..83a6032f 100644 --- a/qse/lib/rad/raddic.c +++ b/qse/lib/rad/raddic.c @@ -217,7 +217,7 @@ qse_raddic_vendor_t* qse_raddic_findvendorbyvalue (qse_raddic_t* dic, int vendor qse_raddic_vendor_t* qse_raddic_addvendor (qse_raddic_t* dic, const qse_char_t* name, int value) { qse_size_t length; - qse_raddic_vendor_t* dv; + qse_raddic_vendor_t* dv, * old_dv; qse_htl_node_t* np; if (value >= 65535) return QSE_NULL; @@ -227,10 +227,11 @@ qse_raddic_vendor_t* qse_raddic_addvendor (qse_raddic_t* dic, const qse_char_t* /* no +1 for the terminating null because dv->name is char[1] */ dv = QSE_MMGR_ALLOC(dic->mmgr, QSE_SIZEOF(*dv) + (length * QSE_SIZEOF(*name))); if (dv == QSE_NULL) return QSE_NULL; - + qse_strcpy(dv->name, name); dv->vendorpec = value; dv->type = dv->length = 1; /* defaults */ + dv->nextv = QSE_NULL; /* return an existing item or insert a new item */ np = qse_htl_ensert(&dic->vendors_byname, dv); @@ -241,16 +242,96 @@ qse_raddic_vendor_t* qse_raddic_addvendor (qse_raddic_t* dic, const qse_char_t* return QSE_NULL; } - /* update indexing by value */ - if (!qse_htl_upsert(&dic->vendors_byvalue, dv)) + /* attempt to update the lookup table by value */ + np = qse_htl_upyank(&dic->vendors_byvalue, dv, (void**)&old_dv); + if (np) { - qse_htl_delete (&dic->vendors_byname, dv); - return QSE_NULL; + /* updated the existing item successfully. + * link the old item to the current item */ + QSE_ASSERT (np->data == dv); + QSE_ASSERT (dv->vendorpec == old_dv->vendorpec); + dv->nextv = old_dv; + } + else + { + /* update failure, this entry must be new. try insertion */ + if (!qse_htl_insert (&dic->vendors_byvalue, dv)) + { + qse_htl_delete (&dic->vendors_byname, dv); + return QSE_NULL; + } } return dv; } +int qse_raddic_deletevendorbyname (qse_raddic_t* dic, const qse_char_t* name) +{ + qse_raddic_vendor_t* dv, * dv2; + + dv = qse_raddic_findvendorbyname(dic, name); + if (!dv) return -1; + + dv2 = qse_raddic_findvendorbyvalue(dic, dv->vendorpec); + QSE_ASSERT (dv2 != QSE_NULL); + + if (dv != dv2) + { + qse_raddic_vendor_t* x, * y; + + QSE_ASSERT (qse_strcasecmp(dv->name, dv2->name) != 0); + QSE_ASSERT (dv->vendorpec == dv2->vendorpec); + + /* when the vendor of the given name is not the first one + * referenced by value, i need to unlink the vendor from the + * vendor chains with the same ID */ + x = dv2; + y = QSE_NULL; + while (x) + { + if (x == dv) + { + if (y) y->nextv = x->nextv; + break; + } + y = x; + x = x->nextv; + } + } + else + { + /* this is the only vendor with the vendor ID. i can + * safely remove it from the lookup table by value */ + qse_htl_delete (&dic->vendors_byvalue, dv); + } + + /* delete the vendor from the lookup table by name */ + qse_htl_delete (&dic->vendors_byname, dv); + return 0; +} + +int qse_raddic_deletevendorbyvalue (qse_raddic_t* dic, int value) +{ + qse_raddic_vendor_t* dv; + + dv = qse_raddic_findvendorbyvalue(dic, value); + if (!dv) return -1; + + if (dv->nextv) + { + qse_htl_update (&dic->vendors_byvalue, dv->nextv); + } + else + { + /* this is the only vendor with the vendor ID. i can + * safely remove it from the lookup table by value */ + qse_htl_delete (&dic->vendors_byvalue, dv); + } + + /* delete the vendor from the lookup table by name */ + qse_htl_delete (&dic->vendors_byname, dv); + return 0; +} #if 0 // XXX /* diff --git a/qse/samples/rad/raddic01.c b/qse/samples/rad/raddic01.c index 0cd541a2..f8e80e86 100644 --- a/qse/samples/rad/raddic01.c +++ b/qse/samples/rad/raddic01.c @@ -16,7 +16,7 @@ static int test1 () { qse_raddic_t* dic; - qse_raddic_vendor_t* vendor; + qse_raddic_vendor_t* vendor, * v; int i; dic = qse_raddic_open (QSE_MMGR_GETDFL(), 0); @@ -44,9 +44,10 @@ static int test1 () _assert (vendor != QSE_NULL && vendor->vendorpec == 12365, "unabled to find a vendor named Abiyo-aliased.Net"); _assert (qse_strcasecmp(vendor->name, QSE_T("abiyo-aliased.net")) == 0, "unabled to find a vendor of value 12365"); -#define COUNT 65535 +#define COUNT1 600 +#define COUNT2 700 - for (i = 0; i < COUNT; i++) + for (i = 0; i < COUNT1; i++) { qse_char_t tmp[64]; qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); @@ -56,7 +57,7 @@ static int test1 () _assert (qse_strcasecmp(vendor->name, tmp) == 0, "wrong vendor name"); } - for (i = 0; i < COUNT; i++) + for (i = 0; i < COUNT1; i++) { qse_char_t tmp[64]; qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); @@ -66,7 +67,7 @@ static int test1 () _assert (qse_strcasecmp(vendor->name, tmp) == 0, "wrong vendor name"); } - for (i = 0; i < COUNT; i++) + for (i = 0; i < COUNT1; i++) { qse_char_t tmp[64]; qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); @@ -76,6 +77,91 @@ static int test1 () _assert (qse_strcasecmp(vendor->name, tmp) == 0, "wrong vendor name"); } + for (i = COUNT1; i < COUNT2; i++) + { + qse_char_t tmp[64]; + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); + vendor = qse_raddic_addvendor (dic, tmp, COUNT1); + // insert different items with the same value + _assert (vendor != QSE_NULL, "unable to add a vendor"); + _assert (vendor->vendorpec == COUNT1, "wrong vendor value"); + _assert (qse_strcasecmp(vendor->name, tmp) == 0, "wrong vendor name"); + + v = qse_raddic_findvendorbyvalue (dic, COUNT1); + _assert (vendor == v, "unable to find a last added vendor by value"); + } + + for (i = COUNT1; i < COUNT2 - 1; i++) + { + qse_char_t tmp[64]; + int n; + + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); + + n = qse_raddic_deletevendorbyname (dic, tmp); + _assert (n == 0, "unable to delete a vendor"); + + v = qse_raddic_findvendorbyname (dic, tmp); + _assert (v == QSE_NULL, "vendor found errorenously"); + + if (i == COUNT2 - 1) + { + v = qse_raddic_findvendorbyvalue (dic, COUNT1); + _assert (v == QSE_NULL, "vendor of COUNT1 found errorenously"); + } + else + { + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i + 1); + v = qse_raddic_findvendorbyname (dic, tmp); + _assert (v != QSE_NULL && v->vendorpec == COUNT1 && qse_strcasecmp(tmp, v->name) == 0, "unable to find an expected vendor"); + + v = qse_raddic_findvendorbyvalue (dic, COUNT1); + _assert (v != QSE_NULL && v->vendorpec == COUNT1, "unable to find the vendor of COUNT1"); + } + } + + for (i = 0; i < COUNT1; i++) + { + qse_char_t tmp[64]; + int n; + + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); + n = qse_raddic_deletevendorbyname (dic, tmp); + _assert (n == 0, "unable to delete a vendor"); + } + + for (i = 0; i < COUNT1; i++) + { + qse_char_t tmp[64]; + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("test%d"), i); + v = qse_raddic_addvendor (dic, tmp, i); + _assert (v != QSE_NULL && v->vendorpec == i, "unable to add a vendor"); + + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("testx%d"), i); + v = qse_raddic_addvendor (dic, tmp, i); + _assert (v != QSE_NULL && v->vendorpec == i, "unable to add a vendor"); + + qse_strxfmt(tmp, QSE_COUNTOF(tmp), QSE_T("testy%d"), i); + v = qse_raddic_addvendor (dic, tmp, i); + _assert (v != QSE_NULL && v->vendorpec == i, "unable to add a vendor"); + } + + for (i = 0; i < COUNT1; i++) + { + int n; + n = qse_raddic_deletevendorbyvalue (dic, i); + _assert (n == 0, "unable to delete a vendor by value"); + + n = qse_raddic_deletevendorbyvalue (dic, i); + _assert (n == 0, "unable to delete a vendor by value"); + + n = qse_raddic_deletevendorbyvalue (dic, i); + _assert (n == 0, "unable to delete a vendor by value"); + + n = qse_raddic_deletevendorbyvalue (dic, i); + _assert (n <= -1, "erroreneously successful vendor deletion by value"); + } + qse_raddic_close (dic); return 0; }