summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/hashmap.goc
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
commitf154da9e12608589e8d5f0508f908a0c3e88a1bb (patch)
treef8255d51e10c6f1e0ed69702200b966c9556a431 /src/pkg/runtime/hashmap.goc
parent8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff)
downloadgolang-upstream/1.4.tar.gz
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/pkg/runtime/hashmap.goc')
-rw-r--r--src/pkg/runtime/hashmap.goc1078
1 files changed, 0 insertions, 1078 deletions
diff --git a/src/pkg/runtime/hashmap.goc b/src/pkg/runtime/hashmap.goc
deleted file mode 100644
index 3327bed65..000000000
--- a/src/pkg/runtime/hashmap.goc
+++ /dev/null
@@ -1,1078 +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.
-
-package runtime
-#include "runtime.h"
-#include "arch_GOARCH.h"
-#include "malloc.h"
-#include "type.h"
-#include "race.h"
-#include "hashmap.h"
-#include "typekind.h"
-#include "../../cmd/ld/textflag.h"
-
-enum
-{
- docheck = 0, // check invariants before and after every op. Slow!!!
- debug = 0, // print every operation
- checkgc = 0 || docheck, // check interaction of mallocgc() with the garbage collector
-};
-static void
-check(MapType *t, Hmap *h)
-{
- uintptr bucket, oldbucket;
- Bucket *b;
- uintptr i;
- uintptr hash;
- uintgo cnt;
- uint8 top;
- bool eq;
- byte *k, *v;
-
- cnt = 0;
-
- // check buckets
- for(bucket = 0; bucket < (uintptr)1 << h->B; bucket++) {
- for(b = (Bucket*)(h->buckets + bucket * h->bucketsize); b != nil; b = b->overflow) {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] == Empty)
- continue;
- if(b->tophash[i] > Empty && b->tophash[i] < MinTopHash)
- runtime·throw("evacuated cell in buckets");
- cnt++;
- t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(!eq)
- continue; // NaN!
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, IK(h, k));
- top = hash >> (8*sizeof(uintptr) - 8);
- if(top < MinTopHash)
- top += MinTopHash;
- if(top != b->tophash[i])
- runtime·throw("bad hash");
- }
- }
- }
-
- // check oldbuckets
- if(h->oldbuckets != nil) {
- for(oldbucket = 0; oldbucket < (uintptr)1 << (h->B - 1); oldbucket++) {
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- for(; b != nil; b = b->overflow) {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] < MinTopHash)
- continue;
- if(oldbucket < h->nevacuate)
- runtime·throw("unevacuated entry in an evacuated bucket");
- cnt++;
- t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(!eq)
- continue; // NaN!
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, IK(h, k));
- top = hash >> (8*sizeof(uintptr) - 8);
- if(top < MinTopHash)
- top += MinTopHash;
- if(top != b->tophash[i])
- runtime·throw("bad hash (old)");
- }
- }
- }
- }
-
- if(cnt != h->count) {
- runtime·printf("%D %D\n", (uint64)cnt, (uint64)h->count);
- runtime·throw("entries missing");
- }
-}
-
-static void
-hash_init(MapType *t, Hmap *h, uint32 hint)
-{
- uint8 B;
- byte *buckets;
- uintptr keysize, valuesize, bucketsize;
- uint8 flags;
-
- flags = 0;
-
- // figure out how big we have to make everything
- keysize = t->key->size;
- if(keysize > MAXKEYSIZE) {
- flags |= IndirectKey;
- keysize = sizeof(byte*);
- }
- valuesize = t->elem->size;
- if(valuesize > MAXVALUESIZE) {
- flags |= IndirectValue;
- valuesize = sizeof(byte*);
- }
- bucketsize = offsetof(Bucket, data[0]) + (keysize + valuesize) * BUCKETSIZE;
- if(bucketsize != t->bucket->size) {
- runtime·printf("runtime: bucketsize=%p but t->bucket->size=%p; t=%S\n", bucketsize, t->bucket->size, *t->string);
- runtime·throw("bucketsize wrong");
- }
-
- // invariants we depend on. We should probably check these at compile time
- // somewhere, but for now we'll do it here.
- if(t->key->align > BUCKETSIZE)
- runtime·throw("key align too big");
- if(t->elem->align > BUCKETSIZE)
- runtime·throw("value align too big");
- if(t->key->size % t->key->align != 0)
- runtime·throw("key size not a multiple of key align");
- if(t->elem->size % t->elem->align != 0)
- runtime·throw("value size not a multiple of value align");
- if(BUCKETSIZE < 8)
- runtime·throw("bucketsize too small for proper alignment");
- if((offsetof(Bucket, data[0]) & (t->key->align-1)) != 0)
- runtime·throw("need padding in bucket (key)");
- if((offsetof(Bucket, data[0]) & (t->elem->align-1)) != 0)
- runtime·throw("need padding in bucket (value)");
-
- // find size parameter which will hold the requested # of elements
- B = 0;
- while(hint > BUCKETSIZE && hint > LOAD * ((uintptr)1 << B))
- B++;
-
- // allocate initial hash table
- // If hint is large zeroing this memory could take a while.
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- if(B == 0) {
- // done lazily later.
- buckets = nil;
- } else {
- buckets = runtime·cnewarray(t->bucket, (uintptr)1 << B);
- }
-
- // initialize Hmap
- h->count = 0;
- h->B = B;
- h->flags = flags;
- h->keysize = keysize;
- h->valuesize = valuesize;
- h->bucketsize = bucketsize;
- h->hash0 = runtime·fastrand1();
- h->buckets = buckets;
- h->oldbuckets = nil;
- h->nevacuate = 0;
- if(docheck)
- check(t, h);
-}
-
-// Moves entries in oldbuckets[i] to buckets[i] and buckets[i+2^k].
-// We leave the original bucket intact, except for marking the topbits
-// entries as evacuated, so that iterators can still iterate through the old buckets.
-static void
-evacuate(MapType *t, Hmap *h, uintptr oldbucket)
-{
- Bucket *b;
- Bucket *x, *y;
- Bucket *newx, *newy;
- uintptr xi, yi;
- uintptr newbit;
- uintptr hash;
- uintptr i;
- byte *k, *v;
- byte *xk, *yk, *xv, *yv;
- uint8 top;
- bool eq;
-
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- newbit = (uintptr)1 << (h->B - 1);
-
- if(!evacuated(b)) {
- // TODO: reuse overflow buckets instead of using new ones, if there
- // is no iterator using the old buckets. (If !OldIterator.)
-
- x = (Bucket*)(h->buckets + oldbucket * h->bucketsize);
- y = (Bucket*)(h->buckets + (oldbucket + newbit) * h->bucketsize);
- xi = 0;
- yi = 0;
- xk = (byte*)x->data;
- yk = (byte*)y->data;
- xv = xk + h->keysize * BUCKETSIZE;
- yv = yk + h->keysize * BUCKETSIZE;
- for(; b != nil; b = b->overflow) {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- top = b->tophash[i];
- if(top == Empty) {
- b->tophash[i] = EvacuatedEmpty;
- continue;
- }
- if(top < MinTopHash)
- runtime·throw("bad state");
-
- // Compute hash to make our evacuation decision (whether we need
- // to send this key/value to bucket x or bucket y).
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, IK(h, k));
- if((h->flags & Iterator) != 0) {
- t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(!eq) {
- // If key != key (NaNs), then the hash could be (and probably
- // will be) entirely different from the old hash. Moreover,
- // it isn't reproducible. Reproducibility is required in the
- // presence of iterators, as our evacuation decision must
- // match whatever decision the iterator made.
- // Fortunately, we have the freedom to send these keys either
- // way. Also, tophash is meaningless for these kinds of keys.
- // We let the low bit of tophash drive the evacuation decision.
- // We recompute a new random tophash for the next level so
- // these keys will get evenly distributed across all buckets
- // after multiple grows.
- if((top & 1) != 0)
- hash |= newbit;
- else
- hash &= ~newbit;
- top = hash >> (8*sizeof(uintptr)-8);
- if(top < MinTopHash)
- top += MinTopHash;
- }
- }
-
- if((hash & newbit) == 0) {
- b->tophash[i] = EvacuatedX;
- if(xi == BUCKETSIZE) {
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newx = runtime·cnew(t->bucket);
- x->overflow = newx;
- x = newx;
- xi = 0;
- xk = (byte*)x->data;
- xv = xk + h->keysize * BUCKETSIZE;
- }
- x->tophash[xi] = top;
- if((h->flags & IndirectKey) != 0) {
- *(byte**)xk = *(byte**)k; // copy pointer
- } else {
- t->key->alg->copy(t->key->size, xk, k); // copy value
- }
- if((h->flags & IndirectValue) != 0) {
- *(byte**)xv = *(byte**)v;
- } else {
- t->elem->alg->copy(t->elem->size, xv, v);
- }
- xi++;
- xk += h->keysize;
- xv += h->valuesize;
- } else {
- b->tophash[i] = EvacuatedY;
- if(yi == BUCKETSIZE) {
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newy = runtime·cnew(t->bucket);
- y->overflow = newy;
- y = newy;
- yi = 0;
- yk = (byte*)y->data;
- yv = yk + h->keysize * BUCKETSIZE;
- }
- y->tophash[yi] = top;
- if((h->flags & IndirectKey) != 0) {
- *(byte**)yk = *(byte**)k;
- } else {
- t->key->alg->copy(t->key->size, yk, k);
- }
- if((h->flags & IndirectValue) != 0) {
- *(byte**)yv = *(byte**)v;
- } else {
- t->elem->alg->copy(t->elem->size, yv, v);
- }
- yi++;
- yk += h->keysize;
- yv += h->valuesize;
- }
- }
- }
-
- // Unlink the overflow buckets & clear key/value to help GC.
- if((h->flags & OldIterator) == 0) {
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- b->overflow = nil;
- runtime·memclr((byte*)b->data, h->bucketsize - offsetof(Bucket, data[0]));
- }
- }
-
- // Advance evacuation mark
- if(oldbucket == h->nevacuate) {
- h->nevacuate = oldbucket + 1;
- if(oldbucket + 1 == newbit) // newbit == # of oldbuckets
- // Growing is all done. Free old main bucket array.
- h->oldbuckets = nil;
- }
- if(docheck)
- check(t, h);
-}
-
-static void
-grow_work(MapType *t, Hmap *h, uintptr bucket)
-{
- uintptr noldbuckets;
-
- noldbuckets = (uintptr)1 << (h->B - 1);
-
- // make sure we evacuate the oldbucket corresponding
- // to the bucket we're about to use
- evacuate(t, h, bucket & (noldbuckets - 1));
-
- // evacuate one more oldbucket to make progress on growing
- if(h->oldbuckets != nil)
- evacuate(t, h, h->nevacuate);
-}
-
-static void
-hash_grow(MapType *t, Hmap *h)
-{
- byte *old_buckets;
- byte *new_buckets;
- uint8 flags;
-
- // allocate a bigger hash table
- if(h->oldbuckets != nil)
- runtime·throw("evacuation not done in time");
- old_buckets = h->buckets;
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- new_buckets = runtime·cnewarray(t->bucket, (uintptr)1 << (h->B + 1));
- flags = (h->flags & ~(Iterator | OldIterator));
- if((h->flags & Iterator) != 0)
- flags |= OldIterator;
-
- // commit the grow (atomic wrt gc)
- h->B++;
- h->flags = flags;
- h->oldbuckets = old_buckets;
- h->buckets = new_buckets;
- h->nevacuate = 0;
-
- // the actual copying of the hash table data is done incrementally
- // by grow_work() and evacuate().
- if(docheck)
- check(t, h);
-}
-
-// returns ptr to value associated with key *keyp, or nil if none.
-// if it returns non-nil, updates *keyp to point to the currently stored key.
-static byte*
-hash_lookup(MapType *t, Hmap *h, byte **keyp)
-{
- void *key;
- uintptr hash;
- uintptr bucket, oldbucket;
- Bucket *b;
- uint8 top;
- uintptr i;
- bool eq;
- byte *k, *k2, *v;
-
- key = *keyp;
- if(docheck)
- check(t, h);
- if(h->count == 0)
- return nil;
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, key);
- bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil) {
- oldbucket = bucket & (((uintptr)1 << (h->B - 1)) - 1);
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- if(evacuated(b)) {
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
- }
- } else {
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
- }
- top = hash >> (sizeof(uintptr)*8 - 8);
- if(top < MinTopHash)
- top += MinTopHash;
- do {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] == top) {
- k2 = IK(h, k);
- t->key->alg->equal(&eq, t->key->size, key, k2);
- if(eq) {
- *keyp = k2;
- return IV(h, v);
- }
- }
- }
- b = b->overflow;
- } while(b != nil);
- return nil;
-}
-
-// Specialized versions of mapaccess1 for specific types.
-// See ./hashmap_fast.c and ../../cmd/gc/walk.c.
-#define HASH_LOOKUP1 runtime·mapaccess1_fast32
-#define HASH_LOOKUP2 runtime·mapaccess2_fast32
-#define KEYTYPE uint32
-#define HASHFUNC runtime·algarray[AMEM32].hash
-#define FASTKEY(x) true
-#define QUICK_NE(x,y) ((x) != (y))
-#define QUICK_EQ(x,y) true
-#define SLOW_EQ(x,y) true
-#define MAYBE_EQ(x,y) true
-#include "hashmap_fast.c"
-
-#undef HASH_LOOKUP1
-#undef HASH_LOOKUP2
-#undef KEYTYPE
-#undef HASHFUNC
-#undef FASTKEY
-#undef QUICK_NE
-#undef QUICK_EQ
-#undef SLOW_EQ
-#undef MAYBE_EQ
-
-#define HASH_LOOKUP1 runtime·mapaccess1_fast64
-#define HASH_LOOKUP2 runtime·mapaccess2_fast64
-#define KEYTYPE uint64
-#define HASHFUNC runtime·algarray[AMEM64].hash
-#define FASTKEY(x) true
-#define QUICK_NE(x,y) ((x) != (y))
-#define QUICK_EQ(x,y) true
-#define SLOW_EQ(x,y) true
-#define MAYBE_EQ(x,y) true
-#include "hashmap_fast.c"
-
-#undef HASH_LOOKUP1
-#undef HASH_LOOKUP2
-#undef KEYTYPE
-#undef HASHFUNC
-#undef FASTKEY
-#undef QUICK_NE
-#undef QUICK_EQ
-#undef SLOW_EQ
-#undef MAYBE_EQ
-
-#ifdef GOARCH_amd64
-#define CHECKTYPE uint64
-#endif
-#ifdef GOARCH_amd64p32
-#define CHECKTYPE uint32
-#endif
-#ifdef GOARCH_386
-#define CHECKTYPE uint32
-#endif
-#ifdef GOARCH_arm
-// can't use uint32 on arm because our loads aren't aligned.
-// TODO: use uint32 for arm v6+?
-#define CHECKTYPE uint8
-#endif
-
-#define HASH_LOOKUP1 runtime·mapaccess1_faststr
-#define HASH_LOOKUP2 runtime·mapaccess2_faststr
-#define KEYTYPE String
-#define HASHFUNC runtime·algarray[ASTRING].hash
-#define FASTKEY(x) ((x).len < 32)
-#define QUICK_NE(x,y) ((x).len != (y).len)
-#define QUICK_EQ(x,y) ((x).str == (y).str)
-#define SLOW_EQ(x,y) runtime·memeq((x).str, (y).str, (x).len)
-#define MAYBE_EQ(x,y) (*(CHECKTYPE*)(x).str == *(CHECKTYPE*)(y).str && *(CHECKTYPE*)((x).str + (x).len - sizeof(CHECKTYPE)) == *(CHECKTYPE*)((y).str + (x).len - sizeof(CHECKTYPE)))
-#include "hashmap_fast.c"
-
-static void
-hash_insert(MapType *t, Hmap *h, void *key, void *value)
-{
- uintptr hash;
- uintptr bucket;
- uintptr i;
- bool eq;
- Bucket *b;
- Bucket *newb;
- uint8 *inserti;
- byte *insertk, *insertv;
- uint8 top;
- byte *k, *v;
- byte *kmem, *vmem;
-
- if(docheck)
- check(t, h);
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, key);
- if(h->buckets == nil)
- h->buckets = runtime·cnewarray(t->bucket, 1);
-
- again:
- bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil)
- grow_work(t, h, bucket);
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
- top = hash >> (sizeof(uintptr)*8 - 8);
- if(top < MinTopHash)
- top += MinTopHash;
- inserti = nil;
- insertk = nil;
- insertv = nil;
- while(true) {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] != top) {
- if(b->tophash[i] == Empty && inserti == nil) {
- inserti = &b->tophash[i];
- insertk = k;
- insertv = v;
- }
- continue;
- }
- t->key->alg->equal(&eq, t->key->size, key, IK(h, k));
- if(!eq)
- continue;
- // already have a mapping for key. Update it.
- t->key->alg->copy(t->key->size, IK(h, k), key); // Need to update key for keys which are distinct but equal (e.g. +0.0 and -0.0)
- t->elem->alg->copy(t->elem->size, IV(h, v), value);
- if(docheck)
- check(t, h);
- return;
- }
- if(b->overflow == nil)
- break;
- b = b->overflow;
- }
-
- // did not find mapping for key. Allocate new cell & add entry.
- if(h->count >= LOAD * ((uintptr)1 << h->B) && h->count >= BUCKETSIZE) {
- hash_grow(t, h);
- goto again; // Growing the table invalidates everything, so try again
- }
-
- if(inserti == nil) {
- // all current buckets are full, allocate a new one.
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- newb = runtime·cnew(t->bucket);
- b->overflow = newb;
- inserti = newb->tophash;
- insertk = (byte*)newb->data;
- insertv = insertk + h->keysize * BUCKETSIZE;
- }
-
- // store new key/value at insert position
- if((h->flags & IndirectKey) != 0) {
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- kmem = runtime·cnew(t->key);
- *(byte**)insertk = kmem;
- insertk = kmem;
- }
- if((h->flags & IndirectValue) != 0) {
- if(checkgc) mstats.next_gc = mstats.heap_alloc;
- vmem = runtime·cnew(t->elem);
- *(byte**)insertv = vmem;
- insertv = vmem;
- }
- t->key->alg->copy(t->key->size, insertk, key);
- t->elem->alg->copy(t->elem->size, insertv, value);
- *inserti = top;
- h->count++;
- if(docheck)
- check(t, h);
-}
-
-static void
-hash_remove(MapType *t, Hmap *h, void *key)
-{
- uintptr hash;
- uintptr bucket;
- Bucket *b;
- uint8 top;
- uintptr i;
- byte *k, *v;
- bool eq;
-
- if(docheck)
- check(t, h);
- if(h->count == 0)
- return;
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, key);
- bucket = hash & (((uintptr)1 << h->B) - 1);
- if(h->oldbuckets != nil)
- grow_work(t, h, bucket);
- b = (Bucket*)(h->buckets + bucket * h->bucketsize);
- top = hash >> (sizeof(uintptr)*8 - 8);
- if(top < MinTopHash)
- top += MinTopHash;
- do {
- for(i = 0, k = (byte*)b->data, v = k + h->keysize * BUCKETSIZE; i < BUCKETSIZE; i++, k += h->keysize, v += h->valuesize) {
- if(b->tophash[i] != top)
- continue;
- t->key->alg->equal(&eq, t->key->size, key, IK(h, k));
- if(!eq)
- continue;
-
- if((h->flags & IndirectKey) != 0) {
- *(byte**)k = nil;
- } else {
- t->key->alg->copy(t->key->size, k, nil);
- }
- if((h->flags & IndirectValue) != 0) {
- *(byte**)v = nil;
- } else {
- t->elem->alg->copy(t->elem->size, v, nil);
- }
-
- b->tophash[i] = Empty;
- h->count--;
-
- // TODO: consolidate buckets if they are mostly empty
- // can only consolidate if there are no live iterators at this size.
- if(docheck)
- check(t, h);
- return;
- }
- b = b->overflow;
- } while(b != nil);
-}
-
-// TODO: shrink the map, the same way we grow it.
-
-// iterator state:
-// bucket: the current bucket ID
-// b: the current Bucket in the chain
-// i: the next offset to check in the current bucket
-static void
-hash_iter_init(MapType *t, Hmap *h, Hiter *it)
-{
- uint32 old;
-
- if(sizeof(Hiter) / sizeof(uintptr) != 10) {
- runtime·throw("hash_iter size incorrect"); // see ../../cmd/gc/reflect.c
- }
- it->t = t;
- it->h = h;
-
- // grab snapshot of bucket state
- it->B = h->B;
- it->buckets = h->buckets;
-
- // iterator state
- it->bucket = 0;
- it->offset = runtime·fastrand1() & (BUCKETSIZE - 1);
- it->done = false;
- it->bptr = nil;
-
- // Remember we have an iterator.
- // Can run concurrently with another hash_iter_init().
- for(;;) {
- old = h->flags;
- if((old&(Iterator|OldIterator)) == (Iterator|OldIterator))
- break;
- if(runtime·cas(&h->flags, old, old|Iterator|OldIterator))
- break;
- }
-
- if(h->buckets == nil) {
- // Empty map. Force next hash_next to exit without
- // evaluating h->bucket.
- it->done = true;
- }
-}
-
-// initializes it->key and it->value to the next key/value pair
-// in the iteration, or nil if we've reached the end.
-static void
-hash_next(Hiter *it)
-{
- Hmap *h;
- MapType *t;
- uintptr bucket, oldbucket;
- uintptr hash;
- Bucket *b;
- uintptr i, offi;
- intptr check_bucket;
- bool eq;
- byte *k, *v;
- byte *rk, *rv;
-
- h = it->h;
- t = it->t;
- bucket = it->bucket;
- b = it->bptr;
- i = it->i;
- check_bucket = it->check_bucket;
-
-next:
- if(b == nil) {
- if(it->done) {
- // end of iteration
- it->key = nil;
- it->value = nil;
- return;
- }
- if(h->oldbuckets != nil && it->B == h->B) {
- // Iterator was started in the middle of a grow, and the grow isn't done yet.
- // If the bucket we're looking at hasn't been filled in yet (i.e. the old
- // bucket hasn't been evacuated) then we need to iterate through the old
- // bucket and only return the ones that will be migrated to this bucket.
- oldbucket = bucket & (((uintptr)1 << (it->B - 1)) - 1);
- b = (Bucket*)(h->oldbuckets + oldbucket * h->bucketsize);
- if(!evacuated(b)) {
- check_bucket = bucket;
- } else {
- b = (Bucket*)(it->buckets + bucket * h->bucketsize);
- check_bucket = -1;
- }
- } else {
- b = (Bucket*)(it->buckets + bucket * h->bucketsize);
- check_bucket = -1;
- }
- bucket++;
- if(bucket == ((uintptr)1 << it->B)) {
- bucket = 0;
- it->done = true;
- }
- i = 0;
- }
- for(; i < BUCKETSIZE; i++) {
- offi = (i + it->offset) & (BUCKETSIZE - 1);
- k = (byte*)b->data + h->keysize * offi;
- v = (byte*)b->data + h->keysize * BUCKETSIZE + h->valuesize * offi;
- if(b->tophash[offi] != Empty && b->tophash[offi] != EvacuatedEmpty) {
- if(check_bucket >= 0) {
- // Special case: iterator was started during a grow and the
- // grow is not done yet. We're working on a bucket whose
- // oldbucket has not been evacuated yet. Or at least, it wasn't
- // evacuated when we started the bucket. So we're iterating
- // through the oldbucket, skipping any keys that will go
- // to the other new bucket (each oldbucket expands to two
- // buckets during a grow).
- t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(eq) {
- // If the item in the oldbucket is not destined for
- // the current new bucket in the iteration, skip it.
- hash = h->hash0;
- t->key->alg->hash(&hash, t->key->size, IK(h, k));
- if((hash & (((uintptr)1 << it->B) - 1)) != check_bucket) {
- continue;
- }
- } else {
- // Hash isn't repeatable if k != k (NaNs). We need a
- // repeatable and randomish choice of which direction
- // to send NaNs during evacuation. We'll use the low
- // bit of tophash to decide which way NaNs go.
- // NOTE: this case is why we need two evacuate tophash
- // values, evacuatedX and evacuatedY, that differ in
- // their low bit.
- if(check_bucket >> (it->B - 1) != (b->tophash[offi] & 1)) {
- continue;
- }
- }
- }
- if(b->tophash[offi] != EvacuatedX && b->tophash[offi] != EvacuatedY) {
- // this is the golden data, we can return it.
- it->key = IK(h, k);
- it->value = IV(h, v);
- } else {
- // The hash table has grown since the iterator was started.
- // The golden data for this key is now somewhere else.
- t->key->alg->equal(&eq, t->key->size, IK(h, k), IK(h, k));
- if(eq) {
- // Check the current hash table for the data.
- // This code handles the case where the key
- // has been deleted, updated, or deleted and reinserted.
- // NOTE: we need to regrab the key as it has potentially been
- // updated to an equal() but not identical key (e.g. +0.0 vs -0.0).
- rk = IK(h, k);
- rv = hash_lookup(t, it->h, &rk);
- if(rv == nil)
- continue; // key has been deleted
- it->key = rk;
- it->value = rv;
- } else {
- // if key!=key then the entry can't be deleted or
- // updated, so we can just return it. That's lucky for
- // us because when key!=key we can't look it up
- // successfully in the current table.
- it->key = IK(h, k);
- it->value = IV(h, v);
- }
- }
- it->bucket = bucket;
- it->bptr = b;
- it->i = i + 1;
- it->check_bucket = check_bucket;
- return;
- }
- }
- b = b->overflow;
- i = 0;
- goto next;
-}
-
-//
-/// interfaces to go runtime
-//
-
-func reflect·ismapkey(typ *Type) (ret bool) {
- ret = typ != nil && typ->alg->hash != runtime·nohash;
-}
-
-static Hmap*
-makemap_c(MapType *typ, int64 hint)
-{
- Hmap *h;
- Type *key;
-
- key = typ->key;
-
- if(sizeof(Hmap) > 48)
- runtime·panicstring("hmap too large");
-
- if(hint < 0 || (int32)hint != hint)
- runtime·panicstring("makemap: size out of range");
-
- if(key->alg->hash == runtime·nohash)
- runtime·throw("runtime.makemap: unsupported map key type");
-
- h = runtime·cnew(typ->hmap);
- hash_init(typ, h, hint);
-
- // these calculations are compiler dependent.
- // figure out offsets of map call arguments.
-
- if(debug) {
- runtime·printf("makemap: map=%p; keysize=%p; valsize=%p; keyalg=%p; valalg=%p\n",
- h, key->size, typ->elem->size, key->alg, typ->elem->alg);
- }
-
- return h;
-}
-
-func makemap(typ *MapType, hint int64) (ret *Hmap) {
- ret = makemap_c(typ, hint);
-}
-
-func reflect·makemap(t *MapType) (ret *Hmap) {
- ret = makemap_c(t, 0);
-}
-
-// NOTE: The returned pointer may keep the whole map live, so don't
-// hold onto it for very long.
-#pragma textflag NOSPLIT
-func mapaccess1(t *MapType, h *Hmap, key *byte) (val *byte) {
- if(raceenabled && h != nil) {
- runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess1);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), runtime·mapaccess1);
- }
- if(h == nil || h->count == 0) {
- val = t->elem->zero;
- } else {
- val = hash_lookup(t, h, &key);
- if(val == nil)
- val = t->elem->zero;
- }
-
- if(debug) {
- runtime·prints("runtime.mapaccess1: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("; val=");
- t->elem->alg->print(t->elem->size, val);
- runtime·prints("\n");
- }
-}
-
-// NOTE: The returned pointer keeps the whole map live, so don't
-// hold onto it for very long.
-#pragma textflag NOSPLIT
-func mapaccess2(t *MapType, h *Hmap, key *byte) (val *byte, pres bool) {
- if(raceenabled && h != nil) {
- runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapaccess2);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), runtime·mapaccess2);
- }
-
- if(h == nil || h->count == 0) {
- val = t->elem->zero;
- pres = false;
- } else {
- val = hash_lookup(t, h, &key);
- if(val == nil) {
- val = t->elem->zero;
- pres = false;
- } else {
- pres = true;
- }
- }
-
- if(debug) {
- runtime·prints("runtime.mapaccess2: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("; val=");
- t->elem->alg->print(t->elem->size, val);
- runtime·prints("; pres=");
- runtime·printbool(pres);
- runtime·prints("\n");
- }
-}
-
-#pragma textflag NOSPLIT
-func reflect·mapaccess(t *MapType, h *Hmap, key *byte) (val *byte) {
- if(h == nil)
- val = nil;
- else {
- if(raceenabled) {
- runtime·racereadpc(h, runtime·getcallerpc(&t), reflect·mapaccess);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), reflect·mapaccess);
- }
- val = hash_lookup(t, h, &key);
- }
-}
-
-#pragma textflag NOSPLIT
-func mapassign1(t *MapType, h *Hmap, key *byte, val *byte) {
- if(h == nil)
- runtime·panicstring("assignment to entry in nil map");
-
- if(raceenabled) {
- runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapassign1);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), runtime·mapassign1);
- runtime·racereadobjectpc(val, t->elem, runtime·getcallerpc(&t), runtime·mapassign1);
- }
-
- hash_insert(t, h, key, val);
-
- if(debug) {
- runtime·prints("mapassign1: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("; val=");
- t->elem->alg->print(t->elem->size, val);
- runtime·prints("\n");
- }
-}
-
-#pragma textflag NOSPLIT
-func mapdelete(t *MapType, h *Hmap, key *byte) {
- if(h == nil)
- return;
-
- if(raceenabled) {
- runtime·racewritepc(h, runtime·getcallerpc(&t), runtime·mapdelete);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), runtime·mapdelete);
- }
-
- hash_remove(t, h, key);
-
- if(debug) {
- runtime·prints("mapdelete: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("\n");
- }
-}
-
-#pragma textflag NOSPLIT
-func reflect·mapassign(t *MapType, h *Hmap, key *byte, val *byte) {
- if(h == nil)
- runtime·panicstring("assignment to entry in nil map");
- if(raceenabled) {
- runtime·racewritepc(h, runtime·getcallerpc(&t), reflect·mapassign);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), reflect·mapassign);
- runtime·racereadobjectpc(val, t->elem, runtime·getcallerpc(&t), reflect·mapassign);
- }
-
- hash_insert(t, h, key, val);
-
- if(debug) {
- runtime·prints("mapassign: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("; val=");
- t->elem->alg->print(t->elem->size, val);
- runtime·prints("\n");
- }
-}
-
-#pragma textflag NOSPLIT
-func reflect·mapdelete(t *MapType, h *Hmap, key *byte) {
- if(h == nil)
- return; // see bug 8051
- if(raceenabled) {
- runtime·racewritepc(h, runtime·getcallerpc(&t), reflect·mapdelete);
- runtime·racereadobjectpc(key, t->key, runtime·getcallerpc(&t), reflect·mapdelete);
- }
- hash_remove(t, h, key);
-
- if(debug) {
- runtime·prints("mapdelete: map=");
- runtime·printpointer(h);
- runtime·prints("; key=");
- t->key->alg->print(t->key->size, key);
- runtime·prints("\n");
- }
-}
-
-#pragma textflag NOSPLIT
-func mapiterinit(t *MapType, h *Hmap, it *Hiter) {
- // Clear pointer fields so garbage collector does not complain.
- it->key = nil;
- it->value = nil;
- it->t = nil;
- it->h = nil;
- it->buckets = nil;
- it->bptr = nil;
-
- if(h == nil || h->count == 0) {
- it->key = nil;
- return;
- }
- if(raceenabled)
- runtime·racereadpc(h, runtime·getcallerpc(&t), runtime·mapiterinit);
- hash_iter_init(t, h, it);
- hash_next(it);
- if(debug) {
- runtime·prints("runtime.mapiterinit: map=");
- runtime·printpointer(h);
- runtime·prints("; iter=");
- runtime·printpointer(it);
- runtime·prints("; key=");
- runtime·printpointer(it->key);
- runtime·prints("\n");
- }
-}
-
-func reflect·mapiterinit(t *MapType, h *Hmap) (it *Hiter) {
- it = runtime·mal(sizeof *it);
- runtime·mapiterinit(t, h, it);
-}
-
-#pragma textflag NOSPLIT
-func mapiternext(it *Hiter) {
- if(raceenabled)
- runtime·racereadpc(it->h, runtime·getcallerpc(&it), runtime·mapiternext);
-
- hash_next(it);
- if(debug) {
- runtime·prints("runtime.mapiternext: iter=");
- runtime·printpointer(it);
- runtime·prints("; key=");
- runtime·printpointer(it->key);
- runtime·prints("\n");
- }
-}
-
-func reflect·mapiternext(it *Hiter) {
- runtime·mapiternext(it);
-}
-
-func reflect·mapiterkey(it *Hiter) (key *byte) {
- key = it->key;
-}
-
-#pragma textflag NOSPLIT
-func reflect·maplen(h *Hmap) (len int) {
- if(h == nil)
- len = 0;
- else {
- len = h->count;
- if(raceenabled)
- runtime·racereadpc(h, runtime·getcallerpc(&h), reflect·maplen);
- }
-}
-
-// exported value for testing
-float64 runtime·hashLoad = LOAD;