github.com/MetalBlockchain/metalgo@v1.11.9/x/merkledb/key.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package merkledb 5 6 import ( 7 "cmp" 8 "errors" 9 "fmt" 10 "slices" 11 "strings" 12 "unsafe" 13 14 "golang.org/x/exp/maps" 15 ) 16 17 var ( 18 ErrInvalidBranchFactor = errors.New("branch factor must match one of the predefined branch factors") 19 20 BranchFactorToTokenSize = map[BranchFactor]int{ 21 BranchFactor2: 1, 22 BranchFactor4: 2, 23 BranchFactor16: 4, 24 BranchFactor256: 8, 25 } 26 27 tokenSizeToBranchFactor = map[int]BranchFactor{ 28 1: BranchFactor2, 29 2: BranchFactor4, 30 4: BranchFactor16, 31 8: BranchFactor256, 32 } 33 34 validTokenSizes = maps.Keys(tokenSizeToBranchFactor) 35 36 validBranchFactors = []BranchFactor{ 37 BranchFactor2, 38 BranchFactor4, 39 BranchFactor16, 40 BranchFactor256, 41 } 42 ) 43 44 type BranchFactor int 45 46 const ( 47 BranchFactor2 = BranchFactor(2) 48 BranchFactor4 = BranchFactor(4) 49 BranchFactor16 = BranchFactor(16) 50 BranchFactor256 = BranchFactor(256) 51 52 BranchFactorLargest = BranchFactor256 53 ) 54 55 // Valid checks if BranchFactor [b] is one of the predefined valid options for BranchFactor 56 func (b BranchFactor) Valid() error { 57 for _, validBF := range validBranchFactors { 58 if validBF == b { 59 return nil 60 } 61 } 62 return fmt.Errorf("%w: %d", ErrInvalidBranchFactor, b) 63 } 64 65 // ToToken creates a key version of the passed byte with bit length equal to tokenSize 66 func ToToken(val byte, tokenSize int) Key { 67 return Key{ 68 value: string([]byte{val << dualBitIndex(tokenSize)}), 69 length: tokenSize, 70 } 71 } 72 73 // Token returns the token at the specified index, 74 // Assumes that bitIndex + tokenSize doesn't cross a byte boundary 75 func (k Key) Token(bitIndex int, tokenSize int) byte { 76 storageByte := k.value[bitIndex/8] 77 // Shift the byte right to get the last bit to the rightmost position. 78 storageByte >>= dualBitIndex((bitIndex + tokenSize) % 8) 79 // Apply a mask to remove any other bits in the byte. 80 return storageByte & (0xFF >> dualBitIndex(tokenSize)) 81 } 82 83 // iteratedHasPrefix checks if the provided prefix key is a prefix of the current key starting after the [bitsOffset]th bit 84 // this has better performance than constructing the actual key via Skip() then calling HasPrefix because it avoids an allocation 85 func (k Key) iteratedHasPrefix(prefix Key, bitsOffset int, tokenSize int) bool { 86 if k.length-bitsOffset < prefix.length { 87 return false 88 } 89 for i := 0; i < prefix.length; i += tokenSize { 90 if k.Token(bitsOffset+i, tokenSize) != prefix.Token(i, tokenSize) { 91 return false 92 } 93 } 94 return true 95 } 96 97 type Key struct { 98 // The number of bits in the key. 99 length int 100 // The string representation of the key 101 value string 102 } 103 104 // ToKey returns [keyBytes] as a new key 105 // Assumes all bits of the keyBytes are part of the Key, call Key.Take if that is not the case 106 // Creates a copy of [keyBytes], so keyBytes are safe to edit after the call 107 func ToKey(keyBytes []byte) Key { 108 return toKey(slices.Clone(keyBytes)) 109 } 110 111 // toKey returns [keyBytes] as a new key 112 // Assumes all bits of the keyBytes are part of the Key, call Key.Take if that is not the case 113 // Caller must not modify [keyBytes] after this call. 114 func toKey(keyBytes []byte) Key { 115 return Key{ 116 value: byteSliceToString(keyBytes), 117 length: len(keyBytes) * 8, 118 } 119 } 120 121 // hasPartialByte returns true iff the key fits into a non-whole number of bytes 122 func (k Key) hasPartialByte() bool { 123 return k.length%8 > 0 124 } 125 126 // HasPrefix returns true iff [prefix] is a prefix of [k] or equal to it. 127 func (k Key) HasPrefix(prefix Key) bool { 128 // [prefix] must be shorter than [k] to be a prefix. 129 if k.length < prefix.length { 130 return false 131 } 132 133 // The number of tokens in the last byte of [prefix], or zero 134 // if [prefix] fits into a whole number of bytes. 135 remainderBitCount := prefix.length % 8 136 if remainderBitCount == 0 { 137 return strings.HasPrefix(k.value, prefix.value) 138 } 139 140 // check that the tokens in the partially filled final byte of [prefix] are 141 // equal to the tokens in the final byte of [k]. 142 remainderBitsMask := byte(0xFF >> remainderBitCount) 143 prefixRemainderTokens := prefix.value[len(prefix.value)-1] | remainderBitsMask 144 remainderTokens := k.value[len(prefix.value)-1] | remainderBitsMask 145 146 if prefixRemainderTokens != remainderTokens { 147 return false 148 } 149 150 // Note that this will never be an index OOB because len(prefix.value) > 0. 151 // If len(prefix.value) == 0 were true, [remainderTokens] would be 0, so we 152 // would have returned above. 153 prefixWithoutPartialByte := prefix.value[:len(prefix.value)-1] 154 return strings.HasPrefix(k.value, prefixWithoutPartialByte) 155 } 156 157 // HasStrictPrefix returns true iff [prefix] is a prefix of [k] 158 // but is not equal to it. 159 func (k Key) HasStrictPrefix(prefix Key) bool { 160 return k != prefix && k.HasPrefix(prefix) 161 } 162 163 // Length returns the number of bits in the Key 164 func (k Key) Length() int { 165 return k.length 166 } 167 168 // Greater returns true if current Key is greater than other Key 169 func (k Key) Greater(other Key) bool { 170 return k.Compare(other) == 1 171 } 172 173 // Less will return true if current Key is less than other Key 174 func (k Key) Less(other Key) bool { 175 return k.Compare(other) == -1 176 } 177 178 func (k Key) Compare(other Key) int { 179 if valueCmp := cmp.Compare(k.value, other.value); valueCmp != 0 { 180 return valueCmp 181 } 182 return cmp.Compare(k.length, other.length) 183 } 184 185 // Extend returns a new Key that is the in-order aggregation of Key [k] with [keys] 186 func (k Key) Extend(keys ...Key) Key { 187 totalBitLength := k.length 188 for _, key := range keys { 189 totalBitLength += key.length 190 } 191 buffer := make([]byte, bytesNeeded(totalBitLength)) 192 copy(buffer, k.value) 193 currentTotal := k.length 194 for _, key := range keys { 195 extendIntoBuffer(buffer, key, currentTotal) 196 currentTotal += key.length 197 } 198 199 return Key{ 200 value: byteSliceToString(buffer), 201 length: totalBitLength, 202 } 203 } 204 205 func extendIntoBuffer(buffer []byte, val Key, bitsOffset int) { 206 if val.length == 0 { 207 return 208 } 209 bytesOffset := bytesNeeded(bitsOffset) 210 bitsRemainder := bitsOffset % 8 211 if bitsRemainder == 0 { 212 copy(buffer[bytesOffset:], val.value) 213 return 214 } 215 216 // Fill the partial byte with the first [shift] bits of the extension path 217 buffer[bytesOffset-1] |= val.value[0] >> bitsRemainder 218 219 // copy the rest of the extension path bytes into the buffer, 220 // shifted byte shift bits 221 shiftCopy(buffer[bytesOffset:], val.value, dualBitIndex(bitsRemainder)) 222 } 223 224 // dualBitIndex gets the dual of the bit index 225 // ex: in a byte, the bit 5 from the right is the same as the bit 3 from the left 226 func dualBitIndex(shift int) int { 227 return (8 - shift) % 8 228 } 229 230 // Treats [src] as a bit array and copies it into [dst] shifted by [shift] bits. 231 // For example, if [src] is [0b0000_0001, 0b0000_0010] and [shift] is 4, 232 // we copy [0b0001_0000, 0b0010_0000] into [dst]. 233 // Assumes len(dst) >= len(src)-1. 234 // If len(dst) == len(src)-1 the last byte of [src] is only partially copied 235 // (i.e. the rightmost bits are not copied). 236 func shiftCopy(dst []byte, src string, shift int) { 237 i := 0 238 dualShift := dualBitIndex(shift) 239 for ; i < len(src)-1; i++ { 240 dst[i] = src[i]<<shift | src[i+1]>>dualShift 241 } 242 243 if i < len(dst) { 244 // the last byte only has values from byte i, as there is no byte i+1 245 dst[i] = src[i] << shift 246 } 247 } 248 249 // Skip returns a new Key that contains the last 250 // k.length-bitsToSkip bits of [k]. 251 func (k Key) Skip(bitsToSkip int) Key { 252 if k.length <= bitsToSkip { 253 return Key{} 254 } 255 result := Key{ 256 value: k.value[bitsToSkip/8:], 257 length: k.length - bitsToSkip, 258 } 259 260 // if the tokens to skip is a whole number of bytes, 261 // the remaining bytes exactly equals the new key. 262 if bitsToSkip%8 == 0 { 263 return result 264 } 265 266 // bitsToSkip does not remove a whole number of bytes. 267 // copy the remaining shifted bytes into a new buffer. 268 buffer := make([]byte, bytesNeeded(result.length)) 269 bitsRemovedFromFirstRemainingByte := bitsToSkip % 8 270 shiftCopy(buffer, result.value, bitsRemovedFromFirstRemainingByte) 271 272 result.value = byteSliceToString(buffer) 273 return result 274 } 275 276 // Take returns a new Key that contains the first bitsToTake bits of the current Key 277 func (k Key) Take(bitsToTake int) Key { 278 if k.length <= bitsToTake { 279 return k 280 } 281 282 result := Key{ 283 length: bitsToTake, 284 } 285 286 remainderBits := result.length % 8 287 if remainderBits == 0 { 288 result.value = k.value[:bitsToTake/8] 289 return result 290 } 291 292 // We need to zero out some bits of the last byte so a simple slice will not work 293 // Create a new []byte to store the altered value 294 buffer := make([]byte, bytesNeeded(bitsToTake)) 295 copy(buffer, k.value) 296 297 // We want to zero out everything to the right of the last token, which is at index bitsToTake-1 298 // Mask will be (8-remainderBits) number of 1's followed by (remainderBits) number of 0's 299 buffer[len(buffer)-1] &= byte(0xFF << dualBitIndex(remainderBits)) 300 301 result.value = byteSliceToString(buffer) 302 return result 303 } 304 305 // Bytes returns the raw bytes of the Key 306 // Invariant: The returned value must not be modified. 307 func (k Key) Bytes() []byte { 308 // avoid copying during the conversion 309 // "safe" because we never edit the value, only used as DB key 310 return stringToByteSlice(k.value) 311 } 312 313 // byteSliceToString converts the []byte to a string 314 // Invariant: The input []byte must not be modified. 315 func byteSliceToString(bs []byte) string { 316 // avoid copying during the conversion 317 // "safe" because we never edit the []byte, and it is never returned by any functions except Bytes() 318 return unsafe.String(unsafe.SliceData(bs), len(bs)) 319 } 320 321 // stringToByteSlice converts the string to a []byte 322 // Invariant: The output []byte must not be modified. 323 func stringToByteSlice(value string) []byte { 324 // avoid copying during the conversion 325 // "safe" because we never edit the []byte 326 return unsafe.Slice(unsafe.StringData(value), len(value)) 327 } 328 329 // Returns the number of bytes needed to store [bits] bits. 330 func bytesNeeded(bits int) int { 331 size := bits / 8 332 if bits%8 != 0 { 333 size++ 334 } 335 return size 336 }