Skip to content

Commit

Permalink
upd
Browse files Browse the repository at this point in the history
  • Loading branch information
wjr-z committed Jul 9, 2024
1 parent 91d6482 commit 7c51ae7
Show file tree
Hide file tree
Showing 10 changed files with 2,465 additions and 450 deletions.
2,127 changes: 2,100 additions & 27 deletions godbolt/wjr.hpp

Large diffs are not rendered by default.

149 changes: 113 additions & 36 deletions include/wjr/container/generic/bplus_tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,18 @@
* @file bplus_tree.hpp
* @brief B+ tree implementation.
*
* @details The addition, deletion, query, iterator and other functions have been
* implemented. The multiset/multimap/set/map adapter has not been implemented yet. The
* @details The multiset/multimap/set/map adapter has not been implemented yet. The
* node_size should be set to 16 by default, and optimization has been made for queries
* less than or equal to
* 16. The general B+ tree query is proportional to node_size. For example, when node_size
* is 16, the number of queries per bit is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
* 14, 15, 16, 16], and the average number of queries is 8.9 times. After improvement, the
* number of queries for the i-th query is [1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
* 10, 10], and the average number of queries is 6.56 times. In fact, the probability of
* querying smaller nodes is slightly greater than that of larger nodes, so the actual
* number of queries will be less. If the comparison operation of key_type is more
* complex, it is not recommended to use B+ tree, because the number of queries of B+ tree
* will be more, thus offsetting the advantages of B+ tree.
* less than or equal to 16. \n
* After improvement, the number of queries for the i-th query is
* [1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10], the average number of queries
* is 6.56 times. In fact, the probability of querying smaller nodes is slightly greater
* than that of larger nodes, so the actual number of queries will be less. If the
* comparison operation of key_type is more complex, it is not recommended to use B+ tree,
* because the number of queries of B+ tree will be more, thus offsetting the advantages
* of B+ tree.
*
* @note Currently not needed for use, and some bugs exists, don't use it.
* @todo
*
* @version 0.1
* @date 2024-05-06
Expand All @@ -30,7 +27,6 @@
#include <wjr/compressed_pair.hpp>
#include <wjr/container/generic/container_fn.hpp>
#include <wjr/container/intrusive/list.hpp>
#include <wjr/inline_arg.hpp>
#include <wjr/memory/uninitialized.hpp>

#if defined(WJR_X86)
Expand All @@ -50,6 +46,67 @@ struct bplus_tree_leaf_node;

namespace bplus_tree_details {

template <typename T, bool Inlined>
class inline_key {
public:
static_assert(!std::is_const_v<T>, "");

using value_type = T;
using reference = std::add_const_t<T> &;
using pointer = std::add_const_t<T> *;

constexpr inline_key() noexcept = default;
constexpr inline_key(const inline_key &other) noexcept = default;
constexpr inline_key(inline_key &&other) noexcept = default;
constexpr inline_key &operator=(const inline_key &other) noexcept = default;
constexpr inline_key &operator=(inline_key &&other) noexcept = default;
~inline_key() noexcept = default;

constexpr inline_key(reference value) noexcept(
std::is_nothrow_constructible_v<algined_storage<T>, reference>)
: m_storage(value) {}

constexpr reference operator*() const noexcept { return *m_storage; }
constexpr reference get() const noexcept { return m_storage.get(); }
constexpr pointer operator->() const noexcept { return get(); }

private:
// no need to check
algined_storage<T> m_storage;
};

template <typename T>
class inline_key<T, false> {
public:
static_assert(!std::is_const_v<T>, "");

using value_type = T;
using reference = std::add_const_t<T> &;
using pointer = std::add_const_t<T> *;

constexpr inline_key() noexcept = default;
constexpr inline_key(const inline_key &other) noexcept = default;
constexpr inline_key(inline_key &&other) noexcept = default;
constexpr inline_key &operator=(const inline_key &other) noexcept = default;
constexpr inline_key &operator=(inline_key &&other) noexcept = default;
~inline_key() noexcept = default;

constexpr inline_key(reference value) noexcept : m_ptr(std::addressof(value)) {}

constexpr reference operator*() const noexcept { return *m_ptr; }
constexpr pointer operator->() const noexcept { return m_ptr; }
constexpr reference get() const noexcept { return *m_ptr; }

private:
pointer m_ptr;
};

template <typename T>
struct is_possible_inline_key : std::is_trivially_copyable<algined_storage<T>> {};

template <typename T>
inline constexpr bool is_possible_inline_key_v = is_possible_inline_key<T>::value;

template <size_t Min, size_t Max, typename Other>
WJR_INTRINSIC_INLINE static void copy(Other *first, Other *last, Other *dest) noexcept {
#if WJR_HAS_BUILTIN(BPLUS_TREE_COPY)
Expand Down Expand Up @@ -89,15 +146,24 @@ struct bplus_tree_traits {
using key_compare = Compare;

static constexpr size_t node_size = Size;
using InlineKey = auto_key<std::remove_const_t<key_type>, 8>;
static constexpr bool is_inline_key = InlineKey::is_inlined;
static constexpr bool is_inline_key =
bplus_tree_details::is_possible_inline_key_v<std::remove_const_t<key_type>> &&
sizeof(key_type) <= 8;
static constexpr bool is_inline_value =
std::is_trivially_copyable_v<value_type> && sizeof(value_type) <= 16;
using InlineValue = std::conditional_t<is_inline_value, value_type, value_type *>;
bplus_tree_details::is_possible_inline_key_v<std::remove_const_t<value_type>> &&
sizeof(value_type) <= 8;

using InlineKey =
bplus_tree_details::inline_key<std::remove_const_t<key_type>, is_inline_key>;
using InlineValue = std::conditional_t<
is_inline_value,
bplus_tree_details::inline_key<std::remove_const_t<value_type>, true>,
value_type *>;

using node_type = bplus_tree_node<bplus_tree_traits>;
using inner_node_type = bplus_tree_inner_node<bplus_tree_traits>;
using leaf_node_type = bplus_tree_leaf_node<bplus_tree_traits, is_inline_key>;
using leaf_node_type =
bplus_tree_leaf_node<bplus_tree_traits, is_inline_key && !is_inline_value>;
static constexpr bool multi = Multi;

WJR_INTRINSIC_INLINE static const key_type &
Expand All @@ -110,13 +176,13 @@ struct bplus_tree_traits {
}

public:
template <size_t Min = 0, size_t Max = node_size, typename Other = char>
template <size_t Min = 0, size_t Max = node_size, typename Other = void>
WJR_INTRINSIC_INLINE static void copy(Other *first, Other *last,
Other *dest) noexcept {
return bplus_tree_details::copy<Min, Max>(first, last, dest);
}

template <size_t Min = 0, size_t Max = node_size, typename Other = char>
template <size_t Min = 0, size_t Max = node_size, typename Other = void>
WJR_INTRINSIC_INLINE static void copy_backward(Other *first, Other *last,
Other *dest) noexcept {
return bplus_tree_details::copy_backward<Min, Max>(first, last, dest);
Expand Down Expand Up @@ -202,6 +268,8 @@ struct bplus_tree_leaf_node<Traits, false> : bplus_tree_node<Traits>, list_node<
using key_type = typename Traits::key_type;
using value_type = typename Traits::value_type;
constexpr static size_t node_size = Traits::node_size;
constexpr static bool is_inline_value = Traits::is_inline_value;
using InlineValue = typename Traits::InlineValue;
using ListNode = list_node<>;

const key_type &__get_key(unsigned int pos) const noexcept {
Expand All @@ -224,15 +292,14 @@ struct bplus_tree_leaf_node<Traits, false> : bplus_tree_node<Traits>, list_node<
dst->m_values + dst_end);
}

WJR_INTRINSIC_INLINE void __assign(unsigned int idx,
value_type *const value) noexcept {
WJR_INTRINSIC_INLINE void __assign(unsigned int idx, InlineValue value) noexcept {
m_values[idx] = value;
}

constexpr ListNode *__get_list() noexcept { return this; }
constexpr const ListNode *__get_list() const noexcept { return this; }

alignas(16) value_type *m_values[node_size];
alignas(16) InlineValue m_values[node_size];
};

template <typename Traits>
Expand Down Expand Up @@ -443,7 +510,9 @@ class basic_bplus_tree {
using mapped_type = typename Traits::mapped_type;
static constexpr size_t node_size = Traits::node_size;
static constexpr bool is_inline_key = Traits::is_inline_key;
static constexpr bool is_inline_value = Traits::is_inline_value;
using InlineKey = typename Traits::InlineKey;
using InlineValue = typename Traits::InlineValue;
static constexpr size_t floor_half = node_size / 2;
static constexpr size_t ceil_half = node_size - floor_half;
static constexpr bool Multi = Traits::Multi;
Expand Down Expand Up @@ -532,19 +601,27 @@ class basic_bplus_tree {

private:
template <typename... Args>
value_type *__create_node(Args &&...args) noexcept {
auto &al = __get_allocator();
value_type *const xval =
(value_type *)_Alty_traits::allocate(al, sizeof(value_type));
uninitialized_construct_using_allocator(xval, al, std::forward<Args>(args)...);
return xval;
InlineValue __create_node(Args &&...args) noexcept {
if constexpr (is_inline_value) {
InlineValue ret(std::forward<Args>(args)...);
return ret;
} else {
auto &al = __get_allocator();
value_type *const xval =
(value_type *)_Alty_traits::allocate(al, sizeof(value_type));
uninitialized_construct_using_allocator(xval, al,
std::forward<Args>(args)...);
return xval;
}
}

template <typename... Args>
void __drop_node(value_type *xval) noexcept {
auto &al = __get_allocator();
_Alty_traits::destroy(al, xval);
_Alty_traits::deallocate(al, (uint8_t *)xval, sizeof(value_type));
void __drop_node(InlineValue xval) noexcept {
if constexpr (!is_inline_value) {
auto &al = __get_allocator();
_Alty_traits::destroy(al, xval);
_Alty_traits::deallocate(al, (uint8_t *)xval, sizeof(value_type));
}
}

const_iterator __get_insert_multi_pos(const key_type &key) const noexcept {
Expand Down Expand Up @@ -842,7 +919,7 @@ class basic_bplus_tree {
return;
}

WJR_NODISCARD iterator __insert_iter(const_iterator iter, value_type *xval) noexcept {
WJR_NODISCARD iterator __insert_iter(const_iterator iter, InlineValue xval) noexcept {
auto &al = __get_allocator();

leaf_node_type *leaf;
Expand Down Expand Up @@ -1006,7 +1083,7 @@ class basic_bplus_tree {
WJR_ASSERT_ASSUME(size <= Max);

if constexpr (Min == 1 && Offset == 1) {
if (size == 1) {
if (WJR_UNLIKELY(size == 1)) {
return 1;
}
}
Expand Down
16 changes: 8 additions & 8 deletions include/wjr/iterator/contiguous_iterator_adpater.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class contiguous_const_iterator_adapter {
std::is_nothrow_move_assignable_v<__pointer>) = default;

WJR_NODISCARD WJR_PURE WJR_CONSTEXPR20 pointer operator->() const noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
WJR_ASSERT_L0(m_container != nullptr,
"Can't dereference an value-initialized iterator.");
WJR_ASSERT_L0(m_ptr != nullptr, "Can't dereference an invalid iterator.");
Expand All @@ -59,7 +59,7 @@ class contiguous_const_iterator_adapter {
}

WJR_CONSTEXPR20 contiguous_const_iterator_adapter &operator++() noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
WJR_ASSERT_L0(m_container != nullptr,
"Can't increment an value-initialized iterator.");
WJR_ASSERT_L0(m_ptr != nullptr, "Can't increment an invalid iterator.");
Expand All @@ -77,7 +77,7 @@ class contiguous_const_iterator_adapter {
}

WJR_CONSTEXPR20 contiguous_const_iterator_adapter &operator--() noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
WJR_ASSERT_L0(m_container != nullptr,
"Can't decrement an value-initialized iterator.");
WJR_ASSERT_L0(m_ptr != nullptr, "Can't decrement an invalid iterator.");
Expand Down Expand Up @@ -171,7 +171,7 @@ class contiguous_const_iterator_adapter {

WJR_CONSTEXPR20 void
check_same_container(WJR_MAYBE_UNUSED const Container *cont) const noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
WJR_ASSERT_L0(m_container == cont,
"Can't compare iterators from different containers.");
#else
Expand All @@ -180,7 +180,7 @@ class contiguous_const_iterator_adapter {
}

private:
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
/// @private
WJR_CONSTEXPR20 void __set_container(const Container *container) noexcept {
m_container = container;
Expand Down Expand Up @@ -235,7 +235,7 @@ class contiguous_const_iterator_adapter {
#endif

__pointer m_ptr;
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
const Container *m_container;
#endif
};
Expand Down Expand Up @@ -344,7 +344,7 @@ struct pointer_traits<wjr::contiguous_const_iterator_adapter<Container, Traits>>
using difference_type = typename pointer::difference_type;

WJR_NODISCARD constexpr static element_type *to_address(const pointer &ptr) noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
const auto cont = ptr.m_container;
if (cont) {
WJR_ASSERT_L0(ptr.m_ptr >= ptr.__begin() && ptr.m_ptr <= ptr.__end(),
Expand All @@ -365,7 +365,7 @@ struct pointer_traits<wjr::contiguous_iterator_adapter<Container, Traits>> {
using difference_type = typename pointer::difference_type;

WJR_NODISCARD constexpr static element_type *to_address(const pointer &ptr) noexcept {
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECK)
#if WJR_HAS_DEBUG(CONTIGUOUS_ITERATOR_CHECKER)
const auto cont = ptr.m_container;
if (cont) {
WJR_ASSERT_L0(ptr.m_ptr >= ptr.__begin() && ptr.m_ptr <= ptr.__end(),
Expand Down
25 changes: 12 additions & 13 deletions include/wjr/math/convert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,14 @@ inline constexpr bool __is_fast_convert_iterator_v =
__is_fast_convert_iterator<Iter>::value;

template <typename Value, typename Converter>
struct __is_valid_converter
: std::disjunction<std::conjunction<std::is_same<Converter, char_converter_t>,
is_nonbool_integral<Value>>,
std::conjunction<std::is_same<Converter, origin_converter_t>,
is_nonbool_unsigned_integral<Value>>> {};
struct __is_valid_converter : std::false_type {};

template <typename Value>
struct __is_valid_converter<Value, char_converter_t> : is_nonbool_integral<Value> {};

template <typename Value>
struct __is_valid_converter<Value, origin_converter_t>
: is_nonbool_unsigned_integral<Value> {};

template <typename Value, typename Converter>
inline constexpr bool __is_valid_converter_v =
Expand Down Expand Up @@ -2405,8 +2408,7 @@ class __unsigned_from_chars_unchecked_fn<10> {
}
};

template <typename Value, typename IBase, typename Converter,
WJR_REQUIRES(is_nonbool_integral_v<Value>)>
template <typename Value, typename IBase, typename Converter>
void __fast_from_chars_unchecked_impl(const uint8_t *first, const uint8_t *last,
Value &val, IBase ibase, Converter conv) noexcept {
int sign = 0;
Expand Down Expand Up @@ -2454,8 +2456,7 @@ void __fast_from_chars_unchecked_impl(const uint8_t *first, const uint8_t *last,
}
}

template <typename Iter, typename Value, typename IBase, typename Converter,
WJR_REQUIRES(is_nonbool_integral_v<Value>)>
template <typename Iter, typename Value, typename IBase, typename Converter>
void __from_chars_unchecked_impl(Iter first, Iter last, Value &val, IBase ibase,
Converter conv) noexcept {
const auto __first = reinterpret_cast<const uint8_t *>(wjr::to_address(first));
Expand Down Expand Up @@ -2628,8 +2629,7 @@ struct __unsigned_from_chars_fn<10> {
}
};

template <typename Value, typename IBase, typename Converter,
WJR_REQUIRES(is_nonbool_integral_v<Value>)>
template <typename Value, typename IBase, typename Converter>
from_chars_result<const uint8_t *>
__fast_from_chars_impl(const uint8_t *first, const uint8_t *last, Value &val, IBase ibase,
Converter conv) noexcept {
Expand Down Expand Up @@ -2698,8 +2698,7 @@ __fast_from_chars_impl(const uint8_t *first, const uint8_t *last, Value &val, IB
return ret;
}

template <typename Value, typename IBase, typename Converter,
WJR_REQUIRES(is_nonbool_integral_v<Value>)>
template <typename Value, typename IBase, typename Converter>
from_chars_result<const char *> __from_chars_impl(const char *first, const char *last,
Value &val, IBase ibase,
Converter conv) noexcept {
Expand Down
5 changes: 2 additions & 3 deletions include/wjr/math/div.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,8 @@ WJR_INTRINSIC_INLINE void div_qr_1(uint64_t *dst, uint64_t &rem, const uint64_t
dst[n - 1] = div_qr_1_impl(dst, rem, src, n, div);
}

WJR_INTRINSIC_CONSTEXPR20 void div_qr_1(uint64_t *dst, uint64_t &rem, const uint64_t *src,
size_t n,
type_identity_t<uint64_t> div) noexcept {
WJR_INTRINSIC_INLINE void div_qr_1(uint64_t *dst, uint64_t &rem, const uint64_t *src,
size_t n, type_identity_t<uint64_t> div) noexcept {
WJR_ASSERT_ASSUME(n >= 1);
WJR_ASSERT_ASSUME(div != 0);

Expand Down
Loading

0 comments on commit 7c51ae7

Please sign in to comment.