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