mirror of
https://github.com/golang/go
synced 2024-11-25 04:47:57 -07:00
runtime: allow large map values
Fixes #772. R=ken2 CC=golang-dev https://golang.org/cl/1206043
This commit is contained in:
parent
a688eb6ee4
commit
b198c8612e
@ -9,13 +9,13 @@
|
|||||||
/* Return a pointer to the struct/union of type "type"
|
/* Return a pointer to the struct/union of type "type"
|
||||||
whose "field" field is addressed by pointer "p". */
|
whose "field" field is addressed by pointer "p". */
|
||||||
|
|
||||||
|
|
||||||
struct hash { /* a hash table; initialize with hash_init() */
|
struct hash { /* a hash table; initialize with hash_init() */
|
||||||
uint32 count; /* elements in table - must be first */
|
uint32 count; /* elements in table - must be first */
|
||||||
|
|
||||||
uint8 datasize; /* amount of data to store in entry */
|
uint8 datasize; /* amount of data to store in entry */
|
||||||
uint8 max_power; /* max power of 2 to create sub-tables */
|
uint8 max_power; /* max power of 2 to create sub-tables */
|
||||||
uint8 max_probes; /* max entries to probe before rehashing */
|
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 */
|
int32 changes; /* inc'ed whenever a subtable is created/grown */
|
||||||
hash_hash_t (*data_hash) (uint32, void *a); /* return hash of *a */
|
hash_hash_t (*data_hash) (uint32, void *a); /* return hash of *a */
|
||||||
uint32 (*data_eq) (uint32, void *a, void *b); /* return whether *a == *b */
|
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) {
|
while (e != end_e && ((e_hash = e->hash) ^ hash) < HASH_SUBHASH) {
|
||||||
if (HASH_DATA_EQ (h, data, e->data)) { /* a match */
|
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);
|
hash_remove_n (st, e, 1);
|
||||||
h->count--;
|
h->count--;
|
||||||
return (1);
|
return (1);
|
||||||
@ -655,6 +655,13 @@ hash_visit (struct hash *h, void (*data_visit) (void *arg, int32 level, void *da
|
|||||||
/// interfaces to go runtime
|
/// 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
|
static void
|
||||||
donothing(uint32 s, void *a, void *b)
|
donothing(uint32 s, void *a, void *b)
|
||||||
{
|
{
|
||||||
@ -663,6 +670,24 @@ donothing(uint32 s, void *a, void *b)
|
|||||||
USED(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;
|
static int32 debug = 0;
|
||||||
|
|
||||||
// makemap(key, val *Type, hint uint32) (hmap *map[any]any);
|
// makemap(key, val *Type, hint uint32) (hmap *map[any]any);
|
||||||
@ -670,7 +695,8 @@ Hmap*
|
|||||||
makemap(Type *key, Type *val, int64 hint)
|
makemap(Type *key, Type *val, int64 hint)
|
||||||
{
|
{
|
||||||
Hmap *h;
|
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)
|
if(hint < 0 || (int32)hint != hint)
|
||||||
panicstring("makemap: size out of range");
|
panicstring("makemap: size out of range");
|
||||||
@ -692,16 +718,24 @@ makemap(Type *key, Type *val, int64 hint)
|
|||||||
|
|
||||||
h = mal(sizeof(*h));
|
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.
|
// align value inside data so that mark-sweep gc can find it.
|
||||||
// might remove in the future and just assume datavo == keysize.
|
// might remove in the future and just assume datavo == keysize.
|
||||||
h->datavo = keysize;
|
h->datavo = keysize;
|
||||||
if(valsize >= sizeof(void*))
|
if(valsize_in_hash >= sizeof(void*))
|
||||||
h->datavo = rnd(keysize, 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].hash,
|
||||||
algarray[keyalg].equal,
|
algarray[keyalg].equal,
|
||||||
donothing,
|
data_del,
|
||||||
hint);
|
hint);
|
||||||
|
|
||||||
h->keysize = keysize;
|
h->keysize = keysize;
|
||||||
@ -753,7 +787,7 @@ mapaccess(Hmap *h, byte *ak, byte *av, bool *pres)
|
|||||||
res = nil;
|
res = nil;
|
||||||
if(hash_lookup(h, ak, (void**)&res)) {
|
if(hash_lookup(h, ak, (void**)&res)) {
|
||||||
*pres = true;
|
*pres = true;
|
||||||
h->valalg->copy(h->valsize, av, res+h->datavo);
|
h->valalg->copy(h->valsize, av, hash_indirect(h, res+h->datavo));
|
||||||
} else {
|
} else {
|
||||||
*pres = false;
|
*pres = false;
|
||||||
h->valalg->copy(h->valsize, av, nil);
|
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);
|
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->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) {
|
if(debug) {
|
||||||
prints("mapassign: map=");
|
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);
|
// mapiterinit(hmap *map[any]any, hiter *any);
|
||||||
void
|
void
|
||||||
·mapiterinit(Hmap *h, struct hash_iter *it)
|
·mapiterinit(Hmap *h, struct hash_iter *it)
|
||||||
@ -893,7 +940,7 @@ void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
hash_iter_init(h, it);
|
hash_iter_init(h, it);
|
||||||
it->data = hash_next(it);
|
it->data = hash_next_and_deref(it);
|
||||||
if(debug) {
|
if(debug) {
|
||||||
prints("runtime.mapiterinit: map=");
|
prints("runtime.mapiterinit: map=");
|
||||||
·printpointer(h);
|
·printpointer(h);
|
||||||
@ -922,7 +969,7 @@ void
|
|||||||
if(gcwaiting)
|
if(gcwaiting)
|
||||||
gosched();
|
gosched();
|
||||||
|
|
||||||
it->data = hash_next(it);
|
it->data = hash_next_and_deref(it);
|
||||||
if(debug) {
|
if(debug) {
|
||||||
prints("runtime.mapiternext: iter=");
|
prints("runtime.mapiternext: iter=");
|
||||||
·printpointer(it);
|
·printpointer(it);
|
||||||
@ -951,7 +998,7 @@ void
|
|||||||
|
|
||||||
res = it->data;
|
res = it->data;
|
||||||
if(res == nil)
|
if(res == nil)
|
||||||
throw("runtime.mapiter2: key:val nil pointer");
|
throw("runtime.mapiter1: key:val nil pointer");
|
||||||
|
|
||||||
h->keyalg->copy(h->keysize, ak, res);
|
h->keyalg->copy(h->keysize, ak, res);
|
||||||
|
|
||||||
@ -995,7 +1042,7 @@ void
|
|||||||
throw("runtime.mapiter2: key:val nil pointer");
|
throw("runtime.mapiter2: key:val nil pointer");
|
||||||
|
|
||||||
h->keyalg->copy(h->keysize, ak, res);
|
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) {
|
if(debug) {
|
||||||
prints("mapiter2: iter=");
|
prints("mapiter2: iter=");
|
||||||
|
34
test/bigmap.go
Normal file
34
test/bigmap.go
Normal file
@ -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))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user