github.com/klaytn/klaytn@v1.12.1/node/sc/bridgepool/sorted_map_list.go (about) 1 // Modifications Copyright 2019 The klaytn Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from core/tx_list.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package bridgepool 22 23 import ( 24 "container/heap" 25 "errors" 26 "fmt" 27 "sort" 28 "sync" 29 ) 30 31 // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for 32 // retrieving sorted transactions from the possibly gapped future queue. 33 type nonceHeap []uint64 34 35 func (h nonceHeap) Len() int { return len(h) } 36 func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } 37 func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } 38 39 func (h *nonceHeap) Push(x interface{}) { 40 *h = append(*h, x.(uint64)) 41 } 42 43 func (h *nonceHeap) Pop() interface{} { 44 old := *h 45 n := len(old) 46 x := old[n-1] 47 *h = old[0 : n-1] 48 return x 49 } 50 51 type itemWithNonce interface { 52 Nonce() uint64 53 } 54 55 type items []itemWithNonce 56 57 func (s items) Len() int { return len(s) } 58 func (s items) Less(i, j int) bool { 59 return s[i].Nonce() < s[j].Nonce() 60 } 61 func (s items) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 62 63 // ItemSortedMap is a nonce->item map with a heap based index to allow 64 // iterating over the contents in a nonce-incrementing way. 65 const ( 66 UnlimitedItemSortedMap = -1 67 ) 68 69 var ErrSizeLimit = errors.New("sorted map size limit") 70 71 type ItemSortedMap struct { 72 mu *sync.Mutex 73 items map[uint64]itemWithNonce // Hash map storing the item data 74 index *nonceHeap // Heap of nonces of all the stored items (non-strict mode) 75 cache items // Cache of the items already sorted 76 sizeLimit int // This is sizeLimit of the sorted map. 77 } 78 79 // NewItemSortedMap creates a new nonce-sorted item map. 80 func NewItemSortedMap(size int) *ItemSortedMap { 81 return &ItemSortedMap{ 82 mu: new(sync.Mutex), 83 items: make(map[uint64]itemWithNonce), 84 index: new(nonceHeap), 85 sizeLimit: size, 86 } 87 } 88 89 // Get retrieves the current items associated with the given nonce. 90 func (m *ItemSortedMap) Get(nonce uint64) itemWithNonce { 91 m.mu.Lock() 92 defer m.mu.Unlock() 93 94 return m.items[nonce] 95 } 96 97 // Exist returns if the nonce exist. 98 func (m *ItemSortedMap) Exist(nonce uint64) bool { 99 m.mu.Lock() 100 defer m.mu.Unlock() 101 102 _, exist := m.items[nonce] 103 104 return exist 105 } 106 107 // Put inserts a new item into the map, also updating the map's nonce 108 // index. If a item already exists with the same nonce, it's overwritten. 109 func (m *ItemSortedMap) Put(event itemWithNonce) error { 110 m.mu.Lock() 111 defer m.mu.Unlock() 112 113 nonce := event.Nonce() 114 if m.sizeLimit != UnlimitedItemSortedMap && len(m.items) >= m.sizeLimit && m.items[nonce] == nil { 115 return fmt.Errorf("failed to put %v nonce : %w : %v", event.Nonce(), ErrSizeLimit, m.sizeLimit) 116 } 117 118 if m.items[nonce] == nil { 119 heap.Push(m.index, nonce) 120 } 121 m.items[nonce], m.cache = event, nil 122 123 return nil 124 } 125 126 // Pop removes given count number of minimum nonce items from the map. 127 // Every removed items is returned for any post-removal maintenance. 128 func (m *ItemSortedMap) Pop(count int) items { 129 m.mu.Lock() 130 defer m.mu.Unlock() 131 132 // Otherwise start accumulating incremental events 133 var ready items 134 for m.index.Len() > 0 && len(ready) < count { 135 nonce := (*m.index)[0] 136 ready = append(ready, m.items[nonce]) 137 delete(m.items, nonce) 138 heap.Pop(m.index) 139 } 140 141 return ready 142 } 143 144 // Ready retrieves a sequentially increasing list of events starting at the 145 // provided nonce that is ready for processing. The returned events will be 146 // removed from the list. 147 // 148 // Note, all events with nonces lower than start will also be returned to 149 // prevent getting into and invalid state. This is not something that should ever 150 // happen but better to be self correcting than failing! 151 func (m *ItemSortedMap) Ready(start uint64) items { 152 m.mu.Lock() 153 defer m.mu.Unlock() 154 155 // Short circuit if no events are available 156 if m.index.Len() == 0 || (*m.index)[0] > start { 157 return nil 158 } 159 // Otherwise start accumulating incremental events 160 var ready items 161 for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { 162 ready = append(ready, m.items[next]) 163 delete(m.items, next) 164 heap.Pop(m.index) 165 } 166 167 return ready 168 } 169 170 // Filter iterates over the list of items and removes all of them for which 171 // the specified function evaluates to true. 172 func (m *ItemSortedMap) Filter(filter func(interface{}) bool) items { 173 m.mu.Lock() 174 defer m.mu.Unlock() 175 176 var removed items 177 178 // Collect all the items to filter out 179 for nonce, tx := range m.items { 180 if filter(tx) { 181 removed = append(removed, tx) 182 delete(m.items, nonce) 183 } 184 } 185 // If items were removed, the heap and cache are ruined 186 if len(removed) > 0 { 187 *m.index = make([]uint64, 0, len(m.items)) 188 for nonce := range m.items { 189 *m.index = append(*m.index, nonce) 190 } 191 heap.Init(m.index) 192 193 m.cache = nil 194 } 195 return removed 196 } 197 198 // Remove deletes a item from the maintained map, returning whether the 199 // item was found. 200 func (m *ItemSortedMap) Remove(nonce uint64) bool { 201 m.mu.Lock() 202 defer m.mu.Unlock() 203 204 // Short circuit if no item is present 205 _, ok := m.items[nonce] 206 if !ok { 207 return false 208 } 209 // Otherwise delete the item and fix the heap index 210 for i := 0; i < m.index.Len(); i++ { 211 if (*m.index)[i] == nonce { 212 heap.Remove(m.index, i) 213 break 214 } 215 } 216 delete(m.items, nonce) 217 m.cache = nil 218 219 return true 220 } 221 222 // Forward removes all items from the map with a nonce lower than the 223 // provided threshold. Every removed transaction is returned for any post-removal 224 // maintenance. 225 func (m *ItemSortedMap) Forward(threshold uint64) items { 226 m.mu.Lock() 227 defer m.mu.Unlock() 228 var removed items 229 230 // Pop off heap items until the threshold is reached 231 for m.index.Len() > 0 && (*m.index)[0] < threshold { 232 nonce := heap.Pop(m.index).(uint64) 233 removed = append(removed, m.items[nonce]) 234 delete(m.items, nonce) 235 } 236 // If we had a cached order, shift the front 237 if m.cache != nil { 238 m.cache = m.cache[len(removed):] 239 } 240 return removed 241 } 242 243 // Len returns the length of the map. 244 func (m *ItemSortedMap) Len() int { 245 m.mu.Lock() 246 defer m.mu.Unlock() 247 248 return len(m.items) 249 } 250 251 // Flatten returns a nonce-sorted slice of items based on the loosely 252 // sorted internal representation. The result of the sorting is cached in case 253 // it's requested again before any modifications are made to the contents. 254 func (m *ItemSortedMap) Flatten() items { 255 return m.FlattenByCount(0) 256 } 257 258 // FlattenByCount returns requested number of nonce-sorted slice of cached 259 // items. The result of the sorting is cached like as Flatten method. 260 func (m *ItemSortedMap) FlattenByCount(count int) items { 261 m.mu.Lock() 262 defer m.mu.Unlock() 263 // If the sorting was not cached yet, create and cache it 264 if m.cache == nil { 265 m.cache = make(items, 0, len(m.items)) 266 for _, tx := range m.items { 267 m.cache = append(m.cache, tx) 268 } 269 sort.Sort(m.cache) 270 } 271 txLen := len(m.cache) 272 if count != 0 && txLen > count { 273 txLen = count 274 } 275 return m.cache[:txLen] 276 }