mruMap

比特币中mruMap的实现

特性

  • 带锁,并发访问
  • 数量有上限
  • 自动移除最不常用

主要接口说明:

  1. Exists 判断某个元素是否存在
  2. Add 起到添加和标记使用的双重作用
  3. Delete主动删除

性能说明:
增删改查都是O(1)

  • 查用的是Map,接近O(1),
  • 增删更新,都是用的双向链表List

其他问题:
要求存储的元素必须能够作为map的key

参考实现

// mruNonceMap provides a concurrency safe map that is limited to a maximum
// number of items with eviction for the oldest entry when the limit is
// exceeded.
type mruNonceMap struct {
	mtx       sync.Mutex
	nonceMap  map[uint64]*list.Element // nearly O(1) lookups
	nonceList *list.List               // O(1) insert, update, delete
	limit     uint
}

// Exists returns whether or not the passed nonce is in the map.
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Exists(nonce uint64) bool {
	m.mtx.Lock()
	_, exists := m.nonceMap[nonce]
	m.mtx.Unlock()

	return exists
}

// Add adds the passed nonce to the map and handles eviction of the oldest item
// if adding the new item would exceed the max limit.  Adding an existing item
// makes it the most recently used item.
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Add(nonce uint64) {
	m.mtx.Lock()
	defer m.mtx.Unlock()

	// When the limit is zero, nothing can be added to the map, so just
	// return.
	if m.limit == 0 {
		return
	}

	// When the entry already exists move it to the front of the list
	// thereby marking it most recently used.
	if node, exists := m.nonceMap[nonce]; exists {
		m.nonceList.MoveToFront(node)
		return
	}

	// 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.
	if uint(len(m.nonceMap))+1 > m.limit {
		node := m.nonceList.Back()
		lru := node.Value.(uint64)

		// Evict least recently used item.
		delete(m.nonceMap, lru)

		// Reuse the list node of the item that was just evicted for the
		// new item.
		node.Value = nonce
		m.nonceList.MoveToFront(node)
		m.nonceMap[nonce] = node
		return
	}

	// The limit hasn't been reached yet, so just add the new item.
	node := m.nonceList.PushFront(nonce)
	m.nonceMap[nonce] = node
}

// Delete deletes the passed nonce from the map (if it exists).
//
// This function is safe for concurrent access.
func (m *mruNonceMap) Delete(nonce uint64) {
	m.mtx.Lock()
	if node, exists := m.nonceMap[nonce]; exists {
		m.nonceList.Remove(node)
		delete(m.nonceMap, nonce)
	}
	m.mtx.Unlock()
}

// newMruNonceMap returns a new nonce 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
// new entry.
func newMruNonceMap(limit uint) *mruNonceMap {
	m := mruNonceMap{
		nonceMap:  make(map[uint64]*list.Element),
		nonceList: list.New(),
		limit:     limit,
	}
	return &m
}