github.com/BlockABC/godash@v0.0.0-20191112120524-f4aa3a32c566/peer/mrunoncemap.go (about) 1 // Copyright (c) 2015 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package peer 7 8 import ( 9 "bytes" 10 "container/list" 11 "fmt" 12 "sync" 13 ) 14 15 // mruNonceMap provides a concurrency safe map that is limited to a maximum 16 // number of items with eviction for the oldest entry when the limit is 17 // exceeded. 18 type mruNonceMap struct { 19 mtx sync.Mutex 20 nonceMap map[uint64]*list.Element // nearly O(1) lookups 21 nonceList *list.List // O(1) insert, update, delete 22 limit uint 23 } 24 25 // String returns the map as a human-readable string. 26 // 27 // This function is safe for concurrent access. 28 func (m *mruNonceMap) String() string { 29 m.mtx.Lock() 30 defer m.mtx.Unlock() 31 32 lastEntryNum := len(m.nonceMap) - 1 33 curEntry := 0 34 buf := bytes.NewBufferString("[") 35 for nonce := range m.nonceMap { 36 buf.WriteString(fmt.Sprintf("%d", nonce)) 37 if curEntry < lastEntryNum { 38 buf.WriteString(", ") 39 } 40 curEntry++ 41 } 42 buf.WriteString("]") 43 44 return fmt.Sprintf("<%d>%s", m.limit, buf.String()) 45 } 46 47 // Exists returns whether or not the passed nonce is in the map. 48 // 49 // This function is safe for concurrent access. 50 func (m *mruNonceMap) Exists(nonce uint64) bool { 51 m.mtx.Lock() 52 defer m.mtx.Unlock() 53 54 if _, exists := m.nonceMap[nonce]; exists { 55 return true 56 } 57 return false 58 } 59 60 // Add adds the passed nonce to the map and handles eviction of the oldest item 61 // if adding the new item would exceed the max limit. Adding an existing item 62 // makes it the most recently used item. 63 // 64 // This function is safe for concurrent access. 65 func (m *mruNonceMap) Add(nonce uint64) { 66 m.mtx.Lock() 67 defer m.mtx.Unlock() 68 69 // When the limit is zero, nothing can be added to the map, so just 70 // return. 71 if m.limit == 0 { 72 return 73 } 74 75 // When the entry already exists move it to the front of the list 76 // thereby marking it most recently used. 77 if node, exists := m.nonceMap[nonce]; exists { 78 m.nonceList.MoveToFront(node) 79 return 80 } 81 82 // Evict the least recently used entry (back of the list) if the the new 83 // entry would exceed the size limit for the map. Also reuse the list 84 // node so a new one doesn't have to be allocated. 85 if uint(len(m.nonceMap))+1 > m.limit { 86 node := m.nonceList.Back() 87 lru := node.Value.(uint64) 88 89 // Evict least recently used item. 90 delete(m.nonceMap, lru) 91 92 // Reuse the list node of the item that was just evicted for the 93 // new item. 94 node.Value = nonce 95 m.nonceList.MoveToFront(node) 96 m.nonceMap[nonce] = node 97 return 98 } 99 100 // The limit hasn't been reached yet, so just add the new item. 101 node := m.nonceList.PushFront(nonce) 102 m.nonceMap[nonce] = node 103 return 104 } 105 106 // Delete deletes the passed nonce from the map (if it exists). 107 // 108 // This function is safe for concurrent access. 109 func (m *mruNonceMap) Delete(nonce uint64) { 110 m.mtx.Lock() 111 defer m.mtx.Unlock() 112 113 if node, exists := m.nonceMap[nonce]; exists { 114 m.nonceList.Remove(node) 115 delete(m.nonceMap, nonce) 116 } 117 } 118 119 // newMruNonceMap returns a new nonce map that is limited to the number of 120 // entries specified by limit. When the number of entries exceeds the limit, 121 // the oldest (least recently used) entry will be removed to make room for the 122 // new entry. 123 func newMruNonceMap(limit uint) *mruNonceMap { 124 m := mruNonceMap{ 125 nonceMap: make(map[uint64]*list.Element), 126 nonceList: list.New(), 127 limit: limit, 128 } 129 return &m 130 }