diff --git a/src/runtime/Makefile b/src/runtime/Makefile index b9e57701037..df33b580151 100644 --- a/src/runtime/Makefile +++ b/src/runtime/Makefile @@ -18,7 +18,7 @@ LIBOFILES=\ rt2_$(GOARCH).$O\ sys_$(GOARCH)_$(GOOS).$O\ runtime.$O\ - map.$O\ + hashmap.$O\ chan.$O\ iface.$O\ array.$O\ @@ -30,7 +30,7 @@ LIBOFILES=\ OFILES=$(RT0OFILES) $(LIBOFILES) OS_H=$(GOARCH)_$(GOOS).h -HFILES=runtime.h $(OS_H_) +HFILES=runtime.h hashmap.h $(OS_H_) install: rt0 $(LIB) runtime.acid cp $(RT0OFILES) $(GOROOT)/lib diff --git a/src/runtime/hashmap.c b/src/runtime/hashmap.c new file mode 100644 index 00000000000..1a8e68c39c6 --- /dev/null +++ b/src/runtime/hashmap.c @@ -0,0 +1,861 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "runtime.h" +#include "hashmap.h" + +/* Return a pointer to the struct/union of type "type" + whose "field" field is addressed by pointer "p". */ + +struct hash { /* a hash table; initialize with hash_init() */ + uint32 count; /* elements in table - must be first */ + + uint8 datasize; /* amount of data to store in entry */ + uint8 max_power; /* max power of 2 to create sub-tables */ + uint8 max_probes; /* max entries to probe before rehashing */ + int32 changes; /* inc'ed whenever a subtable is created/grown */ + hash_hash_t (*data_hash) (uint32, void *a); /* return hash of *a */ + uint32 (*data_eq) (uint32, void *a, void *b); /* return whether *a == *b */ + void (*data_del) (uint32, void *arg, void *data); /* invoked on deletion */ + struct hash_subtable *st; /* first-level table */ + + uint32 keysize; + uint32 valsize; + uint32 ko; + uint32 vo; + uint32 po; + Alg* keyalg; + Alg* valalg; +}; + +struct hash_entry { + hash_hash_t hash; /* hash value of data */ + byte data[1]; /* user data has "datasize" bytes */ +}; + +struct hash_subtable { + uint8 power; /* bits used to index this table */ + uint8 used; /* bits in hash used before reaching this table */ + uint8 datasize; /* bytes of client data in an entry */ + uint8 max_probes; /* max number of probes when searching */ + int16 limit_bytes; /* max_probes * (datasize+sizeof (hash_hash_t)) */ + struct hash_entry *end; /* points just past end of entry[] */ + struct hash_entry entry[1]; /* 2**power+max_probes-1 elements of elemsize bytes */ +}; + +#define HASH_DATA_EQ(h,x,y) ((*h->data_eq) (h->keysize, (x), (y))) + +#define HASH_REHASH 0x2 /* an internal flag */ +/* the number of bits used is stored in the flags word too */ +#define HASH_USED(x) ((x) >> 2) +#define HASH_MAKE_USED(x) ((x) << 2) + +#define HASH_LOW 6 +#define HASH_ONE (((hash_hash_t)1) << HASH_LOW) +#define HASH_MASK (HASH_ONE - 1) +#define HASH_ADJUST(x) (((x) < HASH_ONE) << HASH_LOW) + +#define HASH_BITS (sizeof (hash_hash_t) * 8) + +#define HASH_SUBHASH HASH_MASK +#define HASH_NIL 0 +#define HASH_NIL_MEMSET 0 + +#define HASH_OFFSET(base, byte_offset) \ + ((struct hash_entry *) (((byte *) (base)) + (byte_offset))) + + +/* return a hash layer with 2**power empty entries */ +static struct hash_subtable * +hash_subtable_new (struct hash *h, int32 power, int32 used) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + int32 bytes = elemsize << power; + struct hash_subtable *st; + int32 limit_bytes = h->max_probes * elemsize; + int32 max_probes = h->max_probes; + + if (bytes < limit_bytes) { + limit_bytes = bytes; + max_probes = 1 << power; + } + bytes += limit_bytes - elemsize; + st = malloc (offsetof (struct hash_subtable, entry[0]) + bytes); + st->power = power; + st->used = used; + st->datasize = h->datasize; + st->max_probes = max_probes; + st->limit_bytes = limit_bytes; + st->end = HASH_OFFSET (st->entry, bytes); + memset (st->entry, HASH_NIL_MEMSET, bytes); + return (st); +} + +static void +init_sizes (int64 hint, int32 *init_power, int32 *max_power) +{ + int32 log = 0; + int32 i; + + for (i = 32; i != 0; i >>= 1) { + if ((hint >> (log + i)) != 0) { + log += i; + } + } + log += 1 + (((hint << 3) >> log) >= 11); /* round up for utilization */ + if (log <= 14) { + *init_power = log; + } else { + *init_power = 12; + } + *max_power = 12; +} + +static void +hash_init (struct hash *h, + int32 datasize, + hash_hash_t (*data_hash) (uint32, void *), + uint32 (*data_eq) (uint32, void *, void *), + void (*data_del) (uint32, void *, void *), + int64 hint) +{ + int32 init_power; + int32 max_power; + + if(datasize < sizeof (void *)) + datasize = sizeof (void *); + init_sizes (hint, &init_power, &max_power); + h->datasize = datasize; + h->max_power = max_power; + h->max_probes = 15; + assert (h->datasize == datasize); + assert (h->max_power == max_power); + assert (sizeof (void *) <= h->datasize || h->max_power == 255); + h->count = 0; + h->changes = 0; + h->data_hash = data_hash; + h->data_eq = data_eq; + h->data_del = data_del; + h->st = hash_subtable_new (h, init_power, 0); +} + +static void +hash_remove_n (struct hash_subtable *st, struct hash_entry *dst_e, int32 n) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *src_e = HASH_OFFSET (dst_e, n * elemsize); + struct hash_entry *end_e = st->end; + int32 shift = HASH_BITS - (st->power + st->used); + int32 index_mask = (((hash_hash_t)1) << st->power) - 1; + int32 dst_i = (((byte *) dst_e) - ((byte *) st->entry)) / elemsize; + int32 src_i = dst_i + n; + hash_hash_t hash; + int32 skip; + int32 bytes; + + while (dst_e != src_e) { + if (src_e != end_e) { + struct hash_entry *cp_e = src_e; + int32 save_dst_i = dst_i; + while (cp_e != end_e && (hash = cp_e->hash) != HASH_NIL && + ((hash >> shift) & index_mask) <= dst_i) { + cp_e = HASH_OFFSET (cp_e, elemsize); + dst_i++; + } + bytes = ((byte *) cp_e) - (byte *) src_e; + memmove (dst_e, src_e, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + src_e = cp_e; + src_i += dst_i - save_dst_i; + if (src_e != end_e && (hash = src_e->hash) != HASH_NIL) { + skip = ((hash >> shift) & index_mask) - dst_i; + } else { + skip = src_i - dst_i; + } + } else { + skip = src_i - dst_i; + } + bytes = skip * elemsize; + memset (dst_e, HASH_NIL_MEMSET, bytes); + dst_e = HASH_OFFSET (dst_e, bytes); + dst_i += skip; + } +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres); + +static void +hash_conv (struct hash *h, + struct hash_subtable *st, int32 flags, + hash_hash_t hash, + struct hash_entry *e) +{ + int32 new_flags = (flags + HASH_MAKE_USED (st->power)) | HASH_REHASH; + int32 shift = HASH_BITS - HASH_USED (new_flags); + hash_hash_t prefix_mask = (-(hash_hash_t)1) << shift; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + void *dummy_result; + struct hash_entry *de; + int32 index_mask = (1 << st->power) - 1; + hash_hash_t e_hash; + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + + while (e != st->entry && (e_hash = pe->hash) != HASH_NIL && (e_hash & HASH_MASK) != HASH_SUBHASH) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + + de = e; + while (e != st->end && + (e_hash = e->hash) != HASH_NIL && + (e_hash & HASH_MASK) != HASH_SUBHASH) { + struct hash_entry *target_e = HASH_OFFSET (st->entry, ((e_hash >> shift) & index_mask) * elemsize); + struct hash_entry *ne = HASH_OFFSET (e, elemsize); + hash_hash_t current = e_hash & prefix_mask; + if (de < target_e) { + memset (de, HASH_NIL_MEMSET, ((byte *) target_e) - (byte *) de); + de = target_e; + } + if ((hash & prefix_mask) == current || + (ne != st->end && (e_hash = ne->hash) != HASH_NIL && + (e_hash & prefix_mask) == current)) { + struct hash_subtable *new_st = hash_subtable_new (h, 1, HASH_USED (new_flags)); + int32 rc = hash_insert_internal (&new_st, new_flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = ne; + while (e != st->end && (e_hash = e->hash) != HASH_NIL && (e_hash & prefix_mask) == current) { + assert ((e_hash & HASH_MASK) != HASH_SUBHASH); + rc = hash_insert_internal (&new_st, new_flags, e_hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + e = HASH_OFFSET (e, elemsize); + } + memset (de->data, HASH_NIL_MEMSET, h->datasize); + *(struct hash_subtable **)de->data = new_st; + de->hash = current | HASH_SUBHASH; + } else { + if (e != de) { + memcpy (de, e, elemsize); + } + e = HASH_OFFSET (e, elemsize); + } + de = HASH_OFFSET (de, elemsize); + } + if (e != de) { + hash_remove_n (st, de, (((byte *) e) - (byte *) de) / elemsize); + } +} + +static void +hash_grow (struct hash *h, struct hash_subtable **pst, int32 flags) +{ + struct hash_subtable *old_st = *pst; + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + *pst = hash_subtable_new (h, old_st->power + 1, HASH_USED (flags)); + struct hash_entry *end_e = old_st->end; + struct hash_entry *e; + void *dummy_result; + int32 used = 0; + + flags |= HASH_REHASH; + for (e = old_st->entry; e != end_e; e = HASH_OFFSET (e, elemsize)) { + hash_hash_t hash = e->hash; + if (hash != HASH_NIL) { + int32 rc = hash_insert_internal (pst, flags, e->hash, h, e->data, &dummy_result); + assert (rc == 0); + memcpy(dummy_result, e->data, h->datasize); + used++; + } + } + free (old_st); +} + +int32 +hash_lookup (struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + *pres = e->data; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + *pres = 0; + return (0); +} + +int32 +hash_remove (struct hash *h, void *data, void *arg) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + hash_hash_t hash = (*h->data_hash) (h->keysize, data) & ~HASH_MASK; + struct hash_subtable *st = h->st; + int32 used = 0; + hash_hash_t e_hash; + struct hash_entry *e; + struct hash_entry *end_e; + + hash += HASH_ADJUST (hash); + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + + e = HASH_OFFSET (st->entry, i * elemsize); /* e points to element i */ + e_hash = e->hash; + if ((e_hash & HASH_MASK) != HASH_SUBHASH) { /* a subtable */ + break; + } + used += st->power; + st = *(struct hash_subtable **)e->data; + } + end_e = HASH_OFFSET (e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + } + while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, e->data)) { /* a match */ + (*h->data_del) (h->keysize, arg, e->data); + hash_remove_n (st, e, 1); + h->count--; + return (1); + } + e = HASH_OFFSET (e, elemsize); + } + USED(e_hash); + return (0); +} + +static int32 +hash_insert_internal (struct hash_subtable **pst, int32 flags, hash_hash_t hash, + struct hash *h, void *data, void **pres) +{ + int32 elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + + if ((flags & HASH_REHASH) == 0) { + hash += HASH_ADJUST (hash); + hash &= ~HASH_MASK; + } + for (;;) { + struct hash_subtable *st = *pst; + int32 shift = HASH_BITS - (st->power + HASH_USED (flags)); + int32 index_mask = (1 << st->power) - 1; + int32 i = (hash >> shift) & index_mask; /* i is the natural position of hash */ + struct hash_entry *start_e = + HASH_OFFSET (st->entry, i * elemsize); /* start_e is the pointer to element i */ + struct hash_entry *e = start_e; /* e is going to range over [start_e, end_e) */ + struct hash_entry *end_e; + hash_hash_t e_hash = e->hash; + + if ((e_hash & HASH_MASK) == HASH_SUBHASH) { /* a subtable */ + pst = (struct hash_subtable **) e->data; + flags += HASH_MAKE_USED (st->power); + continue; + } + end_e = HASH_OFFSET (start_e, st->limit_bytes); + while (e != end_e && (e_hash = e->hash) != HASH_NIL && e_hash < hash) { + e = HASH_OFFSET (e, elemsize); + i++; + } + if (e != end_e && e_hash != HASH_NIL) { + /* ins_e ranges over the elements that may match */ + struct hash_entry *ins_e = e; + int32 ins_i = i; + hash_hash_t ins_e_hash; + while (ins_e != end_e && ((e_hash = ins_e->hash) ^ hash) < HASH_SUBHASH) { + if (HASH_DATA_EQ (h, data, ins_e->data)) { /* a match */ + *pres = ins_e->data; + return (1); + } + assert (e_hash != hash || (flags & HASH_REHASH) == 0); + hash += (e_hash == hash); /* adjust hash if it collides */ + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + if (e_hash <= hash) { /* set e to insertion point */ + e = ins_e; + i = ins_i; + } + } + /* set ins_e to the insertion point for the new element */ + ins_e = e; + ins_i = i; + ins_e_hash = 0; + /* move ins_e to point at the end of the contiguous block, but + stop if any element can't be moved by one up */ + while (ins_e != st->end && (ins_e_hash = ins_e->hash) != HASH_NIL && + ins_i + 1 - ((ins_e_hash >> shift) & index_mask) < st->max_probes && + (ins_e_hash & HASH_MASK) != HASH_SUBHASH) { + ins_e = HASH_OFFSET (ins_e, elemsize); + ins_i++; + } + if (e == end_e || ins_e == st->end || ins_e_hash != HASH_NIL) { + e = end_e; /* can't insert; must grow or convert to subtable */ + } else { /* make space for element */ + memmove (HASH_OFFSET (e, elemsize), e, ((byte *) ins_e) - (byte *) e); + } + } + if (e != end_e) { + e->hash = hash; + *pres = e->data; + return (0); + } + h->changes++; + if (st->power < h->max_power) { + hash_grow (h, pst, flags); + } else { + hash_conv (h, st, flags, hash, start_e); + } + } +} + +int32 +hash_insert (struct hash *h, void *data, void **pres) +{ + int32 rc = hash_insert_internal (&h->st, 0, (*h->data_hash) (h->keysize, data), h, data, pres); + + h->count += (rc == 0); /* increment count if element didn't previously exist */ + return (rc); +} + +uint32 +hash_count (struct hash *h) +{ + return (h->count); +} + +static void +iter_restart (struct hash_iter *it, struct hash_subtable *st, int32 used) +{ + int32 elemsize = it->elemsize; + hash_hash_t last_hash = it->last_hash; + struct hash_entry *e; + hash_hash_t e_hash; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *end; + + for (;;) { + int32 shift = HASH_BITS - (st->power + used); + int32 index_mask = (1 << st->power) - 1; + int32 i = (last_hash >> shift) & index_mask; + + end = st->end; + e = HASH_OFFSET (st->entry, i * elemsize); + sub->start = st->entry; + sub->end = end; + + if ((e->hash & HASH_MASK) != HASH_SUBHASH) { + break; + } + sub->e = HASH_OFFSET (e, elemsize); + sub = &it->subtable_state[++(it->i)]; + used += st->power; + st = *(struct hash_subtable **)e->data; + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + sub->e = e; +} + +void * +hash_next (struct hash_iter *it) +{ + int32 elemsize = it->elemsize; + struct hash_iter_sub *sub = &it->subtable_state[it->i]; + struct hash_entry *e = sub->e; + struct hash_entry *end = sub->end; + hash_hash_t e_hash = 0; + + if (it->changes != it->h->changes) { /* hash table's structure changed; recompute */ + it->changes = it->h->changes; + it->i = 0; + iter_restart (it, it->h->st, 0); + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + if (e != sub->start && it->last_hash != HASH_OFFSET (e, -elemsize)->hash) { + struct hash_entry *start = HASH_OFFSET (e, -(elemsize * it->h->max_probes)); + struct hash_entry *pe = HASH_OFFSET (e, -elemsize); + hash_hash_t last_hash = it->last_hash; + if (start < sub->start) { + start = sub->start; + } + while (e != start && ((e_hash = pe->hash) == HASH_NIL || last_hash < e_hash)) { + e = pe; + pe = HASH_OFFSET (pe, -elemsize); + } + while (e != end && ((e_hash = e->hash) == HASH_NIL || e_hash <= last_hash)) { + e = HASH_OFFSET (e, elemsize); + } + } + + for (;;) { + while (e != end && (e_hash = e->hash) == HASH_NIL) { + e = HASH_OFFSET (e, elemsize); + } + if (e == end) { + if (it->i == 0) { + it->last_hash = HASH_OFFSET (e, -elemsize)->hash; + sub->e = e; + return (0); + } else { + it->i--; + sub = &it->subtable_state[it->i]; + e = sub->e; + end = sub->end; + } + } else if ((e_hash & HASH_MASK) != HASH_SUBHASH) { + it->last_hash = e->hash; + sub->e = HASH_OFFSET (e, elemsize); + return (e->data); + } else { + struct hash_subtable *st = + *(struct hash_subtable **)e->data; + sub->e = HASH_OFFSET (e, elemsize); + it->i++; + assert (it->i < sizeof (it->subtable_state) / + sizeof (it->subtable_state[0])); + sub = &it->subtable_state[it->i]; + sub->e = e = st->entry; + sub->start = st->entry; + sub->end = end = st->end; + } + } +} + +void +hash_iter_init (struct hash *h, struct hash_iter *it) +{ + it->elemsize = h->datasize + offsetof (struct hash_entry, data[0]); + it->changes = h->changes; + it->i = 0; + it->h = h; + it->last_hash = 0; + it->subtable_state[0].e = h->st->entry; + it->subtable_state[0].start = h->st->entry; + it->subtable_state[0].end = h->st->end; +} + +static void +clean_st (struct hash_subtable *st, int32 *slots, int32 *used) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + struct hash_entry *end = st->end; + int32 lslots = (((byte *) end) - (byte *) e) / elemsize; + int32 lused = 0; + + while (e != end) { + hash_hash_t hash = e->hash; + if ((hash & HASH_MASK) == HASH_SUBHASH) { + clean_st (*(struct hash_subtable **)e->data, slots, used); + } else { + lused += (hash != HASH_NIL); + } + e = HASH_OFFSET (e, elemsize); + } + free (st); + *slots += lslots; + *used += lused; +} + +void +hash_destroy (struct hash *h) +{ + int32 slots = 0; + int32 used = 0; + + clean_st (h->st, &slots, &used); + free (h); +} + +static void +hash_visit_internal (struct hash_subtable *st, + int32 used, int32 level, + void (*data_visit) (void *arg, int32 level, void *data), + void *arg) +{ + int32 elemsize = st->datasize + offsetof (struct hash_entry, data[0]); + struct hash_entry *e = st->entry; + int32 shift = HASH_BITS - (used + st->power); + int32 i = 0; + + while (e != st->end) { + int32 index = ((e->hash >> (shift - 1)) >> 1) & ((1 << st->power) - 1); + if ((e->hash & HASH_MASK) == HASH_SUBHASH) { + (*data_visit) (arg, level, e->data); + hash_visit_internal (*(struct hash_subtable **)e->data, + used + st->power, level + 1, data_visit, arg); + } else { + (*data_visit) (arg, level, e->data); + } + if (e->hash != HASH_NIL) { + assert (i < index + st->max_probes); + assert (index <= i); + } + e = HASH_OFFSET (e, elemsize); + i++; + } +} + +void +hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg) +{ + hash_visit_internal (h->st, 0, 0, data_visit, arg); +} + +// +/// interfaces to go runtime +// + +static void +donothing(uint32 s, void *a, void *b) +{ + USED(s); + USED(a); + USED(b); +} + +typedef struct hash Hmap; +static int32 debug = 0; + +// newmap(keysize uint32, valsize uint32, +// keyalg uint32, valalg uint32, +// hint uint32) (hmap *map[any]any); +void +sys·newmap(uint32 keysize, uint32 valsize, + uint32 keyalg, uint32 valalg, uint32 hint, + Hmap* ret) +{ + Hmap *h; + + if(keyalg >= 3 || + valalg >= 3) { + prints("0<="); + sys·printint(keyalg); + prints("<"); + sys·printint(nelem(algarray)); + prints("\n0<="); + sys·printint(valalg); + prints("<"); + sys·printint(nelem(algarray)); + prints("\n"); + + throw("sys·newmap: key/val algorithm out of range"); + } + + h = mal(sizeof(*h)); + hash_init(h, keysize+valsize, + algarray[keyalg].hash, + algarray[keyalg].equal, + donothing, + hint); + + h->keysize = keysize; + h->valsize = valsize; + h->keyalg = &algarray[keyalg]; + h->valalg = &algarray[valalg]; + + // these calculations are compiler dependent + h->ko = rnd(sizeof(h), keysize); + h->vo = rnd(h->ko+keysize, valsize); + h->po = rnd(h->vo+valsize, 1); + + ret = h; + FLUSH(&ret); + + if(debug) { + prints("newmap: map="); + sys·printpointer(h); + prints("; keysize="); + sys·printint(keysize); + prints("; valsize="); + sys·printint(valsize); + prints("; keyalg="); + sys·printint(keyalg); + prints("; valalg="); + sys·printint(valalg); + prints("; ko="); + sys·printint(h->ko); + prints("; vo="); + sys·printint(h->vo); + prints("; po="); + sys·printint(h->po); + prints("\n"); + } +} + +// mapaccess1(hmap *map[any]any, key any) (val any); +void +sys·mapaccess1(Hmap *h, ...) +{ + byte *ak, *av; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) + throw("sys·mapaccess1: key not in map"); + h->valalg->copy(h->valsize, av, res+h->keysize); + + if(debug) { + prints("sys·mapaccess1: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapaccess2(hmap *map[any]any, key any) (val any, pres bool); +void +sys·mapaccess2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + res = nil; + hit = hash_lookup(h, ak, (void**)&res); + if(!hit) { + *ap = false; + h->valalg->copy(h->valsize, av, nil); + } else { + *ap = true; + h->valalg->copy(h->valsize, av, res+h->keysize); + } + + if(debug) { + prints("sys·mapaccess2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("; pres="); + sys·printbool(*ap); + prints("\n"); + } +} + +static void +mapassign(Hmap *h, byte *ak, byte *av) +{ + byte *res; + int32 hit; + + res = nil; + hit = hash_insert(h, ak, (void**)&res); + h->keyalg->copy(h->keysize, res, ak); + h->valalg->copy(h->valsize, res+h->keysize, av); + + if(debug) { + prints("mapassign: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; val="); + h->valalg->print(h->valsize, av); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} + +// mapassign1(hmap *map[any]any, key any, val any); +void +sys·mapassign1(Hmap *h, ...) +{ + byte *ak, *av; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + + mapassign(h, ak, av); +} + +// mapassign2(hmap *map[any]any, key any, val any, pres bool); +void +sys·mapassign2(Hmap *h, ...) +{ + byte *ak, *av, *ap; + byte *res; + int32 hit; + + ak = (byte*)&h + h->ko; + av = (byte*)&h + h->vo; + ap = (byte*)&h + h->po; + + if(*ap == true) { + // assign + mapassign(h, ak, av); + return; + } + + // delete + hit = hash_remove(h, ak, (void**)&res); + + if(debug) { + prints("mapassign2: map="); + sys·printpointer(h); + prints("; key="); + h->keyalg->print(h->keysize, ak); + prints("; hit="); + sys·printint(hit); + prints("; res="); + sys·printpointer(res); + prints("\n"); + } +} diff --git a/src/runtime/hashmap.h b/src/runtime/hashmap.h new file mode 100644 index 00000000000..04bb7326995 --- /dev/null +++ b/src/runtime/hashmap.h @@ -0,0 +1,160 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + + +/* A hash table. + Example, hashing nul-terminated char*s: + hash_hash_t str_hash (void *v) { + char *s; + hash_hash_t hash = 0; + for (s = *(char **)v; *s != 0; s++) { + hash = (hash ^ *s) * 2654435769U; + } + return (hash); + } + int str_eq (void *a, void *b) { + return (strcmp (*(char **)a, *(char **)b) == 0); + } + void str_del (void *arg, void *data) { + *(char **)arg = *(char **)data; + } + + struct hash *h = hash_new (sizeof (char *), &str_hash, &str_eq, &str_del, 3, 12, 15); + ... 3=> 2**3 entries initial size + ... 12=> 2**12 entries before sprouting sub-tables + ... 15=> number of adjacent probes to attempt before growing + + Example lookup: + char *key = "foobar"; + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + } else { + printf ("not found in table\n"); + } + + Example insertion: + char *key = strdup ("foobar"); + char **result_ptr; + if (hash_lookup (h, &key, (void **) &result_ptr)) { + printf ("found in table: %s\n", *result_ptr); + printf ("to overwrite, do *result_ptr = key\n"); + } else { + printf ("not found in table; inserted as %s\n", *result_ptr); + assert (*result_ptr == key); + } + + Example deletion: + char *key = "foobar"; + char *result; + if (hash_remove (h, &key, &result)) { + printf ("key found and deleted from table\n"); + printf ("called str_del (&result, data) to copy data to result: %s\n", result); + } else { + printf ("not found in table\n"); + } + + Example iteration over the elements of *h: + char **data; + struct hash_iter it; + hash_iter_init (h, &it); + for (data = hash_next (&it); data != 0; data = hash_next (&it)) { + printf ("%s\n", *data); + } + */ + +#define malloc mal +#define free(a) USED(a) +#define offsetof(s,m) (uint32)(&(((s*)0)->m)) +#define memset(a,b,c) sys·memclr((byte*)(a), (uint32)(c)) +#define memmove(a,b,c) mmov((byte*)(a),(byte*)(b),(uint32)(c)) +#define memcpy(a,b,c) mcpy((byte*)(a),(byte*)(b),(uint32)(c)) +#define assert(a) if(!(a)) throw("assert") + +struct hash; /* opaque */ +struct hash_subtable; /* opaque */ +struct hash_entry; /* opaque */ + +typedef uint64 uintptr_t; +typedef uintptr_t hash_hash_t; + +struct hash_iter { + int32 elemsize; /* size of elements in table */ + int32 changes; /* number of changes observed last time */ + int32 i; /* stack pointer in subtable_state */ + hash_hash_t last_hash; /* last hash value returned */ + struct hash *h; /* the hash table */ + struct hash_iter_sub { + struct hash_entry *e; /* pointer into subtable */ + struct hash_entry *start; /* start of subtable */ + struct hash_entry *end; /* end of subtable */ + } subtable_state[16]; /* Should be large enough unless the hashing is + so bad that many distinct data values hash + to the same hash value. */ +}; + +/* Return a hashtable h 2**init_power empty entries, each with + "datasize" data bytes. + (*data_hash)(a) should return the hash value of data element *a. + (*data_eq)(a,b) should return whether the data at "a" and the data at "b" + are equal. + (*data_del)(arg, a) will be invoked when data element *a is about to be removed + from the table. "arg" is the argument passed to "hash_remove()". + + Growing is accomplished by resizing if the current tables size is less than + a threshold, and by adding subtables otherwise. hint should be set + the expected maximum size of the table. + "datasize" should be in [sizeof (void*), ..., 255]. If you need a + bigger "datasize", store a pointer to another piece of memory. */ + +//struct hash *hash_new (int32 datasize, +// hash_hash_t (*data_hash) (void *), +// int32 (*data_eq) (void *, void *), +// void (*data_del) (void *, void *), +// int64 hint); + +/* Lookup *data in *h. If the data is found, return 1 and place a pointer to + the found element in *pres. Otherwise return 0 and place 0 in *pres. */ +int32 hash_lookup (struct hash *h, void *data, void **pres); + +/* Lookup *data in *h. If the data is found, execute (*data_del) (arg, p) + where p points to the data in the table, then remove it from *h and return + 1. Otherwise return 0. */ +int32 hash_remove (struct hash *h, void *data, void *arg); + +/* Lookup *data in *h. If the data is found, return 1, and place a pointer + to the found element in *pres. Otherwise, return 0, allocate a region + for the data to be inserted, and place a pointer to the inserted element + in *pres; it is the caller's responsibility to copy the data to be + inserted to the pointer returned in *pres in this case. + + If using garbage collection, it is the caller's responsibility to + add references for **pres if HASH_ADDED is returned. */ +int32 hash_insert (struct hash *h, void *data, void **pres); + +/* Return the number of elements in the table. */ +uint32 hash_count (struct hash *h); + +/* The following call is useful only if not using garbage collection on the + table. + Remove all sub-tables associated with *h. + This undoes the effects of hash_init(). + If other memory pointed to by user data must be freed, the caller is + responsible for doiing do by iterating over *h first; see + hash_iter_init()/hash_next(). */ +void hash_destroy (struct hash *h); + +/*----- iteration -----*/ + +/* Initialize *it from *h. */ +void hash_iter_init (struct hash *h, struct hash_iter *it); + +/* Return the next used entry in the table which which *it was initialized. */ +void *hash_next (struct hash_iter *it); + +/*---- test interface ----*/ +/* Call (*data_visit) (arg, level, data) for every data entry in the table, + whether used or not. "level" is the subtable level, 0 means first level. */ +/* TESTING ONLY: DO NOT USE THIS ROUTINE IN NORMAL CODE */ +void hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *data), void *arg); diff --git a/src/runtime/map.c b/src/runtime/map.c deleted file mode 100644 index 0a4b3b8e84f..00000000000 --- a/src/runtime/map.c +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include "runtime.h" - -static int32 debug = 0; - -typedef struct Link Link; -typedef struct Hmap Hmap; - -struct Link -{ - Link* link; - byte data[8]; -}; - -struct Hmap -{ - uint32 len; // must be first - uint32 keysize; - uint32 valsize; - uint32 hint; - uint32 valoffset; - uint32 ko; - uint32 vo; - uint32 po; - Alg* keyalg; - Alg* valalg; - Link* link; -}; - -// newmap(keysize uint32, valsize uint32, -// keyalg uint32, valalg uint32, -// hint uint32) (hmap *map[any]any); -void -sys·newmap(uint32 keysize, uint32 valsize, - uint32 keyalg, uint32 valalg, uint32 hint, - Hmap* ret) -{ - Hmap *m; - - if(keyalg >= 3 || - valalg >= 3) { - prints("0<="); - sys·printint(keyalg); - prints("<"); - sys·printint(nelem(algarray)); - prints("\n0<="); - sys·printint(valalg); - prints("<"); - sys·printint(nelem(algarray)); - prints("\n"); - - throw("sys·newmap: key/val algorithm out of range"); - } - - m = mal(sizeof(*m)); - - m->len = 0; - m->keysize = keysize; - m->valsize = valsize; - m->keyalg = &algarray[keyalg]; - m->valalg = &algarray[valalg]; - m->hint = hint; - - // these calculations are compiler dependent - m->valoffset = rnd(keysize, valsize); - m->ko = rnd(sizeof(m), keysize); - m->vo = rnd(m->ko+keysize, valsize); - m->po = rnd(m->vo+valsize, 1); - - ret = m; - FLUSH(&ret); - - if(debug) { - prints("newmap: map="); - sys·printpointer(m); - prints("; keysize="); - sys·printint(keysize); - prints("; valsize="); - sys·printint(valsize); - prints("; keyalg="); - sys·printint(keyalg); - prints("; valalg="); - sys·printint(valalg); - prints("; valoffset="); - sys·printint(m->valoffset); - prints("; ko="); - sys·printint(m->ko); - prints("; vo="); - sys·printint(m->vo); - prints("; po="); - sys·printint(m->po); - prints("\n"); - } -} - -// mapaccess1(hmap *map[any]any, key any) (val any); -void -sys·mapaccess1(Hmap *m, ...) -{ - Link *l; - byte *ak, *av; - - ak = (byte*)&m + m->ko; - av = (byte*)&m + m->vo; - - for(l=m->link; l!=nil; l=l->link) { - if(m->keyalg->equal(m->keysize, ak, l->data)) { - m->valalg->copy(m->valsize, av, l->data+m->valoffset); - goto out; - } - } - - m->valalg->copy(m->valsize, av, 0); - throw("sys·mapaccess1: key not in map"); - -out: - if(debug) { - prints("sys·mapaccess1: map="); - sys·printpointer(m); - prints("; key="); - m->keyalg->print(m->keysize, ak); - prints("; val="); - m->valalg->print(m->valsize, av); - prints("\n"); - } -} - -// mapaccess2(hmap *map[any]any, key any) (val any, pres bool); -void -sys·mapaccess2(Hmap *m, ...) -{ - Link *l; - byte *ak, *av, *ap; - - ak = (byte*)&m + m->ko; - av = (byte*)&m + m->vo; - ap = (byte*)&m + m->po; - - for(l=m->link; l!=nil; l=l->link) { - if(m->keyalg->equal(m->keysize, ak, l->data)) { - *ap = true; - m->valalg->copy(m->valsize, av, l->data+m->valoffset); - goto out; - } - } - - *ap = false; - m->valalg->copy(m->valsize, av, nil); - -out: - if(debug) { - prints("sys·mapaccess2: map="); - sys·printpointer(m); - prints("; key="); - m->keyalg->print(m->keysize, ak); - prints("; val="); - m->valalg->print(m->valsize, av); - prints("; pres="); - sys·printbool(*ap); - prints("\n"); - } -} - -static void -sys·mapassign(Hmap *m, byte *ak, byte *av) -{ - Link *l; - - // mapassign(hmap *map[any]any, key any, val any); - - for(l=m->link; l!=nil; l=l->link) { - if(m->keyalg->equal(m->keysize, ak, l->data)) - goto out; - } - - l = mal((sizeof(*l)-8) + m->keysize + m->valsize); - l->link = m->link; - m->link = l; - m->keyalg->copy(m->keysize, l->data, ak); - m->len++; - -out: - m->valalg->copy(m->valsize, l->data+m->valoffset, av); - - if(debug) { - prints("mapassign: map="); - sys·printpointer(m); - prints("; key="); - m->keyalg->print(m->keysize, ak); - prints("; val="); - m->valalg->print(m->valsize, av); - prints("\n"); - } -} - -// mapassign1(hmap *map[any]any, key any, val any); -void -sys·mapassign1(Hmap *m, ...) -{ - byte *ak, *av; - - ak = (byte*)&m + m->ko; - av = (byte*)&m + m->vo; - - sys·mapassign(m, ak, av); -} - -// mapassign2(hmap *map[any]any, key any, val any, pres bool); -void -sys·mapassign2(Hmap *m, ...) -{ - Link **ll; - byte *ak, *av, *ap; - - ak = (byte*)&m + m->ko; - av = (byte*)&m + m->vo; - ap = (byte*)&m + m->po; - - if(*ap == true) { - // assign - sys·mapassign(m, ak, av); - return; - } - - // delete - for(ll=&m->link; (*ll)!=nil; ll=&(*ll)->link) { - if(m->keyalg->equal(m->keysize, ak, (*ll)->data)) { - m->valalg->copy(m->valsize, (*ll)->data+m->valoffset, nil); - (*ll) = (*ll)->link; - m->len--; - if(debug) { - prints("mapdelete (found): map="); - sys·printpointer(m); - prints("; key="); - m->keyalg->print(m->keysize, ak); - prints("\n"); - } - return; - } - } - - if(debug) { - prints("mapdelete (not found): map="); - sys·printpointer(m); - prints("; key="); - m->keyalg->print(m->keysize, ak); - prints(" *** not found\n"); - } -} diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c index c84b21092ee..ea2c4323964 100644 --- a/src/runtime/runtime.c +++ b/src/runtime/runtime.c @@ -87,6 +87,28 @@ mcpy(byte *t, byte *f, uint32 n) } } +void +mmov(byte *t, byte *f, uint32 n) +{ + if(t < f) { + while(n > 0) { + *t = *f; + t++; + f++; + n--; + } + } else { + t += n; + f += n; + while(n > 0) { + t--; + f--; + *t = *f; + n--; + } + } +} + uint32 rnd(uint32 n, uint32 m) { @@ -582,9 +604,17 @@ check(void) static uint64 memhash(uint32 s, void *a) { - USED(s, a); - prints("memhash\n"); - return 0x12345; + byte *b; + uint64 hash; + + b = a; + hash = 33054211828000289ULL; + while(s > 0) { + hash = (hash ^ *b) * 23344194077549503ULL; + b++; + s--; + } + return hash; } static uint32 @@ -644,9 +674,7 @@ memcopy(uint32 s, void *a, void *b) static uint64 stringhash(uint32 s, string *a) { - USED(s, a); - prints("stringhash\n"); - return 0x12345; + return memhash((*a)->len, (*a)->str); } static uint32 @@ -677,9 +705,7 @@ stringcopy(uint32 s, string *a, string *b) static uint64 pointerhash(uint32 s, void **a) { - USED(s, a); - prints("pointerhash\n"); - return 0x12345; + return memhash(s, *a); } static uint32 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 74afa3aef1c..5f2ad18b172 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -215,6 +215,7 @@ void throw(int8*); uint32 rnd(uint32, uint32); void prints(int8*); void mcpy(byte*, byte*, uint32); +void mmov(byte*, byte*, uint32); void* mal(uint32); uint32 cmpstring(string, string); void initsig(void);