github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/c-deps/libroach/encoding.cc (about) 1 // Copyright 2014 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 #include "encoding.h" 12 #include <rocksdb/slice.h> 13 #include "db.h" 14 #include "keys.h" 15 16 namespace cockroach { 17 18 void EncodeUint32(std::string* buf, uint32_t v) { 19 const uint8_t tmp[sizeof(v)] = { 20 uint8_t(v >> 24), 21 uint8_t(v >> 16), 22 uint8_t(v >> 8), 23 uint8_t(v), 24 }; 25 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 26 } 27 28 void EncodeUint64(std::string* buf, uint64_t v) { 29 const uint8_t tmp[sizeof(v)] = { 30 uint8_t(v >> 56), uint8_t(v >> 48), uint8_t(v >> 40), uint8_t(v >> 32), 31 uint8_t(v >> 24), uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 32 }; 33 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 34 } 35 36 void EncodeUvarint64(std::string* buf, uint64_t v) { 37 if (v <= kIntSmall) { 38 const uint8_t tmp[1] = { 39 uint8_t(v + kIntZero), 40 }; 41 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 42 } else if (v <= 0xff) { 43 const uint8_t tmp[2] = { 44 uint8_t(kIntMax - 7), 45 uint8_t(v), 46 }; 47 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 48 } else if (v <= 0xffff) { 49 const uint8_t tmp[3] = { 50 uint8_t(kIntMax - 6), 51 uint8_t(v >> 8), 52 uint8_t(v), 53 }; 54 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 55 } else if (v <= 0xffffff) { 56 const uint8_t tmp[4] = { 57 uint8_t(kIntMax - 5), 58 uint8_t(v >> 16), 59 uint8_t(v >> 8), 60 uint8_t(v), 61 }; 62 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 63 } else if (v <= 0xffffffff) { 64 const uint8_t tmp[5] = { 65 uint8_t(kIntMax - 4), uint8_t(v >> 24), uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 66 }; 67 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 68 } else if (v <= 0xffffffffff) { 69 const uint8_t tmp[6] = { 70 uint8_t(kIntMax - 3), uint8_t(v >> 32), uint8_t(v >> 24), 71 uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 72 }; 73 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 74 } else if (v <= 0xffffffffffff) { 75 const uint8_t tmp[7] = { 76 uint8_t(kIntMax - 2), uint8_t(v >> 40), uint8_t(v >> 32), uint8_t(v >> 24), 77 uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 78 }; 79 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 80 } else if (v <= 0xffffffffffffff) { 81 const uint8_t tmp[8] = { 82 uint8_t(kIntMax - 1), uint8_t(v >> 48), uint8_t(v >> 40), uint8_t(v >> 32), 83 uint8_t(v >> 24), uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 84 }; 85 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 86 } else { 87 const uint8_t tmp[9] = { 88 uint8_t(kIntMax), uint8_t(v >> 56), uint8_t(v >> 48), uint8_t(v >> 40), uint8_t(v >> 32), 89 uint8_t(v >> 24), uint8_t(v >> 16), uint8_t(v >> 8), uint8_t(v), 90 }; 91 buf->append(reinterpret_cast<const char*>(tmp), sizeof(tmp)); 92 } 93 } 94 95 bool DecodeUint32(rocksdb::Slice* buf, uint32_t* value) { 96 const int N = sizeof(*value); 97 if (buf->size() < N) { 98 return false; 99 } 100 const uint8_t* b = reinterpret_cast<const uint8_t*>(buf->data()); 101 *value = (uint32_t(b[0]) << 24) | (uint32_t(b[1]) << 16) | (uint32_t(b[2]) << 8) | uint32_t(b[3]); 102 buf->remove_prefix(N); 103 return true; 104 } 105 106 bool DecodeUint64(rocksdb::Slice* buf, uint64_t* value) { 107 const int N = sizeof(*value); 108 if (buf->size() < N) { 109 return false; 110 } 111 const uint8_t* b = reinterpret_cast<const uint8_t*>(buf->data()); 112 *value = (uint64_t(b[0]) << 56) | (uint64_t(b[1]) << 48) | (uint64_t(b[2]) << 40) | 113 (uint64_t(b[3]) << 32) | (uint64_t(b[4]) << 24) | (uint64_t(b[5]) << 16) | 114 (uint64_t(b[6]) << 8) | uint64_t(b[7]); 115 buf->remove_prefix(N); 116 return true; 117 } 118 119 bool DecodeUvarint64(rocksdb::Slice* buf, uint64_t* value) { 120 if (buf->size() == 0) { 121 return false; 122 } 123 int length = uint8_t((*buf)[0]) - kIntZero; 124 buf->remove_prefix(1); // skip length byte 125 if (length <= kIntSmall) { 126 *value = uint64_t(length); 127 return true; 128 } 129 length -= kIntSmall; 130 if (length < 0 || length > 8) { 131 return false; 132 } else if (buf->size() < length) { 133 return false; 134 } 135 *value = 0; 136 for (int i = 0; i < length; i++) { 137 *value = (*value << 8) | uint8_t((*buf)[i]); 138 } 139 buf->remove_prefix(length); 140 return true; 141 } 142 143 void EncodeTimestamp(std::string& s, int64_t wall_time, int32_t logical) { 144 EncodeUint64(&s, uint64_t(wall_time)); 145 if (logical != 0) { 146 EncodeUint32(&s, uint32_t(logical)); 147 } 148 } 149 150 std::string EncodeTimestamp(DBTimestamp ts) { 151 std::string s; 152 s.reserve(kMVCCVersionTimestampSize); 153 EncodeTimestamp(s, ts.wall_time, ts.logical); 154 return s; 155 } 156 157 bool EmptyTimestamp(DBTimestamp ts) { return ts.wall_time == 0 && ts.logical == 0; } 158 159 // MVCC keys are encoded as <key>\x00[<wall_time>[<logical>]]<#timestamp-bytes>. A 160 // custom RocksDB comparator (DBComparator) is used to maintain the desired 161 // ordering as these keys do not sort lexicographically correctly. 162 std::string EncodeKey(const rocksdb::Slice& key, int64_t wall_time, int32_t logical) { 163 std::string s; 164 const bool ts = wall_time != 0 || logical != 0; 165 s.reserve(key.size() + 1 + (ts ? 1 + kMVCCVersionTimestampSize : 0)); 166 s.append(key.data(), key.size()); 167 if (ts) { 168 // Add a NUL prefix to the timestamp data. See DBPrefixExtractor.Transform 169 // for more details. 170 s.push_back(0); 171 EncodeTimestamp(s, wall_time, logical); 172 } 173 s.push_back(char(s.size() - key.size())); 174 return s; 175 } 176 177 // MVCC keys are encoded as <key>\x00[<wall_time>[<logical>]]<#timestamp-bytes>. A 178 // custom RocksDB comparator (DBComparator) is used to maintain the desired 179 // ordering as these keys do not sort lexicographically correctly. 180 std::string EncodeKey(DBKey k) { return EncodeKey(ToSlice(k.key), k.wall_time, k.logical); } 181 182 WARN_UNUSED_RESULT bool DecodeTimestamp(rocksdb::Slice* timestamp, int64_t* wall_time, 183 int32_t* logical) { 184 uint64_t w; 185 if (!DecodeUint64(timestamp, &w)) { 186 return false; 187 } 188 *wall_time = int64_t(w); 189 *logical = 0; 190 if (timestamp->size() > 0) { 191 // TODO(peter): Use varint decoding here. 192 uint32_t l; 193 if (!DecodeUint32(timestamp, &l)) { 194 return false; 195 } 196 *logical = int32_t(l); 197 } 198 return true; 199 } 200 201 WARN_UNUSED_RESULT bool DecodeTimestamp(rocksdb::Slice buf, 202 cockroach::util::hlc::Timestamp* timestamp) { 203 int64_t wall_time; 204 int32_t logical; 205 if (!DecodeTimestamp(&buf, &wall_time, &logical)) { 206 return false; 207 } 208 timestamp->set_wall_time(wall_time); 209 timestamp->set_logical(logical); 210 return true; 211 } 212 213 WARN_UNUSED_RESULT bool DecodeKey(rocksdb::Slice buf, rocksdb::Slice* key, int64_t* wall_time, 214 int32_t* logical) { 215 key->clear(); 216 217 rocksdb::Slice timestamp; 218 if (!SplitKey(buf, key, ×tamp)) { 219 return false; 220 } 221 if (timestamp.size() > 0) { 222 timestamp.remove_prefix(1); // The NUL prefix. 223 if (!DecodeTimestamp(×tamp, wall_time, logical)) { 224 return false; 225 } 226 } 227 return timestamp.empty(); 228 } 229 230 WARN_UNUSED_RESULT bool DecodeRangeIDKey(rocksdb::Slice buf, int64_t* range_id, 231 rocksdb::Slice* infix, rocksdb::Slice* suffix, 232 rocksdb::Slice* detail) { 233 if (!buf.starts_with(kLocalRangeIDPrefix)) { 234 return false; 235 } 236 // Cut the prefix, the Range ID, and the infix specifier. 237 buf.remove_prefix(kLocalRangeIDPrefix.size()); 238 uint64_t range_id_uint; 239 if (!DecodeUvarint64(&buf, &range_id_uint)) { 240 return false; 241 } 242 *range_id = int64_t(range_id_uint); 243 if (buf.size() < kLocalSuffixLength + 1) { 244 return false; 245 } 246 *infix = rocksdb::Slice(buf.data(), 1); 247 buf.remove_prefix(1); 248 *suffix = rocksdb::Slice(buf.data(), kLocalSuffixLength); 249 buf.remove_prefix(kLocalSuffixLength); 250 *detail = buf; 251 return true; 252 } 253 254 rocksdb::Slice KeyPrefix(const rocksdb::Slice& src) { 255 rocksdb::Slice key; 256 rocksdb::Slice ts; 257 if (!SplitKey(src, &key, &ts)) { 258 return src; 259 } 260 // RocksDB requires that keys generated via Transform be comparable with 261 // normal encoded MVCC keys. Encoded MVCC keys have a suffix indicating the 262 // number of bytes of timestamp data. MVCC keys without a timestamp have a 263 // suffix of 0. We're careful in EncodeKey to make sure that the user-key 264 // always has a trailing 0. If there is no timestamp this falls out 265 // naturally. If there is a timestamp we prepend a 0 to the encoded 266 // timestamp data. 267 assert(src.size() > key.size() && src[key.size()] == 0); 268 return rocksdb::Slice(key.data(), key.size() + 1); 269 } 270 271 WARN_UNUSED_RESULT bool IsInt(rocksdb::Slice* buf) { 272 if (buf->size() > 0) { 273 return uint8_t((*buf)[0]) >= kIntMin && uint8_t((*buf)[0]) <= kIntMax; 274 } 275 276 return false; 277 } 278 279 WARN_UNUSED_RESULT bool StripTenantPrefix(rocksdb::Slice* buf) { 280 if (buf->size() == 0) { 281 return true; 282 } 283 // kTenantPrefix is guaranteed to be a single byte. 284 if ((*buf)[0] != kTenantPrefix[0]) { 285 return true; 286 } 287 buf->remove_prefix(1); 288 uint64_t tid; 289 return DecodeUvarint64(buf, &tid); 290 } 291 292 WARN_UNUSED_RESULT bool DecodeTenantAndTablePrefix(rocksdb::Slice* buf, uint64_t* tbl) { 293 if (!StripTenantPrefix(buf)) { 294 return false; 295 } 296 if (!IsInt(buf) || !DecodeUvarint64(buf, tbl)) { 297 return false; 298 } 299 return true; 300 } 301 } // namespace cockroach