github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/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 unbounded bool 14 } 15 16 // Create a []byte key format based on a single byte prefix and fixed width key segments each of whose length is 17 // specified by by the corresponding element of layout. 18 // 19 // For example, to store keys that could index some objects by a version number and their SHA256 hash using the form: 20 // 'c<version uint64><hash [32]byte>' then you would define the KeyFormat with: 21 // 22 // var keyFormat = NewKeyFormat('c', 8, 32) 23 // 24 // Then you can create a key with: 25 // 26 // func ObjectKey(version uint64, objectBytes []byte) []byte { 27 // hasher := sha256.New() 28 // hasher.Sum(nil) 29 // return keyFormat.Key(version, hasher.Sum(nil)) 30 // } 31 // if the last term of the layout ends in 0 32 func NewKeyFormat(prefix byte, layout ...int) *KeyFormat { 33 // For prefix byte 34 length := 1 35 for i, l := range layout { 36 length += l 37 if l == 0 && i != len(layout)-1 { 38 panic("Only the last item in a key format can be 0") 39 } 40 } 41 return &KeyFormat{ 42 prefix: prefix, 43 layout: layout, 44 length: length, 45 unbounded: len(layout) > 0 && layout[len(layout)-1] == 0, 46 } 47 } 48 49 // Format the byte segments into the key format - will panic if the segment lengths do not match the layout. 50 func (kf *KeyFormat) KeyBytes(segments ...[]byte) []byte { 51 keyLen := kf.length 52 // In case segments length is less than layouts length, 53 // we don't have to allocate the whole kf.length, just 54 // enough space to store the segments. 55 if len(segments) < len(kf.layout) { 56 keyLen = 1 57 for i := range segments { 58 keyLen += kf.layout[i] 59 } 60 } 61 62 if kf.unbounded { 63 if len(segments) > 0 { 64 keyLen += len(segments[len(segments)-1]) 65 } 66 } 67 68 key := make([]byte, keyLen) 69 key[0] = kf.prefix 70 n := 1 71 for i, s := range segments { 72 l := kf.layout[i] 73 switch l { 74 case 0: 75 // If the expected segment length is unbounded, increase it by `string length` 76 n += len(s) 77 default: 78 if len(s) > l { 79 panic(fmt.Errorf("length of segment %X provided to KeyFormat.KeyBytes() is longer than the %d bytes "+ 80 "required by layout for segment %d", s, l, i)) 81 } 82 // Otherwise increase n by the segment length 83 n += l 84 } 85 // Big endian so pad on left if not given the full width for this segment 86 copy(key[n-len(s):n], s) 87 } 88 return key[:n] 89 } 90 91 // Format the args passed into the key format - will panic if the arguments passed do not match the length 92 // of the segment to which they correspond. When called with no arguments returns the raw prefix (useful as a start 93 // element of the entire keys space when sorted lexicographically). 94 func (kf *KeyFormat) Key(args ...interface{}) []byte { 95 if len(args) > len(kf.layout) { 96 panic(fmt.Errorf("keyFormat.Key() is provided with %d args but format only has %d segments", 97 len(args), len(kf.layout))) 98 } 99 segments := make([][]byte, len(args)) 100 for i, a := range args { 101 segments[i] = format(a) 102 } 103 return kf.KeyBytes(segments...) 104 } 105 106 // Reads out the bytes associated with each segment of the key format from key. 107 func (kf *KeyFormat) ScanBytes(key []byte) [][]byte { 108 segments := make([][]byte, len(kf.layout)) 109 n := 1 110 for i, l := range kf.layout { 111 n += l 112 // if current section is longer than key, then there are no more subsequent segments. 113 if n > len(key) { 114 return segments[:i] 115 } 116 // if unbounded, segment is rest of key 117 if l == 0 { 118 segments[i] = key[n:] 119 break 120 } else { 121 segments[i] = key[n-l : n] 122 } 123 } 124 return segments 125 } 126 127 // Extracts the segments into the values pointed to by each of args. Each arg must be a pointer to int64, uint64, or 128 // []byte, and the width of the args must match layout. 129 func (kf *KeyFormat) Scan(key []byte, args ...interface{}) { 130 segments := kf.ScanBytes(key) 131 if len(args) > len(segments) { 132 panic(fmt.Errorf("keyFormat.Scan() is provided with %d args but format only has %d segments in key %X", 133 len(args), len(segments), key)) 134 } 135 for i, a := range args { 136 scan(a, segments[i]) 137 } 138 } 139 140 // Return the prefix as a string. 141 func (kf *KeyFormat) Prefix() string { 142 return string([]byte{kf.prefix}) 143 } 144 145 func scan(a interface{}, value []byte) { 146 switch v := a.(type) { 147 case *int64: 148 // Negative values will be mapped correctly when read in as uint64 and then type converted 149 *v = int64(binary.BigEndian.Uint64(value)) 150 case *uint64: 151 *v = binary.BigEndian.Uint64(value) 152 case *[]byte: 153 *v = value 154 default: 155 panic(fmt.Errorf("keyFormat scan() does not support scanning value of type %T: %v", a, a)) 156 } 157 } 158 159 func format(a interface{}) []byte { 160 switch v := a.(type) { 161 case uint64: 162 return formatUint64(v) 163 case int64: 164 return formatUint64(uint64(v)) 165 // Provide formatting from int,uint as a convenience to avoid casting arguments 166 case uint: 167 return formatUint64(uint64(v)) 168 case int: 169 return formatUint64(uint64(v)) 170 case []byte: 171 return v 172 default: 173 panic(fmt.Errorf("keyFormat format() does not support formatting value of type %T: %v", a, a)) 174 } 175 } 176 177 func formatUint64(v uint64) []byte { 178 bs := make([]byte, 8) 179 binary.BigEndian.PutUint64(bs, v) 180 return bs 181 }