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  }