/* * $Id: map.c 483 2008-12-14 13:25:42Z baconevi $ * * {License} */ #include #include "mem.h" #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 keeper_t ase_map_keeper_t #define sizer_t ase_map_sizer_t #define walker_t ase_map_walker_t #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) #define NEXT(p) ASE_MAP_NEXT(p) #define SIZEOF(x) ASE_SIZEOF(x) #define size_t ase_size_t #define byte_t ase_byte_t #define uint_t ase_uint_t #define mmgr_t ase_mmgr_t #define KTOB(map,len) ((len)*(map)->scale[ASE_MAP_KEY]) #define VTOB(map,len) ((len)*(map)->scale[ASE_MAP_VAL]) static int reorganize (map_t* map); static size_t hash_key (map_t* map, const void* kptr, size_t klen) { size_t n = 0; const byte_t* p = (const byte_t*)kptr; const byte_t* bound = p + klen; while (p < bound) { n = n * 31 + *p++; p++; } return n; } 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, 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[ASE_MAP_KEY]; copier_t vcop = map->copier[ASE_MAP_VAL]; size_t as = SIZEOF(pair_t); if (kcop == ASE_MAP_COPIER_INLINE) as += KTOB(map,klen); if (vcop == ASE_MAP_COPIER_INLINE) as += VTOB(map,vlen); n = (pair_t*) ASE_MMGR_ALLOC (map->mmgr, as); if (n == ASE_NULL) return ASE_NULL; NEXT(n) = ASE_NULL; KLEN(n) = klen; if (kcop == ASE_MAP_COPIER_SIMPLE) { KPTR(n) = kptr; } else if (kcop == ASE_MAP_COPIER_INLINE) { KPTR(n) = n + 1; ASE_MEMCPY (KPTR(n), kptr, KTOB(map,klen)); } else { KPTR(n) = kcop (map, kptr, klen); if (KPTR(n) == ASE_NULL) { ASE_MMGR_FREE (map->mmgr, n); return ASE_NULL; } } VLEN(n) = vlen; if (vcop == ASE_MAP_COPIER_SIMPLE) { VPTR(n) = vptr; } else if (vcop == ASE_MAP_COPIER_INLINE) { VPTR(n) = n + 1; if (kcop == ASE_MAP_COPIER_INLINE) VPTR(n) = (byte_t*)VPTR(n) + KTOB(map,klen); ASE_MEMCPY (VPTR(n), vptr, VTOB(map,vlen)); } else { VPTR(n) = vcop (map, vptr, vlen); if (VPTR(n) != ASE_NULL) { if (map->freeer[ASE_MAP_KEY] != ASE_NULL) map->freeer[ASE_MAP_KEY] (map, KPTR(n), KLEN(n)); ASE_MMGR_FREE (map->mmgr, n); return ASE_NULL; } } return n; } 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); } 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 != ASE_NULL) { map->keeper (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_MAP_COPIER_SIMPLE) { VPTR(pair) = vptr; VLEN(pair) = vlen; } else if (vcop == ASE_MAP_COPIER_INLINE) { if (ovlen == vlen) { ASE_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 == 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; } map_t* ase_map_open (mmgr_t* mmgr, size_t ext, size_t capa, int factor) { map_t* map; if (mmgr == ASE_NULL) { mmgr = ASE_MMGR_GETDFL(); ASE_ASSERTX (mmgr != ASE_NULL, "Set the memory manager with ASE_MMGR_SETDFL()"); if (mmgr == ASE_NULL) return ASE_NULL; } map = (ase_map_t*) ASE_MMGR_ALLOC (mmgr, ASE_SIZEOF(ase_map_t) + ext); if (map == ASE_NULL) return ASE_NULL; if (ase_map_init (map, mmgr, capa, factor) == ASE_NULL) { ASE_MMGR_FREE (mmgr, map); return ASE_NULL; } return map; } 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, int factor) { ASE_ASSERTX (capa > 0, "The initial capacity should be greater than 0. Otherwise, it is adjusted to 1 in the release mode"); ASE_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 */ ASE_MEMSET (map, 0, SIZEOF(*map)); map->mmgr = mmgr; map->bucket = ASE_MMGR_ALLOC (mmgr, capa*SIZEOF(pair_t*)); if (map->bucket == ASE_NULL) return ASE_NULL; /*for (i = 0; i < capa; i++) map->bucket[i] = ASE_NULL;*/ ASE_MEMSET (map->bucket, 0, capa*SIZEOF(pair_t*)); map->scale[ASE_MAP_KEY] = 1; map->scale[ASE_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[ASE_MAP_KEY] = ASE_MAP_COPIER_SIMPLE; map->copier[ASE_MAP_VAL] = ASE_MAP_COPIER_SIMPLE; /* map->freeer[ASE_MAP_KEY] = ASE_NULL; map->freeer[ASE_MAP_VAL] = ASE_NULL; map->keeper = ASE_NULL; map->sizer = ASE_NULL; */ return map; } void ase_map_fini (map_t* map) { ase_map_clear (map); ASE_MMGR_FREE (map->mmgr, map->bucket); } void* ase_map_getextension (map_t* map) { return map + 1; } mmgr_t* ase_map_getmmgr (map_t* map) { return map->mmgr; } void ase_map_setmmgr (map_t* map, mmgr_t* mmgr) { map->mmgr = mmgr; } int ase_map_getscale (map_t* map, ase_map_id_t 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->scale[id]; } void ase_map_setscale (map_t* map, ase_map_id_t id, int scale) { ASE_ASSERTX (id == ASE_MAP_KEY || id == ASE_MAP_VAL, "The ID should be either ASE_MAP_KEY or ASE_MAP_VAL"); ASE_ASSERTX (scale > 0 && scale <= ASE_TYPE_MAX(ase_byte_t), "The scale should be larger than 0 and less than or equal to the maximum value that the ase_byte_t type can hold"); if (scale <= 0) scale = 1; if (scale > ASE_TYPE_MAX(ase_byte_t)) scale = ASE_TYPE_MAX(ase_byte_t); map->scale[id] = scale; } copier_t ase_map_getcopier (map_t* map, ase_map_id_t 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, ase_map_id_t 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"); if (copier == ASE_NULL) copier = ASE_MAP_COPIER_SIMPLE; map->copier[id] = copier; } freeer_t ase_map_getfreeer (map_t* map, ase_map_id_t 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, ase_map_id_t 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; 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; map->comper = comper; } keeper_t ase_map_getkeeper (map_t* map) { return map->keeper; } void ase_map_setkeeper (map_t* map, keeper_t keeper) { map->keeper = keeper; } sizer_t ase_map_getsizer (map_t* map) { return map->sizer; } void ase_map_setsizer (map_t* map, sizer_t sizer) { map->sizer = sizer; } size_t ase_map_getsize (map_t* map) { return map->size; } 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) { pair_t* pair; size_t hc; hc = map->hasher(map,kptr,klen) % map->capa; pair = map->bucket[hc]; while (pair != ASE_NULL) { if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0) { return pair; } pair = NEXT(pair); } return ASE_NULL; } int ase_map_put ( map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen, pair_t** px) { pair_t* pair, * p, * prev, * next; size_t hc; hc = map->hasher(map,kptr,klen) % map->capa; pair = map->bucket[hc]; prev = ASE_NULL; while (pair != ASE_NULL) { next = NEXT(pair); if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0) { 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 */ if (prev == ASE_NULL) map->bucket[hc] = p; else NEXT(prev) = p; NEXT(p) = next; } if (px != ASE_NULL) *px = p; return 0; /* value changed for the existing key */ } prev = pair; pair = next; } if (map->threshold > 0 && map->size >= map->threshold) { 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 -1; /* error */ 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) { pair_t* pair; size_t hc; hc = map->hasher(map,kptr,klen) % map->capa; pair = map->bucket[hc]; while (pair != ASE_NULL) { if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0) { return ASE_NULL; } pair = NEXT(pair); } if (map->threshold > 0 && map->size >= map->threshold) { 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; NEXT(pair) = map->bucket[hc]; map->bucket[hc] = pair; map->size++; return pair; } pair_t* ase_map_update (map_t* map, void* kptr, size_t klen, void* vptr, size_t vlen) { pair_t* pair, * p, * prev, * next; size_t hc; hc = map->hasher(map,kptr,klen) % map->capa; pair = map->bucket[hc]; prev = ASE_NULL; while (pair != ASE_NULL) { next = NEXT(pair); if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0) { 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 */ if (prev == ASE_NULL) map->bucket[hc] = p; else NEXT(prev) = p; NEXT(p) = next; } return p; /* value changed for the existing key */ } prev = pair; pair = next; } return ASE_NULL; } int ase_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 = ASE_NULL; while (pair != ASE_NULL) { if (map->comper (map, KPTR(pair), KLEN(pair), kptr, klen) == 0) { if (prev == ASE_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 ase_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 != ASE_NULL) { next = NEXT(pair); free_pair (map, pair); map->size--; pair = next; } map->bucket[i] = ASE_NULL; } } void ase_map_walk (map_t* map, walker_t walker, void* arg) { size_t i; pair_t* pair, * next; for (i = 0; i < map->capa; i++) { pair = map->bucket[i]; while (pair != ASE_NULL) { next = NEXT(pair); if (walker(map, pair, arg) == ASE_MAP_WALK_STOP) return; pair = next; } } } pair_t* ase_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 != ASE_NULL) { *buckno = i; return pair; } } return ASE_NULL; } pair_t* ase_map_getnextpair (map_t* map, pair_t* pair, size_t* buckno) { size_t i; pair_t* next; next = NEXT(pair); if (next != ASE_NULL) { /* no change in bucket number */ return next; } for (i = (*buckno)+1; i < map->capa; i++) { pair = map->bucket[i]; if (pair != ASE_NULL) { *buckno = i; return pair; } } return ASE_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**) ASE_MMGR_ALLOC ( map->mmgr, new_capa*SIZEOF(pair_t*)); if (new_buck == ASE_NULL) { /* reorganization is disabled once it fails */ map->threshold = 0; return -1; } /*for (i = 0; i < new_capa; i++) new_buck[i] = ASE_NULL;*/ ASE_MEMSET (new_buck, 0, new_capa*SIZEOF(pair_t*)); for (i = 0; i < map->capa; i++) { pair_t* pair = map->bucket[i]; while (pair != ASE_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; } } ASE_MMGR_FREE (map->mmgr, map->bucket); map->bucket = new_buck; map->capa = new_capa; map->threshold = map->capa * map->factor / 100; return 0; }