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 }