github.com/ethersphere/bee/v2@v2.2.0/pkg/shed/vector_uint64.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package shed 18 19 import ( 20 "encoding/binary" 21 "errors" 22 "fmt" 23 24 "github.com/syndtr/goleveldb/leveldb" 25 ) 26 27 // Uint64Vector provides a way to have multiple counters in the database. 28 // It transparently encodes uint64 type value to bytes. 29 type Uint64Vector struct { 30 db *DB 31 key []byte 32 } 33 34 // NewUint64Vector returns a new Uint64Vector. 35 // It validates its name and type against the database schema. 36 func (db *DB) NewUint64Vector(name string) (f Uint64Vector, err error) { 37 key, err := db.schemaFieldKey(name, "vector-uint64") 38 if err != nil { 39 return f, fmt.Errorf("get schema key: %w", err) 40 } 41 return Uint64Vector{ 42 db: db, 43 key: key, 44 }, nil 45 } 46 47 // Get retrieves a uint64 value at index i from the database. 48 // If the value is not found in the database a 0 value 49 // is returned and no error. 50 func (f Uint64Vector) Get(i uint64) (val uint64, err error) { 51 b, err := f.db.Get(f.indexKey(i)) 52 if err != nil { 53 if errors.Is(err, leveldb.ErrNotFound) { 54 return 0, nil 55 } 56 return 0, err 57 } 58 return binary.BigEndian.Uint64(b), nil 59 } 60 61 // Put encodes uin64 value and stores it in the database. 62 func (f Uint64Vector) Put(i, val uint64) (err error) { 63 return f.db.Put(f.indexKey(i), encodeUint64(val)) 64 } 65 66 // PutInBatch stores a uint64 value at index i in a batch 67 // that can be saved later in the database. 68 func (f Uint64Vector) PutInBatch(batch *leveldb.Batch, i, val uint64) { 69 batch.Put(f.indexKey(i), encodeUint64(val)) 70 } 71 72 // Inc increments a uint64 value in the database. 73 // This operation is not goroutine safe. 74 func (f Uint64Vector) Inc(i uint64) (val uint64, err error) { 75 val, err = f.Get(i) 76 if err != nil { 77 if errors.Is(err, leveldb.ErrNotFound) { 78 val = 0 79 } else { 80 return 0, err 81 } 82 } 83 val++ 84 return val, f.Put(i, val) 85 } 86 87 // IncInBatch increments a uint64 value at index i in the batch 88 // by retrieving a value from the database, not the same batch. 89 // This operation is not goroutine safe. 90 func (f Uint64Vector) IncInBatch(batch *leveldb.Batch, i uint64) (val uint64, err error) { 91 val, err = f.Get(i) 92 if err != nil { 93 if errors.Is(err, leveldb.ErrNotFound) { 94 val = 0 95 } else { 96 return 0, err 97 } 98 } 99 val++ 100 f.PutInBatch(batch, i, val) 101 return val, nil 102 } 103 104 // Dec decrements a uint64 value at index i in the database. 105 // This operation is not goroutine safe. 106 // The field is protected from overflow to a negative value. 107 func (f Uint64Vector) Dec(i uint64) (val uint64, err error) { 108 val, err = f.Get(i) 109 if err != nil { 110 if errors.Is(err, leveldb.ErrNotFound) { 111 val = 0 112 } else { 113 return 0, err 114 } 115 } 116 if val != 0 { 117 val-- 118 } 119 return val, f.Put(i, val) 120 } 121 122 // DecInBatch decrements a uint64 value at index i in the batch 123 // by retrieving a value from the database, not the same batch. 124 // This operation is not goroutine safe. 125 // The field is protected from overflow to a negative value. 126 func (f Uint64Vector) DecInBatch(batch *leveldb.Batch, i uint64) (val uint64, err error) { 127 val, err = f.Get(i) 128 if err != nil { 129 if errors.Is(err, leveldb.ErrNotFound) { 130 val = 0 131 } else { 132 return 0, err 133 } 134 } 135 if val != 0 { 136 val-- 137 } 138 f.PutInBatch(batch, i, val) 139 return val, nil 140 } 141 142 // indexKey concatenates field prefix and vector index 143 // returning a unique database key for a specific vector element. 144 func (f Uint64Vector) indexKey(i uint64) (key []byte) { 145 b := make([]byte, 8) 146 binary.BigEndian.PutUint64(b, i) 147 return append(f.key, b...) 148 }