]>
git.proxmox.com Git - ceph.git/blob - ceph/src/crimson/common/fixed_kv_node_layout.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
9 #include <boost/iterator/counting_iterator.hpp>
11 #include "include/byteorder.h"
13 #include "crimson/common/layout.h"
15 namespace crimson::common
{
17 template <typename T
, bool is_const
>
18 struct maybe_const_t
{
21 struct maybe_const_t
<T
, true> {
22 using type
= const T
*;
25 struct maybe_const_t
<T
, false> {
33 * Reusable implementation of a fixed size block mapping
34 * K -> V with internal representations KINT and VINT.
36 * Uses absl::container_internal::Layout for the actual memory layout.
38 * The primary interface exposed is centered on the iterator
39 * and related methods.
41 * Also included are helpers for doing splits and merges as for a btree.
51 bool VALIDATE_INVARIANTS
=true>
52 class FixedKVNodeLayout
{
55 using L
= absl::container_internal::Layout
<ceph_le32
, MetaInt
, KINT
, VINT
>;
56 static constexpr L layout
{1, 1, CAPACITY
, CAPACITY
};
59 template <bool is_const
>
61 friend class FixedKVNodeLayout
;
62 using parent_t
= typename maybe_const_t
<FixedKVNodeLayout
, is_const
>::type
;
70 uint16_t offset
) : node(parent
), offset(offset
) {}
72 iter_t(const iter_t
&) noexcept
= default;
73 iter_t(iter_t
&&) noexcept
= default;
74 template<bool is_const_
= is_const
>
75 iter_t(const iter_t
<false>& it
, std::enable_if_t
<is_const_
, int> = 0)
76 : iter_t
{it
.node
, it
.offset
}
78 iter_t
&operator=(const iter_t
&) = default;
79 iter_t
&operator=(iter_t
&&) = default;
81 // Work nicely with for loops without requiring a nested type.
82 using reference
= iter_t
&;
83 iter_t
&operator*() { return *this; }
84 iter_t
*operator->() { return this; }
86 iter_t
operator++(int) {
92 iter_t
&operator++() {
97 iter_t
operator--(int) {
104 iter_t
&operator--() {
110 uint16_t operator-(const iter_t
&rhs
) const {
111 assert(rhs
.node
== node
);
112 return offset
- rhs
.offset
;
115 iter_t
operator+(uint16_t off
) const {
120 iter_t
operator-(uint16_t off
) const {
126 friend bool operator==(const iter_t
&lhs
, const iter_t
&rhs
) {
127 assert(lhs
.node
== rhs
.node
);
128 return lhs
.offset
== rhs
.offset
;
131 friend bool operator!=(const iter_t
&lhs
, const iter_t
&rhs
) {
132 return !(lhs
== rhs
);
135 friend bool operator==(const iter_t
<is_const
> &lhs
, const iter_t
<!is_const
> &rhs
) {
136 assert(lhs
.node
== rhs
.node
);
137 return lhs
.offset
== rhs
.offset
;
139 friend bool operator!=(const iter_t
<is_const
> &lhs
, const iter_t
<!is_const
> &rhs
) {
140 return !(lhs
== rhs
);
143 return K(node
->get_key_ptr()[offset
]);
146 K
get_next_key_or_max() const {
147 auto next
= *this + 1;
148 if (next
== node
->end())
149 return std::numeric_limits
<K
>::max();
151 return next
->get_key();
154 void set_val(V val
) const {
155 static_assert(!is_const
);
156 node
->get_val_ptr()[offset
] = VINT(val
);
160 return V(node
->get_val_ptr()[offset
]);
163 bool contains(K addr
) const {
164 return (get_key() <= addr
) && (get_next_key_or_max() > addr
);
167 uint16_t get_offset() const {
172 void set_key(K _lb
) const {
173 static_assert(!is_const
);
176 node
->get_key_ptr()[offset
] = lb
;
179 typename maybe_const_t
<char, is_const
>::type
get_key_ptr() const {
180 return reinterpret_cast<
181 typename maybe_const_t
<char, is_const
>::type
>(
182 node
->get_key_ptr() + offset
);
185 typename maybe_const_t
<char, is_const
>::type
get_val_ptr() const {
186 return reinterpret_cast<
187 typename maybe_const_t
<char, is_const
>::type
>(
188 node
->get_val_ptr() + offset
);
191 using const_iterator
= iter_t
<true>;
192 using iterator
= iter_t
<false>;
195 enum class op_t
: uint8_t {
203 void replay(FixedKVNodeLayout
&l
) {
206 l
.insert(l
.lower_bound(key
), key
, val
);
210 auto iter
= l
.find(key
);
211 assert(iter
!= l
.end());
216 auto iter
= l
.find(key
);
217 assert(iter
!= l
.end());
222 assert(0 == "Impossible");
226 bool operator==(const delta_t
&rhs
) const {
227 return op
== rhs
.op
&&
234 class delta_buffer_t
{
235 std::vector
<delta_t
> buffer
;
238 return buffer
.empty();
247 delta_t::op_t::INSERT
,
259 delta_t::op_t::UPDATE
,
264 void remove(const K
&key
) {
269 delta_t::op_t::REMOVE
,
274 void replay(FixedKVNodeLayout
&node
) {
275 for (auto &i
: buffer
) {
279 size_t get_bytes() const {
280 return buffer
.size() * sizeof(delta_t
);
282 void copy_out(char *out
, size_t len
) {
283 assert(len
== get_bytes());
284 ::memcpy(out
, reinterpret_cast<const void *>(buffer
.data()), get_bytes());
287 void copy_in(const char *out
, size_t len
) {
289 assert(len
% sizeof(delta_t
) == 0);
290 buffer
= std::vector(
291 reinterpret_cast<const delta_t
*>(out
),
292 reinterpret_cast<const delta_t
*>(out
+ len
));
294 bool operator==(const delta_buffer_t
&rhs
) const {
295 return buffer
== rhs
.buffer
;
300 const_iterator _iter
,
303 delta_buffer_t
*recorder
) {
304 auto iter
= iterator(this, _iter
.offset
);
310 insert(iter
, key
, val
);
314 const_iterator _iter
,
316 delta_buffer_t
*recorder
) {
317 auto iter
= iterator(this, _iter
.offset
);
319 recorder
->update(iter
->get_key(), val
);
324 void journal_replace(
325 const_iterator _iter
,
328 delta_buffer_t
*recorder
) {
329 auto iter
= iterator(this, _iter
.offset
);
331 recorder
->remove(iter
->get_key());
332 recorder
->insert(key
, val
);
334 replace(iter
, key
, val
);
339 const_iterator _iter
,
340 delta_buffer_t
*recorder
) {
341 auto iter
= iterator(this, _iter
.offset
);
343 recorder
->remove(iter
->get_key());
349 FixedKVNodeLayout(char *buf
) :
352 virtual ~FixedKVNodeLayout() = default;
354 const_iterator
begin() const {
355 return const_iterator(
360 const_iterator
end() const {
361 return const_iterator(
378 const_iterator
iter_idx(uint16_t off
) const {
379 return const_iterator(
384 const_iterator
find(K l
) const {
386 for (; ret
!= end(); ++ret
) {
387 if (ret
->get_key() == l
)
393 const auto &tref
= *this;
394 return iterator(this, tref
.find(l
).offset
);
397 const_iterator
lower_bound(K l
) const {
398 auto it
= std::lower_bound(boost::make_counting_iterator
<uint16_t>(0),
399 boost::make_counting_iterator
<uint16_t>(get_size()),
401 [this](uint16_t i
, K key
) {
402 const_iterator
iter(this, i
);
403 return iter
->get_key() < key
;
405 return const_iterator(this, *it
);
408 iterator
lower_bound(K l
) {
409 const auto &tref
= *this;
410 return iterator(this, tref
.lower_bound(l
).offset
);
413 const_iterator
upper_bound(K l
) const {
414 auto it
= std::upper_bound(boost::make_counting_iterator
<uint16_t>(0),
415 boost::make_counting_iterator
<uint16_t>(get_size()),
417 [this](K key
, uint16_t i
) {
418 const_iterator
iter(this, i
);
419 return key
< iter
->get_key();
421 return const_iterator(this, *it
);
424 iterator
upper_bound(K l
) {
425 const auto &tref
= *this;
426 return iterator(this, tref
.upper_bound(l
).offset
);
429 const_iterator
get_split_pivot() const {
430 return iter_idx(get_size() / 2);
433 uint16_t get_size() const {
434 return *layout
.template Pointer
<0>(buf
);
440 * Set size representation to match size
442 void set_size(uint16_t size
) {
443 *layout
.template Pointer
<0>(buf
) = size
;
449 * Enables stashing a templated type within the layout.
450 * Cannot be modified after initial write as it is not represented
453 Meta
get_meta() const {
454 MetaInt
&metaint
= *layout
.template Pointer
<1>(buf
);
455 return Meta(metaint
);
457 void set_meta(const Meta
&meta
) {
458 *layout
.template Pointer
<1>(buf
) = MetaInt(meta
);
461 constexpr static size_t get_capacity() {
465 bool operator==(const FixedKVNodeLayout
&rhs
) const {
466 if (get_size() != rhs
.get_size()) {
471 auto iter2
= rhs
.begin();
472 while (iter
!= end()) {
473 if (iter
->get_key() != iter2
->get_key() ||
474 iter
->get_val() != iter2
->get_val()) {
486 * Takes *this and splits its contents into left and right.
489 FixedKVNodeLayout
&left
,
490 FixedKVNodeLayout
&right
) const {
491 auto piviter
= get_split_pivot();
493 left
.copy_from_foreign(left
.begin(), begin(), piviter
);
494 left
.set_size(piviter
- begin());
496 right
.copy_from_foreign(right
.begin(), piviter
, end());
497 right
.set_size(end() - piviter
);
499 auto [lmeta
, rmeta
] = get_meta().split_into(piviter
->get_key());
500 left
.set_meta(lmeta
);
501 right
.set_meta(rmeta
);
503 return piviter
->get_key();
509 * Takes two nodes and copies their contents into *this.
511 * precondition: left.size() + right.size() < CAPACITY
514 const FixedKVNodeLayout
&left
,
515 const FixedKVNodeLayout
&right
)
521 set_size(left
.get_size());
526 set_size(left
.get_size() + right
.get_size());
527 set_meta(Meta::merge_from(left
.get_meta(), right
.get_meta()));
531 * balance_into_new_nodes
533 * Takes the contents of left and right and copies them into
534 * replacement_left and replacement_right such that in the
535 * event that the number of elements is odd the extra goes to
536 * the left side iff prefer_left.
538 static K
balance_into_new_nodes(
539 const FixedKVNodeLayout
&left
,
540 const FixedKVNodeLayout
&right
,
542 FixedKVNodeLayout
&replacement_left
,
543 FixedKVNodeLayout
&replacement_right
)
545 auto total
= left
.get_size() + right
.get_size();
546 auto pivot_idx
= (left
.get_size() + right
.get_size()) / 2;
547 if (total
% 2 && prefer_left
) {
550 auto replacement_pivot
= pivot_idx
>= left
.get_size() ?
551 right
.iter_idx(pivot_idx
- left
.get_size())->get_key() :
552 left
.iter_idx(pivot_idx
)->get_key();
554 if (pivot_idx
< left
.get_size()) {
555 replacement_left
.copy_from_foreign(
556 replacement_left
.end(),
558 left
.iter_idx(pivot_idx
));
559 replacement_left
.set_size(pivot_idx
);
561 replacement_right
.copy_from_foreign(
562 replacement_right
.end(),
563 left
.iter_idx(pivot_idx
),
566 replacement_right
.set_size(left
.get_size() - pivot_idx
);
567 replacement_right
.copy_from_foreign(
568 replacement_right
.end(),
571 replacement_right
.set_size(total
- pivot_idx
);
573 replacement_left
.copy_from_foreign(
574 replacement_left
.end(),
577 replacement_left
.set_size(left
.get_size());
579 replacement_left
.copy_from_foreign(
580 replacement_left
.end(),
582 right
.iter_idx(pivot_idx
- left
.get_size()));
583 replacement_left
.set_size(pivot_idx
);
585 replacement_right
.copy_from_foreign(
586 replacement_right
.end(),
587 right
.iter_idx(pivot_idx
- left
.get_size()),
589 replacement_right
.set_size(total
- pivot_idx
);
592 auto [lmeta
, rmeta
] = Meta::rebalance(
593 left
.get_meta(), right
.get_meta(), replacement_pivot
);
594 replacement_left
.set_meta(lmeta
);
595 replacement_right
.set_meta(rmeta
);
596 return replacement_pivot
;
604 if (VALIDATE_INVARIANTS
) {
605 if (iter
!= begin()) {
606 assert((iter
- 1)->get_key() < key
);
609 assert(iter
->get_key() > key
);
611 assert(get_size() < CAPACITY
);
613 copy_from_local(iter
+ 1, iter
, end());
616 set_size(get_size() + 1);
622 assert(iter
!= end());
630 assert(iter
!= end());
631 if (VALIDATE_INVARIANTS
) {
632 if (iter
!= begin()) {
633 assert((iter
- 1)->get_key() < key
);
635 if ((iter
+ 1) != end()) {
636 assert((iter
+ 1)->get_key() > key
);
643 void remove(iterator iter
) {
644 assert(iter
!= end());
645 copy_from_local(iter
, iter
+ 1, end());
646 set_size(get_size() - 1);
652 * Get pointer to start of key array
654 KINT
*get_key_ptr() {
655 return layout
.template Pointer
<2>(buf
);
657 const KINT
*get_key_ptr() const {
658 return layout
.template Pointer
<2>(buf
);
664 * Get pointer to start of val array
666 VINT
*get_val_ptr() {
667 return layout
.template Pointer
<3>(buf
);
669 const VINT
*get_val_ptr() const {
670 return layout
.template Pointer
<3>(buf
);
674 * node_resolve/unresolve_vals
676 * If the representation for values depends in some way on the
677 * node in which they are located, users may implement
678 * resolve/unresolve to enable copy_from_foreign to handle that
681 virtual void node_resolve_vals(iterator from
, iterator to
) const {}
682 virtual void node_unresolve_vals(iterator from
, iterator to
) const {}
687 * Copies entries from [from_src, to_src) to tgt.
689 * tgt and from_src must be from different nodes.
690 * from_src and to_src must be from the same node.
692 static void copy_from_foreign(
694 const_iterator from_src
,
695 const_iterator to_src
) {
696 assert(tgt
->node
!= from_src
->node
);
697 assert(to_src
->node
== from_src
->node
);
699 tgt
->get_val_ptr(), from_src
->get_val_ptr(),
700 to_src
->get_val_ptr() - from_src
->get_val_ptr());
702 tgt
->get_key_ptr(), from_src
->get_key_ptr(),
703 to_src
->get_key_ptr() - from_src
->get_key_ptr());
704 from_src
->node
->node_resolve_vals(tgt
, tgt
+ (to_src
- from_src
));
705 tgt
->node
->node_unresolve_vals(tgt
, tgt
+ (to_src
- from_src
));
711 * Copies entries from [from_src, to_src) to tgt.
713 * tgt, from_src, and to_src must be from the same node.
715 static void copy_from_local(
719 assert(tgt
->node
== from_src
->node
);
720 assert(to_src
->node
== from_src
->node
);
722 tgt
->get_val_ptr(), from_src
->get_val_ptr(),
723 to_src
->get_val_ptr() - from_src
->get_val_ptr());
725 tgt
->get_key_ptr(), from_src
->get_key_ptr(),
726 to_src
->get_key_ptr() - from_src
->get_key_ptr());