Coverage Report

Created: 2026-02-14 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/util/slice.cc
Line
Count
Source
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 "rocksdb/slice.h"
11
12
#include <algorithm>
13
#include <cstdio>
14
15
#include "rocksdb/convenience.h"
16
#include "rocksdb/slice_transform.h"
17
#include "rocksdb/utilities/object_registry.h"
18
#include "rocksdb/utilities/options_type.h"
19
#include "util/string_util.h"
20
21
namespace ROCKSDB_NAMESPACE {
22
23
namespace {
24
25
class FixedPrefixTransform : public SliceTransform {
26
 private:
27
  size_t prefix_len_;
28
  std::string id_;
29
30
 public:
31
0
  explicit FixedPrefixTransform(size_t prefix_len) : prefix_len_(prefix_len) {
32
0
    id_ = std::string(kClassName()) + "." + std::to_string(prefix_len_);
33
0
  }
34
35
2
  static const char* kClassName() { return "rocksdb.FixedPrefix"; }
36
2
  static const char* kNickName() { return "fixed"; }
37
0
  const char* Name() const override { return kClassName(); }
38
0
  const char* NickName() const override { return kNickName(); }
39
40
0
  bool IsInstanceOf(const std::string& name) const override {
41
0
    if (name == id_) {
42
0
      return true;
43
0
    } else if (StartsWith(name, kNickName())) {
44
0
      std::string alt_id =
45
0
          std::string(kNickName()) + ":" + std::to_string(prefix_len_);
46
0
      if (name == alt_id) {
47
0
        return true;
48
0
      }
49
0
    }
50
0
    return SliceTransform::IsInstanceOf(name);
51
0
  }
52
53
0
  std::string GetId() const override { return id_; }
54
55
0
  Slice Transform(const Slice& src) const override {
56
0
    assert(InDomain(src));
57
0
    return Slice(src.data(), prefix_len_);
58
0
  }
59
60
0
  bool InDomain(const Slice& src) const override {
61
0
    return (src.size() >= prefix_len_);
62
0
  }
63
64
0
  bool InRange(const Slice& dst) const override {
65
0
    return (dst.size() == prefix_len_);
66
0
  }
67
68
0
  bool FullLengthEnabled(size_t* len) const override {
69
0
    *len = prefix_len_;
70
0
    return true;
71
0
  }
72
73
0
  bool SameResultWhenAppended(const Slice& prefix) const override {
74
0
    return InDomain(prefix);
75
0
  }
76
};
77
78
class CappedPrefixTransform : public SliceTransform {
79
 private:
80
  size_t cap_len_;
81
  std::string id_;
82
83
 public:
84
0
  explicit CappedPrefixTransform(size_t cap_len) : cap_len_(cap_len) {
85
0
    id_ = std::string(kClassName()) + "." + std::to_string(cap_len_);
86
0
  }
87
88
2
  static const char* kClassName() { return "rocksdb.CappedPrefix"; }
89
2
  static const char* kNickName() { return "capped"; }
90
0
  const char* Name() const override { return kClassName(); }
91
0
  const char* NickName() const override { return kNickName(); }
92
0
  std::string GetId() const override { return id_; }
93
94
0
  bool IsInstanceOf(const std::string& name) const override {
95
0
    if (name == id_) {
96
0
      return true;
97
0
    } else if (StartsWith(name, kNickName())) {
98
0
      std::string alt_id =
99
0
          std::string(kNickName()) + ":" + std::to_string(cap_len_);
100
0
      if (name == alt_id) {
101
0
        return true;
102
0
      }
103
0
    }
104
0
    return SliceTransform::IsInstanceOf(name);
105
0
  }
106
107
0
  Slice Transform(const Slice& src) const override {
108
0
    assert(InDomain(src));
109
0
    return Slice(src.data(), std::min(cap_len_, src.size()));
110
0
  }
111
112
0
  bool InDomain(const Slice& /*src*/) const override { return true; }
113
114
0
  bool InRange(const Slice& dst) const override {
115
0
    return (dst.size() <= cap_len_);
116
0
  }
117
118
0
  bool FullLengthEnabled(size_t* len) const override {
119
0
    *len = cap_len_;
120
0
    return true;
121
0
  }
122
123
0
  bool SameResultWhenAppended(const Slice& prefix) const override {
124
0
    return prefix.size() >= cap_len_;
125
0
  }
126
};
127
128
class NoopTransform : public SliceTransform {
129
 public:
130
0
  explicit NoopTransform() = default;
131
132
2
  static const char* kClassName() { return "rocksdb.Noop"; }
133
0
  const char* Name() const override { return kClassName(); }
134
135
0
  Slice Transform(const Slice& src) const override { return src; }
136
137
0
  bool InDomain(const Slice& /*src*/) const override { return true; }
138
139
0
  bool InRange(const Slice& /*dst*/) const override { return true; }
140
141
0
  bool SameResultWhenAppended(const Slice& /*prefix*/) const override {
142
0
    return false;
143
0
  }
144
};
145
146
}  // end namespace
147
148
0
const SliceTransform* NewFixedPrefixTransform(size_t prefix_len) {
149
0
  return new FixedPrefixTransform(prefix_len);
150
0
}
151
152
0
const SliceTransform* NewCappedPrefixTransform(size_t cap_len) {
153
0
  return new CappedPrefixTransform(cap_len);
154
0
}
155
156
0
const SliceTransform* NewNoopTransform() { return new NoopTransform; }
157
158
static int RegisterBuiltinSliceTransform(ObjectLibrary& library,
159
2
                                         const std::string& /*arg*/) {
160
  // For the builtin transforms, the format is typically
161
  // [Name].[0-9]+ or [NickName]:[0-9]+
162
2
  library.AddFactory<const SliceTransform>(
163
2
      NoopTransform::kClassName(),
164
2
      [](const std::string& /*uri*/,
165
2
         std::unique_ptr<const SliceTransform>* guard,
166
2
         std::string* /*errmsg*/) {
167
0
        guard->reset(NewNoopTransform());
168
0
        return guard->get();
169
0
      });
170
2
  library.AddFactory<const SliceTransform>(
171
2
      ObjectLibrary::PatternEntry(FixedPrefixTransform::kNickName(), false)
172
2
          .AddNumber(":"),
173
2
      [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
174
2
         std::string* /*errmsg*/) {
175
0
        auto colon = uri.find(':');
176
0
        auto len = ParseSizeT(uri.substr(colon + 1));
177
0
        guard->reset(NewFixedPrefixTransform(len));
178
0
        return guard->get();
179
0
      });
180
2
  library.AddFactory<const SliceTransform>(
181
2
      ObjectLibrary::PatternEntry(FixedPrefixTransform::kClassName(), false)
182
2
          .AddNumber("."),
183
2
      [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
184
2
         std::string* /*errmsg*/) {
185
0
        auto len = ParseSizeT(
186
0
            uri.substr(strlen(FixedPrefixTransform::kClassName()) + 1));
187
0
        guard->reset(NewFixedPrefixTransform(len));
188
0
        return guard->get();
189
0
      });
190
2
  library.AddFactory<const SliceTransform>(
191
2
      ObjectLibrary::PatternEntry(CappedPrefixTransform::kNickName(), false)
192
2
          .AddNumber(":"),
193
2
      [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
194
2
         std::string* /*errmsg*/) {
195
0
        auto colon = uri.find(':');
196
0
        auto len = ParseSizeT(uri.substr(colon + 1));
197
0
        guard->reset(NewCappedPrefixTransform(len));
198
0
        return guard->get();
199
0
      });
200
2
  library.AddFactory<const SliceTransform>(
201
2
      ObjectLibrary::PatternEntry(CappedPrefixTransform::kClassName(), false)
202
2
          .AddNumber("."),
203
2
      [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
204
2
         std::string* /*errmsg*/) {
205
0
        auto len = ParseSizeT(
206
0
            uri.substr(strlen(CappedPrefixTransform::kClassName()) + 1));
207
0
        guard->reset(NewCappedPrefixTransform(len));
208
0
        return guard->get();
209
0
      });
210
2
  size_t num_types;
211
2
  return static_cast<int>(library.GetFactoryCount(&num_types));
212
2
}
213
214
Status SliceTransform::CreateFromString(
215
    const ConfigOptions& config_options, const std::string& value,
216
144k
    std::shared_ptr<const SliceTransform>* result) {
217
144k
  static std::once_flag once;
218
144k
  std::call_once(once, [&]() {
219
2
    RegisterBuiltinSliceTransform(*(ObjectLibrary::Default().get()), "");
220
2
  });
221
144k
  std::string id;
222
144k
  std::unordered_map<std::string, std::string> opt_map;
223
144k
  Status status = Customizable::GetOptionsMap(config_options, result->get(),
224
144k
                                              value, &id, &opt_map);
225
144k
  if (!status.ok()) {  // GetOptionsMap failed
226
0
    return status;
227
144k
  } else if (id.empty() && opt_map.empty()) {
228
144k
    result->reset();
229
144k
  } else {
230
184
    status = config_options.registry->NewSharedObject(id, result);
231
184
    if (config_options.ignore_unsupported_options && status.IsNotSupported()) {
232
0
      return Status::OK();
233
184
    } else if (status.ok()) {
234
0
      SliceTransform* transform = const_cast<SliceTransform*>(result->get());
235
0
      status =
236
0
          Customizable::ConfigureNewObject(config_options, transform, opt_map);
237
0
    }
238
184
  }
239
144k
  return status;
240
144k
}
241
242
0
std::string SliceTransform::AsString() const {
243
0
  if (HasRegisteredOptions()) {
244
0
    ConfigOptions opts;
245
0
    opts.delimiter = ";";
246
0
    return ToString(opts);
247
0
  }
248
0
  return GetId();
249
0
}
250
251
// 2 small internal utility functions, for efficient hex conversions
252
// and no need for snprintf, toupper etc...
253
// Originally from wdt/util/EncryptionUtils.cpp - for
254
// std::to_string(true)/DecodeHex:
255
728
char toHex(unsigned char v) {
256
728
  if (v <= 9) {
257
482
    return '0' + v;
258
482
  }
259
246
  return 'A' + v - 10;
260
728
}
261
// most of the code is for validation/error check
262
0
int fromHex(char c) {
263
  // toupper:
264
0
  if (c >= 'a' && c <= 'f') {
265
0
    c -= ('a' - 'A');  // aka 0x20
266
0
  }
267
  // validation
268
0
  if (c < '0' || (c > '9' && (c < 'A' || c > 'F'))) {
269
0
    return -1;  // invalid not 0-9A-F hex char
270
0
  }
271
0
  if (c <= '9') {
272
0
    return c - '0';
273
0
  }
274
0
  return c - 'A' + 10;
275
0
}
276
277
0
Slice::Slice(const SliceParts& parts, std::string* buf) {
278
0
  size_t length = 0;
279
0
  for (int i = 0; i < parts.num_parts; ++i) {
280
0
    length += parts.parts[i].size();
281
0
  }
282
0
  buf->reserve(length);
283
284
0
  for (int i = 0; i < parts.num_parts; ++i) {
285
0
    buf->append(parts.parts[i].data(), parts.parts[i].size());
286
0
  }
287
0
  data_ = buf->data();
288
0
  size_ = buf->size();
289
0
}
290
291
// Return a string that contains the copy of the referenced data.
292
2.14M
std::string Slice::ToString(bool hex) const {
293
2.14M
  std::string result;  // RVO/NRVO/move
294
2.14M
  if (hex) {
295
10.7k
    result.reserve(2 * size_);
296
11.1k
    for (size_t i = 0; i < size_; ++i) {
297
364
      unsigned char c = data_[i];
298
364
      result.push_back(toHex(c >> 4));
299
364
      result.push_back(toHex(c & 0xf));
300
364
    }
301
10.7k
    return result;
302
2.12M
  } else {
303
2.12M
    result.assign(data_, size_);
304
2.12M
    return result;
305
2.12M
  }
306
2.14M
}
307
308
// Originally from rocksdb/utilities/ldb_cmd.h
309
0
bool Slice::DecodeHex(std::string* result) const {
310
0
  std::string::size_type len = size_;
311
0
  if (len % 2) {
312
    // Hex string must be even number of hex digits to get complete bytes back
313
0
    return false;
314
0
  }
315
0
  if (!result) {
316
0
    return false;
317
0
  }
318
0
  result->clear();
319
0
  result->reserve(len / 2);
320
321
0
  for (size_t i = 0; i < len;) {
322
0
    int h1 = fromHex(data_[i++]);
323
0
    if (h1 < 0) {
324
0
      return false;
325
0
    }
326
0
    int h2 = fromHex(data_[i++]);
327
0
    if (h2 < 0) {
328
0
      return false;
329
0
    }
330
0
    result->push_back(static_cast<char>((h1 << 4) | h2));
331
0
  }
332
0
  return true;
333
0
}
334
335
0
PinnableSlice::PinnableSlice(PinnableSlice&& other) {
336
0
  *this = std::move(other);
337
0
}
338
339
0
PinnableSlice& PinnableSlice::operator=(PinnableSlice&& other) {
340
0
  if (this != &other) {
341
0
    Cleanable::Reset();
342
0
    Cleanable::operator=(std::move(other));
343
0
    size_ = other.size_;
344
0
    pinned_ = other.pinned_;
345
0
    if (pinned_) {
346
0
      data_ = other.data_;
347
      // When it's pinned, buf should no longer be of use.
348
0
    } else {
349
0
      if (other.buf_ == &other.self_space_) {
350
0
        self_space_ = std::move(other.self_space_);
351
0
        buf_ = &self_space_;
352
0
        data_ = buf_->data();
353
0
      } else {
354
0
        buf_ = other.buf_;
355
0
        data_ = other.data_;
356
0
      }
357
0
    }
358
0
    other.self_space_.clear();
359
0
    other.buf_ = &other.self_space_;
360
0
    other.pinned_ = false;
361
0
    other.PinSelf();
362
0
  }
363
0
  return *this;
364
0
}
365
366
}  // namespace ROCKSDB_NAMESPACE