2015-08-29 22:35:28 +02:00
|
|
|
// Copyright (c) 2013-2015 The btcsuite developers
|
2013-09-09 17:58:56 +02:00
|
|
|
// Use of this source code is governed by an ISC
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-08-29 22:35:28 +02:00
|
|
|
"bytes"
|
2013-10-26 08:01:49 +02:00
|
|
|
"container/list"
|
2013-09-09 17:58:56 +02:00
|
|
|
"fmt"
|
2014-07-02 15:50:08 +02:00
|
|
|
|
2015-02-05 22:16:39 +01:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2013-09-09 17:58:56 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// MruInventoryMap provides a map that is limited to a maximum number of items
|
|
|
|
// with eviction for the oldest entry when the limit is exceeded.
|
|
|
|
type MruInventoryMap struct {
|
2015-02-05 22:16:39 +01:00
|
|
|
invMap map[wire.InvVect]*list.Element // nearly O(1) lookups
|
|
|
|
invList *list.List // O(1) insert, update, delete
|
2013-10-26 08:01:49 +02:00
|
|
|
limit uint
|
2013-09-09 17:58:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// String returns the map as a human-readable string.
|
|
|
|
func (m MruInventoryMap) String() string {
|
2015-08-29 22:35:28 +02:00
|
|
|
lastEntryNum := len(m.invMap) - 1
|
|
|
|
curEntry := 0
|
|
|
|
buf := bytes.NewBufferString("[")
|
|
|
|
for iv := range m.invMap {
|
|
|
|
buf.WriteString(fmt.Sprintf("%v", iv))
|
|
|
|
if curEntry < lastEntryNum {
|
|
|
|
buf.WriteString(", ")
|
|
|
|
}
|
|
|
|
curEntry++
|
|
|
|
}
|
|
|
|
buf.WriteString("]")
|
|
|
|
|
|
|
|
return fmt.Sprintf("<%d>%s", m.limit, buf.String())
|
2013-09-09 17:58:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Exists returns whether or not the passed inventory item is in the map.
|
2015-02-05 22:16:39 +01:00
|
|
|
func (m *MruInventoryMap) Exists(iv *wire.InvVect) bool {
|
2013-09-09 17:58:56 +02:00
|
|
|
if _, exists := m.invMap[*iv]; exists {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add adds the passed inventory to the map and handles eviction of the oldest
|
2015-08-29 22:35:28 +02:00
|
|
|
// item if adding the new item would exceed the max limit. Adding an existing
|
|
|
|
// item makes it the most recently used item.
|
2015-02-05 22:16:39 +01:00
|
|
|
func (m *MruInventoryMap) Add(iv *wire.InvVect) {
|
2013-09-09 17:58:56 +02:00
|
|
|
// When the limit is zero, nothing can be added to the map, so just
|
2013-10-26 08:01:49 +02:00
|
|
|
// return.
|
2013-09-09 17:58:56 +02:00
|
|
|
if m.limit == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-10-26 08:01:49 +02:00
|
|
|
// When the entry already exists move it to the front of the list
|
|
|
|
// thereby marking it most recently used.
|
|
|
|
if node, exists := m.invMap[*iv]; exists {
|
|
|
|
m.invList.MoveToFront(node)
|
2013-09-09 17:58:56 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-10-26 08:01:49 +02:00
|
|
|
// Evict the least recently used entry (back of the list) if the the new
|
|
|
|
// entry would exceed the size limit for the map. Also reuse the list
|
|
|
|
// node so a new one doesn't have to be allocated.
|
2013-09-09 17:58:56 +02:00
|
|
|
if uint(len(m.invMap))+1 > m.limit {
|
2013-10-26 08:01:49 +02:00
|
|
|
node := m.invList.Back()
|
2015-08-29 22:35:28 +02:00
|
|
|
lru := node.Value.(*wire.InvVect)
|
2013-09-09 17:58:56 +02:00
|
|
|
|
2013-10-26 08:01:49 +02:00
|
|
|
// Evict least recently used item.
|
|
|
|
delete(m.invMap, *lru)
|
|
|
|
|
|
|
|
// Reuse the list node of the item that was just evicted for the
|
|
|
|
// new item.
|
|
|
|
node.Value = iv
|
|
|
|
m.invList.MoveToFront(node)
|
|
|
|
m.invMap[*iv] = node
|
|
|
|
return
|
2013-09-09 17:58:56 +02:00
|
|
|
}
|
|
|
|
|
2013-10-26 08:01:49 +02:00
|
|
|
// The limit hasn't been reached yet, so just add the new item.
|
|
|
|
node := m.invList.PushFront(iv)
|
|
|
|
m.invMap[*iv] = node
|
2013-09-09 17:58:56 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete deletes the passed inventory item from the map (if it exists).
|
2015-02-05 22:16:39 +01:00
|
|
|
func (m *MruInventoryMap) Delete(iv *wire.InvVect) {
|
2013-10-26 08:01:49 +02:00
|
|
|
if node, exists := m.invMap[*iv]; exists {
|
|
|
|
m.invList.Remove(node)
|
|
|
|
delete(m.invMap, *iv)
|
|
|
|
}
|
2013-09-09 17:58:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMruInventoryMap returns a new inventory map that is limited to the number
|
|
|
|
// of entries specified by limit. When the number of entries exceeds the limit,
|
|
|
|
// the oldest (least recently used) entry will be removed to make room for the
|
2013-11-18 17:38:24 +01:00
|
|
|
// new entry.
|
2013-09-09 17:58:56 +02:00
|
|
|
func NewMruInventoryMap(limit uint) *MruInventoryMap {
|
|
|
|
m := MruInventoryMap{
|
2015-02-05 22:16:39 +01:00
|
|
|
invMap: make(map[wire.InvVect]*list.Element),
|
2013-10-26 08:01:49 +02:00
|
|
|
invList: list.New(),
|
|
|
|
limit: limit,
|
2013-09-09 17:58:56 +02:00
|
|
|
}
|
|
|
|
return &m
|
|
|
|
}
|