github.com/MetalBlockchain/metalgo@v1.11.9/database/memdb/db.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package memdb 5 6 import ( 7 "context" 8 "slices" 9 "strings" 10 "sync" 11 12 "github.com/MetalBlockchain/metalgo/database" 13 ) 14 15 const ( 16 // Name is the name of this database for database switches 17 Name = "memdb" 18 19 // DefaultSize is the default initial size of the memory database 20 DefaultSize = 1024 21 ) 22 23 var ( 24 _ database.Database = (*Database)(nil) 25 _ database.Batch = (*batch)(nil) 26 _ database.Iterator = (*iterator)(nil) 27 ) 28 29 // Database is an ephemeral key-value store that implements the Database 30 // interface. 31 type Database struct { 32 lock sync.RWMutex 33 db map[string][]byte 34 } 35 36 // New returns a map with the Database interface methods implemented. 37 func New() *Database { 38 return NewWithSize(DefaultSize) 39 } 40 41 // NewWithSize returns a map pre-allocated to the provided size with the 42 // Database interface methods implemented. 43 func NewWithSize(size int) *Database { 44 return &Database{db: make(map[string][]byte, size)} 45 } 46 47 func (db *Database) Close() error { 48 db.lock.Lock() 49 defer db.lock.Unlock() 50 51 if db.db == nil { 52 return database.ErrClosed 53 } 54 db.db = nil 55 return nil 56 } 57 58 func (db *Database) isClosed() bool { 59 db.lock.RLock() 60 defer db.lock.RUnlock() 61 62 return db.db == nil 63 } 64 65 func (db *Database) Has(key []byte) (bool, error) { 66 db.lock.RLock() 67 defer db.lock.RUnlock() 68 69 if db.db == nil { 70 return false, database.ErrClosed 71 } 72 _, ok := db.db[string(key)] 73 return ok, nil 74 } 75 76 func (db *Database) Get(key []byte) ([]byte, error) { 77 db.lock.RLock() 78 defer db.lock.RUnlock() 79 80 if db.db == nil { 81 return nil, database.ErrClosed 82 } 83 if entry, ok := db.db[string(key)]; ok { 84 return slices.Clone(entry), nil 85 } 86 return nil, database.ErrNotFound 87 } 88 89 func (db *Database) Put(key []byte, value []byte) error { 90 db.lock.Lock() 91 defer db.lock.Unlock() 92 93 if db.db == nil { 94 return database.ErrClosed 95 } 96 db.db[string(key)] = slices.Clone(value) 97 return nil 98 } 99 100 func (db *Database) Delete(key []byte) error { 101 db.lock.Lock() 102 defer db.lock.Unlock() 103 104 if db.db == nil { 105 return database.ErrClosed 106 } 107 delete(db.db, string(key)) 108 return nil 109 } 110 111 func (db *Database) NewBatch() database.Batch { 112 return &batch{db: db} 113 } 114 115 func (db *Database) NewIterator() database.Iterator { 116 return db.NewIteratorWithStartAndPrefix(nil, nil) 117 } 118 119 func (db *Database) NewIteratorWithStart(start []byte) database.Iterator { 120 return db.NewIteratorWithStartAndPrefix(start, nil) 121 } 122 123 func (db *Database) NewIteratorWithPrefix(prefix []byte) database.Iterator { 124 return db.NewIteratorWithStartAndPrefix(nil, prefix) 125 } 126 127 func (db *Database) NewIteratorWithStartAndPrefix(start, prefix []byte) database.Iterator { 128 db.lock.RLock() 129 defer db.lock.RUnlock() 130 131 if db.db == nil { 132 return &database.IteratorError{ 133 Err: database.ErrClosed, 134 } 135 } 136 137 startString := string(start) 138 prefixString := string(prefix) 139 keys := make([]string, 0, len(db.db)) 140 for key := range db.db { 141 if strings.HasPrefix(key, prefixString) && key >= startString { 142 keys = append(keys, key) 143 } 144 } 145 slices.Sort(keys) // Keys need to be in sorted order 146 values := make([][]byte, 0, len(keys)) 147 for _, key := range keys { 148 values = append(values, db.db[key]) 149 } 150 return &iterator{ 151 db: db, 152 keys: keys, 153 values: values, 154 } 155 } 156 157 func (db *Database) Compact(_, _ []byte) error { 158 db.lock.RLock() 159 defer db.lock.RUnlock() 160 161 if db.db == nil { 162 return database.ErrClosed 163 } 164 return nil 165 } 166 167 func (db *Database) HealthCheck(context.Context) (interface{}, error) { 168 if db.isClosed() { 169 return nil, database.ErrClosed 170 } 171 return nil, nil 172 } 173 174 type batch struct { 175 database.BatchOps 176 177 db *Database 178 } 179 180 func (b *batch) Write() error { 181 b.db.lock.Lock() 182 defer b.db.lock.Unlock() 183 184 if b.db.db == nil { 185 return database.ErrClosed 186 } 187 188 for _, op := range b.Ops { 189 if op.Delete { 190 delete(b.db.db, string(op.Key)) 191 } else { 192 b.db.db[string(op.Key)] = op.Value 193 } 194 } 195 return nil 196 } 197 198 func (b *batch) Inner() database.Batch { 199 return b 200 } 201 202 type iterator struct { 203 db *Database 204 initialized bool 205 keys []string 206 values [][]byte 207 err error 208 } 209 210 func (it *iterator) Next() bool { 211 // Short-circuit and set an error if the underlying database has been closed. 212 if it.db.isClosed() { 213 it.keys = nil 214 it.values = nil 215 it.err = database.ErrClosed 216 return false 217 } 218 219 // If the iterator was not yet initialized, do it now 220 if !it.initialized { 221 it.initialized = true 222 return len(it.keys) > 0 223 } 224 // Iterator already initialize, advance it 225 if len(it.keys) > 0 { 226 it.keys[0] = "" 227 it.keys = it.keys[1:] 228 it.values[0] = nil 229 it.values = it.values[1:] 230 } 231 return len(it.keys) > 0 232 } 233 234 func (it *iterator) Error() error { 235 return it.err 236 } 237 238 func (it *iterator) Key() []byte { 239 if len(it.keys) > 0 { 240 return []byte(it.keys[0]) 241 } 242 return nil 243 } 244 245 func (it *iterator) Value() []byte { 246 if len(it.values) > 0 { 247 return it.values[0] 248 } 249 return nil 250 } 251 252 func (it *iterator) Release() { 253 it.keys = nil 254 it.values = nil 255 }