From 764b6ec1aaa832650bd0c65aab749fc811a419c8 Mon Sep 17 00:00:00 2001
From: Russ Cox <rsc@golang.org>
Date: Wed, 8 Jul 2009 13:55:57 -0700
Subject: [PATCH] reflection for maps

R=r
DELTA=304  (248 added, 34 deleted, 22 changed)
OCL=31345
CL=31347
---
 src/pkg/reflect/Makefile    |   3 +-
 src/pkg/reflect/all_test.go |  54 ++++++++++++++++
 src/pkg/reflect/value.go    |  77 ++++++++++++++++++++--
 src/pkg/runtime/Makefile    |   1 +
 src/pkg/runtime/hashmap.c   | 124 ++++++++++++++++++++++--------------
 src/pkg/runtime/reflect.cgo |  51 +++++++++++++++
 src/pkg/runtime/runtime.h   |   8 +++
 src/pkg/runtime/type.h      |   8 +++
 8 files changed, 272 insertions(+), 54 deletions(-)
 create mode 100644 src/pkg/runtime/reflect.cgo

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;
+};
-- 
2.30.9