* renamed map to htb
* added qse_awk_findgbl() * added Awk::findGlobal() * began working on red-black tree
This commit is contained in:
@ -7,7 +7,7 @@ libqsecmn_la_SOURCES = \
|
||||
syscall.h mem.h \
|
||||
mem.c chr.c chr_cnv.c rex.c \
|
||||
str_bas.c str_cnv.c str_dyn.c str_utl.c \
|
||||
lda.c map.c sll.c dll.c opt.c \
|
||||
lda.c htb.c rbt.c sll.c dll.c opt.c \
|
||||
tio.c tio_get.c tio_put.c \
|
||||
fio.c pio.c sio.c \
|
||||
time.c \
|
||||
|
@ -74,9 +74,9 @@ am__installdirs = "$(DESTDIR)$(libdir)"
|
||||
LTLIBRARIES = $(lib_LTLIBRARIES)
|
||||
libqsecmn_la_DEPENDENCIES =
|
||||
am_libqsecmn_la_OBJECTS = mem.lo chr.lo chr_cnv.lo rex.lo str_bas.lo \
|
||||
str_cnv.lo str_dyn.lo str_utl.lo lda.lo map.lo sll.lo dll.lo \
|
||||
opt.lo tio.lo tio_get.lo tio_put.lo fio.lo pio.lo sio.lo \
|
||||
time.lo misc.lo assert.lo main.lo stdio.lo
|
||||
str_cnv.lo str_dyn.lo str_utl.lo lda.lo htb.lo rbt.lo sll.lo \
|
||||
dll.lo opt.lo tio.lo tio_get.lo tio_put.lo fio.lo pio.lo \
|
||||
sio.lo time.lo misc.lo assert.lo main.lo stdio.lo
|
||||
libqsecmn_la_OBJECTS = $(am_libqsecmn_la_OBJECTS)
|
||||
libqsecmn_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
@ -263,7 +263,7 @@ libqsecmn_la_SOURCES = \
|
||||
syscall.h mem.h \
|
||||
mem.c chr.c chr_cnv.c rex.c \
|
||||
str_bas.c str_cnv.c str_dyn.c str_utl.c \
|
||||
lda.c map.c sll.c dll.c opt.c \
|
||||
lda.c htb.c rbt.c sll.c dll.c opt.c \
|
||||
tio.c tio_get.c tio_put.c \
|
||||
fio.c pio.c sio.c \
|
||||
time.c \
|
||||
@ -361,13 +361,14 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chr_cnv.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dll.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fio.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htb.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lda.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mem.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opt.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pio.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rbt.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rex.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sio.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sll.Plo@am__quote@
|
||||
|
696
qse/lib/cmn/htb.c
Normal file
696
qse/lib/cmn/htb.c
Normal file
@ -0,0 +1,696 @@
|
||||
/*
|
||||
* $Id: htb.c 328 2010-07-08 06:58:44Z hyunghwan.chung $
|
||||
*
|
||||
Copyright 2006-2009 Chung, Hyung-Hwan.
|
||||
This file is part of QSE.
|
||||
|
||||
QSE is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
QSE is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <qse/cmn/htb.h>
|
||||
#include "mem.h"
|
||||
|
||||
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 KPTR(p) QSE_HTB_KPTR(p)
|
||||
#define KLEN(p) QSE_HTB_KLEN(p)
|
||||
#define VPTR(p) QSE_HTB_VPTR(p)
|
||||
#define VLEN(p) QSE_HTB_VLEN(p)
|
||||
#define NEXT(p) QSE_HTB_NEXT(p)
|
||||
|
||||
#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(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 int reorganize (htb_t* htb);
|
||||
|
||||
static size_t hash_key (htb_t* htb, const void* kptr, size_t klen)
|
||||
{
|
||||
/*size_t h = 2166136261;*/
|
||||
/*size_t h = 0;*/
|
||||
size_t h = 5381;
|
||||
const byte_t* p = (const byte_t*)kptr;
|
||||
const byte_t* bound = p + klen;
|
||||
|
||||
while (p < bound)
|
||||
{
|
||||
/*h = (h * 16777619) ^ *p++;*/
|
||||
/*h = h * 31 + *p++;*/
|
||||
h = ((h << 5) + h) + *p++;
|
||||
}
|
||||
|
||||
return h ;
|
||||
}
|
||||
|
||||
static int comp_key (htb_t* htb,
|
||||
const void* kptr1, size_t klen1,
|
||||
const void* kptr2, size_t klen2)
|
||||
{
|
||||
if (klen1 == klen2) return QSE_MEMCMP (kptr1, kptr2, KTOB(htb,klen1));
|
||||
/* it just returns 1 to indicate that they are different. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pair_t* alloc_pair (htb_t* htb,
|
||||
void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
pair_t* n;
|
||||
copier_t kcop = htb->copier[QSE_HTB_KEY];
|
||||
copier_t vcop = htb->copier[QSE_HTB_VAL];
|
||||
|
||||
size_t as = SIZEOF(pair_t);
|
||||
if (kcop == QSE_HTB_COPIER_INLINE) as += KTOB(htb,klen);
|
||||
if (vcop == QSE_HTB_COPIER_INLINE) as += VTOB(htb,vlen);
|
||||
|
||||
n = (pair_t*) QSE_MMGR_ALLOC (htb->mmgr, as);
|
||||
if (n == QSE_NULL) return QSE_NULL;
|
||||
|
||||
NEXT(n) = QSE_NULL;
|
||||
|
||||
KLEN(n) = klen;
|
||||
if (kcop == QSE_HTB_COPIER_SIMPLE)
|
||||
{
|
||||
KPTR(n) = kptr;
|
||||
}
|
||||
else if (kcop == QSE_HTB_COPIER_INLINE)
|
||||
{
|
||||
KPTR(n) = n + 1;
|
||||
QSE_MEMCPY (KPTR(n), kptr, KTOB(htb,klen));
|
||||
}
|
||||
else
|
||||
{
|
||||
KPTR(n) = kcop (htb, kptr, klen);
|
||||
if (KPTR(n) == QSE_NULL)
|
||||
{
|
||||
QSE_MMGR_FREE (htb->mmgr, n);
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
VLEN(n) = vlen;
|
||||
if (vcop == QSE_HTB_COPIER_SIMPLE)
|
||||
{
|
||||
VPTR(n) = vptr;
|
||||
}
|
||||
else if (vcop == QSE_HTB_COPIER_INLINE)
|
||||
{
|
||||
VPTR(n) = n + 1;
|
||||
if (kcop == QSE_HTB_COPIER_INLINE)
|
||||
VPTR(n) = (byte_t*)VPTR(n) + KTOB(htb,klen);
|
||||
QSE_MEMCPY (VPTR(n), vptr, VTOB(htb,vlen));
|
||||
}
|
||||
else
|
||||
{
|
||||
VPTR(n) = vcop (htb, vptr, vlen);
|
||||
if (VPTR(n) != QSE_NULL)
|
||||
{
|
||||
if (htb->freeer[QSE_HTB_KEY] != QSE_NULL)
|
||||
htb->freeer[QSE_HTB_KEY] (htb, KPTR(n), KLEN(n));
|
||||
QSE_MMGR_FREE (htb->mmgr, n);
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void free_pair (htb_t* htb, pair_t* pair)
|
||||
{
|
||||
if (htb->freeer[QSE_HTB_KEY] != QSE_NULL)
|
||||
htb->freeer[QSE_HTB_KEY] (htb, KPTR(pair), KLEN(pair));
|
||||
if (htb->freeer[QSE_HTB_VAL] != QSE_NULL)
|
||||
htb->freeer[QSE_HTB_VAL] (htb, VPTR(pair), VLEN(pair));
|
||||
QSE_MMGR_FREE (htb->mmgr, pair);
|
||||
}
|
||||
|
||||
static pair_t* change_pair_val (
|
||||
htb_t* htb, pair_t* pair, void* vptr, size_t vlen)
|
||||
{
|
||||
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 (htb->keeper != QSE_NULL)
|
||||
{
|
||||
htb->keeper (htb, vptr, vlen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
copier_t vcop = htb->copier[QSE_HTB_VAL];
|
||||
void* ovptr = VPTR(pair);
|
||||
size_t ovlen = VLEN(pair);
|
||||
|
||||
/* place the new value according to the copier */
|
||||
if (vcop == QSE_HTB_COPIER_SIMPLE)
|
||||
{
|
||||
VPTR(pair) = vptr;
|
||||
VLEN(pair) = vlen;
|
||||
}
|
||||
else if (vcop == QSE_HTB_COPIER_INLINE)
|
||||
{
|
||||
if (ovlen == vlen)
|
||||
{
|
||||
QSE_MEMCPY (VPTR(pair), vptr, VTOB(htb,vlen));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* need to reconstruct the pair */
|
||||
pair_t* p = alloc_pair (htb,
|
||||
KPTR(pair), KLEN(pair),
|
||||
vptr, vlen);
|
||||
if (p == QSE_NULL) return QSE_NULL;
|
||||
free_pair (htb, pair);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void* nvptr = vcop (htb, vptr, vlen);
|
||||
if (nvptr == QSE_NULL) return QSE_NULL;
|
||||
VPTR(pair) = nvptr;
|
||||
VLEN(pair) = vlen;
|
||||
}
|
||||
|
||||
/* free up the old value */
|
||||
if (htb->freeer[QSE_HTB_VAL] != QSE_NULL)
|
||||
{
|
||||
htb->freeer[QSE_HTB_VAL] (htb, ovptr, ovlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
htb_t* qse_htb_open (mmgr_t* mmgr, size_t ext, size_t capa, int factor)
|
||||
{
|
||||
htb_t* htb;
|
||||
|
||||
if (mmgr == QSE_NULL)
|
||||
{
|
||||
mmgr = QSE_MMGR_GETDFL();
|
||||
|
||||
QSE_ASSERTX (mmgr != QSE_NULL,
|
||||
"Set the memory manager with QSE_MMGR_SETDFL()");
|
||||
|
||||
if (mmgr == QSE_NULL) return QSE_NULL;
|
||||
}
|
||||
|
||||
htb = (htb_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(htb_t) + ext);
|
||||
if (htb == QSE_NULL) return QSE_NULL;
|
||||
|
||||
if (qse_htb_init (htb, mmgr, capa, factor) == QSE_NULL)
|
||||
{
|
||||
QSE_MMGR_FREE (mmgr, htb);
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
return htb;
|
||||
}
|
||||
|
||||
void qse_htb_close (htb_t* htb)
|
||||
{
|
||||
qse_htb_fini (htb);
|
||||
QSE_MMGR_FREE (htb->mmgr, htb);
|
||||
}
|
||||
|
||||
htb_t* qse_htb_init (htb_t* htb, mmgr_t* mmgr, size_t capa, int factor)
|
||||
{
|
||||
QSE_ASSERTX (capa > 0,
|
||||
"The initial capacity should be greater than 0. Otherwise, it is adjusted to 1 in the release mode");
|
||||
QSE_ASSERTX (factor >= 0 && factor <= 100,
|
||||
"The load factor should be between 0 and 100 inclusive. In the release mode, a value out of the range is adjusted to 100");
|
||||
|
||||
/* some initial adjustment */
|
||||
if (capa <= 0) capa = 1;
|
||||
if (factor > 100) factor = 100;
|
||||
|
||||
/* do not zero out the extension */
|
||||
QSE_MEMSET (htb, 0, SIZEOF(*htb));
|
||||
htb->mmgr = mmgr;
|
||||
|
||||
htb->bucket = QSE_MMGR_ALLOC (mmgr, capa*SIZEOF(pair_t*));
|
||||
if (htb->bucket == QSE_NULL) return QSE_NULL;
|
||||
|
||||
/*for (i = 0; i < capa; i++) htb->bucket[i] = QSE_NULL;*/
|
||||
QSE_MEMSET (htb->bucket, 0, capa*SIZEOF(pair_t*));
|
||||
|
||||
htb->scale[QSE_HTB_KEY] = 1;
|
||||
htb->scale[QSE_HTB_VAL] = 1;
|
||||
htb->factor = factor;
|
||||
|
||||
htb->size = 0;
|
||||
htb->capa = capa;
|
||||
htb->threshold = htb->capa * htb->factor / 100;
|
||||
if (htb->capa > 0 && htb->threshold <= 0) htb->threshold = 1;
|
||||
|
||||
htb->hasher = hash_key;
|
||||
htb->comper = comp_key;
|
||||
htb->copier[QSE_HTB_KEY] = QSE_HTB_COPIER_SIMPLE;
|
||||
htb->copier[QSE_HTB_VAL] = QSE_HTB_COPIER_SIMPLE;
|
||||
|
||||
/*
|
||||
htb->freeer[QSE_HTB_KEY] = QSE_NULL;
|
||||
htb->freeer[QSE_HTB_VAL] = QSE_NULL;
|
||||
htb->keeper = QSE_NULL;
|
||||
htb->sizer = QSE_NULL;
|
||||
*/
|
||||
|
||||
return htb;
|
||||
}
|
||||
|
||||
void qse_htb_fini (htb_t* htb)
|
||||
{
|
||||
qse_htb_clear (htb);
|
||||
QSE_MMGR_FREE (htb->mmgr, htb->bucket);
|
||||
}
|
||||
|
||||
int qse_htb_getscale (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->scale[id];
|
||||
}
|
||||
|
||||
void qse_htb_setscale (htb_t* htb, qse_htb_id_t id, int scale)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_HTB_KEY || id == QSE_HTB_VAL,
|
||||
"The ID should be either QSE_HTB_KEY or QSE_HTB_VAL");
|
||||
|
||||
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);
|
||||
|
||||
htb->scale[id] = scale;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t qse_htb_getsize (htb_t* htb)
|
||||
{
|
||||
return htb->size;
|
||||
}
|
||||
|
||||
size_t qse_htb_getcapa (htb_t* htb)
|
||||
{
|
||||
return htb->capa;
|
||||
}
|
||||
|
||||
pair_t* qse_htb_search (htb_t* htb, const void* kptr, size_t klen)
|
||||
{
|
||||
pair_t* pair;
|
||||
size_t hc;
|
||||
|
||||
hc = htb->hasher(htb,kptr,klen) % htb->capa;
|
||||
pair = htb->bucket[hc];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
if (htb->comper (htb, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
return pair;
|
||||
}
|
||||
|
||||
pair = NEXT(pair);
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
static pair_t* insert (
|
||||
htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen, int opt)
|
||||
{
|
||||
pair_t* pair, * p, * prev, * next;
|
||||
size_t hc;
|
||||
|
||||
hc = htb->hasher(htb,kptr,klen) % htb->capa;
|
||||
pair = htb->bucket[hc];
|
||||
prev = QSE_NULL;
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
|
||||
if (htb->comper (htb, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
/* found a pair with a matching key */
|
||||
switch (opt)
|
||||
{
|
||||
case UPSERT:
|
||||
case UPDATE:
|
||||
p = change_pair_val (htb, pair, vptr, vlen);
|
||||
if (p == QSE_NULL)
|
||||
{
|
||||
/* error in change the value */
|
||||
return QSE_NULL;
|
||||
}
|
||||
if (p != pair)
|
||||
{
|
||||
/* pair reallocated.
|
||||
* relink it */
|
||||
if (prev == QSE_NULL)
|
||||
htb->bucket[hc] = p;
|
||||
else NEXT(prev) = p;
|
||||
NEXT(p) = next;
|
||||
}
|
||||
return p;
|
||||
|
||||
case ENSERT:
|
||||
/* return existing pair */
|
||||
return pair;
|
||||
|
||||
case INSERT:
|
||||
/* return failure */
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
prev = pair;
|
||||
pair = next;
|
||||
}
|
||||
|
||||
if (opt == UPDATE) return QSE_NULL;
|
||||
|
||||
if (htb->threshold > 0 && htb->size >= htb->threshold)
|
||||
{
|
||||
if (reorganize(htb) == 0) /* ignore the error */
|
||||
{
|
||||
hc = htb->hasher(htb,kptr,klen) % htb->capa;
|
||||
}
|
||||
}
|
||||
|
||||
QSE_ASSERT (pair == QSE_NULL);
|
||||
|
||||
pair = alloc_pair (htb, kptr, klen, vptr, vlen);
|
||||
if (pair == QSE_NULL) return QSE_NULL; /* error */
|
||||
|
||||
NEXT(pair) = htb->bucket[hc];
|
||||
htb->bucket[hc] = pair;
|
||||
htb->size++;
|
||||
|
||||
return pair; /* new key added */
|
||||
}
|
||||
|
||||
pair_t* qse_htb_upsert (
|
||||
htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (htb, kptr, klen, vptr, vlen, UPSERT);
|
||||
}
|
||||
|
||||
pair_t* qse_htb_ensert (
|
||||
htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (htb, kptr, klen, vptr, vlen, ENSERT);
|
||||
}
|
||||
|
||||
pair_t* qse_htb_insert (
|
||||
htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (htb, kptr, klen, vptr, vlen, INSERT);
|
||||
}
|
||||
|
||||
|
||||
pair_t* qse_htb_update (
|
||||
htb_t* htb, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (htb, kptr, klen, vptr, vlen, UPDATE);
|
||||
}
|
||||
|
||||
int qse_htb_delete (htb_t* htb, const void* kptr, size_t klen)
|
||||
{
|
||||
pair_t* pair, * prev;
|
||||
size_t hc;
|
||||
|
||||
hc = htb->hasher(htb,kptr,klen) % htb->capa;
|
||||
pair = htb->bucket[hc];
|
||||
prev = QSE_NULL;
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
if (htb->comper (htb, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
if (prev == QSE_NULL)
|
||||
htb->bucket[hc] = NEXT(pair);
|
||||
else NEXT(prev) = NEXT(pair);
|
||||
|
||||
free_pair (htb, pair);
|
||||
htb->size--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev = pair;
|
||||
pair = NEXT(pair);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void qse_htb_clear (htb_t* htb)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* pair, * next;
|
||||
|
||||
for (i = 0; i < htb->capa; i++)
|
||||
{
|
||||
pair = htb->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
free_pair (htb, pair);
|
||||
htb->size--;
|
||||
pair = next;
|
||||
}
|
||||
|
||||
htb->bucket[i] = QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void qse_htb_walk (htb_t* htb, walker_t walker, void* ctx)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* pair, * next;
|
||||
|
||||
for (i = 0; i < htb->capa; i++)
|
||||
{
|
||||
pair = htb->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
if (walker(htb, pair, ctx) == QSE_HTB_WALK_STOP) return;
|
||||
pair = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pair_t* qse_htb_getfirstpair (htb_t* htb, size_t* buckno)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* pair;
|
||||
|
||||
for (i = 0; i < htb->capa; i++)
|
||||
{
|
||||
pair = htb->bucket[i];
|
||||
if (pair != QSE_NULL)
|
||||
{
|
||||
*buckno = i;
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
pair_t* qse_htb_getnextpair (htb_t* htb, pair_t* pair, size_t* buckno)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* next;
|
||||
|
||||
next = NEXT(pair);
|
||||
if (next != QSE_NULL)
|
||||
{
|
||||
/* no change in bucket number */
|
||||
return next;
|
||||
}
|
||||
|
||||
for (i = (*buckno)+1; i < htb->capa; i++)
|
||||
{
|
||||
pair = htb->bucket[i];
|
||||
if (pair != QSE_NULL)
|
||||
{
|
||||
*buckno = i;
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
static int reorganize (htb_t* htb)
|
||||
{
|
||||
size_t i, hc, new_capa;
|
||||
pair_t** new_buck;
|
||||
|
||||
if (htb->sizer)
|
||||
{
|
||||
new_capa = htb->sizer (htb, htb->capa + 1);
|
||||
|
||||
/* if no change in capacity, return success
|
||||
* without reorganization */
|
||||
if (new_capa == htb->capa) return 0;
|
||||
|
||||
/* adjust to 1 if the new capacity is not reasonable */
|
||||
if (new_capa <= 0) new_capa = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the bucket is doubled until it grows up to 65536 slots.
|
||||
* once it has reached it, it grows by 65536 slots */
|
||||
new_capa = (htb->capa >= 65536)? (htb->capa + 65536): (htb->capa << 1);
|
||||
}
|
||||
|
||||
new_buck = (pair_t**) QSE_MMGR_ALLOC (
|
||||
htb->mmgr, new_capa*SIZEOF(pair_t*));
|
||||
if (new_buck == QSE_NULL)
|
||||
{
|
||||
/* reorganization is disabled once it fails */
|
||||
htb->threshold = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*for (i = 0; i < new_capa; i++) new_buck[i] = QSE_NULL;*/
|
||||
QSE_MEMSET (new_buck, 0, new_capa*SIZEOF(pair_t*));
|
||||
|
||||
for (i = 0; i < htb->capa; i++)
|
||||
{
|
||||
pair_t* pair = htb->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
pair_t* next = NEXT(pair);
|
||||
|
||||
hc = htb->hasher (htb,
|
||||
KPTR(pair),
|
||||
KLEN(pair)) % new_capa;
|
||||
|
||||
NEXT(pair) = new_buck[hc];
|
||||
new_buck[hc] = pair;
|
||||
|
||||
pair = next;
|
||||
}
|
||||
}
|
||||
|
||||
QSE_MMGR_FREE (htb->mmgr, htb->bucket);
|
||||
htb->bucket = new_buck;
|
||||
htb->capa = new_capa;
|
||||
htb->threshold = htb->capa * htb->factor / 100;
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,700 +0,0 @@
|
||||
/*
|
||||
* $Id: map.c 327 2010-05-10 13:15:55Z hyunghwan.chung $
|
||||
*
|
||||
Copyright 2006-2009 Chung, Hyung-Hwan.
|
||||
This file is part of QSE.
|
||||
|
||||
QSE is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
QSE is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <qse/cmn/map.h>
|
||||
#include "mem.h"
|
||||
|
||||
QSE_IMPLEMENT_COMMON_FUNCTIONS (map)
|
||||
|
||||
#define map_t qse_map_t
|
||||
#define pair_t qse_map_pair_t
|
||||
#define copier_t qse_map_copier_t
|
||||
#define freeer_t qse_map_freeer_t
|
||||
#define hasher_t qse_map_hasher_t
|
||||
#define comper_t qse_map_comper_t
|
||||
#define keeper_t qse_map_keeper_t
|
||||
#define sizer_t qse_map_sizer_t
|
||||
#define walker_t qse_map_walker_t
|
||||
|
||||
#define KPTR(p) QSE_MAP_KPTR(p)
|
||||
#define KLEN(p) QSE_MAP_KLEN(p)
|
||||
#define VPTR(p) QSE_MAP_VPTR(p)
|
||||
#define VLEN(p) QSE_MAP_VLEN(p)
|
||||
#define NEXT(p) QSE_MAP_NEXT(p)
|
||||
|
||||
#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(map,len) ((len)*(map)->scale[QSE_MAP_KEY])
|
||||
#define VTOB(map,len) ((len)*(map)->scale[QSE_MAP_VAL])
|
||||
|
||||
#define UPSERT 1
|
||||
#define UPDATE 2
|
||||
#define ENSERT 3
|
||||
#define INSERT 4
|
||||
|
||||
static int reorganize (map_t* map);
|
||||
|
||||
static size_t hash_key (map_t* map, const void* kptr, size_t klen)
|
||||
{
|
||||
/*size_t h = 2166136261;*/
|
||||
/*size_t h = 0;*/
|
||||
size_t h = 5381;
|
||||
const byte_t* p = (const byte_t*)kptr;
|
||||
const byte_t* bound = p + klen;
|
||||
|
||||
while (p < bound)
|
||||
{
|
||||
/*h = (h * 16777619) ^ *p++;*/
|
||||
/*h = h * 31 + *p++;*/
|
||||
h = ((h << 5) + h) + *p++;
|
||||
}
|
||||
|
||||
return h ;
|
||||
}
|
||||
|
||||
static int comp_key (map_t* map,
|
||||
const void* kptr1, size_t klen1,
|
||||
const void* kptr2, size_t klen2)
|
||||
{
|
||||
if (klen1 == klen2) return QSE_MEMCMP (kptr1, kptr2, KTOB(map,klen1));
|
||||
/* it just returns 1 to indicate that they are different. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pair_t* alloc_pair (map_t* map,
|
||||
void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
pair_t* n;
|
||||
copier_t kcop = map->copier[QSE_MAP_KEY];
|
||||
copier_t vcop = map->copier[QSE_MAP_VAL];
|
||||
|
||||
size_t as = SIZEOF(pair_t);
|
||||
if (kcop == QSE_MAP_COPIER_INLINE) as += KTOB(map,klen);
|
||||
if (vcop == QSE_MAP_COPIER_INLINE) as += VTOB(map,vlen);
|
||||
|
||||
n = (pair_t*) QSE_MMGR_ALLOC (map->mmgr, as);
|
||||
if (n == QSE_NULL) return QSE_NULL;
|
||||
|
||||
NEXT(n) = QSE_NULL;
|
||||
|
||||
KLEN(n) = klen;
|
||||
if (kcop == QSE_MAP_COPIER_SIMPLE)
|
||||
{
|
||||
KPTR(n) = kptr;
|
||||
}
|
||||
else if (kcop == QSE_MAP_COPIER_INLINE)
|
||||
{
|
||||
KPTR(n) = n + 1;
|
||||
QSE_MEMCPY (KPTR(n), kptr, KTOB(map,klen));
|
||||
}
|
||||
else
|
||||
{
|
||||
KPTR(n) = kcop (map, kptr, klen);
|
||||
if (KPTR(n) == QSE_NULL)
|
||||
{
|
||||
QSE_MMGR_FREE (map->mmgr, n);
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
VLEN(n) = vlen;
|
||||
if (vcop == QSE_MAP_COPIER_SIMPLE)
|
||||
{
|
||||
VPTR(n) = vptr;
|
||||
}
|
||||
else if (vcop == QSE_MAP_COPIER_INLINE)
|
||||
{
|
||||
VPTR(n) = n + 1;
|
||||
if (kcop == QSE_MAP_COPIER_INLINE)
|
||||
VPTR(n) = (byte_t*)VPTR(n) + KTOB(map,klen);
|
||||
QSE_MEMCPY (VPTR(n), vptr, VTOB(map,vlen));
|
||||
}
|
||||
else
|
||||
{
|
||||
VPTR(n) = vcop (map, vptr, vlen);
|
||||
if (VPTR(n) != QSE_NULL)
|
||||
{
|
||||
if (map->freeer[QSE_MAP_KEY] != QSE_NULL)
|
||||
map->freeer[QSE_MAP_KEY] (map, KPTR(n), KLEN(n));
|
||||
QSE_MMGR_FREE (map->mmgr, n);
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void free_pair (map_t* map, pair_t* pair)
|
||||
{
|
||||
if (map->freeer[QSE_MAP_KEY] != QSE_NULL)
|
||||
map->freeer[QSE_MAP_KEY] (map, KPTR(pair), KLEN(pair));
|
||||
if (map->freeer[QSE_MAP_VAL] != QSE_NULL)
|
||||
map->freeer[QSE_MAP_VAL] (map, VPTR(pair), VLEN(pair));
|
||||
QSE_MMGR_FREE (map->mmgr, pair);
|
||||
}
|
||||
|
||||
static pair_t* change_pair_val (
|
||||
map_t* map, pair_t* pair, void* vptr, size_t vlen)
|
||||
{
|
||||
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 (map->keeper != QSE_NULL)
|
||||
{
|
||||
map->keeper (map, vptr, vlen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
copier_t vcop = map->copier[QSE_MAP_VAL];
|
||||
void* ovptr = VPTR(pair);
|
||||
size_t ovlen = VLEN(pair);
|
||||
|
||||
/* place the new value according to the copier */
|
||||
if (vcop == QSE_MAP_COPIER_SIMPLE)
|
||||
{
|
||||
VPTR(pair) = vptr;
|
||||
VLEN(pair) = vlen;
|
||||
}
|
||||
else if (vcop == QSE_MAP_COPIER_INLINE)
|
||||
{
|
||||
if (ovlen == vlen)
|
||||
{
|
||||
QSE_MEMCPY (VPTR(pair), vptr, VTOB(map,vlen));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* need to reconstruct the pair */
|
||||
pair_t* p = alloc_pair (map,
|
||||
KPTR(pair), KLEN(pair),
|
||||
vptr, vlen);
|
||||
if (p == QSE_NULL) return QSE_NULL;
|
||||
free_pair (map, pair);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
void* nvptr = vcop (map, vptr, vlen);
|
||||
if (nvptr == QSE_NULL) return QSE_NULL;
|
||||
VPTR(pair) = nvptr;
|
||||
VLEN(pair) = vlen;
|
||||
}
|
||||
|
||||
/* free up the old value */
|
||||
if (map->freeer[QSE_MAP_VAL] != QSE_NULL)
|
||||
{
|
||||
map->freeer[QSE_MAP_VAL] (map, ovptr, ovlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
map_t* qse_map_open (mmgr_t* mmgr, size_t ext, size_t capa, int factor)
|
||||
{
|
||||
map_t* map;
|
||||
|
||||
if (mmgr == QSE_NULL)
|
||||
{
|
||||
mmgr = QSE_MMGR_GETDFL();
|
||||
|
||||
QSE_ASSERTX (mmgr != QSE_NULL,
|
||||
"Set the memory manager with QSE_MMGR_SETDFL()");
|
||||
|
||||
if (mmgr == QSE_NULL) return QSE_NULL;
|
||||
}
|
||||
|
||||
map = (map_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(map_t) + ext);
|
||||
if (map == QSE_NULL) return QSE_NULL;
|
||||
|
||||
if (qse_map_init (map, mmgr, capa, factor) == QSE_NULL)
|
||||
{
|
||||
QSE_MMGR_FREE (mmgr, map);
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void qse_map_close (map_t* map)
|
||||
{
|
||||
qse_map_fini (map);
|
||||
QSE_MMGR_FREE (map->mmgr, map);
|
||||
}
|
||||
|
||||
map_t* qse_map_init (map_t* map, mmgr_t* mmgr, size_t capa, int factor)
|
||||
{
|
||||
QSE_ASSERTX (capa > 0,
|
||||
"The initial capacity should be greater than 0. Otherwise, it is adjusted to 1 in the release mode");
|
||||
QSE_ASSERTX (factor >= 0 && factor <= 100,
|
||||
"The load factor should be between 0 and 100 inclusive. In the release mode, a value out of the range is adjusted to 100");
|
||||
|
||||
/* some initial adjustment */
|
||||
if (capa <= 0) capa = 1;
|
||||
if (factor > 100) factor = 100;
|
||||
|
||||
/* do not zero out the extension */
|
||||
QSE_MEMSET (map, 0, SIZEOF(*map));
|
||||
map->mmgr = mmgr;
|
||||
|
||||
map->bucket = QSE_MMGR_ALLOC (mmgr, capa*SIZEOF(pair_t*));
|
||||
if (map->bucket == QSE_NULL) return QSE_NULL;
|
||||
|
||||
/*for (i = 0; i < capa; i++) map->bucket[i] = QSE_NULL;*/
|
||||
QSE_MEMSET (map->bucket, 0, capa*SIZEOF(pair_t*));
|
||||
|
||||
map->scale[QSE_MAP_KEY] = 1;
|
||||
map->scale[QSE_MAP_VAL] = 1;
|
||||
map->factor = factor;
|
||||
|
||||
map->size = 0;
|
||||
map->capa = capa;
|
||||
map->threshold = map->capa * map->factor / 100;
|
||||
if (map->capa > 0 && map->threshold <= 0) map->threshold = 1;
|
||||
|
||||
map->hasher = hash_key;
|
||||
map->comper = comp_key;
|
||||
map->copier[QSE_MAP_KEY] = QSE_MAP_COPIER_SIMPLE;
|
||||
map->copier[QSE_MAP_VAL] = QSE_MAP_COPIER_SIMPLE;
|
||||
|
||||
/*
|
||||
map->freeer[QSE_MAP_KEY] = QSE_NULL;
|
||||
map->freeer[QSE_MAP_VAL] = QSE_NULL;
|
||||
map->keeper = QSE_NULL;
|
||||
map->sizer = QSE_NULL;
|
||||
*/
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void qse_map_fini (map_t* map)
|
||||
{
|
||||
qse_map_clear (map);
|
||||
QSE_MMGR_FREE (map->mmgr, map->bucket);
|
||||
}
|
||||
|
||||
int qse_map_getscale (map_t* map, qse_map_id_t id)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
return map->scale[id];
|
||||
}
|
||||
|
||||
void qse_map_setscale (map_t* map, qse_map_id_t id, int scale)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
|
||||
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);
|
||||
|
||||
map->scale[id] = scale;
|
||||
}
|
||||
|
||||
copier_t qse_map_getcopier (map_t* map, qse_map_id_t id)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
return map->copier[id];
|
||||
}
|
||||
|
||||
void qse_map_setcopier (map_t* map, qse_map_id_t id, copier_t copier)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
if (copier == QSE_NULL) copier = QSE_MAP_COPIER_SIMPLE;
|
||||
map->copier[id] = copier;
|
||||
}
|
||||
|
||||
freeer_t qse_map_getfreeer (map_t* map, qse_map_id_t id)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
return map->freeer[id];
|
||||
}
|
||||
|
||||
void qse_map_setfreeer (map_t* map, qse_map_id_t id, freeer_t freeer)
|
||||
{
|
||||
QSE_ASSERTX (id == QSE_MAP_KEY || id == QSE_MAP_VAL,
|
||||
"The ID should be either QSE_MAP_KEY or QSE_MAP_VAL");
|
||||
map->freeer[id] = freeer;
|
||||
}
|
||||
|
||||
hasher_t qse_map_gethasher (map_t* map)
|
||||
{
|
||||
return map->hasher;
|
||||
}
|
||||
|
||||
void qse_map_sethasher (map_t* map, hasher_t hasher)
|
||||
{
|
||||
if (hasher == QSE_NULL) hasher = hash_key;
|
||||
map->hasher = hasher;
|
||||
}
|
||||
|
||||
comper_t qse_map_getcomper (map_t* map)
|
||||
{
|
||||
return map->comper;
|
||||
}
|
||||
|
||||
void qse_map_setcomper (map_t* map, comper_t comper)
|
||||
{
|
||||
if (comper == QSE_NULL) comper = comp_key;
|
||||
map->comper = comper;
|
||||
}
|
||||
|
||||
keeper_t qse_map_getkeeper (map_t* map)
|
||||
{
|
||||
return map->keeper;
|
||||
}
|
||||
|
||||
void qse_map_setkeeper (map_t* map, keeper_t keeper)
|
||||
{
|
||||
map->keeper = keeper;
|
||||
}
|
||||
|
||||
sizer_t qse_map_getsizer (map_t* map)
|
||||
{
|
||||
return map->sizer;
|
||||
}
|
||||
|
||||
void qse_map_setsizer (map_t* map, sizer_t sizer)
|
||||
{
|
||||
map->sizer = sizer;
|
||||
}
|
||||
|
||||
size_t qse_map_getsize (map_t* map)
|
||||
{
|
||||
return map->size;
|
||||
}
|
||||
|
||||
size_t qse_map_getcapa (map_t* map)
|
||||
{
|
||||
return map->capa;
|
||||
}
|
||||
|
||||
pair_t* qse_map_search (map_t* map, const void* kptr, size_t klen)
|
||||
{
|
||||
pair_t* pair;
|
||||
size_t hc;
|
||||
|
||||
hc = map->hasher(map,kptr,klen) % map->capa;
|
||||
pair = map->bucket[hc];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
return pair;
|
||||
}
|
||||
|
||||
pair = NEXT(pair);
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
static pair_t* insert (
|
||||
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen, int opt)
|
||||
{
|
||||
pair_t* pair, * p, * prev, * next;
|
||||
size_t hc;
|
||||
|
||||
hc = map->hasher(map,kptr,klen) % map->capa;
|
||||
pair = map->bucket[hc];
|
||||
prev = QSE_NULL;
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
|
||||
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
/* found a pair with a matching key */
|
||||
switch (opt)
|
||||
{
|
||||
case UPSERT:
|
||||
case UPDATE:
|
||||
p = change_pair_val (map, pair, vptr, vlen);
|
||||
if (p == QSE_NULL)
|
||||
{
|
||||
/* error in change the value */
|
||||
return QSE_NULL;
|
||||
}
|
||||
if (p != pair)
|
||||
{
|
||||
/* pair reallocated.
|
||||
* relink it */
|
||||
if (prev == QSE_NULL)
|
||||
map->bucket[hc] = p;
|
||||
else NEXT(prev) = p;
|
||||
NEXT(p) = next;
|
||||
}
|
||||
return p;
|
||||
|
||||
case ENSERT:
|
||||
/* return existing pair */
|
||||
return pair;
|
||||
|
||||
case INSERT:
|
||||
/* return failure */
|
||||
return QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
prev = pair;
|
||||
pair = next;
|
||||
}
|
||||
|
||||
if (opt == UPDATE) return QSE_NULL;
|
||||
|
||||
if (map->threshold > 0 && map->size >= map->threshold)
|
||||
{
|
||||
if (reorganize(map) == 0) /* ignore the error */
|
||||
{
|
||||
hc = map->hasher(map,kptr,klen) % map->capa;
|
||||
}
|
||||
}
|
||||
|
||||
QSE_ASSERT (pair == QSE_NULL);
|
||||
|
||||
pair = alloc_pair (map, kptr, klen, vptr, vlen);
|
||||
if (pair == QSE_NULL) return QSE_NULL; /* error */
|
||||
|
||||
NEXT(pair) = map->bucket[hc];
|
||||
map->bucket[hc] = pair;
|
||||
map->size++;
|
||||
|
||||
return pair; /* new key added */
|
||||
}
|
||||
|
||||
pair_t* qse_map_upsert (
|
||||
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (map, kptr, klen, vptr, vlen, UPSERT);
|
||||
}
|
||||
|
||||
pair_t* qse_map_ensert (
|
||||
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (map, kptr, klen, vptr, vlen, ENSERT);
|
||||
}
|
||||
|
||||
pair_t* qse_map_insert (
|
||||
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (map, kptr, klen, vptr, vlen, INSERT);
|
||||
}
|
||||
|
||||
|
||||
pair_t* qse_map_update (
|
||||
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
|
||||
{
|
||||
return insert (map, kptr, klen, vptr, vlen, UPDATE);
|
||||
}
|
||||
|
||||
int qse_map_delete (map_t* map, const void* kptr, size_t klen)
|
||||
{
|
||||
pair_t* pair, * prev;
|
||||
size_t hc;
|
||||
|
||||
hc = map->hasher(map,kptr,klen) % map->capa;
|
||||
pair = map->bucket[hc];
|
||||
prev = QSE_NULL;
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
|
||||
{
|
||||
if (prev == QSE_NULL)
|
||||
map->bucket[hc] = NEXT(pair);
|
||||
else NEXT(prev) = NEXT(pair);
|
||||
|
||||
free_pair (map, pair);
|
||||
map->size--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev = pair;
|
||||
pair = NEXT(pair);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void qse_map_clear (map_t* map)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* pair, * next;
|
||||
|
||||
for (i = 0; i < map->capa; i++)
|
||||
{
|
||||
pair = map->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
free_pair (map, pair);
|
||||
map->size--;
|
||||
pair = next;
|
||||
}
|
||||
|
||||
map->bucket[i] = QSE_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t qse_map_walk (map_t* map, walker_t walker, void* arg)
|
||||
{
|
||||
size_t i, nwalks = 0;
|
||||
pair_t* pair, * next;
|
||||
|
||||
for (i = 0; i < map->capa; i++)
|
||||
{
|
||||
pair = map->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
next = NEXT(pair);
|
||||
if (walker(map, pair, arg) == QSE_MAP_WALK_STOP)
|
||||
return nwalks;
|
||||
nwalks++;
|
||||
pair = next;
|
||||
}
|
||||
}
|
||||
|
||||
return nwalks;
|
||||
}
|
||||
|
||||
pair_t* qse_map_getfirstpair (map_t* map, size_t* buckno)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* pair;
|
||||
|
||||
for (i = 0; i < map->capa; i++)
|
||||
{
|
||||
pair = map->bucket[i];
|
||||
if (pair != QSE_NULL)
|
||||
{
|
||||
*buckno = i;
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
pair_t* qse_map_getnextpair (map_t* map, pair_t* pair, size_t* buckno)
|
||||
{
|
||||
size_t i;
|
||||
pair_t* next;
|
||||
|
||||
next = NEXT(pair);
|
||||
if (next != QSE_NULL)
|
||||
{
|
||||
/* no change in bucket number */
|
||||
return next;
|
||||
}
|
||||
|
||||
for (i = (*buckno)+1; i < map->capa; i++)
|
||||
{
|
||||
pair = map->bucket[i];
|
||||
if (pair != QSE_NULL)
|
||||
{
|
||||
*buckno = i;
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
static int reorganize (map_t* map)
|
||||
{
|
||||
size_t i, hc, new_capa;
|
||||
pair_t** new_buck;
|
||||
|
||||
if (map->sizer)
|
||||
{
|
||||
new_capa = map->sizer (map, map->capa + 1);
|
||||
|
||||
/* if no change in capacity, return success
|
||||
* without reorganization */
|
||||
if (new_capa == map->capa) return 0;
|
||||
|
||||
/* adjust to 1 if the new capacity is not reasonable */
|
||||
if (new_capa <= 0) new_capa = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the bucket is doubled until it grows up to 65536 slots.
|
||||
* once it has reached it, it grows by 65536 slots */
|
||||
new_capa = (map->capa >= 65536)? (map->capa + 65536): (map->capa << 1);
|
||||
}
|
||||
|
||||
new_buck = (pair_t**) QSE_MMGR_ALLOC (
|
||||
map->mmgr, new_capa*SIZEOF(pair_t*));
|
||||
if (new_buck == QSE_NULL)
|
||||
{
|
||||
/* reorganization is disabled once it fails */
|
||||
map->threshold = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*for (i = 0; i < new_capa; i++) new_buck[i] = QSE_NULL;*/
|
||||
QSE_MEMSET (new_buck, 0, new_capa*SIZEOF(pair_t*));
|
||||
|
||||
for (i = 0; i < map->capa; i++)
|
||||
{
|
||||
pair_t* pair = map->bucket[i];
|
||||
|
||||
while (pair != QSE_NULL)
|
||||
{
|
||||
pair_t* next = NEXT(pair);
|
||||
|
||||
hc = map->hasher (map,
|
||||
KPTR(pair),
|
||||
KLEN(pair)) % new_capa;
|
||||
|
||||
NEXT(pair) = new_buck[hc];
|
||||
new_buck[hc] = pair;
|
||||
|
||||
pair = next;
|
||||
}
|
||||
}
|
||||
|
||||
QSE_MMGR_FREE (map->mmgr, map->bucket);
|
||||
map->bucket = new_buck;
|
||||
map->capa = new_capa;
|
||||
map->threshold = map->capa * map->factor / 100;
|
||||
|
||||
return 0;
|
||||
}
|
691
qse/lib/cmn/rbt.c
Normal file
691
qse/lib/cmn/rbt.c
Normal file
@ -0,0 +1,691 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
Copyright 2006-2009 Chung, Hyung-Hwan.
|
||||
This file is part of QSE.
|
||||
|
||||
QSE is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
QSE is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with QSE. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <qse/cmn/rbt.h>
|
||||
#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));
|
||||
|
||||
node->color = color;
|
||||
node->right = &rbt->nil;
|
||||
node->left = &rbt->nil;
|
||||
|
||||
node->key = key;
|
||||
node->value = value;
|
||||
}
|
||||
|
||||
static QSE_INLINE int comp_key (
|
||||
qse_rbt_t* rbt,
|
||||
const void* kptr1, qse_size_t klen1,
|
||||
const void* kptr2, qse_size_t klen2)
|
||||
{
|
||||
qse_size_t min;
|
||||
int n, nn;
|
||||
|
||||
if (klen1 < klen2)
|
||||
{
|
||||
min = klen1;
|
||||
nn = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
min = klen2;
|
||||
nn = (klen1 == klen2)? 0: 1;
|
||||
}
|
||||
|
||||
n = QSE_MEMCMP (kptr1, kptr2, KTOB(rbt,min));
|
||||
if (n == 0) n = nn;
|
||||
return n;
|
||||
}
|
||||
|
||||
qse_rbt_t* qse_rbt_open (qse_mmgr_t* mmgr, qse_size_t ext)
|
||||
{
|
||||
qse_rbt_t* rbt;
|
||||
|
||||
if (mmgr == QSE_NULL)
|
||||
{
|
||||
mmgr = QSE_MMGR_GETDFL();
|
||||
|
||||
QSE_ASSERTX (mmgr != QSE_NULL,
|
||||
"Set the memory manager with QSE_MMGR_SETDFL()");
|
||||
|
||||
if (mmgr == QSE_NULL) return QSE_NULL;
|
||||
}
|
||||
|
||||
rbt = QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(qse_rbt_t) + ext);
|
||||
if (rbt == QSE_NULL) return QSE_NULL;
|
||||
|
||||
if (qse_rbt_init (rbt, mmgr) == QSE_NULL)
|
||||
{
|
||||
QSE_MMGR_FREE (mmgr, rbt);
|
||||
return QSE_NULL;
|
||||
}
|
||||
|
||||
return rbt;
|
||||
}
|
||||
|
||||
void qse_rbt_close (qse_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)
|
||||
{
|
||||
/* do not zero out the extension */
|
||||
QSE_MEMSET (rbt, 0, QSE_SIZEOF(*rbt));
|
||||
|
||||
rbt->mmgr = mmgr;
|
||||
rbt->size = 0;
|
||||
|
||||
rbt->scale[QSE_RBT_KEY] = 1;
|
||||
rbt->scale[QSE_RBT_VAL] = 1;
|
||||
|
||||
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->root = &rbt->nil;
|
||||
return rbt;
|
||||
}
|
||||
|
||||
void qse_rbt_fini (qse_rbt_t* rbt)
|
||||
{
|
||||
qse_rbt_clear (rbt);
|
||||
}
|
||||
|
||||
qse_rbt_node_t* qse_rbt_search (qse_rbt_t* rbt, int key)
|
||||
{
|
||||
/* TODO: enhance this. ues comper... etc */
|
||||
|
||||
qse_rbt_node_t* node = rbt->root;
|
||||
|
||||
while (!IS_NIL(rbt,node))
|
||||
{
|
||||
#if 0
|
||||
int n = rbt->comper (rbt, kptr, klen, node->kptr, node->klen);
|
||||
if (n == 0) return node;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* == leftwise rotation
|
||||
* 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
|
||||
*
|
||||
* == rightwise rotation
|
||||
* 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, * z, * c;
|
||||
int cid1, cid2;
|
||||
|
||||
QSE_ASSERT (pivot != QSE_NULL);
|
||||
|
||||
if (leftwise)
|
||||
{
|
||||
cid1 = RIGHT;
|
||||
cid2 = LEFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
cid1 = LEFT;
|
||||
cid2 = RIGHT;
|
||||
}
|
||||
|
||||
parent = pivot->parent;
|
||||
/* y for leftwise rotation, x for rightwise rotation */
|
||||
z = pivot->child[cid1];
|
||||
/* c1 for leftwise rotation, c1 for rightwise rotation */
|
||||
c = z->child[cid2];
|
||||
|
||||
z->parent = parent;
|
||||
if (parent)
|
||||
{
|
||||
if (parent->left == pivot)
|
||||
{
|
||||
parent->left = z;
|
||||
}
|
||||
else
|
||||
{
|
||||
QSE_ASSERT (parent->right == pivot);
|
||||
parent->right = z;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QSE_ASSERT (rbt->root == pivot);
|
||||
rbt->root = z;
|
||||
}
|
||||
|
||||
z->child[cid2] = pivot;
|
||||
if (!IS_NIL(rbt,pivot)) pivot->parent = z;
|
||||
|
||||
pivot->child[cid1] = c;
|
||||
if (!IS_NIL(rbt,c)) c->parent = pivot;
|
||||
}
|
||||
|
||||
static void adjust (qse_rbt_t* rbt, qse_rbt_node_t* node)
|
||||
{
|
||||
while (node != rbt->root)
|
||||
{
|
||||
qse_rbt_node_t* tmp, * tmp2;
|
||||
int leftwise;
|
||||
|
||||
qse_rbt_node_t* xpar = node->parent;
|
||||
if (xpar->color == QSE_RBT_BLACK) break;
|
||||
|
||||
QSE_ASSERT (xpar->parent != QSE_NULL);
|
||||
|
||||
if (xpar == xpar->parent->child[LEFT])
|
||||
{
|
||||
tmp = xpar->parent->child[RIGHT];
|
||||
tmp2 = xpar->child[RIGHT];
|
||||
leftwise = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = xpar->parent->child[LEFT];
|
||||
tmp2 = xpar->child[LEFT];
|
||||
leftwise = 0;
|
||||
}
|
||||
|
||||
if (tmp->color == QSE_RBT_RED)
|
||||
{
|
||||
xpar->color = QSE_RBT_BLACK;
|
||||
tmp->color = QSE_RBT_BLACK;
|
||||
xpar->parent->color = QSE_RBT_RED;
|
||||
node = xpar->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node == tmp2)
|
||||
{
|
||||
node = xpar;
|
||||
rotate (rbt, node, leftwise);
|
||||
xpar = node->parent;
|
||||
}
|
||||
|
||||
xpar->color = QSE_RBT_BLACK;
|
||||
xpar->parent->color = QSE_RBT_RED;
|
||||
rotate (rbt, xpar->parent, !leftwise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static qse_rbt_node_t* new_node (qse_rbt_t* rbt, int key, int value)
|
||||
{
|
||||
qse_rbt_node_t* node;
|
||||
|
||||
node = QSE_MMGR_ALLOC (rbt->mmgr, QSE_SIZEOF(*node));
|
||||
if (node == QSE_NULL) return QSE_NULL;
|
||||
|
||||
init_node (rbt, node, QSE_RBT_RED, key, value);
|
||||
return node;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
while (!IS_NIL(rbt,xcur))
|
||||
{
|
||||
if (key == xcur->key)
|
||||
{
|
||||
/* TODO: handle various cases depending on insert types.
|
||||
* return error. update value. */
|
||||
xcur->value = value;
|
||||
return xcur;
|
||||
}
|
||||
|
||||
xpar = xcur;
|
||||
|
||||
if (key > xcur->key) xcur = xcur->right;
|
||||
else /* if (key < xcur->key) */ xcur = xcur->left;
|
||||
}
|
||||
|
||||
xnew = new_node (rbt, key, value);
|
||||
if (xnew == QSE_NULL) return QSE_NULL;
|
||||
|
||||
if (xpar == QSE_NULL)
|
||||
{
|
||||
/* the tree contains no node */
|
||||
QSE_ASSERT (rbt->root == &rbt->nil);
|
||||
rbt->root = xnew;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* perform normal binary insert */
|
||||
if (key > xpar->key)
|
||||
{
|
||||
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 (
|
||||
qse_rbt_t* rbt, qse_rbt_node_t* node, qse_rbt_node_t* par)
|
||||
{
|
||||
while (node != rbt->root && node->color == QSE_RBT_BLACK)
|
||||
{
|
||||
qse_rbt_node_t* tmp;
|
||||
|
||||
if (node == par->left)
|
||||
{
|
||||
tmp = par->right;
|
||||
if (tmp->color == QSE_RBT_RED)
|
||||
{
|
||||
tmp->color = QSE_RBT_BLACK;
|
||||
par->color = QSE_RBT_RED;
|
||||
rotate_left (rbt, par);
|
||||
tmp = par->right;
|
||||
}
|
||||
|
||||
if (tmp->left->color == QSE_RBT_BLACK &&
|
||||
tmp->right->color == QSE_RBT_BLACK)
|
||||
{
|
||||
if (!IS_NIL(rbt,tmp)) tmp->color = QSE_RBT_RED;
|
||||
node = par;
|
||||
par = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmp->right->color == QSE_RBT_BLACK)
|
||||
{
|
||||
if (!IS_NIL(rbt,tmp->left))
|
||||
tmp->left->color = QSE_RBT_BLACK;
|
||||
tmp->color = QSE_RBT_RED;
|
||||
rotate_right (rbt, tmp);
|
||||
tmp = par->right;
|
||||
}
|
||||
|
||||
tmp->color = par->color;
|
||||
if (!IS_NIL(rbt,par)) par->color = QSE_RBT_BLACK;
|
||||
if (tmp->right->color == QSE_RBT_RED)
|
||||
tmp->right->color = QSE_RBT_BLACK;
|
||||
|
||||
rotate_left (rbt, par);
|
||||
node = rbt->root;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QSE_ASSERT (node == par->right);
|
||||
tmp = par->left;
|
||||
if (tmp->color == QSE_RBT_RED)
|
||||
{
|
||||
tmp->color = QSE_RBT_BLACK;
|
||||
par->color = QSE_RBT_RED;
|
||||
rotate_right (rbt, par);
|
||||
tmp = par->left;
|
||||
}
|
||||
|
||||
if (tmp->left->color == QSE_RBT_BLACK &&
|
||||
tmp->right->color == QSE_RBT_BLACK)
|
||||
{
|
||||
if (!IS_NIL(rbt,tmp)) tmp->color = QSE_RBT_RED;
|
||||
node = par;
|
||||
par = node->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tmp->left->color == QSE_RBT_BLACK)
|
||||
{
|
||||
if (!IS_NIL(rbt,tmp->right))
|
||||
tmp->right->color = QSE_RBT_BLACK;
|
||||
tmp->color = QSE_RBT_RED;
|
||||
rotate_left (rbt, tmp);
|
||||
tmp = par->left;
|
||||
}
|
||||
tmp->color = par->color;
|
||||
if (!IS_NIL(rbt,par)) par->color = QSE_RBT_BLACK;
|
||||
if (tmp->left->color == QSE_RBT_RED)
|
||||
tmp->left->color = QSE_RBT_BLACK;
|
||||
|
||||
rotate_right (rbt, par);
|
||||
node = rbt->root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node->color = QSE_RBT_BLACK;
|
||||
}
|
||||
|
||||
static void delete_node (qse_rbt_t* rbt, qse_rbt_node_t* node)
|
||||
{
|
||||
qse_rbt_node_t* x, * y, * par;
|
||||
|
||||
QSE_ASSERT (node && !IS_NIL(rbt,node));
|
||||
|
||||
if (IS_NIL(rbt,node->left) || IS_NIL(rbt,node->right))
|
||||
{
|
||||
y = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* find a successor with NIL as a child */
|
||||
y = node->right;
|
||||
while (!IS_NIL(rbt,y->left)) y = y->left;
|
||||
}
|
||||
|
||||
x = IS_NIL(rbt,y->left)? y->right: y->left;
|
||||
|
||||
par = y->parent;
|
||||
if (!IS_NIL(rbt,x)) x->parent = par;
|
||||
|
||||
if (par)
|
||||
{
|
||||
if (y == par->left)
|
||||
par->left = x;
|
||||
else
|
||||
par->right = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
rbt->root = x;
|
||||
}
|
||||
|
||||
if (y == node)
|
||||
{
|
||||
if (y->color == QSE_RBT_BLACK && !IS_NIL(rbt,x))
|
||||
adjust_for_delete (rbt, x, par);
|
||||
|
||||
free_node (rbt, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y->color == QSE_RBT_BLACK && !IS_NIL(rbt,x))
|
||||
adjust_for_delete (rbt, x, par);
|
||||
|
||||
#if 1
|
||||
if (node->parent)
|
||||
{
|
||||
if (node->parent->left == node) node->parent->left = y;
|
||||
if (node->parent->right == node) node->parent->right = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
rbt->root = y;
|
||||
}
|
||||
|
||||
y->parent = node->parent;
|
||||
y->left = node->left;
|
||||
y->right = node->right;
|
||||
y->color = node->color;
|
||||
|
||||
if (node->left->parent == node) node->left->parent = y;
|
||||
if (node->right->parent == node) node->right->parent = y;
|
||||
#else
|
||||
*y = *node;
|
||||
if (y->parent)
|
||||
{
|
||||
if (y->parent->left == node) y->parent->left = y;
|
||||
if (y->parent->right == node) 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;
|
||||
#endif
|
||||
|
||||
free_node (rbt, node);
|
||||
}
|
||||
|
||||
/* TODO: update tally */
|
||||
}
|
||||
|
||||
int qse_rbt_delete (qse_rbt_t* rbt, int key)
|
||||
{
|
||||
qse_rbt_node_t* node;
|
||||
|
||||
node = qse_rbt_search (rbt, key);
|
||||
if (node == QSE_NULL) return -1;
|
||||
|
||||
delete_node (rbt, node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void qse_rbt_clear (qse_rbt_t* rbt)
|
||||
{
|
||||
/* TODO: improve this */
|
||||
while (!IS_NIL(rbt,rbt->root)) delete_node (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)
|
||||
{
|
||||
if (!IS_NIL(rbt,node->left))
|
||||
{
|
||||
if (walk (rbt, walker, ctx, node->left) == QSE_RBT_WALK_STOP)
|
||||
return QSE_RBT_WALK_STOP;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
walk (rbt, walker, ctx, rbt->root);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: rex.c 327 2010-05-10 13:15:55Z hyunghwan.chung $
|
||||
* $Id: rex.c 328 2010-07-08 06:58:44Z hyunghwan.chung $
|
||||
*
|
||||
Copyright 2006-2009 Chung, Hyung-Hwan.
|
||||
This file is part of QSE.
|
||||
@ -375,6 +375,9 @@ static int getc (comp_t* com, int noesc)
|
||||
else if (c == QSE_T('b')) c = QSE_T('\b');
|
||||
else if (c == QSE_T('v')) c = QSE_T('\v');
|
||||
else if (c == QSE_T('a')) c = QSE_T('\a');
|
||||
|
||||
#if 0
|
||||
/* backrefernce conflicts with octal notation */
|
||||
else if (c >= QSE_T('0') && c <= QSE_T('7'))
|
||||
{
|
||||
qse_char_t cx;
|
||||
@ -395,6 +398,8 @@ static int getc (comp_t* com, int noesc)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
else if (c == QSE_T('x'))
|
||||
{
|
||||
qse_char_t cx;
|
||||
@ -505,6 +510,11 @@ static int cc_isupper (exec_t* e, qse_char_t c)
|
||||
|
||||
static int cc_isxdigit (exec_t* e, qse_char_t c) { return QSE_ISXDIGIT (c); }
|
||||
|
||||
static int cc_isword (exec_t* e, qse_char_t c)
|
||||
{
|
||||
return QSE_ISALNUM (c) || c == QSE_T('_');
|
||||
}
|
||||
|
||||
static struct ccinfo_t ccinfo[] =
|
||||
{
|
||||
{ { QSE_T("alnum"), 5 }, cc_isalnum },
|
||||
@ -519,6 +529,7 @@ static struct ccinfo_t ccinfo[] =
|
||||
{ { QSE_T("space"), 5 }, cc_isspace },
|
||||
{ { QSE_T("upper"), 5 }, cc_isupper },
|
||||
{ { QSE_T("xdigit"), 6 }, cc_isxdigit },
|
||||
{ { QSE_T("word"), 4 }, cc_isword },
|
||||
|
||||
/*
|
||||
{ { QSE_T("arabic"), 6 }, cc_isarabic },
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: sll.c 287 2009-09-15 10:01:02Z hyunghwan.chung $
|
||||
* $Id: sll.c 328 2010-07-08 06:58:44Z hyunghwan.chung $
|
||||
*
|
||||
Copyright 2006-2009 Chung, Hyung-Hwan.
|
||||
This file is part of QSE.
|
||||
@ -319,13 +319,13 @@ void qse_sll_poptail (sll_t* sll)
|
||||
qse_sll_delete (sll, TAIL(sll));
|
||||
}
|
||||
|
||||
void qse_sll_walk (sll_t* sll, walker_t walker, void* arg)
|
||||
void qse_sll_walk (sll_t* sll, walker_t walker, void* ctx)
|
||||
{
|
||||
node_t* n = HEAD(sll);
|
||||
|
||||
while (n != QSE_NULL)
|
||||
{
|
||||
if (walker(sll,n,arg) == QSE_SLL_WALK_STOP) return;
|
||||
if (walker(sll,n,ctx) == QSE_SLL_WALK_STOP) return;
|
||||
n = NEXT(n);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user