github.com/ethersphere/bee/v2@v2.2.0/pkg/shed/field_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 // Uint64Field provides a way to have a simple counter in the database. 28 // It transparently encodes uint64 type value to bytes. 29 type Uint64Field struct { 30 db *DB 31 key []byte 32 } 33 34 // NewUint64Field returns a new Uint64Field. 35 // It validates its name and type against the database schema. 36 func (db *DB) NewUint64Field(name string) (f Uint64Field, err error) { 37 key, err := db.schemaFieldKey(name, "uint64") 38 if err != nil { 39 return f, fmt.Errorf("get schema key: %w", err) 40 } 41 return Uint64Field{ 42 db: db, 43 key: key, 44 }, nil 45 } 46 47 // Get retrieves a uint64 value 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 Uint64Field) Get() (val uint64, err error) { 51 b, err := f.db.Get(f.key) 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 Uint64Field) Put(val uint64) (err error) { 63 return f.db.Put(f.key, encodeUint64(val)) 64 } 65 66 // PutInBatch stores a uint64 value in a batch 67 // that can be saved later in the database. 68 func (f Uint64Field) PutInBatch(batch *leveldb.Batch, val uint64) { 69 batch.Put(f.key, encodeUint64(val)) 70 } 71 72 // Inc increments a uint64 value in the database. 73 // This operation is not goroutine save. 74 func (f Uint64Field) Inc() (val uint64, err error) { 75 val, err = f.Get() 76 if err != nil { 77 if errors.Is(err, leveldb.ErrNotFound) { 78 val = 0 79 } else { 80 return 0, fmt.Errorf("get value: %w", err) 81 } 82 } 83 val++ 84 return val, f.Put(val) 85 } 86 87 // IncInBatch increments a uint64 value in the batch 88 // by retrieving a value from the database, not the same batch. 89 // This operation is not goroutine save. 90 func (f Uint64Field) IncInBatch(batch *leveldb.Batch) (val uint64, err error) { 91 val, err = f.Get() 92 if err != nil { 93 if errors.Is(err, leveldb.ErrNotFound) { 94 val = 0 95 } else { 96 return 0, fmt.Errorf("get value: %w", err) 97 } 98 } 99 val++ 100 f.PutInBatch(batch, val) 101 return val, nil 102 } 103 104 // Dec decrements a uint64 value in the database. 105 // This operation is not goroutine save. 106 // The field is protected from overflow to a negative value. 107 func (f Uint64Field) Dec() (val uint64, err error) { 108 val, err = f.Get() 109 if err != nil { 110 if errors.Is(err, leveldb.ErrNotFound) { 111 val = 0 112 } else { 113 return 0, fmt.Errorf("get value: %w", err) 114 } 115 } 116 if val != 0 { 117 val-- 118 } 119 return val, f.Put(val) 120 } 121 122 // DecInBatch decrements a uint64 value in the batch 123 // by retrieving a value from the database, not the same batch. 124 // This operation is not goroutine save. 125 // The field is protected from overflow to a negative value. 126 func (f Uint64Field) DecInBatch(batch *leveldb.Batch) (val uint64, err error) { 127 val, err = f.Get() 128 if err != nil { 129 if errors.Is(err, leveldb.ErrNotFound) { 130 val = 0 131 } else { 132 return 0, fmt.Errorf("get value: %w", err) 133 } 134 } 135 if val != 0 { 136 val-- 137 } 138 f.PutInBatch(batch, val) 139 return val, nil 140 } 141 142 // encode transforms uint64 to 8 byte long 143 // slice in big endian encoding. 144 func encodeUint64(val uint64) (b []byte) { 145 b = make([]byte, 8) 146 binary.BigEndian.PutUint64(b, val) 147 return b 148 }