rust-librocksdb-sys 0.43.0+11.0.4

Native bindings to librocksdb
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
//  Copyright (c) Meta Platforms, Inc. and affiliates.
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).

#pragma once

#include <atomic>
#include <vector>

#include "rocksdb/rocksdb_namespace.h"
#include "test_util/sync_point.h"
#include "util/math.h"

namespace ROCKSDB_NAMESPACE {

// Declares a wrapper type around UnderlyingT that allows it to be divided up
// into and accessed as bit fields. This is mostly intended to aid in packing
// fields into atomic variables to reduce the need for locking in concurrent
// code and/or to simplify reasoning on and accommodation of different
// interesting, bug-prone interleavings. Convenient atomic wrappers
// (RelaxedAtomic, Atomic) are provided below to aid usage with atomics,
// especially for CAS updates, but it is even possible to combine operations on
// multiple bit fields into a single non-CAS atomic operation using Transforms
// below.
//
// Unlike C/C++ bit fields, this implementation guarantees tight bit packing
// so that all available lock-free atomic bits can be utilized.
//
// The specific bit fields are declared outside the declaration using
// BoolBitField and UnsignedBitField below. Example usage:
//
// struct MyState : public BitFields<uint32_t, MyState> {
//   // Extra helper declarations and/or field type declarations
// };
//
// // Starts with a 16-bit field returned as uint16_t
// using Field1 = UnsignedBitField<MyState, 16, NoPrevBitField>;
// using Field2 = BoolBitField<MyState, Field1>;
// using Field3 = BoolBitField<MyState, Field2>;
// using Field4 = UnsignedBitField<MyState, 5, Field3>;  // 5 bits in a uint8_t
//
// // MyState{} is zero-initialized
// auto state = MyState{}.With<Field1>(42U).With<Field2>(true);
// state.Set<Field4>(3U);
// state.Ref<Field1>() += state.Get<Field4>();
//
// Note that there's nothing preventing you from declaring overlapping fields
// in the same 'MyState' family. This could be useful for variant types where
// an earlier field determines which layout later fields are using. For example,
// an alternate field after Field2:
//
// using Field3a = UnsignedBitField<State, 6, Field2>;  // 6 bits in a uint8_t
//
template <typename UnderlyingT, typename DerivedT>
struct BitFields {
  using U = UnderlyingT;
  U underlying = 0;
  static constexpr int kBitCount = sizeof(U) * 8;

  using Derived = DerivedT;

  // Modify a given field in place
  template <typename BitFieldT>
  void Set(typename BitFieldT::V value) {
    static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
    Derived& derived = static_cast<Derived&>(*this);
    BitFieldT::SetIn(derived, value);
  }

  // Return a copy with the given field modified
  template <typename BitFieldT>
  constexpr Derived With(typename BitFieldT::V value) const {
    static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
    Derived rv = static_cast<const Derived&>(*this);
    BitFieldT::SetIn(rv, value);
    return rv;
  }

  // Get the value of a field
  template <typename BitFieldT>
  typename BitFieldT::V Get() const {
    static_assert(std::is_same_v<typename BitFieldT::Parent, Derived>);
    return BitFieldT::GetFrom(static_cast<const Derived&>(*this));
  }

  // Reference and Ref() are not intended to behave as full references but to
  // provide a convenient way to do operations like +=, |=, etc. Get and Set
  // are preferred for simple operations.
  template <typename BitFieldT>
  struct Reference {
    explicit Reference(BitFields& bf) : bf_(bf) {}
    Reference(const Reference&) = default;
    Reference& operator=(const Reference&) = default;
    Reference(Reference&&) = default;
    Reference& operator=(Reference&&) = default;

    void operator=(typename BitFieldT::V value) { bf_.Set<BitFieldT>(value); }
    void operator+=(typename BitFieldT::V value) {
      bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() + value);
    }
    void operator-=(typename BitFieldT::V value) {
      bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() - value);
    }
    void operator|=(typename BitFieldT::V value) {
      bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() | value);
    }
    void operator&=(typename BitFieldT::V value) {
      bf_.Set<BitFieldT>(bf_.Get<BitFieldT>() & value);
    }

   private:
    BitFields& bf_;
  };

  template <typename BitFieldT>
  Reference<BitFieldT> Ref() {
    return Reference<BitFieldT>(*this);
  }

  bool operator==(const BitFields& other) const = default;
  bool operator!=(const BitFields& other) const = default;
};

// For building atomic updates affecting one or more fields, assuming all the
// updates are bitwise-or.
template <typename BitFieldsT>
struct OrTransformer {
  using U = typename BitFieldsT::U;
  U to_or = 0;
  // + for general combine
  OrTransformer<BitFieldsT> operator+(
      const OrTransformer<BitFieldsT>& other) const {
    return OrTransformer<BitFieldsT>{to_or | other.to_or};
  }
};

// For building atomic updates affecting one or more fields, assuming all the
// updates are bitwise-and.
template <typename BitFieldsT>
struct AndTransformer {
  using U = typename BitFieldsT::U;
  U to_and = 0;
  // + for general combine
  AndTransformer<BitFieldsT> operator+(
      const AndTransformer<BitFieldsT>& other) const {
    return AndTransformer<BitFieldsT>{to_and & other.to_and};
  }
};

// Can represent a combination of both subtractions and additions, representing
// subtractions as the addition of a negated value. To ensure we don't create a
// net overflow or underflow between fields, in debug builds we track the
// corresponding preconditions. (NOTE that when representing a subtraction, we
// rely on overflow of the unsigned representation.)
template <typename BitFieldsT>
struct AddTransformer {
  using U = typename BitFieldsT::U;
  U to_add = 0;
#ifndef NDEBUG
  struct Precondition {
    U mask;   // for bits of the target field
    U piece;  // component of to_add for the target field
  };
  std::vector<Precondition> preconditions;
#endif  // NDEBUG
  void AssertPreconditions([[maybe_unused]] U from) const {
#ifndef NDEBUG
    for (auto p : preconditions) {
      U tmp = (from & p.mask) + p.piece;
      // Assert no under/overflow (unless the field is at the top bits of the
      // representation in U, which is allowed because it doesn't lead to
      // leakage into other fields)
      testable_assert((tmp & ~p.mask) == 0);
    }
#endif  // NDEBUG
  }
  // + for general combine
  AddTransformer<BitFieldsT> operator+(
      const AddTransformer<BitFieldsT>& other) const {
    AddTransformer<BitFieldsT> rv{to_add + other.to_add};
#ifndef NDEBUG
    rv.preconditions = preconditions;
    rv.preconditions.insert(rv.preconditions.end(), other.preconditions.begin(),
                            other.preconditions.end());
#endif  // NDEBUG
    return rv;
  }
};

// Placeholder for PrevField for the first field
struct NoPrevBitField {
  // no instances
  NoPrevBitField() = delete;
  static constexpr int kEndBit = 0;
};

// For declaring a single-bit field accessed as a boolean. See example above on
// BitFields
template <typename BitFieldsT, typename PrevField>
struct BoolBitField {
  using Parent = BitFieldsT;
  using ParentBase =
      BitFields<typename BitFieldsT::U, typename BitFieldsT::Derived>;
  using U = typename BitFieldsT::U;
  using V = bool;
  static constexpr int kBitOffset = PrevField::kEndBit;
  static constexpr int kEndBit = kBitOffset + 1;
  static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);

  // no instances
  BoolBitField() = delete;

  // NOTE: allow BitFieldsT to be derived from BitFields<> which can be
  // passed in here
  static bool GetFrom(const ParentBase& bf) {
    return (bf.underlying & (U{1} << kBitOffset)) != 0;
  }
  static void SetIn(ParentBase& bf, bool value) {
    // NOTE: avoiding conditional branches is usually best for speed on modern
    // processors
    bf.underlying =
        (bf.underlying & ~(U{1} << kBitOffset)) | (U{value} << kBitOffset);
  }
  static OrTransformer<BitFieldsT> SetTransform() { return Or(true); }
  static OrTransformer<BitFieldsT> Or(bool b) {
    return OrTransformer<BitFieldsT>{U{b} << kBitOffset};
  }
  static AndTransformer<BitFieldsT> ClearTransform() { return And(false); }
  static AndTransformer<BitFieldsT> And(bool b) {
    return AndTransformer<BitFieldsT>{~(U{!b} << kBitOffset)};
  }
};

// For declaring a multi-bit field accessed as an unsigned int. See example
// above on BitFields
template <typename BitFieldsT, int kBitCount, typename PrevField>
struct UnsignedBitField {
  using Parent = BitFieldsT;
  using U = typename BitFieldsT::U;
  // Smallest uint type that can fit kBitCount bits
  using V = std::conditional_t<
      kBitCount <= 8, uint8_t,
      std::conditional_t<
          kBitCount <= 16, uint16_t,
          std::conditional_t<kBitCount <= 32, uint32_t, uint64_t>>>;
  static constexpr int kBitOffset = PrevField::kEndBit;
  static constexpr int kEndBit = kBitOffset + kBitCount;
  static_assert(kBitCount >= 1);
  static_assert(kBitCount <= 64);
  static_assert(kBitOffset >= 0 && kEndBit <= BitFieldsT::kBitCount);
  static constexpr bool kIncludesTopBit = (kEndBit == BitFieldsT::kBitCount);

  static constexpr V kMask = (V{1} << (kBitCount - 1) << 1) - 1;

  // no instances
  UnsignedBitField() = delete;

  static V GetFrom(const BitFieldsT& bf) {
    return BitwiseAnd(bf.underlying >> kBitOffset, kMask);
  }

  static void SetIn(BitFieldsT& bf, V value) {
    bf.underlying &= ~(static_cast<U>(kMask) << kBitOffset);
    bf.underlying |= static_cast<U>(value & kMask) << kBitOffset;
  }

  // Create a transform for clearing this field to zero.
  static AndTransformer<BitFieldsT> ClearTransform() {
    return AndTransformer<BitFieldsT>{~(static_cast<U>(kMask) << kBitOffset)};
  }

  // Create a transform for bitwise-and
  static AndTransformer<BitFieldsT> AndTransform(V value) {
    assert((value & ~kMask) == 0);
    return AndTransformer<BitFieldsT>{
        ~(static_cast<U>(value ^ kMask) << kBitOffset)};
  }

  // Create a transform for bitwise-or
  static OrTransformer<BitFieldsT> OrTransform(V value) {
    assert((value & ~kMask) == 0);
    return OrTransformer<BitFieldsT>{static_cast<U>(value) << kBitOffset};
  }

  // Create a transform for adding a particular value, but with the precondition
  // that adding the value will not overflow the field. This applies for fields
  // that do not include the top bit of the underlying representation. Can be
  // combined with other additive transforms for other fields.
  static AddTransformer<BitFieldsT> PlusTransformPromiseNoOverflow(V value) {
    static_assert(!kIncludesTopBit);
    AddTransformer<BitFieldsT> rv{static_cast<U>(value) << kBitOffset};
#ifndef NDEBUG
    rv.preconditions.push_back(
        {static_cast<U>(kMask) << kBitOffset, rv.to_add});
#endif  // NDEBUG
    return rv;
  }

  // Create a transform for adding a particular value, but ignoring any overflow
  // in that field. This applies for fields that include the top bit of the
  // underlying representation. Can be combined with other additive transforms
  // for other fields.
  static AddTransformer<BitFieldsT> PlusTransformIgnoreOverflow(V value) {
    static_assert(kIncludesTopBit);
    AddTransformer<BitFieldsT> rv{static_cast<U>(value) << kBitOffset};
    return rv;
  }

  // Create a transform for subtracting a particular value, but with the
  // precondition that subtracting the value will not underflow the field. This
  // applies for fields that do not include the top bit of the underlying
  // representation. Can be combined with other additive transforms for other
  // fields.
  static AddTransformer<BitFieldsT> MinusTransformPromiseNoUnderflow(V value) {
    static_assert(!kIncludesTopBit);
    AddTransformer<BitFieldsT> rv{U{0} - (static_cast<U>(value) << kBitOffset)};
#ifndef NDEBUG
    rv.preconditions.push_back(
        {static_cast<U>(kMask) << kBitOffset, rv.to_add});
#endif  // NDEBUG
    return rv;
  }

  // Create a transform for subtracting a particular value, but ignoring any
  // underflow in that field. This applies for fields that include the top bit
  // of the underlying representation. Can be combined with other additive
  // transforms for other fields.
  static AddTransformer<BitFieldsT> MinusTransformIgnoreUnderflow(V value) {
    static_assert(kIncludesTopBit);
    AddTransformer<BitFieldsT> rv{U{0} - (static_cast<U>(value) << kBitOffset)};
    return rv;
  }
};

// A handy wrapper for a relaxed atomic on some BitFields type, like
// RelaxedAtomic but without direct arithmetic operations. For encapsulation,
// usual arithmetic atomic operations are only available by calling
// ApplyRelaxed() on Transforms returned from field classes. Extending an
// example from BitFields:
//
// auto transform = Field2::ClearTransform() + Field4::ClearTransform();
// MyState old_state;
// my_atomic.ApplyRelaxed(transform, &old_state);
// auto field2_before_clearing = old_state.Get<Field2>();
//
template <typename BitFieldsT>
class RelaxedBitFieldsAtomic {
 public:
  using U = typename BitFieldsT::U;
  explicit RelaxedBitFieldsAtomic(BitFieldsT initial = {})
      : v_(initial.underlying) {}
  void StoreRelaxed(BitFieldsT desired) {
    v_.store(desired.underlying, std::memory_order_relaxed);
  }
  BitFieldsT LoadRelaxed() const {
    return BitFieldsT{v_.load(std::memory_order_relaxed)};
  }
  bool CasWeakRelaxed(BitFieldsT& expected, BitFieldsT desired) {
    return v_.compare_exchange_weak(expected.underlying, desired.underlying,
                                    std::memory_order_relaxed);
  }
  bool CasStrongRelaxed(BitFieldsT& expected, BitFieldsT desired) {
    return v_.compare_exchange_strong(expected.underlying, desired.underlying,
                                      std::memory_order_relaxed);
  }
  BitFieldsT ExchangeRelaxed(BitFieldsT desired) {
    return BitFieldsT{
        v_.exchange(desired.underlying, std::memory_order_relaxed)};
  }
  void ApplyRelaxed(const OrTransformer<BitFieldsT>& transform,
                    BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    ApplyImpl<std::memory_order_relaxed>(transform, before, after);
  }
  void ApplyRelaxed(const AndTransformer<BitFieldsT>& transform,
                    BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    ApplyImpl<std::memory_order_relaxed>(transform, before, after);
  }
  void ApplyRelaxed(const AddTransformer<BitFieldsT>& transform,
                    BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    ApplyImpl<std::memory_order_relaxed>(transform, before, after);
  }

 protected:  // fns
  template <std::memory_order kOrder>
  void ApplyImpl(const OrTransformer<BitFieldsT>& transform,
                 BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    U before_val = v_.fetch_or(transform.to_or, kOrder);
    if (before) {
      before->underlying = before_val;
    }
    if (after) {
      after->underlying = before_val | transform.to_or;
    }
  }
  template <std::memory_order kOrder>
  void ApplyImpl(const AndTransformer<BitFieldsT>& transform,
                 BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    U before_val = v_.fetch_and(transform.to_and, kOrder);
    if (before) {
      before->underlying = before_val;
    }
    if (after) {
      after->underlying = before_val & transform.to_and;
    }
  }
  template <std::memory_order kOrder>
  void ApplyImpl(const AddTransformer<BitFieldsT>& transform,
                 BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    U before_val = v_.fetch_add(transform.to_add, kOrder);
    transform.AssertPreconditions(before_val);
    if (before) {
      before->underlying = before_val;
    }
    if (after) {
      after->underlying = before_val + transform.to_add;
    }
  }

 protected:  // data
  std::atomic<U> v_;
};

// A handy wrapper for an aquire-release atomic (also relaxed semantics
// available) on some BitFields type. See RelaxedBitFieldsAtomic and
// Atomic in atomic.h for more info.
template <typename BitFieldsT>
class BitFieldsAtomic : public RelaxedBitFieldsAtomic<BitFieldsT> {
 public:
  using Base = RelaxedBitFieldsAtomic<BitFieldsT>;
  using U = typename BitFieldsT::U;

  explicit BitFieldsAtomic(BitFieldsT initial = {}) : Base(initial) {}

  void Store(BitFieldsT desired) {
    Base::v_.store(desired.underlying, std::memory_order_release);
  }
  BitFieldsT Load() const {
    return BitFieldsT{Base::v_.load(std::memory_order_acquire)};
  }
  bool CasWeak(BitFieldsT& expected, BitFieldsT desired) {
    return Base::v_.compare_exchange_weak(
        expected.underlying, desired.underlying, std::memory_order_acq_rel);
  }
  bool CasStrong(BitFieldsT& expected, BitFieldsT desired) {
    return Base::v_.compare_exchange_strong(
        expected.underlying, desired.underlying, std::memory_order_acq_rel);
  }
  BitFieldsT Exchange(BitFieldsT desired) {
    return BitFieldsT{
        Base::v_.exchange(desired.underlying, std::memory_order_acq_rel)};
  }
  void Apply(const OrTransformer<BitFieldsT>& transform,
             BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
                                                        after);
  }
  void Apply(const AndTransformer<BitFieldsT>& transform,
             BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
                                                        after);
  }
  void Apply(const AddTransformer<BitFieldsT>& transform,
             BitFieldsT* before = nullptr, BitFieldsT* after = nullptr) {
    Base::template ApplyImpl<std::memory_order_acq_rel>(transform, before,
                                                        after);
  }
};

}  // namespace ROCKSDB_NAMESPACE