]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: allow large map values
authorRuss Cox <rsc@golang.org>
Wed, 19 May 2010 19:57:47 +0000 (12:57 -0700)
committerRuss Cox <rsc@golang.org>
Wed, 19 May 2010 19:57:47 +0000 (12:57 -0700)
Fixes #772.

R=ken2
CC=golang-dev
https://golang.org/cl/1206043

src/pkg/runtime/hashmap.c
test/bigmap.go [new file with mode: 0644]

index 9b039121bb14e767ddc5d63b676164edda361799..eb98ab54aa27d04713814fa771d39c349597ae0c 100644 (file)
@@ -9,13 +9,13 @@
 /* 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 */
+       uint8 indirectval; /* storing pointers to values */
        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 */
@@ -361,7 +361,7 @@ hash_remove (struct hash *h, void *data, void *arg)
        }
        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);
+                       (*h->data_del) (h->datavo, arg, e->data);
                        hash_remove_n (st, e, 1);
                        h->count--;
                        return (1);
@@ -655,6 +655,13 @@ hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *da
 /// interfaces to go runtime
 //
 
+// hash requires < 256 bytes of data (key+value) stored inline.
+// Only basic types can be key - biggest is complex128 (16 bytes).
+// Leave some room to grow, just in case.
+enum {
+       MaxValsize = 256 - 64
+};
+
 static void
 donothing(uint32 s, void *a, void *b)
 {
@@ -663,6 +670,24 @@ donothing(uint32 s, void *a, void *b)
        USED(b);
 }
 
+static void
+freedata(uint32 datavo, void *a, void *b)
+{
+       void *p;
+
+       USED(a);
+       p = *(void**)((byte*)b + datavo);
+       free(p);
+}
+
+static void**
+hash_indirect(Hmap *h, void *p)
+{
+       if(h->indirectval)
+               p = *(void**)p;
+       return p;
+}      
+
 static int32   debug   = 0;
 
 // makemap(key, val *Type, hint uint32) (hmap *map[any]any);
@@ -670,7 +695,8 @@ Hmap*
 makemap(Type *key, Type *val, int64 hint)
 {
        Hmap *h;
-       int32 keyalg, valalg, keysize, valsize;
+       int32 keyalg, valalg, keysize, valsize, valsize_in_hash;
+       void (*data_del)(uint32, void*, void*);
 
        if(hint < 0 || (int32)hint != hint)
                panicstring("makemap: size out of range");
@@ -692,16 +718,24 @@ makemap(Type *key, Type *val, int64 hint)
 
        h = mal(sizeof(*h));
 
+       valsize_in_hash = valsize;
+       data_del = donothing;
+       if (valsize > MaxValsize) {
+               h->indirectval = 1;
+               data_del = freedata;
+               valsize_in_hash = sizeof(void*);
+       } 
+
        // align value inside data so that mark-sweep gc can find it.
        // might remove in the future and just assume datavo == keysize.
        h->datavo = keysize;
-       if(valsize >= sizeof(void*))
+       if(valsize_in_hash >= sizeof(void*))
                h->datavo = rnd(keysize, sizeof(void*));
 
-       hash_init(h, h->datavo+valsize,
+       hash_init(h, h->datavo+valsize_in_hash,
                algarray[keyalg].hash,
                algarray[keyalg].equal,
-               donothing,
+               data_del,
                hint);
 
        h->keysize = keysize;
@@ -753,7 +787,7 @@ mapaccess(Hmap *h, byte *ak, byte *av, bool *pres)
        res = nil;
        if(hash_lookup(h, ak, (void**)&res)) {
                *pres = true;
-               h->valalg->copy(h->valsize, av, res+h->datavo);
+               h->valalg->copy(h->valsize, av, hash_indirect(h, res+h->datavo));
        } else {
                *pres = false;
                h->valalg->copy(h->valsize, av, nil);
@@ -828,8 +862,10 @@ mapassign(Hmap *h, byte *ak, byte *av)
        }
 
        hit = hash_insert(h, ak, (void**)&res);
+       if(!hit && h->indirectval)
+               *(void**)(res+h->datavo) = mal(h->valsize);
        h->keyalg->copy(h->keysize, res, ak);
-       h->valalg->copy(h->valsize, res+h->datavo, av);
+       h->valalg->copy(h->valsize, hash_indirect(h, res+h->datavo), av);
 
        if(debug) {
                prints("mapassign: map=");
@@ -884,6 +920,17 @@ void
        }
 }
 
+void*
+hash_next_and_deref(struct hash_iter *it)
+{
+       void *p;
+
+       p = hash_next(it);
+       if(it->h->indirectval)
+               p = *(void**)p;
+       return p;
+}
+
 // mapiterinit(hmap *map[any]any, hiter *any);
 void
 ·mapiterinit(Hmap *h, struct hash_iter *it)
@@ -893,7 +940,7 @@ void
                return;
        }
        hash_iter_init(h, it);
-       it->data = hash_next(it);
+       it->data = hash_next_and_deref(it);
        if(debug) {
                prints("runtime.mapiterinit: map=");
                ·printpointer(h);
@@ -922,7 +969,7 @@ void
        if(gcwaiting)
                gosched();
 
-       it->data = hash_next(it);
+       it->data = hash_next_and_deref(it);
        if(debug) {
                prints("runtime.mapiternext: iter=");
                ·printpointer(it);
@@ -951,7 +998,7 @@ void
 
        res = it->data;
        if(res == nil)
-               throw("runtime.mapiter2: key:val nil pointer");
+               throw("runtime.mapiter1: key:val nil pointer");
 
        h->keyalg->copy(h->keysize, ak, res);
 
@@ -995,7 +1042,7 @@ void
                throw("runtime.mapiter2: key:val nil pointer");
 
        h->keyalg->copy(h->keysize, ak, res);
-       h->valalg->copy(h->valsize, av, res+h->datavo);
+       h->valalg->copy(h->valsize, av, hash_indirect(h, res+h->datavo));
 
        if(debug) {
                prints("mapiter2: iter=");
diff --git a/test/bigmap.go b/test/bigmap.go
new file mode 100644 (file)
index 0000000..843a151
--- /dev/null
@@ -0,0 +1,34 @@
+// $G $F.go && $L $F.$A && ./$A.out
+
+// 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 main
+
+func seq(x, y int) [1000]byte {
+       var r [1000]byte
+       for i := 0; i < len(r); i++ {
+               r[i] = byte(x + i*y)
+       }
+       return r
+}
+
+func cmp(x, y [1000]byte) {
+       for i := 0; i < len(x); i++ {
+               if x[i] != y[i] {
+                       panic("BUG mismatch")
+               }
+       }
+}
+
+func main() {
+       m := make(map[int][1000]byte)
+       m[1] = seq(11, 13)
+       m[2] = seq(2, 9)
+       m[3] = seq(3, 17)
+
+       cmp(m[1], seq(11, 13))
+       cmp(m[2], seq(2, 9))
+       cmp(m[3], seq(3, 17))
+}