}
}
+func TestMapIterSet(t *testing.T) {
+ m := make(map[string]interface{}, len(valueTests))
+ for _, tt := range valueTests {
+ m[tt.s] = tt.i
+ }
+ v := ValueOf(m)
+
+ k := New(v.Type().Key()).Elem()
+ e := New(v.Type().Elem()).Elem()
+
+ iter := v.MapRange()
+ for iter.Next() {
+ iter.SetKey(k)
+ iter.SetValue(e)
+ want := m[k.String()]
+ got := e.Interface()
+ if got != want {
+ t.Errorf("%q: want (%T) %v, got (%T) %v", k.String(), want, want, got, got)
+ }
+ if setkey, key := valueToString(k), valueToString(iter.Key()); setkey != key {
+ t.Errorf("MapIter.Key() = %q, MapIter.SetKey() = %q", key, setkey)
+ }
+ if setval, val := valueToString(e), valueToString(iter.Value()); setval != val {
+ t.Errorf("MapIter.Value() = %q, MapIter.SetValue() = %q", val, setval)
+ }
+ }
+
+ got := int(testing.AllocsPerRun(10, func() {
+ iter := v.MapRange()
+ for iter.Next() {
+ iter.SetKey(k)
+ iter.SetValue(e)
+ }
+ }))
+ // Making a *MapIter and making an hiter both allocate.
+ // Those should be the only two allocations.
+ if got != 2 {
+ t.Errorf("wanted 2 allocs, got %d", got)
+ }
+}
+
func TestCanSetField(t *testing.T) {
type embed struct{ x, X int }
type Embed struct{ x, X int }
if it.it == nil {
panic("MapIter.Key called before Next")
}
- if mapiterkey(it.it) == nil {
+ iterkey := mapiterkey(it.it)
+ if iterkey == nil {
panic("MapIter.Key called on exhausted iterator")
}
t := (*mapType)(unsafe.Pointer(it.m.typ))
ktype := t.key
- return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), mapiterkey(it.it))
+ return copyVal(ktype, it.m.flag.ro()|flag(ktype.Kind()), iterkey)
+}
+
+// SetKey assigns dst to the key of the iterator's current map entry.
+// It is equivalent to dst.Set(it.Key()), but it avoids allocating a new Value.
+// As in Go, the key must be assignable to dst's type.
+func (it *MapIter) SetKey(dst Value) {
+ if it.it == nil {
+ panic("MapIter.SetKey called before Next")
+ }
+ iterkey := mapiterkey(it.it)
+ if iterkey == nil {
+ panic("MapIter.SetKey called on exhausted iterator")
+ }
+
+ dst.mustBeAssignable()
+ var target unsafe.Pointer
+ if dst.kind() == Interface {
+ target = dst.ptr
+ }
+
+ t := (*mapType)(unsafe.Pointer(it.m.typ))
+ ktype := t.key
+
+ key := Value{ktype, iterkey, it.m.flag | flag(ktype.Kind())}
+ key = key.assignTo("reflect.MapIter.SetKey", dst.typ, target)
+ typedmemmove(dst.typ, dst.ptr, key.ptr)
}
// Value returns the value of the iterator's current map entry.
if it.it == nil {
panic("MapIter.Value called before Next")
}
- if mapiterkey(it.it) == nil {
+ iterelem := mapiterelem(it.it)
+ if iterelem == nil {
panic("MapIter.Value called on exhausted iterator")
}
t := (*mapType)(unsafe.Pointer(it.m.typ))
vtype := t.elem
- return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), mapiterelem(it.it))
+ return copyVal(vtype, it.m.flag.ro()|flag(vtype.Kind()), iterelem)
+}
+
+// SetValue assigns dst to the value of the iterator's current map entry.
+// It is equivalent to dst.Set(it.Value()), but it avoids allocating a new Value.
+// As in Go, the value must be assignable to dst's type.
+func (it *MapIter) SetValue(dst Value) {
+ if it.it == nil {
+ panic("MapIter.SetValue called before Next")
+ }
+ iterelem := mapiterelem(it.it)
+ if iterelem == nil {
+ panic("MapIter.SetValue called on exhausted iterator")
+ }
+
+ dst.mustBeAssignable()
+ var target unsafe.Pointer
+ if dst.kind() == Interface {
+ target = dst.ptr
+ }
+
+ t := (*mapType)(unsafe.Pointer(it.m.typ))
+ vtype := t.elem
+
+ elem := Value{vtype, iterelem, it.m.flag | flag(vtype.Kind())}
+ elem = elem.assignTo("reflect.MapIter.SetValue", dst.typ, target)
+ typedmemmove(dst.typ, dst.ptr, elem.ptr)
}
// Next advances the map iterator and reports whether there is another