github.com/gopherd/gonum@v0.0.4/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 "github.com/gopherd/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 type hiter struct { 35 key unsafe.Pointer 36 elem unsafe.Pointer 37 t unsafe.Pointer 38 h unsafe.Pointer 39 buckets unsafe.Pointer 40 bptr unsafe.Pointer 41 overflow *[]unsafe.Pointer 42 oldoverflow *[]unsafe.Pointer 43 startBucket uintptr 44 offset uint8 45 wrapped bool 46 B uint8 47 i uint8 48 bucket uintptr 49 checkBucket uintptr 50 } 51 52 func (h *hiter) initialized() bool { 53 return h.t != nil 54 } 55 56 // newMapIterNodes returns a range iterator for a map of nodes. 57 // The returned mapIter must not have its line or weightedLine methods called. 58 func newMapIterNodes(m map[int64]graph.Node) *mapIter { 59 return &mapIter{m: eface(m)} 60 } 61 62 // newMapIterEdges returns a range iterator for a map of edges. 63 // The returned mapIter must not have its node, line or weightedLine methods called. 64 func newMapIterEdges(m map[int64]graph.Edge) *mapIter { 65 return &mapIter{m: eface(m)} 66 } 67 68 // newMapIterLines returns a range iterator for a map of line. 69 // The returned mapIter must not have its node or weightedLine method called. 70 func newMapIterLines(m map[int64]graph.Line) *mapIter { 71 return &mapIter{m: eface(m)} 72 } 73 74 // newMapIterWeightedLines returns a range iterator for a map of line. 75 // The returned mapIter must not have its node, line or weightedLine methods called. 76 func newMapIterWeightedLines(m map[int64]graph.WeightedLine) *mapIter { 77 return &mapIter{m: eface(m)} 78 } 79 80 // newMapIterByWeightedEdges returns a range iterator for a map of edges. 81 // The returned mapIter must not have its node, line or weightedLine methods called. 82 func newMapIterByWeightedEdges(m map[int64]graph.WeightedEdge) *mapIter { 83 return &mapIter{m: eface(m)} 84 } 85 86 // newMapIterByLines returns a range iterator for a map of edges. 87 // The returned mapIter must not have its node, line or weightedLine methods called. 88 func newMapIterByLines(m map[int64]map[int64]graph.Line) *mapIter { 89 return &mapIter{m: eface(m)} 90 } 91 92 // newMapIterByWeightedLines returns a range iterator for a map of edges. 93 // The returned mapIter must not have its node, line or weightedLine methods called. 94 func newMapIterByWeightedLines(m map[int64]map[int64]graph.WeightedLine) *mapIter { 95 return &mapIter{m: eface(m)} 96 } 97 98 func eface(i interface{}) *emptyInterface { 99 return (*emptyInterface)(unsafe.Pointer(&i)) 100 } 101 102 // id returns the key of the iterator's current map entry. 103 func (it *mapIter) id() int64 { 104 if !it.hiter.initialized() { 105 panic("mapIter.id called before Next") 106 } 107 if mapiterkey(&it.hiter) == nil { 108 panic("mapIter.id called on exhausted iterator") 109 } 110 return *(*int64)(mapiterkey(&it.hiter)) 111 } 112 113 // node returns the value of the iterator's current map entry. 114 func (it *mapIter) node() graph.Node { 115 if !it.hiter.initialized() { 116 panic("mapIter.node called before next") 117 } 118 if mapiterkey(&it.hiter) == nil { 119 panic("mapIter.node called on exhausted iterator") 120 } 121 return *(*graph.Node)(mapiterelem(&it.hiter)) 122 } 123 124 // line returns the value of the iterator's current map entry. 125 func (it *mapIter) line() graph.Line { 126 if !it.hiter.initialized() { 127 panic("mapIter.line called before next") 128 } 129 if mapiterkey(&it.hiter) == nil { 130 panic("mapIter.line called on exhausted iterator") 131 } 132 return *(*graph.Line)(mapiterelem(&it.hiter)) 133 } 134 135 // weightedLine returns the value of the iterator's current map entry. 136 func (it *mapIter) weightedLine() graph.WeightedLine { 137 if !it.hiter.initialized() { 138 panic("mapIter.weightedLine called before next") 139 } 140 if mapiterkey(&it.hiter) == nil { 141 panic("mapIter.weightedLine called on exhausted iterator") 142 } 143 return *(*graph.WeightedLine)(mapiterelem(&it.hiter)) 144 } 145 146 // next advances the map iterator and reports whether there is another 147 // entry. It returns false when the iterator is exhausted; subsequent 148 // calls to Key, Value, or next will panic. 149 func (it *mapIter) next() bool { 150 if !it.hiter.initialized() { 151 mapiterinit(it.m.typ, it.m.word, &it.hiter) 152 } else { 153 if mapiterkey(&it.hiter) == nil { 154 panic("mapIter.next called on exhausted iterator") 155 } 156 mapiternext(&it.hiter) 157 } 158 return mapiterkey(&it.hiter) != nil 159 } 160 161 //go:linkname mapiterinit runtime.mapiterinit 162 //go:noescape 163 func mapiterinit(t, m unsafe.Pointer, it *hiter) 164 165 //go:linkname mapiterkey reflect.mapiterkey 166 //go:noescape 167 func mapiterkey(it *hiter) (key unsafe.Pointer) 168 169 //go:linkname mapiterelem reflect.mapiterelem 170 //go:noescape 171 func mapiterelem(it *hiter) (elem unsafe.Pointer) 172 173 //go:linkname mapiternext reflect.mapiternext 174 //go:noescape 175 func mapiternext(it *hiter)