microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
address_space.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 "../microfmt.hpp"
11#include "../reloco.hpp"
12#include "../scratch_allocator.hpp"
13
14#include <cstddef>
15#include <cstdint>
16#include <cstring>
17#include <string_view>
18#include <type_traits>
19#include <utility>
20namespace microfmt {
21
31
33 size_t length{};
34 bool null_terminated{};
35};
36
37template <typename Tag> struct address_space_traits;
38
46
47namespace detail {
48
49// decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths checked
50// here.
51RELOCO_BEGIN_UNSAFE_CODE
52constexpr uintptr_t add_address_offset(uintptr_t address, ptrdiff_t offset) noexcept {
53 if (offset >= 0)
54 return address + static_cast<uintptr_t>(offset);
55
56 const auto magnitude = static_cast<uintptr_t>(-(offset + 1)) + uintptr_t{1};
57 return address - magnitude;
58}
60
61template <typename Tag, typename = void> struct has_address_space_write_bytes : std::false_type {};
62
63template <typename Tag>
65 Tag, std::void_t<decltype(address_space_traits<Tag>::write_bytes(
66 std::declval<value_ref<const typename address_space_traits<Tag>::context_type>>(),
67 std::declval<uintptr_t>(), std::declval<const void *>(), std::declval<size_t>()))>>
68 : std::is_same<decltype(address_space_traits<Tag>::write_bytes(
69 std::declval<value_ref<const typename address_space_traits<Tag>::context_type>>(),
70 std::declval<uintptr_t>(), std::declval<const void *>(), std::declval<size_t>())),
71 bool> {};
72
73template <typename Tag, typename = void> struct has_stateless_address_space_write_bytes : std::false_type {};
74
75template <typename Tag>
77 Tag, std::void_t<decltype(address_space_traits<Tag>::write_bytes(
78 std::declval<uintptr_t>(), std::declval<const void *>(), std::declval<size_t>()))>>
79 : std::is_same<decltype(address_space_traits<Tag>::write_bytes(
80 std::declval<uintptr_t>(), std::declval<const void *>(), std::declval<size_t>())),
81 bool> {};
82
83} // namespace detail
84
85// ============================================================================
86// Static Customization Point
87// ============================================================================
88
104
111 using context_type = void;
112
120 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
121 // checked here.
122 RELOCO_BEGIN_UNSAFE_CODE
123 static bool read_bytes(uintptr_t addr, void *dest, size_t size) noexcept {
124 if (addr == 0)
125 return false;
126 if (size == 0)
127 return true;
128 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
129 std::memcpy(dest, reinterpret_cast<const void *>(addr), size);
130 RELOCO_END_UNSAFE_BUFFER_USAGE;
131 return true;
132 }
134
142 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
143 // checked here.
144 RELOCO_BEGIN_UNSAFE_CODE
145 static bool write_bytes(uintptr_t addr, const void *src, size_t size) noexcept {
146 if (addr == 0)
147 return false;
148 if (size == 0)
149 return true;
150 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
151 std::memcpy(reinterpret_cast<void *>(addr), src, size);
152 RELOCO_END_UNSAFE_BUFFER_USAGE;
153 return true;
154 }
156
166 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
167 // checked here.
168 RELOCO_BEGIN_UNSAFE_CODE
169 static bool read_string(uintptr_t addr, char *dest, size_t max_len, size_t &out_len, bool &null_term) noexcept {
170 if (addr == 0)
171 return false;
172 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
173
174 const auto *src = reinterpret_cast<const char *>(addr);
175 size_t i = 0;
176 while (i < max_len) {
177 dest[i] = src[i];
178 if (dest[i] == '\0') {
179 out_len = i;
180 null_term = true;
181 return true;
182 }
183 ++i;
184 }
185 RELOCO_END_UNSAFE_BUFFER_USAGE;
186
187 out_len = max_len;
188 null_term = false;
189 return true;
190 }
192};
193
194// ============================================================================
195// Minimal-Stack Type-Erased Transport (2 Words: ctx_ + vtbl_)
196// ============================================================================
197
205public:
209 struct vtable {
213 bool (*read_bytes)(const void *ctx, uintptr_t addr, void *dest, size_t size) noexcept = nullptr;
217 bool (*write_bytes)(const void *ctx, uintptr_t addr, const void *src, size_t size) noexcept = nullptr;
221 bool (*read_string)(const void *ctx, uintptr_t addr, char *dest, size_t max_len, size_t &out_len,
222 bool &null_term) noexcept = nullptr;
223 };
224
228 constexpr address_space_ref() noexcept = default;
229
235 template <typename Tag, typename Traits = address_space_traits<Tag>,
236 std::enable_if_t<std::is_void_v<typename Traits::context_type>, int> = 0>
237 constexpr explicit address_space_ref(Tag) noexcept : ctx_(nullptr), vtbl_(&s_vtbl<Tag>) {}
238
247 template <typename Tag, typename Context, typename Traits = address_space_traits<Tag>,
248 std::enable_if_t<!std::is_void_v<typename Traits::context_type> &&
249 std::is_convertible_v<const Context *, const typename Traits::context_type *>,
250 int> = 0>
251 constexpr address_space_ref(Tag, const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
252 : ctx_(&ctx), vtbl_(&s_vtbl<Tag>) {}
253
254 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
255 constexpr address_space_ref(Tag, Context &&) = delete;
256
263 template <typename Tag, typename Traits = address_space_traits<Tag>,
264 std::enable_if_t<std::is_void_v<typename Traits::context_type>, int> = 0>
265 [[nodiscard]] static constexpr address_space_ref make() noexcept {
266 return address_space_ref(Tag{});
267 }
268
277 template <typename Tag, typename Context, typename Traits = address_space_traits<Tag>,
278 std::enable_if_t<!std::is_void_v<typename Traits::context_type>, int> = 0>
279 [[nodiscard]] static constexpr address_space_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept {
280 return address_space_ref(Tag{}, ctx);
281 }
282
283 template <typename Tag, typename Context, std::enable_if_t<!std::is_lvalue_reference_v<Context>, int> = 0>
284 static address_space_ref make(Context &&) = delete;
285
294 [[nodiscard]] expected<void, address_space_error> read_bytes(uintptr_t addr, void *dest, size_t size) const noexcept {
295 if (vtbl_ == nullptr)
296 return unexpected(address_space_error::invalid_handle);
297 if (addr == 0)
298 return unexpected(address_space_error::invalid_address);
299 if (dest == nullptr && size != 0)
300 return unexpected(address_space_error::invalid_buffer);
301 if (!vtbl_->read_bytes(ctx_.get(), addr, dest, size))
302 return unexpected(address_space_error::read_failed);
303 return {};
304 }
305
313 template <typename T>
314 [[nodiscard]] expected<void, address_space_error> read(uintptr_t addr, T &out_obj) const noexcept {
315 static_assert(std::is_trivially_copyable_v<T>, "Target type T must be trivially copyable");
316 return read_bytes(addr, &out_obj, sizeof(T));
317 }
318
326 template <typename T> [[nodiscard]] expected<T, address_space_error> read(uintptr_t addr) const noexcept {
327 static_assert(std::is_trivially_copyable_v<T>, "Target type T must be trivially copyable");
328 static_assert(std::is_nothrow_default_constructible_v<T>, "Target type T must be nothrow default constructible");
329 static_assert(std::is_nothrow_move_constructible_v<T>, "Target type T must be nothrow move constructible");
330 T object{};
331 auto result = read_bytes(addr, &object, sizeof(T));
332 if (!result)
333 return unexpected(result.error());
334 return object;
335 }
336
345 [[nodiscard]] expected<void, address_space_error> write_bytes(uintptr_t addr, const void *src,
346 size_t size) const noexcept {
347 if (vtbl_ == nullptr)
348 return unexpected(address_space_error::invalid_handle);
349 if (addr == 0)
350 return unexpected(address_space_error::invalid_address);
351 if (src == nullptr && size != 0)
352 return unexpected(address_space_error::invalid_buffer);
353 if (vtbl_->write_bytes == nullptr)
354 return unexpected(address_space_error::write_unsupported);
355 if (!vtbl_->write_bytes(ctx_.get(), addr, src, size))
356 return unexpected(address_space_error::write_failed);
357 return {};
358 }
359
367 template <typename T>
368 [[nodiscard]] expected<void, address_space_error> write(uintptr_t addr, const T &object) const noexcept {
369 static_assert(std::is_trivially_copyable_v<T>, "Target type T must be trivially copyable");
370 return write_bytes(addr, &object, sizeof(T));
371 }
372
380 [[nodiscard]] expected<string_chunk, address_space_error> read_string_chunk(uintptr_t addr,
381 span<char> buffer) const noexcept {
382 if (vtbl_ == nullptr)
383 return unexpected(address_space_error::invalid_handle);
384 if (addr == 0)
385 return unexpected(address_space_error::invalid_address);
386 if (buffer.empty())
387 return unexpected(address_space_error::empty_buffer);
388
389 string_chunk chunk{};
390 if (!vtbl_->read_string(ctx_.get(), addr, buffer.data(), buffer.size(), chunk.length, chunk.null_terminated)) {
391 return unexpected(address_space_error::read_failed);
392 }
393 return chunk;
394 }
395
400 [[nodiscard]] constexpr explicit operator bool() const noexcept { return vtbl_ != nullptr; }
401
405 [[nodiscard]] constexpr bool can_write() const noexcept { return vtbl_ != nullptr && vtbl_->write_bytes != nullptr; }
406
407private:
408 template <typename Tag> [[nodiscard]] static constexpr auto write_bytes_entry() noexcept {
409 using context_type = typename address_space_traits<Tag>::context_type;
410 if constexpr (std::is_void_v<context_type>) {
412 return +[](const void *, uintptr_t address, const void *source, size_t size) noexcept {
413 return address_space_traits<Tag>::write_bytes(address, source, size);
414 };
415 } else {
416 return static_cast<bool (*)(const void *, uintptr_t, const void *, size_t) noexcept>(nullptr);
417 }
418 } else if constexpr (detail::has_address_space_write_bytes<Tag>::value) {
419 return +[](const void *context, uintptr_t address, const void *source, size_t size) noexcept {
420 const auto &typed_context = *static_cast<const context_type *>(context);
421 return address_space_traits<Tag>::write_bytes(value_ref<const context_type>(typed_context), address, source,
422 size);
423 };
424 } else {
425 return static_cast<bool (*)(const void *, uintptr_t, const void *, size_t) noexcept>(nullptr);
426 }
427 }
428
429 template <typename Tag>
430 static bool read_bytes_entry(const void *context, uintptr_t address, void *destination, size_t size) noexcept {
431 using context_type = typename address_space_traits<Tag>::context_type;
432 if constexpr (std::is_void_v<context_type>) {
433 return address_space_traits<Tag>::read_bytes(address, destination, size);
434 } else {
435 const auto &typed_context = *static_cast<const context_type *>(context);
436 return address_space_traits<Tag>::read_bytes(value_ref<const context_type>(typed_context), address, destination,
437 size);
438 }
439 }
440
441 template <typename Tag>
442 static bool read_string_entry(const void *context, uintptr_t address, char *destination, size_t capacity,
443 size_t &length, bool &terminated) noexcept {
444 using context_type = typename address_space_traits<Tag>::context_type;
445 if constexpr (std::is_void_v<context_type>) {
446 return address_space_traits<Tag>::read_string(address, destination, capacity, length, terminated);
447 } else {
448 const auto &typed_context = *static_cast<const context_type *>(context);
449 return address_space_traits<Tag>::read_string(value_ref<const context_type>(typed_context), address, destination,
450 capacity, length, terminated);
451 }
452 }
453
454 template <typename Tag>
455 static constexpr vtable s_vtbl{&read_bytes_entry<Tag>, write_bytes_entry<Tag>(), &read_string_entry<Tag>};
456
457 value_ptr<const void> ctx_{};
458 const vtable *vtbl_{nullptr};
459};
460
461template <typename Tag, bool Stateless = std::is_void_v<typename address_space_traits<Tag>::context_type>>
463
464template <typename Tag> class RELOCO_OWNER address_space<Tag, false> {
465public:
467 using context_type = typename traits_type::context_type;
468
469 constexpr explicit address_space(context_type context) noexcept : context_(std::move(context)) {}
470
471 [[nodiscard]] constexpr value_ref<context_type> context() & noexcept RELOCO_LIFETIMEBOUND {
472 return value_ref<context_type>(context_);
473 }
474
475 [[nodiscard]] constexpr value_ref<const context_type> context() const & noexcept RELOCO_LIFETIMEBOUND {
476 return value_ref<const context_type>(context_);
477 }
478
479 [[nodiscard]] constexpr address_space_ref ref() const & noexcept RELOCO_LIFETIMEBOUND {
480 return address_space_ref(Tag{}, context_);
481 }
482
483 value_ref<context_type> context() && = delete;
484 value_ref<const context_type> context() const && = delete;
485 address_space_ref ref() const && = delete;
486
487private:
488 context_type context_;
489};
490
491template <typename Tag> class address_space<Tag, true> {
492public:
493 [[nodiscard]] static constexpr address_space_ref ref() noexcept { return address_space_ref(Tag{}); }
494};
495
496// ============================================================================
497// Compact Views
498// ============================================================================
499
507public:
511 constexpr remote_string_view() noexcept = default;
512
520 constexpr remote_string_view(uintptr_t addr, address_space_ref space,
521 span<char> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS,
522 size_t max_limit = 4096) noexcept
523 : addr_(addr), space_(space), scratch_(scratch), max_limit_(max_limit) {}
524
534 template <size_t N>
535 constexpr remote_string_view(uintptr_t addr, address_space_ref space,
536 char (&scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS)[N],
537 size_t max_limit = 4096) noexcept
538 : addr_(addr), space_(space), scratch_(scratch, N), max_limit_(max_limit) {}
539
544 [[nodiscard]] constexpr uintptr_t address() const noexcept { return addr_; }
549 [[nodiscard]] constexpr address_space_ref space() const noexcept { return space_; }
554 [[nodiscard]] constexpr span<char> scratch() const noexcept RELOCO_LIFETIMEBOUND { return scratch_; }
559 [[nodiscard]] constexpr size_t max_limit() const noexcept { return max_limit_; }
560
561private:
563 uintptr_t addr_{0};
565 address_space_ref space_{};
567 span<char> scratch_{};
569 size_t max_limit_{4096};
570};
571
577template <typename T> class RELOCO_POINTER remote_ref {
578public:
585 constexpr remote_ref(uintptr_t addr, address_space_ref space,
586 span<std::byte> scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
587 : addr_(addr), space_(space), scratch_(scratch) {}
588
596 template <size_t N>
597 constexpr remote_ref(uintptr_t addr, address_space_ref space, std::byte (&scratch)[N]) noexcept
598 : addr_(addr), space_(space), scratch_(scratch, N) {}
599
604 [[nodiscard]] constexpr uintptr_t address() const noexcept { return addr_; }
609 [[nodiscard]] constexpr bool is_null() const noexcept { return addr_ == 0; }
610
615 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
616 // checked here.
617 RELOCO_BEGIN_UNSAFE_CODE
618 [[nodiscard]] expected<T *, remote_load_error> load() const noexcept {
619 if (addr_ == 0)
620 return unexpected(remote_load_error::null_address);
621 if (scratch_.size() < sizeof(T))
622 return unexpected(remote_load_error::scratch_too_small);
623 if (reinterpret_cast<uintptr_t>(scratch_.data()) % alignof(T) != 0)
624 return unexpected(remote_load_error::scratch_misaligned);
625 if (!space_)
626 return unexpected(remote_load_error::invalid_address_space);
627
628 auto *out_ptr = static_cast<T *>(static_cast<void *>(scratch_.data()));
629 if (!space_.read_bytes(addr_, out_ptr, sizeof(T)))
630 return unexpected(remote_load_error::read_failed);
631 return out_ptr;
632 }
634
635private:
637 uintptr_t addr_{0};
639 address_space_ref space_{};
641 span<std::byte> scratch_{};
642};
643
644// ============================================================================
645// microfmt Formatters
646// ============================================================================
647
654template <> struct formatter<remote_string_view> {
658 constexpr void parse(format_parse_context &) noexcept {}
659
665 void format(const remote_string_view &view, const sink &out) const noexcept {
666 if (view.address() == 0) {
667 out.write("(null)");
668 return;
669 }
670
671 if (!view.space() || view.scratch().empty()) {
672 microfmt::format_to(out, "<invalid-space@{:#x}>", view.address());
673 return;
674 }
675
676 uintptr_t cur = view.address();
677 size_t total = 0;
678
679 while (total < view.max_limit()) {
680 auto chunk = view.space().read_string_chunk(cur, view.scratch());
681 if (!chunk) {
682 if (total == 0) {
683 microfmt::format_to(out, "<fault@{:#x}>", view.address());
684 } else {
685 out.write("<fault>");
686 }
687 return;
688 }
689
690 const size_t chunk_len = chunk->length;
691 if (chunk_len > 0) {
692 const size_t limit_left = view.max_limit() - total;
693 const size_t to_write = (chunk_len > limit_left) ? limit_left : chunk_len;
694 const auto scratch = view.scratch();
695 out.write(microfmt::string_view(scratch.data(), to_write));
696 total += to_write;
697 cur += to_write;
698 }
699
700 if (chunk->null_terminated || total >= view.max_limit()) {
701 break;
702 }
703 }
704
705 if (total >= view.max_limit()) {
706 out.write("...");
707 }
708 }
709};
710
716template <typename T> struct formatter<remote_ref<T>> {
720 microfmt::string_view spec_{""};
721
726 constexpr void parse(format_parse_context &ctx) noexcept { spec_ = ctx.spec(); }
727
733 void format(const remote_ref<T> &view, const sink &out) const noexcept {
734 if (view.is_null()) {
735 out.write("(null)");
736 return;
737 }
738
739 auto staged = view.load();
740 if (!staged) {
741 microfmt::format_to(out, "<fault@{:#x}>", view.address());
742 return;
743 }
744
745 formatter<T> elem_fmt;
746 format_parse_context pctx(spec_);
747 elem_fmt.parse(pctx);
748 elem_fmt.format(**staged, out);
749 }
750};
751
752} // namespace microfmt
constexpr value_ref< context_type > context() &noexcept RELOCO_LIFETIMEBOUND
Definition address_space.hpp:471
constexpr address_space_ref ref() const &noexcept RELOCO_LIFETIMEBOUND
Definition address_space.hpp:479
typename traits_type::context_type context_type
Definition address_space.hpp:467
value_ref< const context_type > context() const &&=delete
value_ref< context_type > context() &&=delete
constexpr address_space(context_type context) noexcept
Definition address_space.hpp:469
constexpr value_ref< const context_type > context() const &noexcept RELOCO_LIFETIMEBOUND
Definition address_space.hpp:475
static constexpr address_space_ref ref() noexcept
Definition address_space.hpp:493
Type-erased, two-word handle to a remote address space.
Definition address_space.hpp:204
static constexpr address_space_ref make(const Context &ctx RELOCO_LIFETIMEBOUND) noexcept
Creates a handle for a stateful address-space tag.
Definition address_space.hpp:279
constexpr bool can_write() const noexcept
Reports whether the bound backend provides write access.
Definition address_space.hpp:405
constexpr address_space_ref() noexcept=default
Constructs an empty (invalid) handle.
constexpr address_space_ref(Tag, Context &&)=delete
expected< void, address_space_error > read(uintptr_t addr, T &out_obj) const noexcept
Reads a trivially-copyable object from the remote address space.
Definition address_space.hpp:314
expected< string_chunk, address_space_error > read_string_chunk(uintptr_t addr, span< char > buffer) const noexcept
Reads a bounded chunk of a remote NUL-terminated string.
Definition address_space.hpp:380
constexpr address_space_ref(Tag, const Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a handle for a stateful address-space tag.
Definition address_space.hpp:251
expected< void, address_space_error > write(uintptr_t addr, const T &object) const noexcept
Writes a trivially-copyable object into the remote address space.
Definition address_space.hpp:368
expected< void, address_space_error > read_bytes(uintptr_t addr, void *dest, size_t size) const noexcept
Reads raw bytes from the remote address space.
Definition address_space.hpp:294
expected< T, address_space_error > read(uintptr_t addr) const noexcept
Reads and returns a trivially-copyable object.
Definition address_space.hpp:326
static address_space_ref make(Context &&)=delete
expected< void, address_space_error > write_bytes(uintptr_t addr, const void *src, size_t size) const noexcept
Writes raw bytes into the remote address space.
Definition address_space.hpp:345
static constexpr address_space_ref make() noexcept
Creates a handle for a stateless address-space tag.
Definition address_space.hpp:265
Definition address_space.hpp:462
Definition microfmt.hpp:881
Lazily-loaded reference to a trivially-copyable remote object.
Definition address_space.hpp:577
constexpr remote_ref(uintptr_t addr, address_space_ref space, std::byte(&scratch)[N]) noexcept
Constructs a remote object reference over a fixed C array.
Definition address_space.hpp:597
constexpr bool is_null() const noexcept
Reports whether the reference points to address zero.
Definition address_space.hpp:609
constexpr uintptr_t address() const noexcept
Returns the remote object address.
Definition address_space.hpp:604
RELOCO_BEGIN_UNSAFE_CODE expected< T *, remote_load_error > load() const noexcept
Loads the object into the scratch buffer.
Definition address_space.hpp:618
constexpr remote_ref(uintptr_t addr, address_space_ref space, span< std::byte > scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a remote object reference.
Definition address_space.hpp:585
Streaming view of a remote NUL-terminated string.
Definition address_space.hpp:506
constexpr remote_string_view() noexcept=default
Constructs an empty (null) view.
constexpr size_t max_limit() const noexcept
Returns the maximum render length.
Definition address_space.hpp:559
constexpr address_space_ref space() const noexcept
Returns the address space handle.
Definition address_space.hpp:549
constexpr remote_string_view(uintptr_t addr, address_space_ref space, char(&scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS)[N], size_t max_limit=4096) noexcept
Constructs a view over a remote string using a fixed C array as scratch.
Definition address_space.hpp:535
constexpr uintptr_t address() const noexcept
Returns the remote string address.
Definition address_space.hpp:544
constexpr span< char > scratch() const noexcept RELOCO_LIFETIMEBOUND
Returns the scratch chunk buffer.
Definition address_space.hpp:554
#define MICROFMT_API_CLASS
Definition compat.hpp:181
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
RELOCO_CONSTEXPR20 void format_to(const sink &out, microfmt::string_view fmt, const Args &...args) noexcept
Definition microfmt.hpp:1766
remote_load_error
Definition address_space.hpp:39
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
address_space_error
Definition address_space.hpp:22
Virtual table of address-space operations.
Definition address_space.hpp:209
static RELOCO_BEGIN_UNSAFE_CODE bool read_bytes(uintptr_t addr, void *dest, size_t size) noexcept
Copies raw bytes from addr into dest.
Definition address_space.hpp:123
RELOCO_END_UNSAFE_CODE static RELOCO_BEGIN_UNSAFE_CODE bool write_bytes(uintptr_t addr, const void *src, size_t size) noexcept
Copies raw bytes from src into addr.
Definition address_space.hpp:145
void context_type
Definition address_space.hpp:111
RELOCO_END_UNSAFE_CODE static RELOCO_BEGIN_UNSAFE_CODE bool read_string(uintptr_t addr, char *dest, size_t max_len, size_t &out_len, bool &null_term) noexcept
Reads a string until a null terminator or the length limit.
Definition address_space.hpp:169
Definition address_space.hpp:37
constexpr void parse(format_parse_context &ctx) noexcept
Captures the specifier for the element formatter.
Definition address_space.hpp:726
void format(const remote_ref< T > &view, const sink &out) const noexcept
Loads and renders the remote object.
Definition address_space.hpp:733
void format(const remote_string_view &view, const sink &out) const noexcept
Renders the remote string, chunk-by-chunk.
Definition address_space.hpp:665
constexpr void parse(format_parse_context &) noexcept
No-op parse; remote strings accept no format specifier.
Definition address_space.hpp:658
Definition microfmt.hpp:985
Static customization point describing a remote address space.
Definition address_space.hpp:103
Definition address_space.hpp:32