diff --git a/src/pkg/reflect/Makefile b/src/pkg/reflect/Makefile index deaa49e2a0..32c3509032 100644 --- a/src/pkg/reflect/Makefile +++ b/src/pkg/reflect/Makefile @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. + # DO NOT EDIT. Automatically generated by gobuild. # gobuild -m >Makefile @@ -20,7 +21,7 @@ test: packages coverage: packages gotest - 6cov -g `pwd` | grep -v '_test\.go:' + 6cov -g $$(pwd) | grep -v '_test\.go:' %.$O: %.go $(GC) -I_obj $*.go diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 56f0deb6c2..af1504b8a0 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -597,3 +597,57 @@ func TestNilPtrValueSub(t *testing.T) { t.Error("NewValue((*int)(nil)).(*PtrValue).Elem() != nil"); } } + +func TestMapAccess(t *testing.T) { + m := map[string]int{ "a": 1, "b": 2 }; + mv := NewValue(m).(*MapValue); + if n := mv.Len(); n != len(m) { + t.Errorf("Len = %d, want %d", n, len(m)); + } + keys := mv.Keys(); + i := 0; + newmap := MakeMap(mv.Type().(*MapType)); + for k, v := range m { + // Check that returned Keys match keys in range. + // These aren't required to be in the same order, + // but they are in this implementation, which makes + // the test easier. + if i >= len(keys) { + t.Errorf("Missing key #%d %q", i, k); + } else if kv := keys[i].(*StringValue); kv.Get() != k { + t.Errorf("Keys[%d] = %q, want %q", i, kv.Get(), k); + } + i++; + + // Check that value lookup is correct. + vv := mv.Get(NewValue(k)); + if vi := vv.(*IntValue).Get(); vi != v { + t.Errorf("Key %q: have value %d, want %d", vi, v); + } + + // Copy into new map. + newmap.Put(NewValue(k), NewValue(v)); + } + vv := mv.Get(NewValue("not-present")); + if vv != nil { + t.Errorf("Invalid key: got non-nil value %s", valueToString(vv)); + } + + newm := newmap.Interface().(map[string]int); + if len(newm) != len(m) { + t.Errorf("length after copy: newm=%d, m=%d", newm, m); + } + + for k, v := range newm { + mv, ok := m[k]; + if mv != v { + t.Errorf("newm[%q] = %d, but m[%q] = %d, %v", k, v, k, mv, ok); + } + } + + newmap.Put(NewValue("a"), nil); + v, ok := newm["a"]; + if ok { + t.Errorf("newm[\"a\"] = %d after delete", v); + } +} diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 69e1eb3c41..11c07c5e4c 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -6,6 +6,7 @@ package reflect import ( "reflect"; + "runtime"; "unsafe"; ) @@ -58,8 +59,12 @@ type Value interface { // It is for advanced clients that also // import the "unsafe" package. Addr() uintptr; + + getAddr() addr; } +func MakeZero(typ Type) Value + type value struct { typ Type; addr addr; @@ -74,6 +79,10 @@ func (v *value) Addr() uintptr { return uintptr(v.addr); } +func (v *value) getAddr() addr { + return v.addr; +} + type InterfaceValue struct type StructValue struct @@ -742,21 +751,77 @@ func (v *MapValue) Set(x *MapValue) { *(*uintptr)(v.addr) = *(*uintptr)(x.addr); } -// Elem returns the value associated with key in the map v. +// implemented in ../pkg/runtime/reflect.cgo +func mapaccess(m, key, val *byte) bool +func mapassign(m, key, val *byte) +func maplen(m *byte) int32 +func mapiterinit(m *byte) *byte +func mapiternext(it *byte) +func mapiterkey(it *byte, key *byte) bool +func makemap(t *runtime.MapType) *byte + +// Get returns the value associated with key in the map v. // It returns nil if key is not found in the map. -func (v *MapValue) Elem(key Value) Value { - panic("unimplemented: map Elem"); +func (v *MapValue) Get(key Value) Value { + t := v.Type().(*MapType); + typesMustMatch(t.Key(), key.Type()); + m := *(**byte)(v.addr); + if m == nil { + return nil; + } + newval := MakeZero(t.Elem()); + if !mapaccess(m, (*byte)(key.getAddr()), (*byte)(newval.getAddr())) { + return nil; + } + return newval; +} + +// Put sets the value associated with key in the map v to val. +// If val is nil, Put deletes the key from map. +func (v *MapValue) Put(key, val Value) { + t := v.Type().(*MapType); + typesMustMatch(t.Key(), key.Type()); + var vaddr *byte; + if val != nil { + typesMustMatch(t.Elem(), val.Type()); + vaddr = (*byte)(val.getAddr()); + } + m := *(**byte)(v.addr); + mapassign(m, (*byte)(key.getAddr()), vaddr); } // Len returns the number of keys in the map v. func (v *MapValue) Len() int { - panic("unimplemented: map Len"); + m := *(**byte)(v.addr); + if m == nil { + return 0; + } + return int(maplen(m)); } // Keys returns a slice containing all the keys present in the map, // in unspecified order. func (v *MapValue) Keys() []Value { - panic("unimplemented: map Keys"); + tk := v.Type().(*MapType).Key(); + m := *(**byte)(v.addr); + it := mapiterinit(m); + a := make([]Value, maplen(m)); + var i int; + for i = 0; i < len(a); i++ { + k := MakeZero(tk); + if !mapiterkey(it, (*byte)(k.getAddr())) { + break; + } + a[i] = k; + mapiternext(it); + } + return a[0:i]; +} + +func MakeMap(typ *MapType) *MapValue { + v := MakeZero(typ).(*MapValue); + *(**byte)(v.addr) = makemap((*runtime.MapType)(unsafe.Pointer(typ))); + return v; } /* @@ -946,7 +1011,7 @@ func newFuncValue(typ Type, addr addr) *FuncValue { return newValue(typ, addr, true).(*FuncValue); } -// MakeZeroValue returns a zero Value for the specified Type. +// MakeZero returns a zero Value for the specified Type. func MakeZero(typ Type) Value { // TODO: this will have to move into // the runtime proper in order to play nicely diff --git a/src/pkg/runtime/Makefile b/src/pkg/runtime/Makefile index f3eb4046a0..f9f40baddb 100644 --- a/src/pkg/runtime/Makefile +++ b/src/pkg/runtime/Makefile @@ -60,6 +60,7 @@ OFILES=\ msize.$O\ print.$O\ proc.$O\ + reflect.$O\ rune.$O\ runtime.$O\ rt0.$O\ diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 1c8dd09cce..91be38443a 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -662,16 +662,14 @@ donothing(uint32 s, void *a, void *b) 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* +makemap(uint32 keysize, uint32 valsize, + uint32 keyalg, uint32 valalg, uint32 hint) { Hmap *h; @@ -721,13 +719,39 @@ sys·newmap(uint32 keysize, uint32 valsize, h->vo2 = rnd(h->ko2+keysize, valsize); h->po2 = rnd(h->vo2+valsize, 1); - ret = h; - FLUSH(&ret); - if(debug) { printf("newmap: map=%p; keysize=%d; valsize=%d; keyalg=%d; valalg=%d; offsets=%d,%d; %d,%d,%d; %d,%d,%d\n", h, keysize, valsize, keyalg, valalg, h->ko0, h->vo0, h->ko1, h->vo1, h->po1, h->ko2, h->vo2, h->po2); } + + return h; +} + +// 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) +{ + ret = makemap(keysize, valsize, keyalg, valalg, hint); + FLUSH(&ret); +} + +void +mapaccess(Hmap *h, byte *ak, byte *av, bool *pres) +{ + byte *res; + + res = nil; + if(hash_lookup(h, ak, (void**)&res)) { + *pres = true; + h->valalg->copy(h->valsize, av, res+h->datavo); + } else { + *pres = false; + h->valalg->copy(h->valsize, av, nil); + } } // mapaccess1(hmap *map[any]any, key any) (val any); @@ -735,17 +759,14 @@ void sys·mapaccess1(Hmap *h, ...) { byte *ak, *av; - byte *res; - int32 hit; + bool pres; ak = (byte*)&h + h->ko1; av = (byte*)&h + h->vo1; - res = nil; - hit = hash_lookup(h, ak, (void**)&res); - if(!hit) + mapaccess(h, ak, av, &pres); + if(!pres) throw("sys·mapaccess1: key not in map"); - h->valalg->copy(h->valsize, av, res+h->datavo); if(debug) { prints("sys·mapaccess1: map="); @@ -754,10 +775,8 @@ sys·mapaccess1(Hmap *h, ...) 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(pres); prints("\n"); } } @@ -767,22 +786,12 @@ void sys·mapaccess2(Hmap *h, ...) { byte *ak, *av, *ap; - byte *res; - int32 hit; ak = (byte*)&h + h->ko1; av = (byte*)&h + h->vo1; ap = (byte*)&h + h->po1; - 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->datavo); - } + mapaccess(h, ak, av, ap); if(debug) { prints("sys·mapaccess2: map="); @@ -791,23 +800,24 @@ sys·mapaccess2(Hmap *h, ...) 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 +void mapassign(Hmap *h, byte *ak, byte *av) { byte *res; int32 hit; res = nil; + if(av == nil) { + hash_remove(h, ak, (void**)&res); + return; + } + hit = hash_insert(h, ak, (void**)&res); h->keyalg->copy(h->keysize, res, ak); h->valalg->copy(h->valsize, res+h->datavo, av); @@ -844,31 +854,21 @@ void sys·mapassign2(Hmap *h, ...) { byte *ak, *av, *ap; - byte *res; - int32 hit; ak = (byte*)&h + h->ko2; av = (byte*)&h + h->vo2; ap = (byte*)&h + h->po2; - if(*ap == true) { - // assign - mapassign(h, ak, av); - return; - } + if(*ap == false) + av = nil; // delete - // delete - hit = hash_remove(h, ak, (void**)&res); + mapassign(h, ak, av); 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"); } } @@ -894,6 +894,16 @@ sys·mapiterinit(Hmap *h, struct hash_iter *it) } } +struct hash_iter* +mapiterinit(Hmap *h) +{ + struct hash_iter *it; + + it = mal(sizeof *it); + sys·mapiterinit(h, it); + return it; +} + // mapiternext(hiter *any); void sys·mapiternext(struct hash_iter *it) @@ -908,6 +918,12 @@ sys·mapiternext(struct hash_iter *it) } } +void +mapiternext(struct hash_iter *it) +{ + sys·mapiternext(it); +} + // mapiter1(hiter *any) (key any); void sys·mapiter1(struct hash_iter *it, ...) @@ -933,6 +949,20 @@ sys·mapiter1(struct hash_iter *it, ...) } } +bool +mapiterkey(struct hash_iter *it, void *ak) +{ + Hmap *h; + byte *res; + + h = it->h; + res = it->data; + if(res == nil) + return false; + h->keyalg->copy(h->keysize, ak, res); + return true; +} + // mapiter2(hiter *any) (key any, val any); void sys·mapiter2(struct hash_iter *it, ...) diff --git a/src/pkg/runtime/reflect.cgo b/src/pkg/runtime/reflect.cgo new file mode 100644 index 0000000000..da74195092 --- /dev/null +++ b/src/pkg/runtime/reflect.cgo @@ -0,0 +1,51 @@ +// 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 reflect +#include "runtime.h" +#include "type.h" + +/* + * Go wrappers around the C functions near the bottom of hashmap.c + * There's no recursion here even though it looks like there is: + * the names after func are in the reflect package name space + * but the names in the C bodies are in the standard C name space. + */ + +func mapaccess(map *byte, key *byte, val *byte) (pres bool) { + mapaccess((Hmap*)map, key, val, &pres); +} + +func mapassign(map *byte, key *byte, val *byte) { + mapassign((Hmap*)map, key, val); +} + +func maplen(map *byte) (len int32) { + // length is first word of map + len = *(uint32*)map; +} + +func mapiterinit(map *byte) (it *byte) { + it = (byte*)mapiterinit((Hmap*)map); +} + +func mapiternext(it *byte) { + mapiternext((struct hash_iter*)it); +} + +func mapiterkey(it *byte, key *byte) (ok bool) { + ok = mapiterkey((struct hash_iter*)it, key); +} + +func makemap(typ *byte) (map *byte) { + MapType *t; + + // typ is a *runtime.MapType, but the MapType + // defined in type.h includes an interface value header + // in front of the raw MapType. the -2 below backs up + // to the interface value header. + t = (MapType*)((void**)typ - 2); + + map = (byte*)makemap(t->key->size, t->elem->size, t->key->alg, t->elem->alg, 0); +} diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 26dfe70167..ee2f9820af 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -60,6 +60,7 @@ typedef struct Itab Itab; typedef struct Eface Eface; typedef struct Type Type; typedef struct Defer Defer; +typedef struct hash Hmap; /* * per cpu declaration @@ -457,3 +458,10 @@ float64 ldexp(float64 d, int32 e); float64 modf(float64 d, float64 *ip); void semacquire(uint32*); void semrelease(uint32*); +void mapassign(Hmap*, byte*, byte*); +void mapaccess(Hmap*, byte*, byte*, bool*); +struct hash_iter* mapiterinit(Hmap*); +void mapiternext(struct hash_iter*); +bool mapiterkey(struct hash_iter*, void*); +void mapiterkeyvalue(struct hash_iter*, void*, void*); +Hmap* makemap(uint32, uint32, uint32, uint32, uint32); diff --git a/src/pkg/runtime/type.h b/src/pkg/runtime/type.h index 3b49f24c56..21c1dd7b8c 100644 --- a/src/pkg/runtime/type.h +++ b/src/pkg/runtime/type.h @@ -11,6 +11,7 @@ typedef struct UncommonType UncommonType; typedef struct InterfaceType InterfaceType; typedef struct Method Method; typedef struct IMethod IMethod; +typedef struct MapType MapType; struct CommonType { @@ -63,3 +64,10 @@ struct InterfaceType Array mhdr; IMethod m[]; }; + +struct MapType +{ + Type; + Type *key; + Type *elem; +};