github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/storage/enginepb/decode.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package enginepb
    12  
    13  import (
    14  	"encoding/binary"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    17  	"github.com/cockroachdb/errors"
    18  )
    19  
    20  // Helpers here are split out of storage/engine since that package also contains
    21  // rocksdb-interfacing code that pulls in our c-deps build requirements. These
    22  // helpers are used by packages that do not want to pull in all of that, so they
    23  // live here instead. We might want to undertake a larger refactor to split up
    24  // the engine package, pulling MVCC logic out and/or pulling concrete rocksdb
    25  // code out from abstract interfaces -- See #30114 and #30001.
    26  
    27  // SplitMVCCKey returns the key and timestamp components of an encoded MVCC key.
    28  // This decoding must match engine/db.cc:SplitKey().
    29  func SplitMVCCKey(mvccKey []byte) (key []byte, ts []byte, ok bool) {
    30  	if len(mvccKey) == 0 {
    31  		return nil, nil, false
    32  	}
    33  	tsLen := int(mvccKey[len(mvccKey)-1])
    34  	keyPartEnd := len(mvccKey) - 1 - tsLen
    35  	if keyPartEnd < 0 {
    36  		return nil, nil, false
    37  	}
    38  
    39  	key = mvccKey[:keyPartEnd]
    40  	if tsLen > 0 {
    41  		ts = mvccKey[keyPartEnd+1 : len(mvccKey)-1]
    42  	}
    43  	return key, ts, true
    44  }
    45  
    46  // DecodeKey decodes an key/timestamp from its serialized representation. This
    47  // decoding must match libroach/encoding.cc:DecodeKey().
    48  func DecodeKey(encodedKey []byte) (key []byte, timestamp hlc.Timestamp, _ error) {
    49  	key, ts, ok := SplitMVCCKey(encodedKey)
    50  	if !ok {
    51  		return nil, timestamp, errors.Errorf("invalid encoded mvcc key: %x", encodedKey)
    52  	}
    53  	switch len(ts) {
    54  	case 0:
    55  		// No-op.
    56  	case 8:
    57  		timestamp.WallTime = int64(binary.BigEndian.Uint64(ts[0:8]))
    58  	case 12:
    59  		timestamp.WallTime = int64(binary.BigEndian.Uint64(ts[0:8]))
    60  		timestamp.Logical = int32(binary.BigEndian.Uint32(ts[8:12]))
    61  	default:
    62  		return nil, timestamp, errors.Errorf(
    63  			"invalid encoded mvcc key: %x bad timestamp %x", encodedKey, ts)
    64  	}
    65  
    66  	return key, timestamp, nil
    67  }
    68  
    69  // kvLenSize is the number of bytes in the length prefix for each key/value
    70  // pair in a MVCCScan batch. The first 4 bytes are a little-endian uint32
    71  // containing the value size in bytes. The second 4 bytes are a little-endian
    72  // uint32 containing the key size in bytes.
    73  const kvLenSize = 8
    74  
    75  // ScanDecodeKeyValue decodes a key/value pair from a binary stream, such as in
    76  // an MVCCScan "batch" (this is not the RocksDB batch repr format), returning
    77  // the key/value, the timestamp, and the suffix of data remaining in the batch.
    78  func ScanDecodeKeyValue(
    79  	repr []byte,
    80  ) (key []byte, ts hlc.Timestamp, value []byte, orepr []byte, err error) {
    81  	if len(repr) < kvLenSize {
    82  		return key, ts, nil, repr, errors.Errorf("unexpected batch EOF")
    83  	}
    84  	valSize := binary.LittleEndian.Uint32(repr)
    85  	keyEnd := binary.LittleEndian.Uint32(repr[4:kvLenSize]) + kvLenSize
    86  	if (keyEnd + valSize) > uint32(len(repr)) {
    87  		return key, ts, nil, nil, errors.Errorf("expected %d bytes, but only %d remaining",
    88  			keyEnd+valSize, len(repr))
    89  	}
    90  	rawKey := repr[kvLenSize:keyEnd]
    91  	value = repr[keyEnd : keyEnd+valSize]
    92  	repr = repr[keyEnd+valSize:]
    93  	key, ts, err = DecodeKey(rawKey)
    94  	return key, ts, value, repr, err
    95  }
    96  
    97  // ScanDecodeKeyValueNoTS decodes a key/value pair from a binary stream, such as
    98  // in an MVCCScan "batch" (this is not the RocksDB batch repr format), returning
    99  // the key/value and the suffix of data remaining in the batch.
   100  func ScanDecodeKeyValueNoTS(repr []byte) (key []byte, value []byte, orepr []byte, err error) {
   101  	if len(repr) < kvLenSize {
   102  		return key, nil, repr, errors.Errorf("unexpected batch EOF")
   103  	}
   104  	valSize := binary.LittleEndian.Uint32(repr)
   105  	keyEnd := binary.LittleEndian.Uint32(repr[4:kvLenSize]) + kvLenSize
   106  	if len(repr) < int(keyEnd+valSize) {
   107  		return key, nil, nil, errors.Errorf("expected %d bytes, but only %d remaining",
   108  			keyEnd+valSize, len(repr))
   109  	}
   110  
   111  	ret := repr[keyEnd+valSize:]
   112  	value = repr[keyEnd : keyEnd+valSize]
   113  	var ok bool
   114  	rawKey := repr[kvLenSize:keyEnd]
   115  	key, _, ok = SplitMVCCKey(rawKey)
   116  	if !ok {
   117  		return nil, nil, nil, errors.Errorf("invalid encoded mvcc key: %x", rawKey)
   118  	}
   119  	return key, value, ret, err
   120  }