Coverage Report

Created: 2025-07-23 07:17

/src/rocksdb/util/comparator.cc
Line
Count
Source (jump to first uncovered line)
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) 2011 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/comparator.h"
11
12
#include <algorithm>
13
#include <cstdint>
14
#include <memory>
15
#include <mutex>
16
#include <sstream>
17
18
#include "db/dbformat.h"
19
#include "port/lang.h"
20
#include "port/port.h"
21
#include "rocksdb/convenience.h"
22
#include "rocksdb/slice.h"
23
#include "rocksdb/utilities/customizable_util.h"
24
#include "rocksdb/utilities/object_registry.h"
25
#include "util/coding.h"
26
27
namespace ROCKSDB_NAMESPACE {
28
29
namespace {
30
class BytewiseComparatorImpl : public Comparator {
31
 public:
32
2
  BytewiseComparatorImpl() = default;
33
1.06M
  static const char* kClassName() { return "leveldb.BytewiseComparator"; }
34
916k
  const char* Name() const override { return kClassName(); }
35
36
1.13G
  int Compare(const Slice& a, const Slice& b) const override {
37
1.13G
    return a.compare(b);
38
1.13G
  }
39
40
0
  bool Equal(const Slice& a, const Slice& b) const override { return a == b; }
41
42
  void FindShortestSeparator(std::string* start,
43
3.95k
                             const Slice& limit) const override {
44
    // Find length of common prefix
45
3.95k
    size_t min_length = std::min(start->size(), limit.size());
46
3.95k
    size_t diff_index = 0;
47
1.94M
    while ((diff_index < min_length) &&
48
1.94M
           ((*start)[diff_index] == limit[diff_index])) {
49
1.94M
      diff_index++;
50
1.94M
    }
51
52
3.95k
    if (diff_index >= min_length) {
53
      // Do not shorten if one string is a prefix of the other
54
3.75k
    } else {
55
3.75k
      uint8_t start_byte = static_cast<uint8_t>((*start)[diff_index]);
56
3.75k
      uint8_t limit_byte = static_cast<uint8_t>(limit[diff_index]);
57
3.75k
      if (start_byte >= limit_byte) {
58
        // Cannot shorten since limit is smaller than start or start is
59
        // already the shortest possible.
60
0
        return;
61
0
      }
62
3.75k
      assert(start_byte < limit_byte);
63
64
3.75k
      if (diff_index < limit.size() - 1 || start_byte + 1 < limit_byte) {
65
3.61k
        (*start)[diff_index]++;
66
3.61k
        start->resize(diff_index + 1);
67
3.61k
      } else {
68
        //     v
69
        // A A 1 A A A
70
        // A A 2
71
        //
72
        // Incrementing the current byte will make start bigger than limit, we
73
        // will skip this byte, and find the first non 0xFF byte in start and
74
        // increment it.
75
133
        diff_index++;
76
77
290
        while (diff_index < start->size()) {
78
          // Keep moving until we find the first non 0xFF byte to
79
          // increment it
80
277
          if (static_cast<uint8_t>((*start)[diff_index]) <
81
277
              static_cast<uint8_t>(0xff)) {
82
120
            (*start)[diff_index]++;
83
120
            start->resize(diff_index + 1);
84
120
            break;
85
120
          }
86
157
          diff_index++;
87
157
        }
88
133
      }
89
3.75k
      assert(Compare(*start, limit) < 0);
90
3.75k
    }
91
3.95k
  }
92
93
0
  void FindShortSuccessor(std::string* key) const override {
94
    // Find first character that can be incremented
95
0
    size_t n = key->size();
96
0
    for (size_t i = 0; i < n; i++) {
97
0
      const uint8_t byte = (*key)[i];
98
0
      if (byte != static_cast<uint8_t>(0xff)) {
99
0
        (*key)[i] = byte + 1;
100
0
        key->resize(i + 1);
101
0
        return;
102
0
      }
103
0
    }
104
    // *key is a run of 0xffs.  Leave it alone.
105
0
  }
106
107
  bool IsSameLengthImmediateSuccessor(const Slice& s,
108
0
                                      const Slice& t) const override {
109
0
    if (s.size() != t.size() || s.size() == 0) {
110
0
      return false;
111
0
    }
112
0
    size_t diff_ind = s.difference_offset(t);
113
    // same slice
114
0
    if (diff_ind >= s.size()) {
115
0
      return false;
116
0
    }
117
0
    uint8_t byte_s = static_cast<uint8_t>(s[diff_ind]);
118
0
    uint8_t byte_t = static_cast<uint8_t>(t[diff_ind]);
119
    // first different byte must be consecutive, and remaining bytes must be
120
    // 0xff for s and 0x00 for t
121
0
    if (byte_s != uint8_t{0xff} && byte_s + 1 == byte_t) {
122
0
      for (size_t i = diff_ind + 1; i < s.size(); ++i) {
123
0
        byte_s = static_cast<uint8_t>(s[i]);
124
0
        byte_t = static_cast<uint8_t>(t[i]);
125
0
        if (byte_s != uint8_t{0xff} || byte_t != uint8_t{0x00}) {
126
0
          return false;
127
0
        }
128
0
      }
129
0
      return true;
130
0
    } else {
131
0
      return false;
132
0
    }
133
0
  }
134
135
24.5k
  bool CanKeysWithDifferentByteContentsBeEqual() const override {
136
24.5k
    return false;
137
24.5k
  }
138
139
  using Comparator::CompareWithoutTimestamp;
140
  int CompareWithoutTimestamp(const Slice& a, bool /*a_has_ts*/, const Slice& b,
141
69.8M
                              bool /*b_has_ts*/) const override {
142
69.8M
    return a.compare(b);
143
69.8M
  }
144
145
545k
  bool EqualWithoutTimestamp(const Slice& a, const Slice& b) const override {
146
545k
    return a == b;
147
545k
  }
148
};
149
150
class ReverseBytewiseComparatorImpl : public BytewiseComparatorImpl {
151
 public:
152
0
  ReverseBytewiseComparatorImpl() = default;
153
154
4
  static const char* kClassName() {
155
4
    return "rocksdb.ReverseBytewiseComparator";
156
4
  }
157
0
  const char* Name() const override { return kClassName(); }
158
159
0
  int Compare(const Slice& a, const Slice& b) const override {
160
0
    return -a.compare(b);
161
0
  }
162
163
  void FindShortestSeparator(std::string* start,
164
0
                             const Slice& limit) const override {
165
    // Find length of common prefix
166
0
    size_t min_length = std::min(start->size(), limit.size());
167
0
    size_t diff_index = 0;
168
0
    while ((diff_index < min_length) &&
169
0
           ((*start)[diff_index] == limit[diff_index])) {
170
0
      diff_index++;
171
0
    }
172
173
0
    assert(diff_index <= min_length);
174
0
    if (diff_index == min_length) {
175
      // Do not shorten if one string is a prefix of the other
176
      //
177
      // We could handle cases like:
178
      //     V
179
      // A A 2 X Y
180
      // A A 2
181
      // in a similar way as BytewiseComparator::FindShortestSeparator().
182
      // We keep it simple by not implementing it. We can come back to it
183
      // later when needed.
184
0
    } else {
185
0
      uint8_t start_byte = static_cast<uint8_t>((*start)[diff_index]);
186
0
      uint8_t limit_byte = static_cast<uint8_t>(limit[diff_index]);
187
0
      if (start_byte > limit_byte && diff_index < start->size() - 1) {
188
        // Case like
189
        //     V
190
        // A A 3 A A
191
        // A A 1 B B
192
        //
193
        // or
194
        //     v
195
        // A A 2 A A
196
        // A A 1 B B
197
        // In this case "AA2" will be good.
198
#ifndef NDEBUG
199
        std::string old_start = *start;
200
#endif
201
0
        start->resize(diff_index + 1);
202
#ifndef NDEBUG
203
        assert(old_start >= *start);
204
#endif
205
0
        assert(Slice(*start).compare(limit) > 0);
206
0
      }
207
0
    }
208
0
  }
209
210
0
  void FindShortSuccessor(std::string* /*key*/) const override {
211
    // Don't do anything for simplicity.
212
0
  }
213
214
  bool IsSameLengthImmediateSuccessor(const Slice& s,
215
0
                                      const Slice& t) const override {
216
    // Always returning false to prevent surfacing design flaws in
217
    // auto_prefix_mode
218
0
    (void)s, (void)t;
219
0
    return false;
220
    // "Correct" implementation:
221
    // return BytewiseComparatorImpl::IsSameLengthImmediateSuccessor(t, s);
222
0
  }
223
224
0
  bool CanKeysWithDifferentByteContentsBeEqual() const override {
225
0
    return false;
226
0
  }
227
228
  using Comparator::CompareWithoutTimestamp;
229
  int CompareWithoutTimestamp(const Slice& a, bool /*a_has_ts*/, const Slice& b,
230
0
                              bool /*b_has_ts*/) const override {
231
0
    return -a.compare(b);
232
0
  }
233
};
234
235
// Comparator with 64-bit integer timestamp.
236
// We did not performance test this yet.
237
template <typename TComparator>
238
class ComparatorWithU64TsImpl : public Comparator {
239
  static_assert(std::is_base_of<Comparator, TComparator>::value,
240
                "template type must be a inherited type of comparator");
241
242
 public:
243
0
  explicit ComparatorWithU64TsImpl() : Comparator(/*ts_sz=*/sizeof(uint64_t)) {
244
0
    assert(cmp_without_ts_.timestamp_size() == 0);
245
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::ComparatorWithU64TsImpl()
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::ComparatorWithU64TsImpl()
246
247
4
  static const char* kClassName() {
248
4
    static std::string class_name = kClassNameInternal();
249
4
    return class_name.c_str();
250
4
  }
comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::kClassName()
Line
Count
Source
247
2
  static const char* kClassName() {
248
2
    static std::string class_name = kClassNameInternal();
249
2
    return class_name.c_str();
250
2
  }
comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::kClassName()
Line
Count
Source
247
2
  static const char* kClassName() {
248
2
    static std::string class_name = kClassNameInternal();
249
2
    return class_name.c_str();
250
2
  }
251
252
0
  const char* Name() const override { return kClassName(); }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::Name() const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::Name() const
253
254
  // The comparator that compares the user key without timestamp part is treated
255
  // as the root comparator.
256
0
  const Comparator* GetRootComparator() const override {
257
0
    return &cmp_without_ts_;
258
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::GetRootComparator() const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::GetRootComparator() const
259
260
0
  void FindShortSuccessor(std::string*) const override {}
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::FindShortSuccessor(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::FindShortSuccessor(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*) const
261
0
  void FindShortestSeparator(std::string*, const Slice&) const override {}
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::FindShortestSeparator(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, rocksdb::Slice const&) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::FindShortestSeparator(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >*, rocksdb::Slice const&) const
262
0
  int Compare(const Slice& a, const Slice& b) const override {
263
0
    int ret = CompareWithoutTimestamp(a, b);
264
0
    size_t ts_sz = timestamp_size();
265
0
    if (ret != 0) {
266
0
      return ret;
267
0
    }
268
    // Compare timestamp.
269
    // For the same user key with different timestamps, larger (newer) timestamp
270
    // comes first.
271
0
    return -CompareTimestamp(ExtractTimestampFromUserKey(a, ts_sz),
272
0
                             ExtractTimestampFromUserKey(b, ts_sz));
273
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::Compare(rocksdb::Slice const&, rocksdb::Slice const&) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::Compare(rocksdb::Slice const&, rocksdb::Slice const&) const
274
275
0
  Slice GetMaxTimestamp() const override { return MaxU64Ts(); }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::GetMaxTimestamp() const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::GetMaxTimestamp() const
276
277
0
  Slice GetMinTimestamp() const override { return MinU64Ts(); }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::GetMinTimestamp() const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::GetMinTimestamp() const
278
279
0
  std::string TimestampToString(const Slice& timestamp) const override {
280
0
    assert(timestamp.size() == sizeof(uint64_t));
281
0
    uint64_t ts = 0;
282
0
    DecodeU64Ts(timestamp, &ts).PermitUncheckedError();
283
0
    return std::to_string(ts);
284
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::TimestampToString(rocksdb::Slice const&) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::TimestampToString(rocksdb::Slice const&) const
285
286
  using Comparator::CompareWithoutTimestamp;
287
  int CompareWithoutTimestamp(const Slice& a, bool a_has_ts, const Slice& b,
288
0
                              bool b_has_ts) const override {
289
0
    const size_t ts_sz = timestamp_size();
290
0
    assert(!a_has_ts || a.size() >= ts_sz);
291
0
    assert(!b_has_ts || b.size() >= ts_sz);
292
0
    Slice lhs = a_has_ts ? StripTimestampFromUserKey(a, ts_sz) : a;
293
0
    Slice rhs = b_has_ts ? StripTimestampFromUserKey(b, ts_sz) : b;
294
0
    return cmp_without_ts_.Compare(lhs, rhs);
295
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::CompareWithoutTimestamp(rocksdb::Slice const&, bool, rocksdb::Slice const&, bool) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::CompareWithoutTimestamp(rocksdb::Slice const&, bool, rocksdb::Slice const&, bool) const
296
0
  int CompareTimestamp(const Slice& ts1, const Slice& ts2) const override {
297
0
    assert(ts1.size() == sizeof(uint64_t));
298
0
    assert(ts2.size() == sizeof(uint64_t));
299
0
    uint64_t lhs = DecodeFixed64(ts1.data());
300
0
    uint64_t rhs = DecodeFixed64(ts2.data());
301
0
    if (lhs < rhs) {
302
0
      return -1;
303
0
    } else if (lhs > rhs) {
304
0
      return 1;
305
0
    } else {
306
0
      return 0;
307
0
    }
308
0
  }
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::CompareTimestamp(rocksdb::Slice const&, rocksdb::Slice const&) const
Unexecuted instantiation: comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::CompareTimestamp(rocksdb::Slice const&, rocksdb::Slice const&) const
309
310
 private:
311
4
  static std::string kClassNameInternal() {
312
4
    std::stringstream ss;
313
4
    ss << TComparator::kClassName() << ".u64ts";
314
4
    return ss.str();
315
4
  }
comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::BytewiseComparatorImpl>::kClassNameInternal()
Line
Count
Source
311
2
  static std::string kClassNameInternal() {
312
2
    std::stringstream ss;
313
2
    ss << TComparator::kClassName() << ".u64ts";
314
2
    return ss.str();
315
2
  }
comparator.cc:rocksdb::(anonymous namespace)::ComparatorWithU64TsImpl<rocksdb::(anonymous namespace)::ReverseBytewiseComparatorImpl>::kClassNameInternal()
Line
Count
Source
311
2
  static std::string kClassNameInternal() {
312
2
    std::stringstream ss;
313
2
    ss << TComparator::kClassName() << ".u64ts";
314
2
    return ss.str();
315
2
  }
316
317
  TComparator cmp_without_ts_;
318
};
319
320
}  // namespace
321
322
5.19M
const Comparator* BytewiseComparator() {
323
5.19M
  STATIC_AVOID_DESTRUCTION(BytewiseComparatorImpl, bytewise);
324
5.19M
  return &bytewise;
325
5.19M
}
326
327
0
const Comparator* ReverseBytewiseComparator() {
328
0
  STATIC_AVOID_DESTRUCTION(ReverseBytewiseComparatorImpl, rbytewise);
329
0
  return &rbytewise;
330
0
}
331
332
0
const Comparator* BytewiseComparatorWithU64Ts() {
333
0
  STATIC_AVOID_DESTRUCTION(ComparatorWithU64TsImpl<BytewiseComparatorImpl>,
334
0
                           comp_with_u64_ts);
335
0
  return &comp_with_u64_ts;
336
0
}
337
338
0
const Comparator* ReverseBytewiseComparatorWithU64Ts() {
339
0
  STATIC_AVOID_DESTRUCTION(
340
0
      ComparatorWithU64TsImpl<ReverseBytewiseComparatorImpl>, comp_with_u64_ts);
341
0
  return &comp_with_u64_ts;
342
0
}
343
344
0
Status DecodeU64Ts(const Slice& ts, uint64_t* int_ts) {
345
0
  if (ts.size() != sizeof(uint64_t)) {
346
0
    return Status::InvalidArgument("U64Ts timestamp size mismatch.");
347
0
  }
348
0
  *int_ts = DecodeFixed64(ts.data());
349
0
  return Status::OK();
350
0
}
351
352
0
Slice EncodeU64Ts(uint64_t ts, std::string* ts_buf) {
353
0
  char buf[sizeof(ts)];
354
0
  EncodeFixed64(buf, ts);
355
0
  ts_buf->assign(buf, sizeof(buf));
356
0
  return Slice(*ts_buf);
357
0
}
358
359
0
Slice MaxU64Ts() {
360
0
  static constexpr char kTsMax[] = "\xff\xff\xff\xff\xff\xff\xff\xff";
361
0
  return Slice(kTsMax, sizeof(uint64_t));
362
0
}
363
364
0
Slice MinU64Ts() {
365
0
  static constexpr char kTsMin[] = "\x00\x00\x00\x00\x00\x00\x00\x00";
366
0
  return Slice(kTsMin, sizeof(uint64_t));
367
0
}
368
369
static int RegisterBuiltinComparators(ObjectLibrary& library,
370
2
                                      const std::string& /*arg*/) {
371
2
  library.AddFactory<const Comparator>(
372
2
      BytewiseComparatorImpl::kClassName(),
373
2
      [](const std::string& /*uri*/,
374
2
         std::unique_ptr<const Comparator>* /*guard*/,
375
2
         std::string* /*errmsg*/) { return BytewiseComparator(); });
376
2
  library.AddFactory<const Comparator>(
377
2
      ReverseBytewiseComparatorImpl::kClassName(),
378
2
      [](const std::string& /*uri*/,
379
2
         std::unique_ptr<const Comparator>* /*guard*/,
380
2
         std::string* /*errmsg*/) { return ReverseBytewiseComparator(); });
381
2
  library.AddFactory<const Comparator>(
382
2
      ComparatorWithU64TsImpl<BytewiseComparatorImpl>::kClassName(),
383
2
      [](const std::string& /*uri*/,
384
2
         std::unique_ptr<const Comparator>* /*guard*/,
385
2
         std::string* /*errmsg*/) { return BytewiseComparatorWithU64Ts(); });
386
2
  library.AddFactory<const Comparator>(
387
2
      ComparatorWithU64TsImpl<ReverseBytewiseComparatorImpl>::kClassName(),
388
2
      [](const std::string& /*uri*/,
389
2
         std::unique_ptr<const Comparator>* /*guard*/,
390
2
         std::string* /*errmsg*/) {
391
0
        return ReverseBytewiseComparatorWithU64Ts();
392
0
      });
393
2
  return 4;
394
2
}
395
396
Status Comparator::CreateFromString(const ConfigOptions& config_options,
397
                                    const std::string& value,
398
146k
                                    const Comparator** result) {
399
146k
  static std::once_flag once;
400
146k
  std::call_once(once, [&]() {
401
2
    RegisterBuiltinComparators(*(ObjectLibrary::Default().get()), "");
402
2
  });
403
146k
  std::string id;
404
146k
  std::unordered_map<std::string, std::string> opt_map;
405
146k
  Status status = Customizable::GetOptionsMap(config_options, *result, value,
406
146k
                                              &id, &opt_map);
407
146k
  if (!status.ok()) {  // GetOptionsMap failed
408
0
    return status;
409
0
  }
410
146k
  if (id == BytewiseComparatorImpl::kClassName()) {
411
146k
    *result = BytewiseComparator();
412
146k
  } else if (id == ReverseBytewiseComparatorImpl::kClassName()) {
413
0
    *result = ReverseBytewiseComparator();
414
0
  } else if (id ==
415
0
             ComparatorWithU64TsImpl<BytewiseComparatorImpl>::kClassName()) {
416
0
    *result = BytewiseComparatorWithU64Ts();
417
0
  } else if (id == ComparatorWithU64TsImpl<
418
0
                       ReverseBytewiseComparatorImpl>::kClassName()) {
419
0
    *result = ReverseBytewiseComparatorWithU64Ts();
420
0
  } else if (value.empty()) {
421
    // No Id and no options.  Clear the object
422
0
    *result = nullptr;
423
0
    return Status::OK();
424
0
  } else if (id.empty()) {  // We have no Id but have options.  Not good
425
0
    return Status::NotSupported("Cannot reset object ", id);
426
0
  } else {
427
0
    status = config_options.registry->NewStaticObject(id, result);
428
0
    if (!status.ok()) {
429
0
      if (config_options.ignore_unsupported_options &&
430
0
          status.IsNotSupported()) {
431
0
        return Status::OK();
432
0
      } else {
433
0
        return status;
434
0
      }
435
0
    } else {
436
0
      Comparator* comparator = const_cast<Comparator*>(*result);
437
0
      status =
438
0
          Customizable::ConfigureNewObject(config_options, comparator, opt_map);
439
0
    }
440
0
  }
441
146k
  return status;
442
146k
}
443
}  // namespace ROCKSDB_NAMESPACE