blob: a3ead9a21998052f824dad5542d6440edc5502c2 [file] [log] [blame] [edit]
// Copyright ©2020 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !safe
package iterator
import (
"unsafe"
"gonum.org/v1/gonum/graph"
)
// A mapIter is an iterator for ranging over a map.
type mapIter struct {
m *emptyInterface
it unsafe.Pointer
}
type emptyInterface struct {
typ, word unsafe.Pointer
}
// newMapIterNodes returns a range iterator for a map of nodes.
func newMapIterNodes(m map[int64]graph.Node) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterEdges returns a range iterator for a map of edges.
// The returned mapIter must not have its node method called.
func newMapIterEdges(m map[int64]graph.Edge) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterWeightedEdges returns a range iterator for a map of edges.
// The returned mapIter must not have its node method called.
func newMapIterWeightedEdges(m map[int64]graph.WeightedEdge) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterLines returns a range iterator for a map of edges.
// The returned mapIter must not have its node method called.
func newMapIterLines(m map[int64]map[int64]graph.Line) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterWeightedLines returns a range iterator for a map of edges.
// The returned mapIter must not have its node method called.
func newMapIterWeightedLines(m map[int64]map[int64]graph.WeightedLine) *mapIter {
return &mapIter{m: eface(m)}
}
func eface(i interface{}) *emptyInterface {
return (*emptyInterface)(unsafe.Pointer(&i))
}
// id returns the key of the iterator's current map entry.
func (it *mapIter) id() int64 {
if it.it == nil {
panic("mapIter.id called before Next")
}
if mapiterkey(it.it) == nil {
panic("mapIter.id called on exhausted iterator")
}
return *(*int64)(mapiterkey(it.it))
}
// node returns the value of the iterator's current map entry.
func (it *mapIter) node() graph.Node {
if it.it == nil {
panic("mapIter.node called before next")
}
if mapiterkey(it.it) == nil {
panic("mapIter.node called on exhausted iterator")
}
return *(*graph.Node)(mapiterelem(it.it))
}
// next advances the map iterator and reports whether there is another
// 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.word)
} else {
if mapiterkey(it.it) == nil {
panic("mapIter.next called on exhausted iterator")
}
mapiternext(it.it)
}
return mapiterkey(it.it) != nil
}
// m escapes into the return value, but the caller of mapiterinit
// doesn't let the return value escape.
//go:linkname mapiterinit reflect.mapiterinit
//go:noescape
func mapiterinit(t, m unsafe.Pointer) unsafe.Pointer
//go:linkname mapiterkey reflect.mapiterkey
//go:noescape
func mapiterkey(it unsafe.Pointer) (key unsafe.Pointer)
//go:linkname mapiterelem reflect.mapiterelem
//go:noescape
func mapiterelem(it unsafe.Pointer) (elem unsafe.Pointer)
//go:linkname mapiternext reflect.mapiternext
//go:noescape
func mapiternext(it unsafe.Pointer)