microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
remote_binary_tree.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
10#include "remote_container.hpp"
12#include "remote_object.hpp"
13
14#include <algorithm>
15
16namespace microfmt {
17
24template <typename Tag> struct remote_binary_tree_traits;
25
26namespace detail {
27
28template <typename Tag> struct remote_binary_tree_dispatch {
30 using context_type = typename traits_type::context_type;
31
32 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
33 // checked here.
34 RELOCO_BEGIN_UNSAFE_CODE
35 static bool format(value_ref<const context_type> context, uintptr_t container_addr, const container_options &opts,
36 address_space_ref space, span<std::byte> scratch, const sink &out) noexcept {
37 out.write(opts.open_bracket);
38 if (scratch.size() < sizeof(uintptr_t) * 8) {
39 out.write("<fault>");
40 out.write(opts.close_bracket);
41 return true;
42 }
43
44 constexpr size_t max_tree_stack_depth = 64;
45 const size_t available_elements = scratch.size() / sizeof(uintptr_t);
46 const size_t stack_capacity = std::min(available_elements / 2, max_tree_stack_depth);
47 scratch_allocator allocator(scratch);
48 uintptr_t *node_stack = allocator.allocate<uintptr_t>(stack_capacity);
49 if (node_stack == nullptr) {
50 out.write("<fault>");
51 out.write(opts.close_bracket);
52 return true;
53 }
54 // NOLINTNEXTLINE(reloco-borrow-check): bump allocator; remaining_span() is disjoint from node_stack
55 span<std::byte> element_scratch = allocator.remaining_span();
56
57 uintptr_t root_node = 0;
58 if (!traits_type::get_root_node(context, container_addr, space, element_scratch, root_node) || root_node == 0) {
59 out.write(opts.close_bracket);
60 return true;
61 }
62
63 size_t stack_top = 0;
64 uintptr_t current = root_node;
65 size_t print_count = 0;
66 while (current != 0 || stack_top > 0) {
67 while (current != 0) {
68 if (stack_top >= stack_capacity)
69 break;
70 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
71 node_stack[stack_top++] = current;
72 RELOCO_END_UNSAFE_BUFFER_USAGE;
73
74 uintptr_t left = 0;
75 if (!traits_type::get_left_node(context, space, element_scratch, current, left))
76 break;
77 current = left;
78 }
79 if (stack_top == 0)
80 break;
81
82 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
83 current = node_stack[--stack_top];
84 RELOCO_END_UNSAFE_BUFFER_USAGE;
85
86 if (print_count >= opts.max_print) {
87 if (print_count > 0)
88 out.write(opts.entry_separator);
89 out.write("...");
90 break;
91 }
92 if (print_count > 0)
93 out.write(opts.entry_separator);
94 if (!traits_type::format_node(context, space, element_scratch, current, opts, out)) {
95 out.write("<fault>");
96 break;
97 }
98 ++print_count;
99
100 uintptr_t right = 0;
101 current = traits_type::get_right_node(context, space, element_scratch, current, right) ? right : 0;
102 }
103
104 out.write(opts.close_bracket);
105 return true;
106 }
108};
109
110template <typename Key, typename Value, typename RemotePtr> struct binary_tree_layout_tag {};
111
112} // namespace detail
113
114template <typename Tag>
116
117template <typename Tag>
118[[nodiscard]] constexpr remote_binary_tree<Tag>
119make_remote_binary_tree(uintptr_t container_addr,
120 typename remote_binary_tree_traits<Tag>::context_type context) noexcept {
121 return remote_binary_tree<Tag>(container_addr, std::move(context));
122}
123
124template <typename Key, typename Value, typename RemotePtr>
125struct remote_binary_tree_traits<detail::binary_tree_layout_tag<Key, Value, RemotePtr>> {
126 using pointer_query = decltype(make_remote_offset_query<uintptr_t, RemotePtr>(0));
127
135
136 static bool get_root_node(value_ref<const context_type> context, uintptr_t container_addr, address_space_ref space,
137 span<std::byte>, uintptr_t &out_node_addr) noexcept {
138 return context->root(space, container_addr, out_node_addr);
139 }
140
141 static bool get_left_node(value_ref<const context_type> context, address_space_ref space, span<std::byte>,
142 uintptr_t node_addr, uintptr_t &out_left_addr) noexcept {
143 return context->left(space, node_addr, out_left_addr);
144 }
145
146 static bool get_right_node(value_ref<const context_type> context, address_space_ref space, span<std::byte>,
147 uintptr_t node_addr, uintptr_t &out_right_addr) noexcept {
148 return context->right(space, node_addr, out_right_addr);
149 }
150
151 static bool format_node(value_ref<const context_type> context, address_space_ref space, span<std::byte> scratch,
152 uintptr_t node_addr, const container_options &opts, const sink &out) noexcept {
153 if (opts.print_key) {
154 const uintptr_t key_addr = detail::add_address_offset(node_addr, context->key_offset);
156 remote_object_view view(key_addr, space, type_tag<Key>{}, scratch);
157 formatter<remote_object_view>().format(view, out);
158 } else {
159 scratch_allocator allocator(scratch);
160 Key *key = allocator.allocate<Key>();
161 if (!key || !space.read_bytes(key_addr, key, sizeof(Key)))
162 return false;
163 formatter<Key>().format(*key, out);
164 }
165 out.write(opts.kv_separator);
166 }
167
168 if (opts.print_value) {
169 const uintptr_t value_addr = detail::add_address_offset(node_addr, context->value_offset);
171 remote_object_view view(value_addr, space, type_tag<Value>{}, scratch);
172 formatter<remote_object_view>().format(view, out);
173 } else {
174 scratch_allocator allocator(scratch);
175 Value *value = allocator.allocate<Value>();
176 if (!value || !space.read_bytes(value_addr, value, sizeof(Value)))
177 return false;
178 formatter<Value>().format(*value, out);
179 }
180 }
181 return true;
182 }
183};
184
188template <typename Key, typename Value, typename RemotePtr = uintptr_t>
189[[nodiscard]] constexpr auto make_remote_binary_tree(uintptr_t container_addr, ptrdiff_t root_offset,
190 ptrdiff_t left_offset, ptrdiff_t right_offset,
191 ptrdiff_t key_offset, ptrdiff_t value_offset) noexcept {
193 using traits = remote_binary_tree_traits<tag>;
194 typename traits::context_type context{make_remote_offset_query<uintptr_t, RemotePtr>(root_offset),
195 make_remote_offset_query<uintptr_t, RemotePtr>(left_offset),
196 make_remote_offset_query<uintptr_t, RemotePtr>(right_offset), key_offset,
197 value_offset};
198 return remote_binary_tree<tag>(container_addr, std::move(context));
199}
200
201} // namespace microfmt
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Owning typed container bound to a compile-time traits dispatcher.
Definition remote_container.hpp:102
Definition remote_object.hpp:231
Zero-allocation bump allocator for scratch buffers.
Definition scratch_allocator.hpp:27
constexpr span< std::byte > remaining_span() const noexcept RELOCO_LIFETIMEBOUND
Returns a span over the remaining unallocated space.
Definition scratch_allocator.hpp:113
RELOCO_BEGIN_UNSAFE_CODE constexpr uintptr_t add_address_offset(uintptr_t address, ptrdiff_t offset) noexcept
Definition address_space.hpp:52
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
constexpr remote_binary_tree< Tag > make_remote_binary_tree(uintptr_t container_addr, typename remote_binary_tree_traits< Tag >::context_type context) noexcept
Definition remote_binary_tree.hpp:119
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
Type-erased views and contexts for formatting remote containers.
Reusable callback and typed-offset accessors for remote layouts.
Remote object dumping, layout macros, and string pointer wrappers with 32-bit compatibility support.
Configuration options for formatting container views.
Definition remote_container.hpp:25
Definition remote_binary_tree.hpp:110
Definition remote_binary_tree.hpp:28
static RELOCO_BEGIN_UNSAFE_CODE bool format(value_ref< const context_type > context, uintptr_t container_addr, const container_options &opts, address_space_ref space, span< std::byte > scratch, const sink &out) noexcept
Definition remote_binary_tree.hpp:35
typename traits_type::context_type context_type
Definition remote_binary_tree.hpp:30
Definition microfmt.hpp:985
static bool get_left_node(value_ref< const context_type > context, address_space_ref space, span< std::byte >, uintptr_t node_addr, uintptr_t &out_left_addr) noexcept
Definition remote_binary_tree.hpp:141
static bool get_root_node(value_ref< const context_type > context, uintptr_t container_addr, address_space_ref space, span< std::byte >, uintptr_t &out_node_addr) noexcept
Definition remote_binary_tree.hpp:136
static bool format_node(value_ref< const context_type > context, address_space_ref space, span< std::byte > scratch, uintptr_t node_addr, const container_options &opts, const sink &out) noexcept
Definition remote_binary_tree.hpp:151
static bool get_right_node(value_ref< const context_type > context, address_space_ref space, span< std::byte >, uintptr_t node_addr, uintptr_t &out_right_addr) noexcept
Definition remote_binary_tree.hpp:146
decltype(make_remote_offset_query< uintptr_t, RemotePtr >(0)) pointer_query
Definition remote_binary_tree.hpp:126
Static customization point for a remote binary tree.
Definition remote_binary_tree.hpp:24
Traits customization point for remote object metadata.
Definition remote_object.hpp:217
Definition remote_object.hpp:229