1 // Copyright (c) 2013-2015 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
13 "github.com/btcsuite/btcd/wire"
16 // mruInventoryMap provides a concurrency safe map that is limited to a maximum
17 // number of items with eviction for the oldest entry when the limit is
19 type mruInventoryMap struct {
21 invMap map[wire.InvVect]*list.Element // nearly O(1) lookups
22 invList *list.List // O(1) insert, update, delete
26 // String returns the map as a human-readable string.
28 // This function is safe for concurrent access.
29 func (m *mruInventoryMap) String() string {
31 defer m.invMtx.Unlock()
33 lastEntryNum := len(m.invMap) - 1
35 buf := bytes.NewBufferString("[")
36 for iv := range m.invMap {
37 buf.WriteString(fmt.Sprintf("%v", iv))
38 if curEntry < lastEntryNum {
45 return fmt.Sprintf("<%d>%s", m.limit, buf.String())
48 // Exists returns whether or not the passed inventory item is in the map.
50 // This function is safe for concurrent access.
51 func (m *mruInventoryMap) Exists(iv *wire.InvVect) bool {
53 _, exists := m.invMap[*iv]
59 // Add adds the passed inventory to the map and handles eviction of the oldest
60 // item if adding the new item would exceed the max limit. Adding an existing
61 // item makes it the most recently used item.
63 // This function is safe for concurrent access.
64 func (m *mruInventoryMap) Add(iv *wire.InvVect) {
66 defer m.invMtx.Unlock()
68 // When the limit is zero, nothing can be added to the map, so just
74 // When the entry already exists move it to the front of the list
75 // thereby marking it most recently used.
76 if node, exists := m.invMap[*iv]; exists {
77 m.invList.MoveToFront(node)
81 // Evict the least recently used entry (back of the list) if the the new
82 // entry would exceed the size limit for the map. Also reuse the list
83 // node so a new one doesn't have to be allocated.
84 if uint(len(m.invMap))+1 > m.limit {
85 node := m.invList.Back()
86 lru := node.Value.(*wire.InvVect)
88 // Evict least recently used item.
89 delete(m.invMap, *lru)
91 // Reuse the list node of the item that was just evicted for the
94 m.invList.MoveToFront(node)
99 // The limit hasn't been reached yet, so just add the new item.
100 node := m.invList.PushFront(iv)
104 // Delete deletes the passed inventory item from the map (if it exists).
106 // This function is safe for concurrent access.
107 func (m *mruInventoryMap) Delete(iv *wire.InvVect) {
109 if node, exists := m.invMap[*iv]; exists {
110 m.invList.Remove(node)
111 delete(m.invMap, *iv)
116 // newMruInventoryMap returns a new inventory map that is limited to the number
117 // of entries specified by limit. When the number of entries exceeds the limit,
118 // the oldest (least recently used) entry will be removed to make room for the
120 func newMruInventoryMap(limit uint) *mruInventoryMap {
121 m := mruInventoryMap{
122 invMap: make(map[wire.InvVect]*list.Element),