github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/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  }