github.com/klaytn/klaytn@v1.12.1/node/sc/bridgepool/sorted_map_list.go (about)

     1  // Modifications Copyright 2019 The klaytn Authors
     2  // Copyright 2016 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from core/tx_list.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package bridgepool
    22  
    23  import (
    24  	"container/heap"
    25  	"errors"
    26  	"fmt"
    27  	"sort"
    28  	"sync"
    29  )
    30  
    31  // nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
    32  // retrieving sorted transactions from the possibly gapped future queue.
    33  type nonceHeap []uint64
    34  
    35  func (h nonceHeap) Len() int           { return len(h) }
    36  func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
    37  func (h nonceHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }
    38  
    39  func (h *nonceHeap) Push(x interface{}) {
    40  	*h = append(*h, x.(uint64))
    41  }
    42  
    43  func (h *nonceHeap) Pop() interface{} {
    44  	old := *h
    45  	n := len(old)
    46  	x := old[n-1]
    47  	*h = old[0 : n-1]
    48  	return x
    49  }
    50  
    51  type itemWithNonce interface {
    52  	Nonce() uint64
    53  }
    54  
    55  type items []itemWithNonce
    56  
    57  func (s items) Len() int { return len(s) }
    58  func (s items) Less(i, j int) bool {
    59  	return s[i].Nonce() < s[j].Nonce()
    60  }
    61  func (s items) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    62  
    63  // ItemSortedMap is a nonce->item map with a heap based index to allow
    64  // iterating over the contents in a nonce-incrementing way.
    65  const (
    66  	UnlimitedItemSortedMap = -1
    67  )
    68  
    69  var ErrSizeLimit = errors.New("sorted map size limit")
    70  
    71  type ItemSortedMap struct {
    72  	mu        *sync.Mutex
    73  	items     map[uint64]itemWithNonce // Hash map storing the item data
    74  	index     *nonceHeap               // Heap of nonces of all the stored items (non-strict mode)
    75  	cache     items                    // Cache of the items already sorted
    76  	sizeLimit int                      // This is sizeLimit of the sorted map.
    77  }
    78  
    79  // NewItemSortedMap creates a new nonce-sorted item map.
    80  func NewItemSortedMap(size int) *ItemSortedMap {
    81  	return &ItemSortedMap{
    82  		mu:        new(sync.Mutex),
    83  		items:     make(map[uint64]itemWithNonce),
    84  		index:     new(nonceHeap),
    85  		sizeLimit: size,
    86  	}
    87  }
    88  
    89  // Get retrieves the current items associated with the given nonce.
    90  func (m *ItemSortedMap) Get(nonce uint64) itemWithNonce {
    91  	m.mu.Lock()
    92  	defer m.mu.Unlock()
    93  
    94  	return m.items[nonce]
    95  }
    96  
    97  // Exist returns if the nonce exist.
    98  func (m *ItemSortedMap) Exist(nonce uint64) bool {
    99  	m.mu.Lock()
   100  	defer m.mu.Unlock()
   101  
   102  	_, exist := m.items[nonce]
   103  
   104  	return exist
   105  }
   106  
   107  // Put inserts a new item into the map, also updating the map's nonce
   108  // index. If a item already exists with the same nonce, it's overwritten.
   109  func (m *ItemSortedMap) Put(event itemWithNonce) error {
   110  	m.mu.Lock()
   111  	defer m.mu.Unlock()
   112  
   113  	nonce := event.Nonce()
   114  	if m.sizeLimit != UnlimitedItemSortedMap && len(m.items) >= m.sizeLimit && m.items[nonce] == nil {
   115  		return fmt.Errorf("failed to put %v nonce : %w : %v", event.Nonce(), ErrSizeLimit, m.sizeLimit)
   116  	}
   117  
   118  	if m.items[nonce] == nil {
   119  		heap.Push(m.index, nonce)
   120  	}
   121  	m.items[nonce], m.cache = event, nil
   122  
   123  	return nil
   124  }
   125  
   126  // Pop removes given count number of minimum nonce items from the map.
   127  // Every removed items is returned for any post-removal maintenance.
   128  func (m *ItemSortedMap) Pop(count int) items {
   129  	m.mu.Lock()
   130  	defer m.mu.Unlock()
   131  
   132  	// Otherwise start accumulating incremental events
   133  	var ready items
   134  	for m.index.Len() > 0 && len(ready) < count {
   135  		nonce := (*m.index)[0]
   136  		ready = append(ready, m.items[nonce])
   137  		delete(m.items, nonce)
   138  		heap.Pop(m.index)
   139  	}
   140  
   141  	return ready
   142  }
   143  
   144  // Ready retrieves a sequentially increasing list of events starting at the
   145  // provided nonce that is ready for processing. The returned events will be
   146  // removed from the list.
   147  //
   148  // Note, all events with nonces lower than start will also be returned to
   149  // prevent getting into and invalid state. This is not something that should ever
   150  // happen but better to be self correcting than failing!
   151  func (m *ItemSortedMap) Ready(start uint64) items {
   152  	m.mu.Lock()
   153  	defer m.mu.Unlock()
   154  
   155  	// Short circuit if no events are available
   156  	if m.index.Len() == 0 || (*m.index)[0] > start {
   157  		return nil
   158  	}
   159  	// Otherwise start accumulating incremental events
   160  	var ready items
   161  	for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
   162  		ready = append(ready, m.items[next])
   163  		delete(m.items, next)
   164  		heap.Pop(m.index)
   165  	}
   166  
   167  	return ready
   168  }
   169  
   170  // Filter iterates over the list of items and removes all of them for which
   171  // the specified function evaluates to true.
   172  func (m *ItemSortedMap) Filter(filter func(interface{}) bool) items {
   173  	m.mu.Lock()
   174  	defer m.mu.Unlock()
   175  
   176  	var removed items
   177  
   178  	// Collect all the items to filter out
   179  	for nonce, tx := range m.items {
   180  		if filter(tx) {
   181  			removed = append(removed, tx)
   182  			delete(m.items, nonce)
   183  		}
   184  	}
   185  	// If items were removed, the heap and cache are ruined
   186  	if len(removed) > 0 {
   187  		*m.index = make([]uint64, 0, len(m.items))
   188  		for nonce := range m.items {
   189  			*m.index = append(*m.index, nonce)
   190  		}
   191  		heap.Init(m.index)
   192  
   193  		m.cache = nil
   194  	}
   195  	return removed
   196  }
   197  
   198  // Remove deletes a item from the maintained map, returning whether the
   199  // item was found.
   200  func (m *ItemSortedMap) Remove(nonce uint64) bool {
   201  	m.mu.Lock()
   202  	defer m.mu.Unlock()
   203  
   204  	// Short circuit if no item is present
   205  	_, ok := m.items[nonce]
   206  	if !ok {
   207  		return false
   208  	}
   209  	// Otherwise delete the item and fix the heap index
   210  	for i := 0; i < m.index.Len(); i++ {
   211  		if (*m.index)[i] == nonce {
   212  			heap.Remove(m.index, i)
   213  			break
   214  		}
   215  	}
   216  	delete(m.items, nonce)
   217  	m.cache = nil
   218  
   219  	return true
   220  }
   221  
   222  // Forward removes all items from the map with a nonce lower than the
   223  // provided threshold. Every removed transaction is returned for any post-removal
   224  // maintenance.
   225  func (m *ItemSortedMap) Forward(threshold uint64) items {
   226  	m.mu.Lock()
   227  	defer m.mu.Unlock()
   228  	var removed items
   229  
   230  	// Pop off heap items until the threshold is reached
   231  	for m.index.Len() > 0 && (*m.index)[0] < threshold {
   232  		nonce := heap.Pop(m.index).(uint64)
   233  		removed = append(removed, m.items[nonce])
   234  		delete(m.items, nonce)
   235  	}
   236  	// If we had a cached order, shift the front
   237  	if m.cache != nil {
   238  		m.cache = m.cache[len(removed):]
   239  	}
   240  	return removed
   241  }
   242  
   243  // Len returns the length of the map.
   244  func (m *ItemSortedMap) Len() int {
   245  	m.mu.Lock()
   246  	defer m.mu.Unlock()
   247  
   248  	return len(m.items)
   249  }
   250  
   251  // Flatten returns a nonce-sorted slice of items based on the loosely
   252  // sorted internal representation. The result of the sorting is cached in case
   253  // it's requested again before any modifications are made to the contents.
   254  func (m *ItemSortedMap) Flatten() items {
   255  	return m.FlattenByCount(0)
   256  }
   257  
   258  // FlattenByCount returns requested number of nonce-sorted slice of cached
   259  // items. The result of the sorting is cached like as Flatten method.
   260  func (m *ItemSortedMap) FlattenByCount(count int) items {
   261  	m.mu.Lock()
   262  	defer m.mu.Unlock()
   263  	// If the sorting was not cached yet, create and cache it
   264  	if m.cache == nil {
   265  		m.cache = make(items, 0, len(m.items))
   266  		for _, tx := range m.items {
   267  			m.cache = append(m.cache, tx)
   268  		}
   269  		sort.Sort(m.cache)
   270  	}
   271  	txLen := len(m.cache)
   272  	if count != 0 && txLen > count {
   273  		txLen = count
   274  	}
   275  	return m.cache[:txLen]
   276  }