github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/storage/memory_store.go (about)

     1  package storage
     2  
     3  import (
     4  	"bytes"
     5  	"sort"
     6  	"strings"
     7  	"sync"
     8  )
     9  
    10  // MemoryStore is an in-memory implementation of a Store, mainly
    11  // used for testing. Do not use MemoryStore in production.
    12  type MemoryStore struct {
    13  	mut  sync.RWMutex
    14  	mem  map[string][]byte
    15  	stor map[string][]byte
    16  }
    17  
    18  // NewMemoryStore creates a new MemoryStore object.
    19  func NewMemoryStore() *MemoryStore {
    20  	return &MemoryStore{
    21  		mem:  make(map[string][]byte),
    22  		stor: make(map[string][]byte),
    23  	}
    24  }
    25  
    26  // Get implements the Store interface.
    27  func (s *MemoryStore) Get(key []byte) ([]byte, error) {
    28  	s.mut.RLock()
    29  	defer s.mut.RUnlock()
    30  	m := s.chooseMap(key)
    31  	if val, ok := m[string(key)]; ok && val != nil {
    32  		return val, nil
    33  	}
    34  	return nil, ErrKeyNotFound
    35  }
    36  
    37  func (s *MemoryStore) chooseMap(key []byte) map[string][]byte {
    38  	switch KeyPrefix(key[0]) {
    39  	case STStorage, STTempStorage:
    40  		return s.stor
    41  	default:
    42  		return s.mem
    43  	}
    44  }
    45  
    46  // put puts a key-value pair into the store, it's supposed to be called
    47  // with mutex locked.
    48  func put(m map[string][]byte, key string, value []byte) {
    49  	m[key] = value
    50  }
    51  
    52  // PutChangeSet implements the Store interface. Never returns an error.
    53  func (s *MemoryStore) PutChangeSet(puts map[string][]byte, stores map[string][]byte) error {
    54  	s.mut.Lock()
    55  	s.putChangeSet(puts, stores)
    56  	s.mut.Unlock()
    57  	return nil
    58  }
    59  
    60  func (s *MemoryStore) putChangeSet(puts map[string][]byte, stores map[string][]byte) {
    61  	for k := range puts {
    62  		put(s.mem, k, puts[k])
    63  	}
    64  	for k := range stores {
    65  		put(s.stor, k, stores[k])
    66  	}
    67  }
    68  
    69  // Seek implements the Store interface.
    70  func (s *MemoryStore) Seek(rng SeekRange, f func(k, v []byte) bool) {
    71  	s.seek(rng, f, s.mut.RLock, s.mut.RUnlock)
    72  }
    73  
    74  // SeekGC implements the Store interface.
    75  func (s *MemoryStore) SeekGC(rng SeekRange, keep func(k, v []byte) bool) error {
    76  	noop := func() {}
    77  	// Keep RW lock for the whole Seek time, state must be consistent across whole
    78  	// operation and we call delete in the handler.
    79  	s.mut.Lock()
    80  	// We still need to perform normal seek, some GC operations can be
    81  	// sensitive to the order of KV pairs.
    82  	s.seek(rng, func(k, v []byte) bool {
    83  		if !keep(k, v) {
    84  			delete(s.chooseMap(k), string(k))
    85  		}
    86  		return true
    87  	}, noop, noop)
    88  	s.mut.Unlock()
    89  	return nil
    90  }
    91  
    92  // seek is an internal unlocked implementation of Seek. `start` denotes whether
    93  // seeking starting from the provided prefix should be performed. Backwards
    94  // seeking from some point is supported with corresponding SeekRange field set.
    95  func (s *MemoryStore) seek(rng SeekRange, f func(k, v []byte) bool, lock func(), unlock func()) {
    96  	sPrefix := string(rng.Prefix)
    97  	lPrefix := len(sPrefix)
    98  	sStart := string(rng.Start)
    99  	lStart := len(sStart)
   100  	var memList []KeyValue
   101  
   102  	isKeyOK := func(key string) bool {
   103  		return strings.HasPrefix(key, sPrefix) && (lStart == 0 || strings.Compare(key[lPrefix:], sStart) >= 0)
   104  	}
   105  	if rng.Backwards {
   106  		isKeyOK = func(key string) bool {
   107  			return strings.HasPrefix(key, sPrefix) && (lStart == 0 || strings.Compare(key[lPrefix:], sStart) <= 0)
   108  		}
   109  	}
   110  	less := func(k1, k2 []byte) bool {
   111  		res := bytes.Compare(k1, k2)
   112  		return res != 0 && rng.Backwards == (res > 0)
   113  	}
   114  
   115  	lock()
   116  	m := s.chooseMap(rng.Prefix)
   117  	for k, v := range m {
   118  		if v != nil && isKeyOK(k) {
   119  			memList = append(memList, KeyValue{
   120  				Key:   []byte(k),
   121  				Value: v,
   122  			})
   123  		}
   124  	}
   125  	unlock()
   126  	sort.Slice(memList, func(i, j int) bool {
   127  		return less(memList[i].Key, memList[j].Key)
   128  	})
   129  	for _, kv := range memList {
   130  		if !f(kv.Key, kv.Value) {
   131  			break
   132  		}
   133  	}
   134  }
   135  
   136  // Close implements Store interface and clears up memory. Never returns an
   137  // error.
   138  func (s *MemoryStore) Close() error {
   139  	s.mut.Lock()
   140  	s.mem = nil
   141  	s.stor = nil
   142  	s.mut.Unlock()
   143  	return nil
   144  }