qse/ase/lib/cmn/map.c

651 lines
12 KiB
C
Raw Normal View History

/*
* $Id: map.c 356 2008-08-31 11:16:52Z baconevi $
*
* {License}
*/
2008-02-13 01:39:56 +00:00
#include <ase/cmn/map.h>
2008-08-19 05:21:48 +00:00
#include "mem.h"
2008-08-23 09:23:09 +00:00
#define map_t ase_map_t
#define pair_t ase_map_pair_t
#define copier_t ase_map_copier_t
#define freeer_t ase_map_freeer_t
#define hasher_t ase_map_hasher_t
#define comper_t ase_map_comper_t
#define walker_t ase_map_walker_t
2008-08-23 09:23:09 +00:00
#define KPTR(p) ASE_MAP_KPTR(p)
#define KLEN(p) ASE_MAP_KLEN(p)
#define VPTR(p) ASE_MAP_VPTR(p)
#define VLEN(p) ASE_MAP_VLEN(p)
2008-08-27 04:31:24 +00:00
#define NEXT(p) ASE_MAP_NEXT(p)
2008-08-29 04:29:53 +00:00
#define SIZEOF(x) ASE_SIZEOF(x)
2008-08-23 09:23:09 +00:00
#define size_t ase_size_t
#define byte_t ase_byte_t
2008-08-30 09:02:26 +00:00
#define uint_t ase_uint_t
2008-08-23 09:23:09 +00:00
#define mmgr_t ase_mmgr_t
static int reorganize (map_t* map);
static size_t hash_key (map_t* map, const void* kptr, size_t klen)
{
2008-08-23 09:23:09 +00:00
size_t n = 0;
const byte_t* p = (const byte_t*)kptr;
const byte_t* bound = p + klen;
2008-08-23 09:23:09 +00:00
while (p < bound)
{
n = n * 31 + *p++;
p++;
}
2008-08-23 09:23:09 +00:00
return n;
}
2008-08-23 09:23:09 +00:00
static int comp_key (map_t* map,
const void* kptr1, size_t klen1,
const void* kptr2, size_t klen2)
{
if (klen1 == klen2) return ASE_MEMCMP (kptr1, kptr2, klen1);
2008-08-27 05:04:16 +00:00
/* it just returns 1 to indicate that they are different. */
2008-08-23 09:23:09 +00:00
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[ASE_MAP_KEY];
copier_t vcop = map->copier[ASE_MAP_VAL];
2008-08-29 04:29:53 +00:00
size_t as = SIZEOF(pair_t);
2008-08-23 09:23:09 +00:00
if (kcop == ASE_MAP_COPIER_INLINE) as += klen;
if (vcop == ASE_MAP_COPIER_INLINE) as += vlen;
n = ASE_MMGR_ALLOC (map->mmgr, as);
if (n == ASE_NULL) return ASE_NULL;
2008-08-27 04:31:24 +00:00
NEXT(n) = ASE_NULL;
2008-08-23 09:23:09 +00:00
KLEN(n) = klen;
if (kcop == ASE_NULL)
{
KPTR(n) = kptr;
}
else if (kcop == ASE_MAP_COPIER_INLINE)
{
2008-08-23 09:23:09 +00:00
KPTR(n) = n + 1;
ASE_MEMCPY (KPTR(n), kptr, klen);
}
else
{
n->kptr = kcop (map, kptr, klen);
if (n->kptr == ASE_NULL)
{
ASE_MMGR_FREE (map->mmgr, n);
return ASE_NULL;
}
}
2008-08-23 09:23:09 +00:00
VLEN(n) = vlen;
if (vcop == ASE_NULL)
{
VPTR(n) = vptr;
}
else if (vcop == ASE_MAP_COPIER_INLINE)
{
VPTR(n) = n + 1;
ASE_MEMCPY (VPTR(n), vptr, vlen);
}
else
{
n->vptr = vcop (map, vptr, vlen);
if (n->vptr != ASE_NULL)
{
if (map->freeer[ASE_MAP_KEY] != ASE_NULL)
map->freeer[ASE_MAP_KEY] (map, n->kptr, n->klen);
ASE_MMGR_FREE (map->mmgr, n);
return ASE_NULL;
}
}
2008-08-23 09:23:09 +00:00
return n;
}
2008-08-23 09:23:09 +00:00
static void free_pair (map_t* map, pair_t* pair)
{
if (map->freeer[ASE_MAP_KEY] != ASE_NULL)
map->freeer[ASE_MAP_KEY] (map, KPTR(pair), KLEN(pair));
if (map->freeer[ASE_MAP_VAL] != ASE_NULL)
map->freeer[ASE_MAP_VAL] (map, VPTR(pair), VLEN(pair));
ASE_MMGR_FREE (map->mmgr, pair);
}
2008-08-23 09:23:09 +00:00
static pair_t* change_pair_val (
map_t* map, pair_t* pair, void* vptr, size_t vlen)
{
2008-08-23 09:23:09 +00:00
if (VPTR(pair) == vptr)
{
/* 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->sameval != ASE_NULL)
{
map->sameval (map, vptr, vlen);
}
}
else
{
copier_t vcop = map->copier[ASE_MAP_VAL];
void* ovptr = VPTR(pair);
size_t ovlen = VLEN(pair);
/* place the new value according to the copier */
if (vcop == ASE_NULL)
{
VPTR(pair) = vptr;
VLEN(pair) = vlen;
}
else if (vcop == ASE_MAP_COPIER_INLINE)
{
if (ovlen == vlen)
{
ASE_MEMCPY (VPTR(pair), vptr, vlen);
}
else
{
/* need to reconstruct the pair */
pair_t* p = alloc_pair (map,
KPTR(pair), KLEN(pair),
vptr, vlen);
if (p == ASE_NULL) return ASE_NULL;
free_pair (map, pair);
return p;
}
}
else
{
void* nvptr = vcop (map, vptr, vlen);
if (nvptr == ASE_NULL) return ASE_NULL;
VPTR(pair) = nvptr;
VLEN(pair) = vlen;
}
/* free up the old value */
if (map->freeer[ASE_MAP_VAL] != ASE_NULL)
{
map->freeer[ASE_MAP_VAL] (map, ovptr, ovlen);
}
}
return pair;
}
2008-08-30 09:02:26 +00:00
map_t* ase_map_open (mmgr_t* mmgr, size_t ext, size_t capa, uint_t factor)
{
2008-08-23 09:23:09 +00:00
map_t* map;
2008-08-23 09:23:09 +00:00
if (mmgr == ASE_NULL)
2008-02-29 00:50:10 +00:00
{
2008-08-23 09:23:09 +00:00
mmgr = ASE_MMGR_GETDFL();
ASE_ASSERTX (mmgr != ASE_NULL,
"Set the memory manager with ASE_MMGR_SETDFL()");
if (mmgr == ASE_NULL) return ASE_NULL;
2008-02-29 00:50:10 +00:00
}
2008-08-23 09:23:09 +00:00
2008-08-30 09:02:26 +00:00
if (ase_map_init (map, mmgr, capa, factor) == ASE_NULL)
{
ASE_MMGR_FREE (mmgr, map);
return ASE_NULL;
}
2008-09-01 04:56:47 +00:00
return map;
2008-08-30 09:02:26 +00:00
}
void ase_map_close (map_t* map)
{
ase_map_fini (map);
ASE_MMGR_FREE (map->mmgr, map);
}
map_t* ase_map_init (map_t* map, mmgr_t* mmgr, size_t capa, uint_t factor)
{
2008-08-29 08:21:25 +00:00
ASE_ASSERTX (capa >= 0,
2008-08-29 04:29:53 +00:00
"The initial capacity should be greater than 0");
2008-08-30 09:02:26 +00:00
ASE_ASSERTX (factor >= 0 && factor <= 100,
2008-08-29 04:29:53 +00:00
"The load factor should be between 0 and 100 inclusive");
2008-08-30 09:02:26 +00:00
/* do not zero out the extension */
ASE_MEMSET (map, 0, SIZEOF(map_t));
map->mmgr = mmgr;
2008-08-23 09:23:09 +00:00
2008-08-30 09:02:26 +00:00
map->bucket = ASE_MMGR_ALLOC (mmgr, capa*SIZEOF(pair_t*));
if (map->bucket == ASE_NULL) return ASE_NULL;
2008-08-29 04:29:53 +00:00
2008-08-23 09:23:09 +00:00
map->size = 0;
2008-08-29 08:21:25 +00:00
map->capa = capa;
2008-08-30 09:02:26 +00:00
map->factor = factor;
2008-08-23 09:23:09 +00:00
map->hasher = hash_key;
map->comper = comp_key;
2008-08-29 04:29:53 +00:00
2008-08-23 09:23:09 +00:00
return map;
}
2008-08-30 09:02:26 +00:00
void ase_map_fini (map_t* map)
2008-08-27 02:50:12 +00:00
{
ase_map_clear (map);
2008-08-30 09:02:26 +00:00
ASE_MMGR_FREE (map->mmgr, map->bucket);
2008-08-27 02:50:12 +00:00
}
void ase_map_clear (map_t* map)
{
size_t i;
pair_t* pair, * next;
for (i = 0; i < map->capa; i++)
{
2008-08-30 09:02:26 +00:00
pair = map->bucket[i];
2008-08-27 02:50:12 +00:00
while (pair != ASE_NULL)
{
2008-08-27 04:31:24 +00:00
next = NEXT(pair);
2008-08-27 02:50:12 +00:00
free_pair (map, pair);
map->size--;
pair = next;
}
2008-08-30 09:02:26 +00:00
map->bucket[i] = ASE_NULL;
2008-08-27 02:50:12 +00:00
}
}
2008-08-23 09:23:09 +00:00
copier_t ase_map_getcopier (map_t* map, int id)
{
ASE_ASSERTX (id == ASE_MAP_KEY || id == ASE_MAP_VAL,
"The ID should be either ASE_MAP_KEY or ASE_MAP_VAL");
return map->copier[id];
}
void ase_map_setcopier (map_t* map, int id, copier_t copier)
{
ASE_ASSERTX (id == ASE_MAP_KEY || id == ASE_MAP_VAL,
"The ID should be either ASE_MAP_KEY or ASE_MAP_VAL");
map->copier[id] = copier;
}
freeer_t ase_map_getfreeer (map_t* map, int id)
{
ASE_ASSERTX (id == ASE_MAP_KEY || id == ASE_MAP_VAL,
"The ID should be either ASE_MAP_KEY or ASE_MAP_VAL");
return map->freeer[id];
}
void ase_map_setfreeer (map_t* map, int id, freeer_t freeer)
{
ASE_ASSERTX (id == ASE_MAP_KEY || id == ASE_MAP_VAL,
"The ID should be either ASE_MAP_KEY or ASE_MAP_VAL");
map->freeer[id] = freeer;
}
hasher_t ase_map_gethasher (map_t* map)
{
return map->hasher;
}
void ase_map_sethasher (map_t* map, hasher_t hasher)
{
if (hasher == ASE_NULL) hasher = hash_key;
2008-08-23 09:23:09 +00:00
map->hasher = hasher;
}
comper_t ase_map_getcomper (map_t* map)
{
return map->comper;
}
void ase_map_setcomper (map_t* map, comper_t comper)
{
if (comper == ASE_NULL) comper = comp_key;
2008-08-23 09:23:09 +00:00
map->comper = comper;
}
void* ase_map_getextension (map_t* map)
{
2008-08-30 09:02:26 +00:00
return map + 1;
2008-08-23 09:23:09 +00:00
}
mmgr_t* ase_map_getmmgr (map_t* map)
{
return map->mmgr;
}
void ase_map_setmmgr (map_t* map, mmgr_t* mmgr)
{
map->mmgr = mmgr;
}
size_t ase_map_getsize (map_t* map)
{
return map->size;
}
2008-08-29 04:29:53 +00:00
size_t ase_map_getcapa (map_t* map)
{
return map->capa;
}
pair_t* ase_map_search (map_t* map, const void* kptr, size_t klen)
{
2008-08-23 09:23:09 +00:00
pair_t* pair;
size_t hc;
2008-08-23 09:23:09 +00:00
hc = map->hasher(map,kptr,klen) % map->capa;
2008-08-30 09:02:26 +00:00
pair = map->bucket[hc];
while (pair != ASE_NULL)
{
2008-08-27 02:50:12 +00:00
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
{
return pair;
}
2008-08-27 04:31:24 +00:00
pair = NEXT(pair);
}
return ASE_NULL;
}
int ase_map_put (
2008-08-23 09:23:09 +00:00
map_t* map, void* kptr, size_t klen,
void* vptr, size_t vlen, pair_t** px)
{
2008-08-27 04:31:24 +00:00
pair_t* pair, * p, * prev, * next;
2008-08-23 09:23:09 +00:00
size_t hc;
2008-08-23 09:23:09 +00:00
hc = map->hasher(map,kptr,klen) % map->capa;
2008-08-30 09:02:26 +00:00
pair = map->bucket[hc];
2008-08-27 04:31:24 +00:00
prev = ASE_NULL;
while (pair != ASE_NULL)
{
2008-08-27 04:31:24 +00:00
next = NEXT(pair);
2008-08-27 02:50:12 +00:00
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
2008-08-23 09:23:09 +00:00
{
p = change_pair_val (map, pair, vptr, vlen);
if (p == ASE_NULL) return -1; /* change error */
if (p != pair)
{
/* the pair has been reallocated. relink it */
2008-08-30 09:02:26 +00:00
if (prev == ASE_NULL) map->bucket[hc] = p;
2008-08-27 04:31:24 +00:00
else NEXT(prev) = p;
NEXT(p) = next;
2008-08-23 09:23:09 +00:00
}
if (px != ASE_NULL) *px = p;
return 0; /* value changed for the existing key */
}
2008-08-23 09:23:09 +00:00
prev = pair;
pair = next;
}
2008-08-30 09:02:26 +00:00
if (map->threshold > 0 && map->size >= map->threshold)
{
2008-08-23 09:23:09 +00:00
if (reorganize(map) == 0) /* ignore the error */
{
2008-08-23 09:23:09 +00:00
hc = map->hasher(map,kptr,klen) % map->capa;
}
}
2008-02-29 00:50:10 +00:00
ASE_ASSERT (pair == ASE_NULL);
2008-08-23 09:23:09 +00:00
pair = alloc_pair (map, kptr, klen, vptr, vlen);
if (pair == ASE_NULL) return -1; /* error */
2008-08-30 09:02:26 +00:00
NEXT(pair) = map->bucket[hc];
map->bucket[hc] = pair;
map->size++;
if (px != ASE_NULL) *px = pair;
return 1; /* new key added */
}
pair_t* ase_map_upsert (
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
{
/* update if the key exists, otherwise insert a new pair */
int n;
pair_t* px;
n = ase_map_put (map, kptr, klen, vptr, vlen, &px);
if (n < 0) return ASE_NULL;
return px;
}
pair_t* ase_map_insert (
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
2008-08-27 05:04:16 +00:00
{
pair_t* pair;
size_t hc;
hc = map->hasher(map,kptr,klen) % map->capa;
2008-08-30 09:02:26 +00:00
pair = map->bucket[hc];
2008-08-27 05:04:16 +00:00
while (pair != ASE_NULL)
{
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
{
return ASE_NULL;
}
pair = NEXT(pair);
}
2008-08-30 09:02:26 +00:00
if (map->threshold > 0 && map->size >= map->threshold)
2008-08-27 05:04:16 +00:00
{
if (reorganize(map) == 0) /* ignore the error */
{
hc = map->hasher(map,kptr,klen) % map->capa;
}
}
ASE_ASSERT (pair == ASE_NULL);
pair = alloc_pair (map, kptr, klen, vptr, vlen);
if (pair == ASE_NULL) return ASE_NULL;
2008-08-30 09:02:26 +00:00
NEXT(pair) = map->bucket[hc];
map->bucket[hc] = pair;
2008-08-27 05:04:16 +00:00
map->size++;
return pair;
}
pair_t* ase_map_update (
map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen)
{
2008-08-27 04:31:24 +00:00
pair_t* pair, * p, * prev, * next;
2008-08-23 09:23:09 +00:00
size_t hc;
2008-08-23 09:23:09 +00:00
hc = map->hasher(map,kptr,klen) % map->capa;
2008-08-30 09:02:26 +00:00
pair = map->bucket[hc];
2008-08-27 04:31:24 +00:00
prev = ASE_NULL;
while (pair != ASE_NULL)
{
2008-08-27 04:31:24 +00:00
next = NEXT(pair);
2008-08-27 02:50:12 +00:00
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
{
2008-08-27 04:31:24 +00:00
p = change_pair_val (map, pair, vptr, vlen);
if (p == ASE_NULL) return ASE_NULL; /* change error */
if (p != pair)
{
/* the pair has been reallocated. relink it */
2008-08-30 09:02:26 +00:00
if (prev == ASE_NULL) map->bucket[hc] = p;
2008-08-27 04:31:24 +00:00
else NEXT(prev) = p;
NEXT(p) = next;
}
return 0; /* value changed for the existing key */
}
2008-08-27 04:31:24 +00:00
prev = pair;
pair = next;
}
return ASE_NULL;
}
2008-08-27 04:31:24 +00:00
int ase_map_delete (map_t* map, const void* kptr, size_t klen)
{
2008-08-23 09:23:09 +00:00
pair_t* pair, * prev;
size_t hc;
2008-08-23 09:23:09 +00:00
hc = map->hasher(map,kptr,klen) % map->capa;
2008-08-30 09:02:26 +00:00
pair = map->bucket[hc];
prev = ASE_NULL;
while (pair != ASE_NULL)
{
2008-08-27 02:50:12 +00:00
if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0)
{
if (prev == ASE_NULL)
2008-08-30 09:02:26 +00:00
map->bucket[hc] = NEXT(pair);
2008-08-27 04:31:24 +00:00
else NEXT(prev) = NEXT(pair);
2008-02-29 00:50:10 +00:00
2008-08-23 09:23:09 +00:00
free_pair (map, pair);
map->size--;
return 0;
}
prev = pair;
2008-08-27 04:31:24 +00:00
pair = NEXT(pair);
}
return -1;
}
2008-08-27 02:50:12 +00:00
void ase_map_walk (map_t* map, walker_t walker, void* arg)
{
2008-08-23 09:23:09 +00:00
size_t i;
pair_t* pair, * next;
for (i = 0; i < map->capa; i++)
{
2008-08-30 09:02:26 +00:00
pair = map->bucket[i];
while (pair != ASE_NULL)
{
2008-08-27 04:31:24 +00:00
next = NEXT(pair);
2008-08-27 02:50:12 +00:00
if (walker(map, pair, arg) == ASE_MAP_WALK_STOP) return;
pair = next;
}
}
}
2008-08-23 09:23:09 +00:00
pair_t* ase_map_getfirstpair (map_t* map, size_t* buckno)
{
2008-08-23 09:23:09 +00:00
size_t i;
pair_t* pair;
for (i = 0; i < map->capa; i++)
{
2008-08-30 09:02:26 +00:00
pair = map->bucket[i];
if (pair != ASE_NULL)
{
*buckno = i;
return pair;
}
}
return ASE_NULL;
}
2008-08-29 04:29:53 +00:00
pair_t* ase_map_getnextpair (map_t* map, pair_t* pair, size_t* buckno)
{
2008-08-23 09:23:09 +00:00
size_t i;
pair_t* next;
2008-08-27 04:31:24 +00:00
next = NEXT(pair);
if (next != ASE_NULL)
{
/* no change in bucket number */
return next;
}
for (i = (*buckno)+1; i < map->capa; i++)
{
2008-08-30 09:02:26 +00:00
pair = map->bucket[i];
if (pair != ASE_NULL)
{
*buckno = i;
return pair;
}
}
return ASE_NULL;
}
2008-08-23 09:23:09 +00:00
static int reorganize (map_t* map)
{
2008-08-23 09:23:09 +00:00
size_t i, hc, new_capa;
pair_t** new_buck;
2008-08-23 09:23:09 +00:00
/* 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);
2008-08-23 09:23:09 +00:00
new_buck = (pair_t**) ASE_MMGR_ALLOC (
2008-08-29 04:29:53 +00:00
map->mmgr, SIZEOF(pair_t*) * new_capa);
if (new_buck == ASE_NULL)
{
2008-08-29 04:29:53 +00:00
/* reorganization is disabled once it fails */
2008-08-30 09:02:26 +00:00
map->threshold = 0;
return -1;
}
for (i = 0; i < new_capa; i++) new_buck[i] = ASE_NULL;
for (i = 0; i < map->capa; i++)
{
2008-08-30 09:02:26 +00:00
pair_t* pair = map->bucket[i];
while (pair != ASE_NULL)
{
2008-08-27 04:31:24 +00:00
pair_t* next = NEXT(pair);
2008-08-23 09:23:09 +00:00
hc = map->hasher (map,
KPTR(pair),
KLEN(pair)) % new_capa;
2008-08-27 04:31:24 +00:00
NEXT(pair) = new_buck[hc];
new_buck[hc] = pair;
pair = next;
}
}
2008-08-30 09:02:26 +00:00
ASE_MMGR_FREE (map->mmgr, map->bucket);
map->bucket = new_buck;
map->capa = new_capa;
2008-08-30 09:02:26 +00:00
map->threshold = map->capa * map->factor / 100;
return 0;
}
2008-08-23 09:23:09 +00:00
void* ase_map_copyinline (map_t* map, void* dptr, size_t dlen)
{
/* this is a dummy copier */
return ASE_NULL;
}