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  }