| // 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) |