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 }