gonum.org/v1/gonum@v0.14.0/graph/iterator/map.go (about) 1 // Copyright ©2020 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Copyright 2009 The Go Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style 7 // license that can be found in the LICENSE file. 8 9 //go:build !safe 10 // +build !safe 11 12 package iterator 13 14 import ( 15 "unsafe" 16 17 "gonum.org/v1/gonum/graph" 18 ) 19 20 // A mapIter is an iterator for ranging over a map. 21 type mapIter struct { 22 m *emptyInterface 23 hiter hiter 24 } 25 26 type emptyInterface struct { 27 typ, word unsafe.Pointer 28 } 29 30 // hiter's structure matches runtime.hiter's structure. 31 // Having a clone here allows us to embed a map iterator 32 // inside type mapIter so that mapIters can be re-used 33 // without doing any allocations. 34 // 35 //lint:ignore U1000 This is a verbatim copy of the runtime type. 36 type hiter struct { 37 key unsafe.Pointer 38 elem unsafe.Pointer 39 t unsafe.Pointer 40 h unsafe.Pointer 41 buckets unsafe.Pointer 42 bptr unsafe.Pointer 43 overflow *[]unsafe.Pointer 44 oldoverflow *[]unsafe.Pointer 45 startBucket uintptr 46 offset uint8 47 wrapped bool 48 B uint8 49 i uint8 50 bucket uintptr 51 checkBucket uintptr 52 } 53 54 func (h *hiter) initialized() bool { 55 return h.t != nil 56 } 57 58 // newMapIterNodes returns a range iterator for a map of nodes. 59 // The returned mapIter must not have its line or weightedLine methods called. 60 func newMapIterNodes(m map[int64]graph.Node) *mapIter { 61 return &mapIter{m: eface(m)} 62 } 63 64 // newMapIterEdges returns a range iterator for a map of edges. 65 // The returned mapIter must not have its node, line or weightedLine methods called. 66 func newMapIterEdges(m map[int64]graph.Edge) *mapIter { 67 return &mapIter{m: eface(m)} 68 } 69 70 // newMapIterLines returns a range iterator for a map of line. 71 // The returned mapIter must not have its node or weightedLine method called. 72 func newMapIterLines(m map[int64]graph.Line) *mapIter { 73 return &mapIter{m: eface(m)} 74 } 75 76 // newMapIterWeightedLines returns a range iterator for a map of line. 77 // The returned mapIter must not have its node, line or weightedLine methods called. 78 func newMapIterWeightedLines(m map[int64]graph.WeightedLine) *mapIter { 79 return &mapIter{m: eface(m)} 80 } 81 82 // newMapIterByWeightedEdges returns a range iterator for a map of edges. 83 // The returned mapIter must not have its node, line or weightedLine methods called. 84 func newMapIterByWeightedEdges(m map[int64]graph.WeightedEdge) *mapIter { 85 return &mapIter{m: eface(m)} 86 } 87 88 // newMapIterByLines returns a range iterator for a map of edges. 89 // The returned mapIter must not have its node, line or weightedLine methods called. 90 func newMapIterByLines(m map[int64]map[int64]graph.Line) *mapIter { 91 return &mapIter{m: eface(m)} 92 } 93 94 // newMapIterByWeightedLines returns a range iterator for a map of edges. 95 // The returned mapIter must not have its node, line or weightedLine methods called. 96 func newMapIterByWeightedLines(m map[int64]map[int64]graph.WeightedLine) *mapIter { 97 return &mapIter{m: eface(m)} 98 } 99 100 func eface(i interface{}) *emptyInterface { 101 return (*emptyInterface)(unsafe.Pointer(&i)) 102 } 103 104 // id returns the key of the iterator's current map entry. 105 func (it *mapIter) id() int64 { 106 if !it.hiter.initialized() { 107 panic("mapIter.id called before Next") 108 } 109 if mapiterkey(&it.hiter) == nil { 110 panic("mapIter.id called on exhausted iterator") 111 } 112 return *(*int64)(mapiterkey(&it.hiter)) 113 } 114 115 // node returns the value of the iterator's current map entry. 116 func (it *mapIter) node() graph.Node { 117 if !it.hiter.initialized() { 118 panic("mapIter.node called before next") 119 } 120 if mapiterkey(&it.hiter) == nil { 121 panic("mapIter.node called on exhausted iterator") 122 } 123 return *(*graph.Node)(mapiterelem(&it.hiter)) 124 } 125 126 // line returns the value of the iterator's current map entry. 127 func (it *mapIter) line() graph.Line { 128 if !it.hiter.initialized() { 129 panic("mapIter.line called before next") 130 } 131 if mapiterkey(&it.hiter) == nil { 132 panic("mapIter.line called on exhausted iterator") 133 } 134 return *(*graph.Line)(mapiterelem(&it.hiter)) 135 } 136 137 // weightedLine returns the value of the iterator's current map entry. 138 func (it *mapIter) weightedLine() graph.WeightedLine { 139 if !it.hiter.initialized() { 140 panic("mapIter.weightedLine called before next") 141 } 142 if mapiterkey(&it.hiter) == nil { 143 panic("mapIter.weightedLine called on exhausted iterator") 144 } 145 return *(*graph.WeightedLine)(mapiterelem(&it.hiter)) 146 } 147 148 // next advances the map iterator and reports whether there is another 149 // entry. It returns false when the iterator is exhausted; subsequent 150 // calls to Key, Value, or next will panic. 151 func (it *mapIter) next() bool { 152 if !it.hiter.initialized() { 153 mapiterinit(it.m.typ, it.m.word, &it.hiter) 154 } else { 155 if mapiterkey(&it.hiter) == nil { 156 panic("mapIter.next called on exhausted iterator") 157 } 158 mapiternext(&it.hiter) 159 } 160 return mapiterkey(&it.hiter) != nil 161 } 162 163 //go:linkname mapiterinit runtime.mapiterinit 164 //go:noescape 165 func mapiterinit(t, m unsafe.Pointer, it *hiter) 166 167 //go:linkname mapiterkey reflect.mapiterkey 168 //go:noescape 169 func mapiterkey(it *hiter) (key unsafe.Pointer) 170 171 //go:linkname mapiterelem reflect.mapiterelem 172 //go:noescape 173 func mapiterelem(it *hiter) (elem unsafe.Pointer) 174 175 //go:linkname mapiternext reflect.mapiternext 176 //go:noescape 177 func mapiternext(it *hiter)