blob: 2bfa27649d7128dae04efe5687fbc74e6e9b79cf [file] [log] [blame]
// 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.
//go:build !safe
// +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
hiter hiter
}
type emptyInterface struct {
typ, word unsafe.Pointer
}
// 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.
//
//lint:ignore U1000 This is a verbatim copy of the runtime type.
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
}
// newMapIterNodes returns a range iterator for a map of nodes.
// The returned mapIter must not have its line or weightedLine methods called.
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, line or weightedLine methods called.
func newMapIterEdges(m map[int64]graph.Edge) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterLines returns a range iterator for a map of line.
// The returned mapIter must not have its node or weightedLine method called.
func newMapIterLines(m map[int64]graph.Line) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterWeightedLines returns a range iterator for a map of line.
// The returned mapIter must not have its node, line or weightedLine methods called.
func newMapIterWeightedLines(m map[int64]graph.WeightedLine) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterByWeightedEdges returns a range iterator for a map of edges.
// The returned mapIter must not have its node, line or weightedLine methods called.
func newMapIterByWeightedEdges(m map[int64]graph.WeightedEdge) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterByLines returns a range iterator for a map of edges.
// The returned mapIter must not have its node, line or weightedLine methods called.
func newMapIterByLines(m map[int64]map[int64]graph.Line) *mapIter {
return &mapIter{m: eface(m)}
}
// newMapIterByWeightedLines returns a range iterator for a map of edges.
// The returned mapIter must not have its node, line or weightedLine methods called.
func newMapIterByWeightedLines(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.hiter.initialized() {
panic("mapIter.id called before Next")
}
if mapiterkey(&it.hiter) == nil {
panic("mapIter.id called on exhausted iterator")
}
return *(*int64)(mapiterkey(&it.hiter))
}
// node returns the value of the iterator's current map entry.
func (it *mapIter) node() graph.Node {
if !it.hiter.initialized() {
panic("mapIter.node called before next")
}
if mapiterkey(&it.hiter) == nil {
panic("mapIter.node called on exhausted iterator")
}
return *(*graph.Node)(mapiterelem(&it.hiter))
}
// line returns the value of the iterator's current map entry.
func (it *mapIter) line() graph.Line {
if !it.hiter.initialized() {
panic("mapIter.line called before next")
}
if mapiterkey(&it.hiter) == nil {
panic("mapIter.line called on exhausted iterator")
}
return *(*graph.Line)(mapiterelem(&it.hiter))
}
// weightedLine returns the value of the iterator's current map entry.
func (it *mapIter) weightedLine() graph.WeightedLine {
if !it.hiter.initialized() {
panic("mapIter.weightedLine called before next")
}
if mapiterkey(&it.hiter) == nil {
panic("mapIter.weightedLine called on exhausted iterator")
}
return *(*graph.WeightedLine)(mapiterelem(&it.hiter))
}
// 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.hiter.initialized() {
mapiterinit(it.m.typ, it.m.word, &it.hiter)
} else {
if mapiterkey(&it.hiter) == nil {
panic("mapIter.next called on exhausted iterator")
}
mapiternext(&it.hiter)
}
return mapiterkey(&it.hiter) != nil
}
//go:linkname mapiterinit runtime.mapiterinit
//go:noescape
func mapiterinit(t, m unsafe.Pointer, it *hiter)
//go:linkname mapiterkey reflect.mapiterkey
//go:noescape
func mapiterkey(it *hiter) (key unsafe.Pointer)
//go:linkname mapiterelem reflect.mapiterelem
//go:noescape
func mapiterelem(it *hiter) (elem unsafe.Pointer)
//go:linkname mapiternext reflect.mapiternext
//go:noescape
func mapiternext(it *hiter)