Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 68 additions & 11 deletions src/internal/reflectlite/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,9 @@ func (v Value) MapIndex(key Value) Value {
//go:linkname hashmapNewIterator runtime.hashmapNewIterator
func hashmapNewIterator() unsafe.Pointer

//go:linkname hashmapResetIterator runtime.hashmapResetIterator
func hashmapResetIterator(it unsafe.Pointer) unsafe.Pointer

//go:linkname hashmapNext runtime.hashmapNext
func hashmapNext(m unsafe.Pointer, it unsafe.Pointer, key, value unsafe.Pointer) bool

Expand All @@ -1213,50 +1216,104 @@ type MapIter struct {
key Value
val Value

valid bool
started bool
valid bool
}

func (it *MapIter) Key() Value {
if !it.started {
panic("reflect: MapIter.Key called before Next")
}
if !it.valid {
panic("reflect.MapIter.Key called on invalid iterator")
panic("reflect: MapIter.Key called on exhausted iterator")
}

return it.key.Elem()
key := it.key.Elem()
key.flags |= it.m.flags & valueFlagRO
return key
}

func (v Value) SetIterKey(iter *MapIter) {
v.Set(iter.Key())
if !iter.started {
panic("reflect: Value.SetIterKey called before Next")
}
if !iter.valid {
panic("reflect: Value.SetIterKey called on exhausted iterator")
}
if !v.isIndirect() {
panic("reflect.Value.SetIterKey using unaddressable value")
}
if v.isRO() || iter.m.isRO() {
panic("reflect.Value.SetIterKey using value obtained using unexported field")
}
key := iter.key.Elem()
if !key.typecode.AssignableTo(v.typecode) {
panic("reflect.Value.SetIterKey: value of type " + key.typecode.String() + " is not assignable to type " + v.typecode.String())
}
v.Set(key)
}

func (it *MapIter) Value() Value {
if !it.started {
panic("reflect: MapIter.Value called before Next")
}
if !it.valid {
panic("reflect.MapIter.Value called on invalid iterator")
panic("reflect: MapIter.Value called on exhausted iterator")
}

return it.val.Elem()
value := it.val.Elem()
value.flags |= it.m.flags & valueFlagRO
return value
}

func (v Value) SetIterValue(iter *MapIter) {
v.Set(iter.Value())
if !iter.started {
panic("reflect: Value.SetIterValue called before Next")
}
if !iter.valid {
panic("reflect: Value.SetIterValue called on exhausted iterator")
}
if !v.isIndirect() {
panic("reflect.Value.SetIterValue using unaddressable value")
}
if v.isRO() || iter.m.isRO() {
panic("reflect.Value.SetIterValue using value obtained using unexported field")
}
value := iter.val.Elem()
if !value.typecode.AssignableTo(v.typecode) {
panic("reflect.Value.SetIterValue: value of type " + value.typecode.String() + " is not assignable to type " + v.typecode.String())
}
v.Set(value)
}

func (it *MapIter) Next() bool {
if !it.m.IsValid() {
panic("reflect: MapIter.Next called on an iterator that does not have an associated map Value")
}
if it.started && !it.valid {
panic("reflect: MapIter.Next called on exhausted iterator")
}
it.key = New(it.m.typecode.Key())
it.val = New(it.m.typecode.Elem())

it.started = true
it.valid = hashmapNext(it.m.pointer(), it.it, it.key.value, it.val.value)
return it.valid
}

func (iter *MapIter) Reset(v Value) {
if v.Kind() != Map {
if v.IsValid() && v.Kind() != Map {
panic(&ValueError{Method: "MapRange", Kind: v.Kind()})
}

*iter = MapIter{
m: v,
it: hashmapNewIterator(),
if v.IsValid() || iter.it != nil {
iter.it = hashmapResetIterator(iter.it)
}
iter.m = v
iter.key = Value{}
iter.val = Value{}
iter.started = false
iter.valid = false
}

func (v Value) Set(x Value) {
Expand Down
4 changes: 0 additions & 4 deletions src/reflect/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7782,8 +7782,6 @@ func TestMapIterNilMap(t *testing.T) {
}
}

/*

func TestMapIterReset(t *testing.T) {
iter := new(MapIter)

Expand Down Expand Up @@ -7904,8 +7902,6 @@ func TestMapIterSafety(t *testing.T) {
}()
}

*/

func TestMapIterNext(t *testing.T) {
// The first call to Next should reflect any
// insertions to the map since the iterator was created.
Expand Down
8 changes: 8 additions & 0 deletions src/runtime/hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ func hashmapNewIterator() unsafe.Pointer {
return unsafe.Pointer(new(hashmapIterator))
}

func hashmapResetIterator(it unsafe.Pointer) unsafe.Pointer {
if it == nil {
return hashmapNewIterator()
}
*(*hashmapIterator)(it) = hashmapIterator{}
return it
}

// Get the topmost 8 bits of the hash, without using a special value (like 0).
func hashmapTopHash(hash uint32) uint8 {
tophash := uint8(hash >> 24)
Expand Down
Loading