/******************************************************************** ** Copyright (c) 2018-2020 Guan Wenliang ** This file is part of the Berry default interpreter. ** skiars@qq.com, https://github.com/Skiars/berry ** See Copyright Notice in the LICENSE file or at ** https://github.com/Skiars/berry/blob/master/LICENSE ********************************************************************/ #include "be_map.h" #include "be_string.h" #include "be_vector.h" #include "be_class.h" #include "be_mem.h" #include "be_gc.h" #include "be_vm.h" #include "be_exec.h" #include #define key(node) (&(node)->key) #define value(node) (&(node)->value) #define isnil(node) var_isnil(key(node)) #define setnil(node) var_setnil(key(node)) #define hash2slot(m, h) ((m)->slots + (h) % (m)->size) #define hashcode(_v) _hashcode(vm, (_v)->type, (_v)->v) #define keytype(key) ((signed char)(key)->type) #define next(node) ((node)->key.next) #define pos2slot(map, n) ((n) != LASTNODE ? ((map)->slots + (n)) : NULL) #define pos(map, node) ((int)((node) - (map)->slots)) #define setkey(node, _v) { (node)->key.type = (bbyte)(_v)->type; \ (node)->key.v = (_v)->v; } #define datasize(size) ((size) * sizeof(bmapnode)) #define LASTNODE ((1 << 24) - 1) static int map_nextsize(int size) { be_assert(size < LASTNODE); if (size < LASTNODE) { return be_nextsize(size); } return LASTNODE + 1; } static uint32_t hashptr(void *p) { uintptr_t i = (uintptr_t)p; return (uint32_t)((i ^ (i >> 16)) & 0xFFFFFFFF); } #if BE_USE_SINGLE_FLOAT #define hashreal(v) ((uint32_t)((v).i)) #else static uint32_t hashreal(union bvaldata v) { union { breal r; uint32_t i[2]; } u; u.r = v.r; return u.i[0] ^ u.i[1]; } #endif #if BE_USE_OVERLOAD_HASH static uint32_t hashins(bvm *vm, binstance *obj) { int type = be_instance_member(vm, obj, str_literal(vm, "hash"), vm->top); if (basetype(type) == BE_FUNCTION) { bvalue *top = vm->top; var_setinstance(top + 1, obj); vm->top += 2; be_dofunc(vm, top, 1); /* call method 'item' */ vm->top -= 2; if (!var_isint(vm->top)) { /* check the return value */ const char *name = str(be_instance_name(obj)); be_raise(vm, "runtime_error", be_pushfstring(vm, "the value of `%s::hash()` is not a 'int'", strlen(name) ? name : "")); } return (uint32_t)var_toint(vm->top); } return hashptr(obj); } #endif static uint32_t _hashcode(bvm *vm, int type, union bvaldata v) { (void)vm; switch (type) { case BE_NIL: return 0; case BE_BOOL: return (uint32_t)v.b; case BE_INT: return (uint32_t)v.i; case BE_REAL: return hashreal(v); case BE_STRING: return be_strhash(v.s); #if BE_USE_OVERLOAD_HASH case BE_INSTANCE: return hashins(vm, v.p); #endif default: return hashptr(v.p); } } static int eqnode(bvm *vm, bmapnode *node, bvalue *key, uint32_t hash) { (void)vm; if (!var_isnil(key)) { bmapkey *k = key(node); #if BE_USE_OVERLOAD_HASH if (var_isinstance(key)) { bvalue kv; kv.type = k->type; kv.v = k->v; return be_vm_iseq(vm, key, &kv); } #endif if(keytype(k) == key->type && hashcode(k) == hash) { switch (key->type) { case BE_INT: return var_toint(key) == var_toint(k); case BE_REAL: return var_toreal(key) == var_toreal(k); case BE_STRING: return be_eqstr(var_tostr(key), var_tostr(k)); default: return var_toobj(key) == var_toobj(k); } } } return 0; } static bmapnode* findprev(bmap *map, bmapnode *list, bmapnode *slot) { int n, pos = pos(map, slot); bmapnode *prev = list; for (;;) { n = next(prev); if (n == pos || n == LASTNODE) { break; } prev = map->slots + n; } return n == pos ? prev : NULL; } static bmapnode* nextfree(bmap *map) { bmapnode *base = map->slots; while (map->lastfree >= base) { if (isnil(map->lastfree)) { return map->lastfree; } --map->lastfree; } return NULL; } static bmapnode* insert(bvm *vm, bmap *map, bvalue *key, uint32_t hash) { bmapnode *slot = hash2slot(map, hash); if (isnil(slot)) { /* empty slot */ setkey(slot, key); next(slot) = LASTNODE; } else { uint32_t h = hashcode(key(slot)); /* get the hashcode of the exist node */ bmapnode *mainslot = hash2slot(map, h); /* get the main-slot */ bmapnode *new = nextfree(map); /* get a free slot */ if (mainslot == slot) { /* old is main slot */ /* insert in first */ setkey(new, key); next(new) = next(slot); next(slot) = pos(map, new); slot = new; } else { bmapnode *prev = findprev(map, mainslot, slot); next(prev) = pos(map, new); /* link the previous node */ *new = *slot; /* copy to new slot */ setkey(slot, key); next(slot) = LASTNODE; } } return slot; } static bmapnode* find(bvm *vm, bmap *map, bvalue *key, uint32_t hash) { bmapnode *slot = hash2slot(map, hash); if (isnil(slot)) { return NULL; } while (!eqnode(vm, slot, key, hash)) { int n = next(slot); if (n == LASTNODE) { return NULL; } slot = map->slots + n; } return slot; } static void resize(bvm *vm, bmap *map, int size) { int i, oldsize; bmapnode *slots, *oldslots; if (size < map->count) { return; } oldsize = map->size; oldslots = map->slots; slots = be_malloc(vm, datasize(size)); for (i = 0; i < size; ++i) { setnil(slots + i); } map->size = size; map->slots = slots; map->lastfree = slots + size - 1; /* rehash */ for (i = 0; i < oldsize; ++i) { bmapnode *node = oldslots + i; if (!isnil(node)) { bvalue v; bmapnode *newslot; v.type = node->key.type; v.v = node->key.v; newslot = insert(vm, map, &v, hashcode(&v)); newslot->value = node->value; } } be_free(vm, oldslots, datasize(oldsize)); } bmap* be_map_new(bvm *vm) { bgcobject *gco = be_gcnew(vm, BE_MAP, bmap); bmap *map = cast_map(gco); if (map) { map->size = 0; map->count = 0; map->slots = NULL; var_setmap(vm->top, map); be_incrtop(vm); resize(vm, map, 2); be_stackpop(vm, 1); } return map; } void be_map_delete(bvm *vm, bmap *map) { be_free(vm, map->slots, datasize(map->size)); be_free(vm, map, sizeof(bmap)); } bvalue* be_map_find(bvm *vm, bmap *map, bvalue *key) { bmapnode *entry = find(vm, map, key, hashcode(key)); return entry ? value(entry) : NULL; } bvalue* be_map_insert(bvm *vm, bmap *map, bvalue *key, bvalue *value) { uint32_t hash = hashcode(key); bmapnode *entry = find(vm, map, key, hash); if (!entry) { /* new entry */ if (map->count >= map->size) { resize(vm, map, map_nextsize(map->size)); } entry = insert(vm, map, key, hash); ++map->count; } if (value) { entry->value = *value; } return value(entry); } int be_map_remove(bvm *vm, bmap *map, bvalue *key) { uint32_t hash = hashcode(key); bmapnode *slot = hash2slot(map, hash); /* main slot */ if (eqnode(vm, slot, key, hash)) { /* first node */ bmapnode *next = pos2slot(map, next(slot)); if (next) { /* has next */ *slot = *next; /* first: copy the second node to the slot */ slot = next; /* second: set the second node to nil (empty) */ } } else { /* the node will be remove is not first-node */ bmapnode *prev = slot; for (;;) { /* find the previous node */ int n = next(prev); slot = pos2slot(map, n); if (slot == NULL) { /* node not found */ return bfalse; } if (eqnode(vm, slot, key, hash)) { break; } prev = slot; } /* link the list */ next(prev) = next(slot); } /* set to nil */ setnil(slot); /* add to lastfree */ if (map->lastfree < slot) { map->lastfree = slot; } --map->count; return btrue; } bvalue* be_map_findstr(bvm *vm, bmap *map, bstring *key) { bvalue v; var_setstr(&v, key); return be_map_find(vm, map, &v); } bvalue* be_map_insertstr(bvm *vm, bmap *map, bstring *key, bvalue *value) { bvalue v; var_setstr(&v, key); set_fixed(key); bvalue * r = be_map_insert(vm, map, &v, value); restore_fixed(key); return r; } void be_map_removestr(bvm *vm, bmap *map, bstring *key) { bvalue v; var_setstr(&v, key); be_map_remove(vm, map, &v); } bmapnode* be_map_next(bmap *map, bmapiter *iter) { bmapnode *end = map->slots + map->size; *iter = *iter ? *iter + 1 : map->slots; while (*iter < end && isnil(*iter)) { ++(*iter); } return *iter < end ? *iter : NULL; } bmapnode* be_map_val2node(bvalue *value) { return (bmapnode *)((size_t)value - sizeof(bmapkey)); } void be_map_release(bvm *vm, bmap *map) { (void)vm; resize(vm, map, map->count ? map->count : 1); }