github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/iavl/key_format.go (about)

     1  package iavl
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  )
     7  
     8  // Provides a fixed-width lexicographically sortable []byte key format
     9  type KeyFormat struct {
    10  	prefix byte
    11  	layout []int
    12  	length int
    13  }
    14  
    15  // Create a []byte key format based on a single byte prefix and fixed width key segments each of whose length is
    16  // specified by by the corresponding element of layout.
    17  //
    18  // For example, to store keys that could index some objects by a version number and their SHA256 hash using the form:
    19  // 'c<version uint64><hash [32]byte>' then you would define the KeyFormat with:
    20  //
    21  //	var keyFormat = NewKeyFormat('c', 8, 32)
    22  //
    23  // Then you can create a key with:
    24  //
    25  //	func ObjectKey(version uint64, objectBytes []byte) []byte {
    26  //		hasher := sha256.New()
    27  //		hasher.Sum(nil)
    28  //		return keyFormat.Key(version, hasher.Sum(nil))
    29  //	}
    30  func NewKeyFormat(prefix byte, layout ...int) *KeyFormat {
    31  	// For prefix byte
    32  	length := 1
    33  	for _, l := range layout {
    34  		length += l
    35  	}
    36  	return &KeyFormat{
    37  		prefix: prefix,
    38  		layout: layout,
    39  		length: length,
    40  	}
    41  }
    42  
    43  // Format the byte segments into the key format - will panic if the segment lengths do not match the layout.
    44  func (kf *KeyFormat) KeyBytes(segments ...[]byte) []byte {
    45  	key := make([]byte, kf.length)
    46  	key[0] = kf.prefix
    47  	n := 1
    48  	for i, s := range segments {
    49  		l := kf.layout[i]
    50  		if len(s) > l {
    51  			panic(fmt.Errorf("length of segment %X provided to KeyFormat.KeyBytes() is longer than the %d bytes "+
    52  				"required by layout for segment %d", s, l, i))
    53  		}
    54  		n += l
    55  		// Big endian so pad on left if not given the full width for this segment
    56  		copy(key[n-len(s):n], s)
    57  	}
    58  	return key[:n]
    59  }
    60  
    61  // Format the args passed into the key format - will panic if the arguments passed do not match the length
    62  // of the segment to which they correspond. When called with no arguments returns the raw prefix (useful as a start
    63  // element of the entire keys space when sorted lexicographically).
    64  func (kf *KeyFormat) Key(args ...interface{}) []byte {
    65  	if len(args) > len(kf.layout) {
    66  		panic(fmt.Errorf("KeyFormat.Key() is provided with %d args but format only has %d segments",
    67  			len(args), len(kf.layout)))
    68  	}
    69  	segments := make([][]byte, len(args))
    70  	for i, a := range args {
    71  		segments[i] = format(a)
    72  	}
    73  	return kf.KeyBytes(segments...)
    74  }
    75  
    76  // Reads out the bytes associated with each segment of the key format from key.
    77  func (kf *KeyFormat) ScanBytes(key []byte) [][]byte {
    78  	segments := make([][]byte, len(kf.layout))
    79  	n := 1
    80  	for i, l := range kf.layout {
    81  		n += l
    82  		if n > len(key) {
    83  			return segments[:i]
    84  		}
    85  		segments[i] = key[n-l : n]
    86  	}
    87  	return segments
    88  }
    89  
    90  // Extracts the segments into the values pointed to by each of args. Each arg must be a pointer to int64, uint64, or
    91  // []byte, and the width of the args must match layout.
    92  func (kf *KeyFormat) Scan(key []byte, args ...interface{}) {
    93  	segments := kf.ScanBytes(key)
    94  	if len(args) > len(segments) {
    95  		panic(fmt.Errorf("KeyFormat.Scan() is provided with %d args but format only has %d segments in key %X",
    96  			len(args), len(segments), key))
    97  	}
    98  	for i, a := range args {
    99  		scan(a, segments[i])
   100  	}
   101  }
   102  
   103  // Return the prefix as a string.
   104  func (kf *KeyFormat) Prefix() string {
   105  	return string([]byte{kf.prefix})
   106  }
   107  
   108  func scan(a interface{}, value []byte) {
   109  	switch v := a.(type) {
   110  	case *int64:
   111  		// Negative values will be mapped correctly when read in as uint64 and then type converted
   112  		*v = int64(binary.BigEndian.Uint64(value))
   113  	case *uint64:
   114  		*v = binary.BigEndian.Uint64(value)
   115  	case *[]byte:
   116  		*v = value
   117  	default:
   118  		panic(fmt.Errorf("KeyFormat scan() does not support scanning value of type %T: %v", a, a))
   119  	}
   120  }
   121  
   122  func format(a interface{}) []byte {
   123  	switch v := a.(type) {
   124  	case uint64:
   125  		return formatUint64(v)
   126  	case int64:
   127  		return formatUint64(uint64(v))
   128  	// Provide formatting from int,uint as a convenience to avoid casting arguments
   129  	case uint:
   130  		return formatUint64(uint64(v))
   131  	case int:
   132  		return formatUint64(uint64(v))
   133  	case []byte:
   134  		return v
   135  	default:
   136  		panic(fmt.Errorf("KeyFormat format() does not support formatting value of type %T: %v", a, a))
   137  	}
   138  }
   139  
   140  func formatUint64(v uint64) []byte {
   141  	bs := make([]byte, 8)
   142  	binary.BigEndian.PutUint64(bs, v)
   143  	return bs
   144  }