if m != nil {
mlen = maplen(m)
}
- it := mapiterinit(v.typ, m)
+ var it hiter
+ mapiterinit(v.typ, m, &it)
a := make([]Value, mlen)
var i int
for i = 0; i < len(a); i++ {
- key := mapiterkey(it)
+ key := mapiterkey(&it)
if key == nil {
// Someone deleted an entry from the map since we
// called maplen above. It's a data race, but nothing
break
}
a[i] = copyVal(keyType, fl, key)
- mapiternext(it)
+ mapiternext(&it)
}
return a[:i]
}
+// hiter's structure matches runtime.hiter's structure.
+// Having a clone here allows us to embed a map iterator
+// inside type MapIter so that MapIters can be re-used
+// without doing any allocations.
+type hiter struct {
+ key unsafe.Pointer
+ elem unsafe.Pointer
+ t unsafe.Pointer
+ h unsafe.Pointer
+ buckets unsafe.Pointer
+ bptr unsafe.Pointer
+ overflow *[]unsafe.Pointer
+ oldoverflow *[]unsafe.Pointer
+ startBucket uintptr
+ offset uint8
+ wrapped bool
+ B uint8
+ i uint8
+ bucket uintptr
+ checkBucket uintptr
+}
+
+func (h hiter) initialized() bool {
+ return h.t != nil
+}
+
// A MapIter is an iterator for ranging over a map.
// See Value.MapRange.
type MapIter struct {
- m Value
- it unsafe.Pointer
+ m Value
+ hiter hiter
}
// Key returns the key of the iterator's current map entry.
func (it *MapIter) Key() Value {
- if it.it == nil {
+ if !it.hiter.initialized() {
panic("MapIter.Key called before Next")
}
- iterkey := mapiterkey(it.it)
+ iterkey := mapiterkey(&it.hiter)
if iterkey == nil {
panic("MapIter.Key called on exhausted iterator")
}
// 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 {
+ if !it.hiter.initialized() {
panic("MapIter.SetKey called before Next")
}
- iterkey := mapiterkey(it.it)
+ iterkey := mapiterkey(&it.hiter)
if iterkey == nil {
panic("MapIter.SetKey called on exhausted iterator")
}
// Value returns the value of the iterator's current map entry.
func (it *MapIter) Value() Value {
- if it.it == nil {
+ if !it.hiter.initialized() {
panic("MapIter.Value called before Next")
}
- iterelem := mapiterelem(it.it)
+ iterelem := mapiterelem(&it.hiter)
if iterelem == nil {
panic("MapIter.Value called on exhausted iterator")
}
// 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 {
+ if !it.hiter.initialized() {
panic("MapIter.SetValue called before Next")
}
- iterelem := mapiterelem(it.it)
+ iterelem := mapiterelem(&it.hiter)
if iterelem == nil {
panic("MapIter.SetValue called on exhausted iterator")
}
// entry. It returns false when the iterator is exhausted; subsequent
// calls to Key, Value, or Next will panic.
func (it *MapIter) Next() bool {
- if it.it == nil {
- it.it = mapiterinit(it.m.typ, it.m.pointer())
+ if !it.hiter.initialized() {
+ mapiterinit(it.m.typ, it.m.pointer(), &it.hiter)
} else {
- if mapiterkey(it.it) == nil {
+ if mapiterkey(&it.hiter) == nil {
panic("MapIter.Next called on exhausted iterator")
}
- mapiternext(it.it)
+ mapiternext(&it.hiter)
}
- return mapiterkey(it.it) != nil
+ return mapiterkey(&it.hiter) != nil
}
// MapRange returns a range iterator for a map.
//go:noescape
func mapdelete(t *rtype, m unsafe.Pointer, key unsafe.Pointer)
-// m escapes into the return value, but the caller of mapiterinit
-// doesn't let the return value escape.
//go:noescape
-func mapiterinit(t *rtype, m unsafe.Pointer) unsafe.Pointer
+func mapiterinit(t *rtype, m unsafe.Pointer, it *hiter)
//go:noescape
-func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)
+func mapiterkey(it *hiter) (key unsafe.Pointer)
//go:noescape
-func mapiterelem(it unsafe.Pointer) (elem unsafe.Pointer)
+func mapiterelem(it *hiter) (elem unsafe.Pointer)
//go:noescape
-func mapiternext(it unsafe.Pointer)
+func mapiternext(it *hiter)
//go:noescape
func maplen(m unsafe.Pointer) int
}
// A hash iteration structure.
-// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go to indicate
-// the layout of this structure.
+// If you modify hiter, also change cmd/compile/internal/reflectdata/reflect.go
+// and reflect/value.go to match the layout of this structure.
type hiter struct {
key unsafe.Pointer // Must be in first position. Write nil to indicate iteration end (see cmd/compile/internal/walk/range.go).
elem unsafe.Pointer // Must be in second position (see cmd/compile/internal/walk/range.go).
racereadpc(unsafe.Pointer(h), callerpc, abi.FuncPCABIInternal(mapiterinit))
}
+ it.t = t
if h == nil || h.count == 0 {
return
}
if unsafe.Sizeof(hiter{})/goarch.PtrSize != 12 {
throw("hash_iter size incorrect") // see cmd/compile/internal/reflectdata/reflect.go
}
- it.t = t
it.h = h
// grab snapshot of bucket state
}
//go:linkname reflect_mapiterinit reflect.mapiterinit
-func reflect_mapiterinit(t *maptype, h *hmap) *hiter {
- it := new(hiter)
+func reflect_mapiterinit(t *maptype, h *hmap, it *hiter) {
mapiterinit(t, h, it)
- return it
}
//go:linkname reflect_mapiternext reflect.mapiternext