diff --git a/src/reflect/type.go b/src/reflect/type.go index 1f077609..4b486459 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -785,6 +785,29 @@ func (t *rawType) Comparable() bool { } } +// isbinary() returns if the hashmapAlgorithmBinary functions can be used on this type +func (t *rawType) isBinary() bool { + switch t.Kind() { + case Bool, Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return true + case Float32, Float64, Complex64, Complex128: + return true + case Pointer: + return true + case Array: + return t.elem().isBinary() + case Struct: + numField := t.NumField() + for i := 0; i < numField; i++ { + if !t.rawField(i).Type.isBinary() { + return false + } + } + return true + } + return false +} + func (t rawType) ChanDir() ChanDir { panic("unimplemented: (reflect.Type).ChanDir()") } diff --git a/src/reflect/value.go b/src/reflect/value.go index 32898f0d..2759d804 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -644,7 +644,46 @@ func (v Value) MapKeys() []Value { panic("unimplemented: (reflect.Value).MapKeys()") } +//go:linkname hashmapStringGet runtime.hashmapStringGetUnsafePointer +func hashmapStringGet(m unsafe.Pointer, key string, value unsafe.Pointer, valueSize uintptr) bool + +//go:linkname hashmapBinaryGet runtime.hashmapBinaryGetUnsafePointer +func hashmapBinaryGet(m unsafe.Pointer, key, value unsafe.Pointer, valueSize uintptr) bool + func (v Value) MapIndex(key Value) Value { + if v.Kind() != Map { + panic(&ValueError{Method: "MapIndex", Kind: v.Kind()}) + } + + // compare key type with actual key type of map + if key.typecode != v.typecode.key() { + // type error? + panic("reflect.Value.MapIndex: incompatible types for key") + } + + elemType := v.typecode.Elem() + elem := New(elemType) + + if key.Kind() == String { + if ok := hashmapStringGet(v.pointer(), *(*string)(key.value), elem.value, elemType.Size()); !ok { + return Value{} + } + return elem.Elem() + } else if key.typecode.isBinary() { + var keyptr unsafe.Pointer + if key.isIndirect() || key.typecode.Size() > unsafe.Sizeof(uintptr(0)) { + keyptr = key.value + } else { + keyptr = unsafe.Pointer(&key.value) + } + //TODO(dgryski): zero out padding bytes in key, if any + if ok := hashmapBinaryGet(v.pointer(), keyptr, elem.value, elemType.Size()); !ok { + return Value{} + } + return elem.Elem() + } + + // TODO(dgryski): Add other map types. For now, just string and binary types are supported. panic("unimplemented: (reflect.Value).MapIndex()") } diff --git a/src/reflect/value_test.go b/src/reflect/value_test.go index d3d0aa0a..f78f542f 100644 --- a/src/reflect/value_test.go +++ b/src/reflect/value_test.go @@ -44,4 +44,13 @@ func TestMap(t *testing.T) { if got, want := mtyp.Elem().Kind().String(), "int"; got != want { t.Errorf("m.Elem().String()=%q, want %q", got, want) } + + m["foo"] = 2 + + mref := ValueOf(m) + two := mref.MapIndex(ValueOf("foo")) + + if got, want := two.Interface().(int), 2; got != want { + t.Errorf("MapIndex(`foo`)=%v, want %v", got, want) + } }