Pixie
Loading...
Searching...
No Matches
tree_policy.h
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <bit>
6#include <cassert>
7#include <cstddef>
8#include <utility>
9
11namespace pixie::permutations::detail {
12
13// Default child addressing. Policy headers retain the count and regular
14// prefix in one word; state-free policies add no per-node storage.
15template <std::size_t Fanout>
16struct DirectChildOrder {
17 static constexpr bool supports_bias = true;
18 struct Header {
19 std::size_t count : std::bit_width(Fanout) = 0;
20 std::size_t regular_prefix : std::bit_width(Fanout) = 0;
21 };
22 template <class Node>
23 static std::size_t slot(const Node&, std::size_t i, std::size_t) noexcept {
24 return i;
25 }
26 template <class Node>
27 static void reset(Node&) noexcept {}
28 template <class Tree, class Node>
29 static std::size_t rotate(Node& node,
30 std::size_t begin,
31 std::size_t middle,
32 std::size_t end,
33 std::size_t) noexcept {
34 std::rotate(node.children.begin() + begin, node.children.begin() + middle,
35 node.children.begin() + end);
36 return end - begin;
37 }
38 template <class Node>
39 static bool valid(const Node&, std::size_t) noexcept {
40 return true;
41 }
42};
43
44// The default owner retains no spare allocations between operations.
45template <class Node>
46struct NoNodeReserve {
47 NoNodeReserve* for_rotation(std::size_t, std::size_t) noexcept {
48 return nullptr;
49 }
50 void trim(std::size_t, std::size_t) noexcept {}
51 void clear() noexcept {}
52 std::size_t node_count() const noexcept { return 0; }
53};
54
55// Transaction-local preflight storage. Dismantled nodes are recycled through
56// their unoccupied first pointer slot; occupied children belong to Owners.
57// Allocator supplies instrumented allocation and node-header initialization.
58template <class Node, class Owner, class Allocator>
59struct SequenceNodePool {
60 Node* nodes = nullptr;
61 std::array<Owner, 3> leaves;
62 std::size_t leaf_count = 0;
63
64 explicit SequenceNodePool(NoNodeReserve<Node>* = nullptr) noexcept {}
65 SequenceNodePool(const SequenceNodePool&) = delete;
66 SequenceNodePool& operator=(const SequenceNodePool&) = delete;
67 ~SequenceNodePool() {
68 while (nodes) {
69 auto* next = static_cast<Node*>(nodes->children[0]);
70 Allocator::dispose(nodes);
71 nodes = next;
72 }
73 }
74 void commit() noexcept {}
75 void reserve(std::size_t node_count, std::size_t leaf_spares) {
76 for (std::size_t i = 0; i < node_count; ++i) {
77 recycle(Allocator::node());
78 }
79 for (; leaf_count < leaf_spares; ++leaf_count) {
80 leaves[leaf_count] = Allocator::leaf();
81 }
82 }
83 void recycle(Node* node) noexcept {
84 Allocator::reset(node);
85 node->children[0] = nodes;
86 nodes = node;
87 }
88 Node* node() noexcept {
89 assert(nodes && nodes->count == 0);
90 auto* result = nodes;
91 nodes = static_cast<Node*>(nodes->children[0]);
92 result->children[0] = nullptr;
93 return result;
94 }
95 Owner leaf() noexcept {
96 assert(leaf_count);
97 return std::move(leaves[--leaf_count]);
98 }
99};
100
101// Internal extension boundary: production uses direct pointers and strictly
102// transaction-local allocation. Other policies must preserve noexcept commit,
103// ownership, child occupancy, and preflight rollback contracts.
104struct SequenceTreePolicy {
105 template <std::size_t Fanout>
106 using Order = DirectChildOrder<Fanout>;
107 template <class Node, class Allocator>
108 using Reserve = NoNodeReserve<Node>;
109 template <class Node, class Owner, class Allocator>
110 using Pool = SequenceNodePool<Node, Owner, Allocator>;
111 struct TestCounters {};
112};
113
114} // namespace pixie::permutations::detail