github.com/cosmos/cosmos-sdk@v0.50.10/x/group/internal/orm/key_codec.go (about)

     1  package orm
     2  
     3  import (
     4  	"fmt"
     5  
     6  	errorsmod "cosmossdk.io/errors"
     7  
     8  	"github.com/cosmos/cosmos-sdk/x/group/errors"
     9  )
    10  
    11  // MaxBytesLen is the maximum allowed length for a key part of type []byte
    12  const MaxBytesLen = 255
    13  
    14  // buildKeyFromParts encodes and concatenates primary key and index parts.
    15  // They can be []byte, string, and integer types. The function will return
    16  // an error if there is a part of any other type.
    17  // Key parts, except the last part, follow these rules:
    18  //   - []byte is encoded with a single byte length prefix
    19  //   - strings are null-terminated
    20  //   - integers are encoded using 8 byte big endian.
    21  func buildKeyFromParts(parts []interface{}) ([]byte, error) {
    22  	bytesSlice := make([][]byte, len(parts))
    23  	totalLen := 0
    24  	var err error
    25  	for i, part := range parts {
    26  		bytesSlice[i], err = keyPartBytes(part, len(parts) > 1 && i == len(parts)-1)
    27  		if err != nil {
    28  			return nil, err
    29  		}
    30  		totalLen += len(bytesSlice[i])
    31  	}
    32  	key := make([]byte, 0, totalLen)
    33  	for _, bs := range bytesSlice {
    34  		key = append(key, bs...)
    35  	}
    36  	return key, nil
    37  }
    38  
    39  func keyPartBytes(part interface{}, last bool) ([]byte, error) {
    40  	switch v := part.(type) {
    41  	case []byte:
    42  		if last || len(v) == 0 {
    43  			return v, nil
    44  		}
    45  		return AddLengthPrefix(v), nil
    46  	case string:
    47  		if last || len(v) == 0 {
    48  			return []byte(v), nil
    49  		}
    50  		return NullTerminatedBytes(v), nil
    51  	case uint64:
    52  		return EncodeSequence(v), nil
    53  	default:
    54  		return nil, fmt.Errorf("type %T not allowed as key part", v)
    55  	}
    56  }
    57  
    58  // AddLengthPrefix prefixes the byte array with its length as 8 bytes. The function will panic
    59  // if the bytes length is bigger than 255.
    60  func AddLengthPrefix(bytes []byte) []byte {
    61  	byteLen := len(bytes)
    62  	if byteLen > MaxBytesLen {
    63  		panic(errorsmod.Wrap(errors.ErrORMKeyMaxLength, "Cannot create key part with an []byte of length greater than 255 bytes. Try again with a smaller []byte."))
    64  	}
    65  
    66  	prefixedBytes := make([]byte, 1+len(bytes))
    67  	copy(prefixedBytes, []byte{uint8(byteLen)})
    68  	copy(prefixedBytes[1:], bytes)
    69  	return prefixedBytes
    70  }
    71  
    72  // NullTerminatedBytes converts string to byte array and null terminate it
    73  func NullTerminatedBytes(s string) []byte {
    74  	bytes := make([]byte, len(s)+1)
    75  	copy(bytes, s)
    76  	return bytes
    77  }
    78  
    79  // stripRowID returns the RowID from the indexKey based on secondaryIndexKey type.
    80  // It is the reverse operation to buildKeyFromParts for index keys
    81  // where the first part is the encoded secondaryIndexKey and the second part is the RowID.
    82  func stripRowID(indexKey []byte, secondaryIndexKey interface{}) (RowID, error) {
    83  	switch v := secondaryIndexKey.(type) {
    84  	case []byte:
    85  		searchableKeyLen := indexKey[0]
    86  		return indexKey[1+searchableKeyLen:], nil
    87  	case string:
    88  		searchableKeyLen := 0
    89  		for i, b := range indexKey {
    90  			if b == 0 {
    91  				searchableKeyLen = i
    92  				break
    93  			}
    94  		}
    95  		return indexKey[1+searchableKeyLen:], nil
    96  	case uint64:
    97  		return indexKey[EncodedSeqLength:], nil
    98  	default:
    99  		return nil, fmt.Errorf("type %T not allowed as index key", v)
   100  	}
   101  }