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  }