Skip to content

Commit 4c85d98

Browse files
committed
[tailscale1.17] reflect: add MapIter.Reset
This allows callers to do (amortized) allocation-free iteration over many maps. Fixes golang#46293 (cherry picked from golang.org/cl/321891) Change-Id: I3aa6134dd00da35b508bd1e3b487332a871a3673
1 parent b7cc3c6 commit 4c85d98

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

src/reflect/all_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7298,6 +7298,48 @@ func TestMapIterNilMap(t *testing.T) {
72987298
}
72997299
}
73007300

7301+
func TestMapIterReset(t *testing.T) {
7302+
iter := new(MapIter)
7303+
7304+
// Use of zero iterator should panic.
7305+
func() {
7306+
defer func() { recover() }()
7307+
iter.Next()
7308+
t.Fatal("Next did not panic")
7309+
}()
7310+
7311+
// Reset to new Map should work.
7312+
m := map[string]int{"one": 1, "two": 2, "three": 3}
7313+
iter.Reset(ValueOf(m))
7314+
if got, want := iterateToString(iter), `[one: 1, three: 3, two: 2]`; got != want {
7315+
t.Errorf("iterator returned %s (after sorting), want %s", got, want)
7316+
}
7317+
7318+
// Reset to Zero value should work, but iterating over it should panic.
7319+
iter.Reset(Value{})
7320+
func() {
7321+
defer func() { recover() }()
7322+
iter.Next()
7323+
t.Fatal("Next did not panic")
7324+
}()
7325+
7326+
// Reset to a diferent Map with different types should work.
7327+
m2 := map[int]string{1: "one", 2: "two", 3: "three"}
7328+
iter.Reset(ValueOf(m2))
7329+
if got, want := iterateToString(iter), `[1: one, 2: two, 3: three]`; got != want {
7330+
t.Errorf("iterator returned %s (after sorting), want %s", got, want)
7331+
}
7332+
7333+
// Reset should not allocate.
7334+
n := int(testing.AllocsPerRun(10, func() {
7335+
iter.Reset(ValueOf(m2))
7336+
iter.Reset(Value{})
7337+
}))
7338+
if n > 0 {
7339+
t.Errorf("MapIter.Reset allocated %d times", n)
7340+
}
7341+
}
7342+
73017343
func TestMapIterSafety(t *testing.T) {
73027344
// Using a zero MapIter causes a panic, but not a crash.
73037345
func() {

src/reflect/value.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,17 @@ func (it *MapIter) Next() bool {
16981698
return mapiterkey(it.it) != nil
16991699
}
17001700

1701+
// Reset modifies it to iterate over v.
1702+
// It panics if v's Kind is not Map and v is not the zero Value.
1703+
func (it *MapIter) Reset(v Value) {
1704+
if v.IsValid() {
1705+
v.mustBe(Map)
1706+
}
1707+
it.m = v
1708+
it.it = nil
1709+
it.hiter = hiter{}
1710+
}
1711+
17011712
// MapRange returns a range iterator for a map.
17021713
// It panics if v's Kind is not Map.
17031714
//

0 commit comments

Comments
 (0)