1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. 2 // This source code is licensed under both the GPLv2 (found in the 3 // COPYING file in the root directory) and Apache 2.0 License 4 // (found in the LICENSE.Apache file in the root directory). 5 // 6 // Copyright (c) 2012 The LevelDB Authors. All rights reserved. 7 // Use of this source code is governed by a BSD-style license that can be 8 // found in the LICENSE file. See the AUTHORS file for names of contributors. 9 10 #include <algorithm> 11 #include "rocksdb/slice_transform.h" 12 #include "rocksdb/slice.h" 13 #include "util/string_util.h" 14 #include <stdio.h> 15 16 namespace ROCKSDB_NAMESPACE { 17 18 namespace { 19 20 class FixedPrefixTransform : public SliceTransform { 21 private: 22 size_t prefix_len_; 23 std::string name_; 24 25 public: 26 explicit FixedPrefixTransform(size_t prefix_len) 27 : prefix_len_(prefix_len), 28 // Note that if any part of the name format changes, it will require 29 // changes on options_helper in order to make RocksDBOptionsParser work 30 // for the new change. 31 // TODO(yhchiang): move serialization / deserializaion code inside 32 // the class implementation itself. 33 name_("rocksdb.FixedPrefix." + ToString(prefix_len_)) {} 34 35 const char* Name() const override { return name_.c_str(); } 36 37 Slice Transform(const Slice& src) const override { 38 assert(InDomain(src)); 39 return Slice(src.data(), prefix_len_); 40 } 41 42 bool InDomain(const Slice& src) const override { 43 return (src.size() >= prefix_len_); 44 } 45 46 bool InRange(const Slice& dst) const override { 47 return (dst.size() == prefix_len_); 48 } 49 50 bool FullLengthEnabled(size_t* len) const override { 51 *len = prefix_len_; 52 return true; 53 } 54 55 bool SameResultWhenAppended(const Slice& prefix) const override { 56 return InDomain(prefix); 57 } 58 }; 59 60 class CappedPrefixTransform : public SliceTransform { 61 private: 62 size_t cap_len_; 63 std::string name_; 64 65 public: 66 explicit CappedPrefixTransform(size_t cap_len) 67 : cap_len_(cap_len), 68 // Note that if any part of the name format changes, it will require 69 // changes on options_helper in order to make RocksDBOptionsParser work 70 // for the new change. 71 // TODO(yhchiang): move serialization / deserializaion code inside 72 // the class implementation itself. 73 name_("rocksdb.CappedPrefix." + ToString(cap_len_)) {} 74 75 const char* Name() const override { return name_.c_str(); } 76 77 Slice Transform(const Slice& src) const override { 78 assert(InDomain(src)); 79 return Slice(src.data(), std::min(cap_len_, src.size())); 80 } 81 82 bool InDomain(const Slice& /*src*/) const override { return true; } 83 84 bool InRange(const Slice& dst) const override { 85 return (dst.size() <= cap_len_); 86 } 87 88 bool FullLengthEnabled(size_t* len) const override { 89 *len = cap_len_; 90 return true; 91 } 92 93 bool SameResultWhenAppended(const Slice& prefix) const override { 94 return prefix.size() >= cap_len_; 95 } 96 }; 97 98 class NoopTransform : public SliceTransform { 99 public: 100 explicit NoopTransform() { } 101 102 const char* Name() const override { return "rocksdb.Noop"; } 103 104 Slice Transform(const Slice& src) const override { return src; } 105 106 bool InDomain(const Slice& /*src*/) const override { return true; } 107 108 bool InRange(const Slice& /*dst*/) const override { return true; } 109 110 bool SameResultWhenAppended(const Slice& /*prefix*/) const override { 111 return false; 112 } 113 }; 114 115 } 116 117 // 2 small internal utility functions, for efficient hex conversions 118 // and no need for snprintf, toupper etc... 119 // Originally from wdt/util/EncryptionUtils.cpp - for ToString(true)/DecodeHex: 120 char toHex(unsigned char v) { 121 if (v <= 9) { 122 return '0' + v; 123 } 124 return 'A' + v - 10; 125 } 126 // most of the code is for validation/error check 127 int fromHex(char c) { 128 // toupper: 129 if (c >= 'a' && c <= 'f') { 130 c -= ('a' - 'A'); // aka 0x20 131 } 132 // validation 133 if (c < '0' || (c > '9' && (c < 'A' || c > 'F'))) { 134 return -1; // invalid not 0-9A-F hex char 135 } 136 if (c <= '9') { 137 return c - '0'; 138 } 139 return c - 'A' + 10; 140 } 141 142 Slice::Slice(const SliceParts& parts, std::string* buf) { 143 size_t length = 0; 144 for (int i = 0; i < parts.num_parts; ++i) { 145 length += parts.parts[i].size(); 146 } 147 buf->reserve(length); 148 149 for (int i = 0; i < parts.num_parts; ++i) { 150 buf->append(parts.parts[i].data(), parts.parts[i].size()); 151 } 152 data_ = buf->data(); 153 size_ = buf->size(); 154 } 155 156 // Return a string that contains the copy of the referenced data. 157 std::string Slice::ToString(bool hex) const { 158 std::string result; // RVO/NRVO/move 159 if (hex) { 160 result.reserve(2 * size_); 161 for (size_t i = 0; i < size_; ++i) { 162 unsigned char c = data_[i]; 163 result.push_back(toHex(c >> 4)); 164 result.push_back(toHex(c & 0xf)); 165 } 166 return result; 167 } else { 168 result.assign(data_, size_); 169 return result; 170 } 171 } 172 173 // Originally from rocksdb/utilities/ldb_cmd.h 174 bool Slice::DecodeHex(std::string* result) const { 175 std::string::size_type len = size_; 176 if (len % 2) { 177 // Hex string must be even number of hex digits to get complete bytes back 178 return false; 179 } 180 if (!result) { 181 return false; 182 } 183 result->clear(); 184 result->reserve(len / 2); 185 186 for (size_t i = 0; i < len;) { 187 int h1 = fromHex(data_[i++]); 188 if (h1 < 0) { 189 return false; 190 } 191 int h2 = fromHex(data_[i++]); 192 if (h2 < 0) { 193 return false; 194 } 195 result->push_back(static_cast<char>((h1 << 4) | h2)); 196 } 197 return true; 198 } 199 200 const SliceTransform* NewFixedPrefixTransform(size_t prefix_len) { 201 return new FixedPrefixTransform(prefix_len); 202 } 203 204 const SliceTransform* NewCappedPrefixTransform(size_t cap_len) { 205 return new CappedPrefixTransform(cap_len); 206 } 207 208 const SliceTransform* NewNoopTransform() { 209 return new NoopTransform; 210 } 211 212 PinnableSlice::PinnableSlice(PinnableSlice&& other) { 213 *this = std::move(other); 214 } 215 216 PinnableSlice& PinnableSlice::operator=(PinnableSlice&& other) { 217 if (this != &other) { 218 Cleanable::Reset(); 219 Cleanable::operator=(std::move(other)); 220 size_ = other.size_; 221 pinned_ = other.pinned_; 222 if (pinned_) { 223 data_ = other.data_; 224 // When it's pinned, buf should no longer be of use. 225 } else { 226 if (other.buf_ == &other.self_space_) { 227 self_space_ = std::move(other.self_space_); 228 buf_ = &self_space_; 229 data_ = buf_->data(); 230 } else { 231 buf_ = other.buf_; 232 data_ = other.data_; 233 } 234 } 235 other.self_space_.clear(); 236 other.buf_ = &other.self_space_; 237 other.pinned_ = false; 238 other.PinSelf(); 239 } 240 return *this; 241 } 242 243 } // namespace ROCKSDB_NAMESPACE 244