github.com/MetalBlockchain/metalgo@v1.11.9/database/helpers.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package database 5 6 import ( 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "time" 11 12 "github.com/MetalBlockchain/metalgo/ids" 13 ) 14 15 const ( 16 Uint64Size = 8 // bytes 17 BoolSize = 1 // bytes 18 BoolFalse = 0x00 19 BoolTrue = 0x01 20 21 // kvPairOverhead is an estimated overhead for a kv pair in a database. 22 kvPairOverhead = 8 // bytes 23 ) 24 25 var ( 26 boolFalseKey = []byte{BoolFalse} 27 boolTrueKey = []byte{BoolTrue} 28 29 errWrongSize = errors.New("value has unexpected size") 30 ) 31 32 func PutID(db KeyValueWriter, key []byte, val ids.ID) error { 33 return db.Put(key, val[:]) 34 } 35 36 func GetID(db KeyValueReader, key []byte) (ids.ID, error) { 37 b, err := db.Get(key) 38 if err != nil { 39 return ids.Empty, err 40 } 41 return ids.ToID(b) 42 } 43 44 func ParseID(b []byte) (ids.ID, error) { 45 return ids.ToID(b) 46 } 47 48 func PutUInt64(db KeyValueWriter, key []byte, val uint64) error { 49 b := PackUInt64(val) 50 return db.Put(key, b) 51 } 52 53 func GetUInt64(db KeyValueReader, key []byte) (uint64, error) { 54 b, err := db.Get(key) 55 if err != nil { 56 return 0, err 57 } 58 return ParseUInt64(b) 59 } 60 61 func PackUInt64(val uint64) []byte { 62 bytes := make([]byte, Uint64Size) 63 binary.BigEndian.PutUint64(bytes, val) 64 return bytes 65 } 66 67 func ParseUInt64(b []byte) (uint64, error) { 68 if len(b) != Uint64Size { 69 return 0, errWrongSize 70 } 71 return binary.BigEndian.Uint64(b), nil 72 } 73 74 func PutUInt32(db KeyValueWriter, key []byte, val uint32) error { 75 b := PackUInt32(val) 76 return db.Put(key, b) 77 } 78 79 func GetUInt32(db KeyValueReader, key []byte) (uint32, error) { 80 b, err := db.Get(key) 81 if err != nil { 82 return 0, err 83 } 84 return ParseUInt32(b) 85 } 86 87 func PackUInt32(val uint32) []byte { 88 bytes := make([]byte, 4) 89 binary.BigEndian.PutUint32(bytes, val) 90 return bytes 91 } 92 93 func ParseUInt32(b []byte) (uint32, error) { 94 if len(b) != 4 { 95 return 0, errWrongSize 96 } 97 return binary.BigEndian.Uint32(b), nil 98 } 99 100 func PutTimestamp(db KeyValueWriter, key []byte, val time.Time) error { 101 valBytes, err := val.MarshalBinary() 102 if err != nil { 103 return err 104 } 105 return db.Put(key, valBytes) 106 } 107 108 func GetTimestamp(db KeyValueReader, key []byte) (time.Time, error) { 109 b, err := db.Get(key) 110 if err != nil { 111 return time.Time{}, err 112 } 113 return ParseTimestamp(b) 114 } 115 116 func ParseTimestamp(b []byte) (time.Time, error) { 117 val := time.Time{} 118 if err := val.UnmarshalBinary(b); err != nil { 119 return time.Time{}, err 120 } 121 return val, nil 122 } 123 124 func PutBool(db KeyValueWriter, key []byte, b bool) error { 125 if b { 126 return db.Put(key, boolTrueKey) 127 } 128 return db.Put(key, boolFalseKey) 129 } 130 131 func GetBool(db KeyValueReader, key []byte) (bool, error) { 132 b, err := db.Get(key) 133 switch { 134 case err != nil: 135 return false, err 136 case len(b) != BoolSize: 137 return false, fmt.Errorf("length should be %d but is %d", BoolSize, len(b)) 138 case b[0] != BoolFalse && b[0] != BoolTrue: 139 return false, fmt.Errorf("should be %d or %d but is %d", BoolFalse, BoolTrue, b[0]) 140 } 141 return b[0] == BoolTrue, nil 142 } 143 144 func Count(db Iteratee) (int, error) { 145 iterator := db.NewIterator() 146 defer iterator.Release() 147 148 count := 0 149 for iterator.Next() { 150 count++ 151 } 152 return count, iterator.Error() 153 } 154 155 func Size(db Iteratee) (int, error) { 156 iterator := db.NewIterator() 157 defer iterator.Release() 158 159 size := 0 160 for iterator.Next() { 161 size += len(iterator.Key()) + len(iterator.Value()) + kvPairOverhead 162 } 163 return size, iterator.Error() 164 } 165 166 func IsEmpty(db Iteratee) (bool, error) { 167 iterator := db.NewIterator() 168 defer iterator.Release() 169 170 return !iterator.Next(), iterator.Error() 171 } 172 173 func AtomicClear(readerDB Iteratee, deleterDB KeyValueDeleter) error { 174 return AtomicClearPrefix(readerDB, deleterDB, nil) 175 } 176 177 // AtomicClearPrefix deletes from [deleterDB] all keys in [readerDB] that have the given [prefix]. 178 func AtomicClearPrefix(readerDB Iteratee, deleterDB KeyValueDeleter, prefix []byte) error { 179 iterator := readerDB.NewIteratorWithPrefix(prefix) 180 defer iterator.Release() 181 182 for iterator.Next() { 183 key := iterator.Key() 184 if err := deleterDB.Delete(key); err != nil { 185 return err 186 } 187 } 188 return iterator.Error() 189 } 190 191 // Remove all key-value pairs from [db]. 192 // Writes each batch when it reaches [writeSize]. 193 func Clear(db Database, writeSize int) error { 194 return ClearPrefix(db, nil, writeSize) 195 } 196 197 // Removes all keys with the given [prefix] from [db]. 198 // Writes each batch when it reaches [writeSize]. 199 func ClearPrefix(db Database, prefix []byte, writeSize int) error { 200 b := db.NewBatch() 201 it := db.NewIteratorWithPrefix(prefix) 202 // Defer the release of the iterator inside a closure to guarantee that the 203 // latest, not the first, iterator is released on return. 204 defer func() { 205 it.Release() 206 }() 207 208 for it.Next() { 209 key := it.Key() 210 if err := b.Delete(key); err != nil { 211 return err 212 } 213 214 // Avoid too much memory pressure by periodically writing to the 215 // database. 216 if b.Size() < writeSize { 217 continue 218 } 219 220 if err := b.Write(); err != nil { 221 return err 222 } 223 b.Reset() 224 225 // Reset the iterator to release references to now deleted keys. 226 if err := it.Error(); err != nil { 227 return err 228 } 229 it.Release() 230 it = db.NewIteratorWithPrefix(prefix) 231 } 232 233 if err := b.Write(); err != nil { 234 return err 235 } 236 return it.Error() 237 }