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 }