github.com/ethersphere/bee/v2@v2.2.0/pkg/storage/inmemstore/inmemstore.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package inmemstore 6 7 import ( 8 "errors" 9 "fmt" 10 "strings" 11 "sync" 12 13 "github.com/armon/go-radix" 14 "github.com/ethersphere/bee/v2/pkg/storage" 15 ) 16 17 const ( 18 separator = "/" 19 ) 20 21 // Store implements an in-memory Store. We will use the hashicorp/go-radix implementation. 22 // This pkg provides a mutable radix which gives O(k) lookup and ordered iteration. 23 type Store struct { 24 st *radix.Tree 25 mu sync.RWMutex 26 } 27 28 func New() *Store { 29 return &Store{st: radix.New()} 30 } 31 32 func key(i storage.Key) string { 33 return strings.Join([]string{i.Namespace(), i.ID()}, separator) 34 } 35 36 func idFromKey(key, pfx string) string { 37 return strings.TrimPrefix(key, pfx+separator) 38 } 39 40 func (s *Store) Get(i storage.Item) error { 41 s.mu.RLock() 42 val, found := s.st.Get(key(i)) 43 s.mu.RUnlock() 44 if !found { 45 return storage.ErrNotFound 46 } 47 48 err := i.Unmarshal(val.([]byte)) 49 if err != nil { 50 return fmt.Errorf("failed unmarshaling item: %w", err) 51 } 52 53 return nil 54 } 55 56 func (s *Store) Has(k storage.Key) (bool, error) { 57 s.mu.RLock() 58 _, found := s.st.Get(key(k)) 59 s.mu.RUnlock() 60 61 return found, nil 62 } 63 64 func (s *Store) GetSize(k storage.Key) (int, error) { 65 s.mu.RLock() 66 val, found := s.st.Get(key(k)) 67 s.mu.RUnlock() 68 if !found { 69 return 0, storage.ErrNotFound 70 } 71 72 return len(val.([]byte)), nil 73 } 74 75 func (s *Store) Put(i storage.Item) error { 76 s.mu.Lock() 77 defer s.mu.Unlock() 78 return s.put(i) 79 } 80 81 func (s *Store) put(i storage.Item) error { 82 val, err := i.Marshal() 83 if err != nil { 84 return fmt.Errorf("failed marshaling item: %w", err) 85 } 86 87 s.st.Insert(key(i), val) 88 return nil 89 } 90 91 func (s *Store) Delete(i storage.Item) error { 92 s.mu.Lock() 93 defer s.mu.Unlock() 94 return s.delete(i) 95 } 96 97 func (s *Store) delete(i storage.Item) error { 98 s.st.Delete(key(i)) 99 return nil 100 } 101 102 func (s *Store) Count(k storage.Key) (int, error) { 103 s.mu.RLock() 104 defer s.mu.RUnlock() 105 106 count := 0 107 s.st.WalkPrefix(k.Namespace(), func(_ string, _ interface{}) bool { 108 count++ 109 return false 110 }) 111 return count, nil 112 } 113 114 func (s *Store) Iterate(q storage.Query, fn storage.IterateFn) error { 115 if err := q.Validate(); err != nil { 116 return fmt.Errorf("failed iteration: %w", err) 117 } 118 119 getNext := func(k string, v interface{}) (*storage.Result, error) { 120 for _, filter := range q.Filters { 121 if filter(idFromKey(k, q.Factory().Namespace()), v.([]byte)) { 122 return nil, nil 123 } 124 } 125 var res *storage.Result 126 switch q.ItemProperty { 127 case storage.QueryItemID, storage.QueryItemSize: 128 res = &storage.Result{ID: idFromKey(k, q.Factory().Namespace()), Size: len(v.([]byte))} 129 case storage.QueryItem: 130 newItem := q.Factory() 131 err := newItem.Unmarshal(v.([]byte)) 132 if err != nil { 133 return nil, fmt.Errorf("failed unmarshaling: %w", err) 134 } 135 res = &storage.Result{ID: idFromKey(k, q.Factory().Namespace()), Entry: newItem} 136 } 137 return res, nil 138 } 139 140 var prefix string 141 if q.PrefixAtStart { 142 prefix = q.Factory().Namespace() 143 } else { 144 prefix = q.Factory().Namespace() + separator + q.Prefix 145 } 146 147 var ( 148 retErr error 149 firstSkipped = !q.SkipFirst 150 skipUntil = false 151 ) 152 153 s.mu.RLock() 154 defer s.mu.RUnlock() 155 156 switch q.Order { 157 case storage.KeyAscendingOrder: 158 s.st.WalkPrefix(prefix, func(k string, v interface{}) bool { 159 160 if q.PrefixAtStart && !skipUntil { 161 if k >= prefix+separator+q.Prefix { 162 skipUntil = true 163 } else { 164 return false 165 } 166 } 167 168 if q.SkipFirst && !firstSkipped { 169 firstSkipped = true 170 return false 171 } 172 173 res, err := getNext(k, v) 174 if err != nil { 175 retErr = errors.Join(retErr, err) 176 return true 177 } 178 if res != nil { 179 stop, err := fn(*res) 180 if err != nil { 181 retErr = errors.Join(retErr, fmt.Errorf("failed in iterate function: %w", err)) 182 return true 183 } 184 return stop 185 } 186 return false 187 }) 188 case storage.KeyDescendingOrder: 189 // currently there is no optimal way to do reverse iteration. We can efficiently do forward 190 // iteration. So we have two options, first is to reduce time complexity by compromising 191 // on space complexity. So we keep track of keys and values during forward iteration 192 // to do a simple reverse iteration. Other option is to reduce space complexity by keeping 193 // track of only keys during forward iteration, then use Get to read the value on reverse 194 // iteration. This would involve additional complexity of doing a Get on reverse iteration. 195 // For now, inmem implementation is not meant to work for large datasets, so first option 196 // is chosen. 197 results := make([]storage.Result, 0) 198 s.st.WalkPrefix(prefix, func(k string, v interface{}) bool { 199 res, err := getNext(k, v) 200 if err != nil { 201 retErr = errors.Join(retErr, err) 202 return true 203 } 204 if res != nil { 205 results = append(results, *res) 206 } 207 return false 208 }) 209 if retErr != nil { 210 break 211 } 212 for i := len(results) - 1; i >= 0; i-- { 213 if q.SkipFirst && !firstSkipped { 214 firstSkipped = true 215 continue 216 } 217 stop, err := fn(results[i]) 218 if err != nil { 219 return fmt.Errorf("failed in iterate function: %w", err) 220 } 221 if stop { 222 break 223 } 224 } 225 } 226 return retErr 227 } 228 229 func (s *Store) Close() error { 230 return nil 231 }