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  }