5#include <unordered_map>
24std::pair<std::vector<UniqueLeaf>, std::vector<std::uint8_t>>
25group_duplicates(std::vector<BtreeEntry> entries) {
26 std::stable_sort(entries.begin(), entries.end(), [](
const BtreeEntry& a,
const BtreeEntry& b) {
27 return compare_keys(a.key, b.key) < 0;
30 std::vector<UniqueLeaf> unique_leaves;
31 std::vector<std::uint8_t> payload_data;
33 while (i < entries.size()) {
34 std::size_t j = i + 1;
39 unique_leaves.push_back(UniqueLeaf{entries[i].key, entries[i].offset});
41 std::vector<std::uint64_t> offsets;
42 offsets.reserve(j - i);
43 for (std::size_t k = i; k < j; ++k)
44 offsets.push_back(entries[k].offset);
45 const std::uint64_t rel = payload_data.size();
47 unique_leaves.push_back(UniqueLeaf{entries[i].key,
kPayloadTag | rel});
51 return {unique_leaves, payload_data};
60void generate_nodes(std::vector<UniqueLeaf>& tree,
const std::vector<StreeLevelBound>& level_bounds,
61 std::uint16_t branching_factor, std::size_t num_leaf_nodes,
KeyKind kind) {
62 const std::uint64_t
node_size =
static_cast<std::uint64_t
>(branching_factor) - 1;
63 const std::uint64_t skip_size =
static_cast<std::uint64_t
>(branching_factor) *
node_size;
64 const std::uint64_t bf2 =
static_cast<std::uint64_t
>(branching_factor) * branching_factor;
65 const std::uint64_t num_nodes = tree.size();
66 const std::uint64_t leaf_start = num_nodes - num_leaf_nodes;
73 std::unordered_map<std::uint64_t, KeyValue> parent_min_key;
75 auto require_min_key = [&](std::uint64_t idx) ->
const KeyValue& {
76 auto it = parent_min_key.find(idx);
77 if (it == parent_min_key.end()) {
79 "static B+tree builder: missing parent_min_key entry -- this is a bug in "
80 "the builder itself, not malformed input");
85 for (std::size_t level = 0; level + 1 < level_bounds.size(); ++level) {
86 const StreeLevelBound& children_level = level_bounds[level];
87 const StreeLevelBound& parent_level = level_bounds[level + 1];
89 std::uint64_t parent_idx = parent_level.start;
90 std::uint64_t child_idx = children_level.start;
92 while (child_idx < children_level.end) {
93 if (parent_idx >= parent_level.end)
96 const std::uint64_t child_idx_diff = child_idx - children_level.start;
97 const std::uint64_t m = child_idx_diff % skip_size;
99 const bool has_next_node = child_idx +
node_size < children_level.end;
101 if (is_right_most_child) {
106 if (!has_next_node) {
108 tree[parent_idx] = UniqueLeaf{parent_key, child_idx};
126 const KeyValue& candidate =
127 parent_min_key.count(child_idx) ? parent_min_key.at(child_idx) :
key_max(
kind);
128 const KeyValue& own_min =
compare_keys(tree[child_idx].
key, candidate) < 0
129 ? tree[child_idx].key
131 parent_min_key.insert_or_assign(parent_idx, own_min);
137 const std::uint64_t right_node_idx = child_idx +
node_size;
138 const bool is_leaf_node = child_idx >= leaf_start;
141 const KeyValue parent_key =
142 right_node_idx < children_level.end ? tree[right_node_idx].key :
key_max(
kind);
143 tree[parent_idx] = UniqueLeaf{parent_key, child_idx};
144 parent_min_key.insert_or_assign(parent_idx, tree[child_idx].
key);
150 const KeyValue parent_key = right_node_idx < children_level.end
153 tree[parent_idx] = UniqueLeaf{parent_key, child_idx};
154 parent_min_key.insert_or_assign(parent_idx, require_min_key(child_idx));
164 std::uint16_t branching_factor) {
176 branching_factor = std::clamp<std::uint16_t>(branching_factor, 2, 65535);
183 if (entries.empty()) {
185 "cannot build a static B+tree index with no entries");
196 for (
const auto& e : entries) {
197 if (e.key.kind() !=
kind) {
199 "static B+tree: entry key kind does not match the column's declared kind");
203 auto [unique_leaves, payload_data] = group_duplicates(entries);
206 const std::uint64_t num_nodes = level_bounds.front().end;
208 std::vector<UniqueLeaf> tree(
static_cast<std::size_t
>(num_nodes), UniqueLeaf{
KeyValue{}, 0});
209 const std::uint64_t leaf_start = num_nodes - unique_leaves.size();
210 for (std::size_t i = 0; i < unique_leaves.size(); ++i)
211 tree[
static_cast<std::size_t
>(leaf_start) + i] = unique_leaves[i];
213 generate_nodes(tree, level_bounds, branching_factor, unique_leaves.size(),
kind);
215 std::vector<std::uint8_t> bytes;
217 for (
const auto& node : tree) {
219 bytes.insert(bytes.end(), key_bytes.begin(), key_bytes.end());
220 for (
int i = 0; i < 8; ++i)
221 bytes.push_back(
static_cast<std::uint8_t
>((node.offset >> (8 * i)) & 0xFF));
223 bytes.insert(bytes.end(), payload_data.begin(), payload_data.end());
226 static_cast<std::uint32_t
>(unique_leaves.size())};
Every failure the library reports is one of these.
std::vector< StreeLevelBound > stree_level_bounds(std::uint64_t num_items, std::uint16_t branching_factor)
Mirrors Stree::generate_level_bounds (stree.rs:474-508).
BuiltBtreeIndex build_static_btree(const std::vector< BtreeEntry > &entries, KeyKind kind, std::uint16_t branching_factor)
Builds one column's complete attribute index blob from its (key, offset) entries.
std::vector< std::uint8_t > encode_key(const KeyValue &v)
void encode_payload_entry(std::vector< std::uint8_t > &out, const std::vector< std::uint64_t > &offsets)
Encode a payload entry: u32 count then count x u64, all little-endian (mirrors PayloadEntry::serializ...
KeyValue key_max(KeyKind kind)
constexpr std::uint64_t kPayloadTag
The MSB of a leaf offset marks a payload reference rather than a direct feature offset (stree....
int compare_keys(const KeyValue &a, const KeyValue &b)
Three-way comparison.
KeyKind
The concrete key types the B+tree index can hold.
std::size_t key_serialized_size(KeyKind kind)
Serialized width in bytes. DateTime is 12: i64 seconds + u32 nanos.
The finished index: the flat node array concatenated with the payload section (mirrors Stree::stream_...