microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
remote_smart_ptr.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 "address_space.hpp"
12#include "remote_object.hpp"
13#include <cstddef>
14#include <cstdint>
15
16namespace microfmt {
17
18// ============================================================================
19// Unique Pointer View & Traits
20// ============================================================================
21
27template <typename T, typename RemotePtr = uintptr_t> class RELOCO_POINTER remote_unique_ptr_view {
28 using pointer_query = decltype(make_remote_offset_query<uintptr_t, RemotePtr>(0));
29
30public:
38 constexpr remote_unique_ptr_view(uintptr_t ptr_addr, address_space_ref space,
39 span<std::byte> scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset = 0) noexcept
40 : ptr_addr_(ptr_addr), space_(space), scratch_(scratch),
41 pointer_(make_remote_offset_query<uintptr_t, RemotePtr>(ptr_offset)) {}
42
48 bool format(const sink &out) const noexcept {
49 uintptr_t obj_addr = 0;
50 if (!pointer_(space_, ptr_addr_, obj_addr)) {
51 return false;
52 }
53
54 if (obj_addr == 0) {
55 out.write("nullptr");
56 return true;
57 }
58
60 remote_object_view obj_view(obj_addr, space_, type_tag<T>{}, scratch_);
61 formatter<remote_object_view>().format(obj_view, out);
62 } else {
63 scratch_allocator allocator(scratch_);
64 T *val_ptr = allocator.allocate<T>();
65 if (!val_ptr)
66 return false;
67 if (!space_.read_bytes(obj_addr, val_ptr, sizeof(T)))
68 return false;
69 formatter<T>().format(*val_ptr, out);
70 }
71 return true;
72 }
73
74private:
75 uintptr_t ptr_addr_;
76 address_space_ref space_;
77 span<std::byte> scratch_;
78 pointer_query pointer_;
79};
80
81// ============================================================================
82// Shared Pointer View & Traits (Control Block Reference Counting)
83// ============================================================================
84
91template <typename T, typename RemotePtr = uintptr_t, typename RemoteRefCount = int32_t>
92class RELOCO_POINTER remote_shared_ptr_view {
93 using pointer_query = decltype(make_remote_offset_query<uintptr_t, RemotePtr>(0));
94 using count_query = decltype(make_remote_offset_query<RemoteRefCount, RemoteRefCount>(0));
95
96public:
108 constexpr remote_shared_ptr_view(uintptr_t shared_ptr_addr, address_space_ref space,
109 span<std::byte> scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset,
110 ptrdiff_t control_block_offset, ptrdiff_t use_count_offset,
111 ptrdiff_t weak_count_offset) noexcept
112 : addr_(shared_ptr_addr), space_(space), scratch_(scratch),
113 pointer_(make_remote_offset_query<uintptr_t, RemotePtr>(ptr_offset)),
114 control_block_(make_remote_offset_query<uintptr_t, RemotePtr>(control_block_offset)),
115 use_count_(make_remote_offset_query<RemoteRefCount, RemoteRefCount>(use_count_offset)),
116 weak_count_(make_remote_offset_query<RemoteRefCount, RemoteRefCount>(weak_count_offset)) {}
117
123 bool format(const sink &out) const noexcept {
124 uintptr_t obj_addr = 0;
125 if (!pointer_(space_, addr_, obj_addr)) {
126 return false;
127 }
128
129 if (obj_addr == 0) {
130 out.write("shared_ptr(nullptr)");
131 return true;
132 }
133
134 uintptr_t cb_addr = 0;
135 if (!control_block_(space_, addr_, cb_addr)) {
136 return false;
137 }
138
139 RemoteRefCount use_cnt = -1;
140 RemoteRefCount weak_cnt = -1;
141
142 if (cb_addr != 0) {
143 std::ignore = use_count_(space_, cb_addr, use_cnt);
144 std::ignore = weak_count_(space_, cb_addr, weak_cnt);
145 }
146
147 out.write("shared_ptr(");
148
150 remote_object_view obj_view(obj_addr, space_, type_tag<T>{}, scratch_);
151 formatter<remote_object_view>().format(obj_view, out);
152 } else {
153 scratch_allocator allocator(scratch_);
154 if (T *val_ptr = allocator.allocate<T>()) {
155 if (space_.read_bytes(obj_addr, val_ptr, sizeof(T))) {
156 formatter<T>().format(*val_ptr, out);
157 } else {
158 out.write("<fault>");
159 }
160 }
161 }
162
163 microfmt::format_to(out, ", use_count={}, weak_count={})", static_cast<int64_t>(use_cnt),
164 static_cast<int64_t>(weak_cnt));
165 return true;
166 }
167
168private:
169 uintptr_t addr_;
170 address_space_ref space_;
171 span<std::byte> scratch_;
172 pointer_query pointer_;
173 pointer_query control_block_;
174 count_query use_count_;
175 count_query weak_count_;
176};
177
178// ============================================================================
179// Intrusive Pointer View & Traits (Embedded Reference Counting)
180// ============================================================================
181
188template <typename T, typename RemotePtr = uintptr_t, typename RemoteRefCount = int32_t>
189class RELOCO_POINTER remote_intrusive_ptr_view {
190 using pointer_query = decltype(make_remote_offset_query<uintptr_t, RemotePtr>(0));
191 using count_query = decltype(make_remote_offset_query<RemoteRefCount, RemoteRefCount>(0));
192
193public:
202 constexpr remote_intrusive_ptr_view(uintptr_t intrusive_ptr_addr, address_space_ref space,
203 span<std::byte> scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset,
204 ptrdiff_t ref_count_offset) noexcept
205 : addr_(intrusive_ptr_addr), space_(space), scratch_(scratch),
206 pointer_(make_remote_offset_query<uintptr_t, RemotePtr>(ptr_offset)),
207 ref_count_(make_remote_offset_query<RemoteRefCount, RemoteRefCount>(ref_count_offset)) {}
208
214 bool format(const sink &out) const noexcept {
215 uintptr_t obj_addr = 0;
216 if (!pointer_(space_, addr_, obj_addr)) {
217 return false;
218 }
219
220 if (obj_addr == 0) {
221 out.write("intrusive_ptr(nullptr)");
222 return true;
223 }
224
225 RemoteRefCount ref_cnt = 0;
226 std::ignore = ref_count_(space_, obj_addr, ref_cnt);
227
228 out.write("intrusive_ptr(");
229
231 remote_object_view obj_view(obj_addr, space_, type_tag<T>{}, scratch_);
232 formatter<remote_object_view>().format(obj_view, out);
233 } else {
234 scratch_allocator allocator(scratch_);
235 if (T *val_ptr = allocator.allocate<T>()) {
236 if (space_.read_bytes(obj_addr, val_ptr, sizeof(T))) {
237 formatter<T>().format(*val_ptr, out);
238 } else {
239 out.write("<fault>");
240 }
241 }
242 }
243
244 microfmt::format_to(out, ", ref_count={})", ref_cnt);
245 return true;
246 }
247
248private:
249 uintptr_t addr_;
250 address_space_ref space_;
251 span<std::byte> scratch_;
252 pointer_query pointer_;
253 count_query ref_count_;
254};
255
256// ============================================================================
257// Inline Smart Pointer Wrapper Classes
258// ============================================================================
259
265template <typename T, typename RemotePtr = uintptr_t> class remote_unique_ptr {
266public:
267 constexpr remote_unique_ptr() noexcept : ptr_(0) {}
268 constexpr explicit remote_unique_ptr(RemotePtr ptr) noexcept : ptr_(ptr) {}
269
270 [[nodiscard]] constexpr uintptr_t address() const noexcept { return static_cast<uintptr_t>(ptr_); }
271 [[nodiscard]] constexpr bool is_null() const noexcept { return ptr_ == 0; }
272 [[nodiscard]] constexpr explicit operator bool() const noexcept { return ptr_ != 0; }
273
274private:
275 RemotePtr ptr_{0};
276 template <typename U, typename RP> friend struct remote_field_traits;
277};
278
285template <typename T, typename RemotePtr = uintptr_t, typename RemoteRefCount = int32_t> class remote_shared_ptr {
286public:
287 constexpr remote_shared_ptr() noexcept : ptr_(0), control_block_(0) {}
288
289 [[nodiscard]] constexpr uintptr_t address() const noexcept { return static_cast<uintptr_t>(ptr_); }
290 [[nodiscard]] constexpr uintptr_t control_block_address() const noexcept {
291 return static_cast<uintptr_t>(control_block_);
292 }
293 [[nodiscard]] constexpr bool is_null() const noexcept { return ptr_ == 0; }
294 [[nodiscard]] constexpr explicit operator bool() const noexcept { return ptr_ != 0; }
295
296private:
297 RemotePtr ptr_{0};
298 RemotePtr control_block_{0};
299};
300
308template <typename T, ptrdiff_t RefCountOffset = 0, typename RemotePtr = uintptr_t, typename RemoteRefCount = int32_t>
310public:
311 constexpr remote_intrusive_ptr() noexcept : ptr_(0) {}
312 constexpr explicit remote_intrusive_ptr(RemotePtr ptr) noexcept : ptr_(ptr) {}
313
314 [[nodiscard]] constexpr uintptr_t address() const noexcept { return static_cast<uintptr_t>(ptr_); }
315 [[nodiscard]] constexpr bool is_null() const noexcept { return ptr_ == 0; }
316 [[nodiscard]] constexpr explicit operator bool() const noexcept { return ptr_ != 0; }
317
318 static constexpr ptrdiff_t ref_count_offset() noexcept { return RefCountOffset; }
319
320private:
321 RemotePtr ptr_{0};
322};
323
324// ============================================================================
325// remote_field_traits Specializations
326// ============================================================================
327
328template <typename T, typename RemotePtr> struct remote_field_traits<remote_unique_ptr<T, RemotePtr>> {
329 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
330 // checked here.
331 RELOCO_BEGIN_UNSAFE_CODE
332 static void format(address_space_ref space, const void *field_ptr, span<char> work_buf, const sink &out) noexcept {
333 const auto &uptr = *static_cast<const remote_unique_ptr<T, RemotePtr> *>(field_ptr);
334 uintptr_t obj_addr = uptr.address();
335 if (obj_addr == 0) {
336 out.write("nullptr");
337 return;
338 }
339 span<std::byte> scratch_bytes(reinterpret_cast<std::byte *>(work_buf.data()), work_buf.size());
340 remote_unique_ptr_view<T, RemotePtr> view(reinterpret_cast<uintptr_t>(field_ptr), space, scratch_bytes, 0);
341 view.format(out);
342 }
344};
345
346template <typename T, typename RemotePtr, typename RemoteRefCount>
347struct remote_field_traits<remote_shared_ptr<T, RemotePtr, RemoteRefCount>> {
348 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
349 // checked here.
350 RELOCO_BEGIN_UNSAFE_CODE
351 static void format(address_space_ref space, const void *field_ptr, span<char> work_buf, const sink &out) noexcept {
352 const auto &sptr = *static_cast<const remote_shared_ptr<T, RemotePtr, RemoteRefCount> *>(field_ptr);
353 if (sptr.is_null()) {
354 out.write("shared_ptr(nullptr)");
355 return;
356 }
357 span<std::byte> scratch_bytes(reinterpret_cast<std::byte *>(work_buf.data()), work_buf.size());
359 reinterpret_cast<uintptr_t>(field_ptr), space, scratch_bytes, 0, sizeof(RemotePtr), 0, sizeof(RemoteRefCount));
360 view.format(out);
361 }
363};
364
365template <typename T, ptrdiff_t RefCountOffset, typename RemotePtr, typename RemoteRefCount>
366struct remote_field_traits<remote_intrusive_ptr<T, RefCountOffset, RemotePtr, RemoteRefCount>> {
367 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
368 // checked here.
369 RELOCO_BEGIN_UNSAFE_CODE
370 static void format(address_space_ref space, const void *field_ptr, span<char> work_buf, const sink &out) noexcept {
371 const auto &iptr =
373 if (iptr.is_null()) {
374 out.write("intrusive_ptr(nullptr)");
375 return;
376 }
377
378 span<std::byte> scratch_bytes(reinterpret_cast<std::byte *>(work_buf.data()), work_buf.size());
379 remote_intrusive_ptr_view<T, RemotePtr, RemoteRefCount> view(reinterpret_cast<uintptr_t>(field_ptr), space,
380 scratch_bytes, 0, RefCountOffset);
381 view.format(out);
382 }
384};
385
386// ============================================================================
387// Formatter Metadata Specializations
388// ============================================================================
389
390template <typename T, typename RP> struct formatter<remote_unique_ptr<T, RP>> {
391 constexpr void parse(format_parse_context &) noexcept {}
392 void format(const remote_unique_ptr<T, RP> &ptr, const sink &out) const noexcept {
393 if (ptr.is_null()) {
394 out.write("nullptr");
395 } else {
396 microfmt::format_to(out, "0x{:x}", ptr.address());
397 }
398 }
399};
400
401template <typename T, typename RP, typename RRC> struct formatter<remote_shared_ptr<T, RP, RRC>> {
402 constexpr void parse(format_parse_context &) noexcept {}
403 void format(const remote_shared_ptr<T, RP, RRC> &ptr, const sink &out) const noexcept {
404 if (ptr.is_null()) {
405 out.write("shared_ptr(nullptr)");
406 } else {
407 microfmt::format_to(out, "0x{:x}", ptr.address());
408 }
409 }
410};
411
412template <typename T, ptrdiff_t RO, typename RP, typename RRC> struct formatter<remote_intrusive_ptr<T, RO, RP, RRC>> {
413 constexpr void parse(format_parse_context &) noexcept {}
414 void format(const remote_intrusive_ptr<T, RO, RP, RRC> &ptr, const sink &out) const noexcept {
415 if (ptr.is_null()) {
416 out.write("intrusive_ptr(nullptr)");
417 } else {
418 microfmt::format_to(out, "0x{:x}", ptr.address());
419 }
420 }
421};
422
423} // namespace microfmt
424
425// ============================================================================
426// Formatter Specializations for microfmt::print Integration
427// ============================================================================
428
429template <typename T, typename RemotePtr> struct microfmt::formatter<microfmt::remote_unique_ptr_view<T, RemotePtr>> {
430 constexpr void parse(microfmt::format_parse_context &) noexcept {}
431
432 void format(const microfmt::remote_unique_ptr_view<T, RemotePtr> &view, const microfmt::sink &out) const noexcept {
433 if (!view.format(out)) {
434 out.write("<fault>");
435 }
436 }
437};
438
439template <typename T, typename RemotePtr, typename RemoteRefCount>
440struct microfmt::formatter<microfmt::remote_shared_ptr_view<T, RemotePtr, RemoteRefCount>> {
441 constexpr void parse(microfmt::format_parse_context &) noexcept {}
442
444 const microfmt::sink &out) const noexcept {
445 if (!view.format(out)) {
446 out.write("<fault>");
447 }
448 }
449};
450
451template <typename T, typename RemotePtr, typename RemoteRefCount>
452struct microfmt::formatter<microfmt::remote_intrusive_ptr_view<T, RemotePtr, RemoteRefCount>> {
453 constexpr void parse(microfmt::format_parse_context &) noexcept {}
454
456 const microfmt::sink &out) const noexcept {
457 if (!view.format(out)) {
458 out.write("<fault>");
459 }
460 }
461};
Type-erased remote address-space access and remote string/object views.
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
Definition microfmt.hpp:881
Formattable view over an intrusive pointer with an embedded count.
Definition remote_smart_ptr.hpp:189
constexpr remote_intrusive_ptr_view(uintptr_t intrusive_ptr_addr, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset, ptrdiff_t ref_count_offset) noexcept
Constructs a view over a remote intrusive-pointer object.
Definition remote_smart_ptr.hpp:202
bool format(const sink &out) const noexcept
Loads and renders the pointee and its embedded reference count.
Definition remote_smart_ptr.hpp:214
Layout wrapper for a remote intrusive pointer.
Definition remote_smart_ptr.hpp:309
static constexpr ptrdiff_t ref_count_offset() noexcept
Definition remote_smart_ptr.hpp:318
constexpr remote_intrusive_ptr(RemotePtr ptr) noexcept
Definition remote_smart_ptr.hpp:312
constexpr bool is_null() const noexcept
Definition remote_smart_ptr.hpp:315
constexpr remote_intrusive_ptr() noexcept
Definition remote_smart_ptr.hpp:311
constexpr uintptr_t address() const noexcept
Definition remote_smart_ptr.hpp:314
Definition remote_object.hpp:231
Formattable view over a shared pointer and its remote control block.
Definition remote_smart_ptr.hpp:92
constexpr remote_shared_ptr_view(uintptr_t shared_ptr_addr, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset, ptrdiff_t control_block_offset, ptrdiff_t use_count_offset, ptrdiff_t weak_count_offset) noexcept
Constructs a view over a remote shared-pointer object.
Definition remote_smart_ptr.hpp:108
bool format(const sink &out) const noexcept
Loads and renders the pointee and reference counts.
Definition remote_smart_ptr.hpp:123
Layout wrapper for a remote shared pointer.
Definition remote_smart_ptr.hpp:285
constexpr uintptr_t control_block_address() const noexcept
Definition remote_smart_ptr.hpp:290
constexpr bool is_null() const noexcept
Definition remote_smart_ptr.hpp:293
constexpr remote_shared_ptr() noexcept
Definition remote_smart_ptr.hpp:287
constexpr uintptr_t address() const noexcept
Definition remote_smart_ptr.hpp:289
Formattable view over a unique pointer stored in a remote process.
Definition remote_smart_ptr.hpp:27
bool format(const sink &out) const noexcept
Loads and renders the pointee or nullptr.
Definition remote_smart_ptr.hpp:48
constexpr remote_unique_ptr_view(uintptr_t ptr_addr, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND, ptrdiff_t ptr_offset=0) noexcept
Constructs a view over a remote unique-pointer object.
Definition remote_smart_ptr.hpp:38
Layout wrapper for a remote unique pointer.
Definition remote_smart_ptr.hpp:265
constexpr bool is_null() const noexcept
Definition remote_smart_ptr.hpp:271
constexpr remote_unique_ptr(RemotePtr ptr) noexcept
Definition remote_smart_ptr.hpp:268
constexpr remote_unique_ptr() noexcept
Definition remote_smart_ptr.hpp:267
constexpr uintptr_t address() const noexcept
Definition remote_smart_ptr.hpp:270
Zero-allocation bump allocator for scratch buffers.
Definition scratch_allocator.hpp:27
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
Reusable callback and typed-offset accessors for remote layouts.
Remote object dumping, layout macros, and string pointer wrappers with 32-bit compatibility support.
constexpr void parse(microfmt::format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:453
void format(const microfmt::remote_intrusive_ptr_view< T, RemotePtr, RemoteRefCount > &view, const microfmt::sink &out) const noexcept
Definition remote_smart_ptr.hpp:455
constexpr void parse(microfmt::format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:441
void format(const microfmt::remote_shared_ptr_view< T, RemotePtr, RemoteRefCount > &view, const microfmt::sink &out) const noexcept
Definition remote_smart_ptr.hpp:443
void format(const microfmt::remote_unique_ptr_view< T, RemotePtr > &view, const microfmt::sink &out) const noexcept
Definition remote_smart_ptr.hpp:432
constexpr void parse(microfmt::format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:430
void format(const remote_intrusive_ptr< T, RO, RP, RRC > &ptr, const sink &out) const noexcept
Definition remote_smart_ptr.hpp:414
constexpr void parse(format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:413
constexpr void parse(format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:402
void format(const remote_shared_ptr< T, RP, RRC > &ptr, const sink &out) const noexcept
Definition remote_smart_ptr.hpp:403
constexpr void parse(format_parse_context &) noexcept
Definition remote_smart_ptr.hpp:391
void format(const remote_unique_ptr< T, RP > &ptr, const sink &out) const noexcept
Definition remote_smart_ptr.hpp:392
Definition microfmt.hpp:985
static RELOCO_BEGIN_UNSAFE_CODE void format(address_space_ref space, const void *field_ptr, span< char > work_buf, const sink &out) noexcept
Definition remote_smart_ptr.hpp:370
static RELOCO_BEGIN_UNSAFE_CODE void format(address_space_ref space, const void *field_ptr, span< char > work_buf, const sink &out) noexcept
Definition remote_smart_ptr.hpp:351
static RELOCO_BEGIN_UNSAFE_CODE void format(address_space_ref space, const void *field_ptr, span< char > work_buf, const sink &out) noexcept
Definition remote_smart_ptr.hpp:332
Default field formatter policy. Can be specialized or overloaded for custom remote types,...
Definition remote_object.hpp:49
Traits customization point for remote object metadata.
Definition remote_object.hpp:217
Definition remote_object.hpp:229