microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
remote_memory_diff.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
9#include <algorithm>
10#include <cstddef>
11#include <cstdint>
12
13#include "address_space.hpp"
14
15namespace microfmt {
16
22 uintptr_t old_address{};
23
25 uintptr_t new_address{};
26
27 size_t size{};
28 span<std::byte> scratch; // Caller-owned scratch buffer for safe chunk reads
29 uint8_t bytes_per_row{16};
30};
31
35[[nodiscard]] constexpr remote_memory_diff_view remote_mem_diff(address_space_ref old_space, uintptr_t old_addr,
36 address_space_ref new_space, uintptr_t new_addr,
37 size_t size, span<std::byte> scratch_buffer) noexcept {
38 return remote_memory_diff_view{old_space, old_addr, new_space, new_addr, size, scratch_buffer, 16};
39}
40
41} // namespace microfmt
42
43// ============================================================================
44// Formatter Specialization
45// ============================================================================
46
47namespace microfmt {
48
50 constexpr void parse(format_parse_context &ctx) noexcept { (void)ctx; }
51
52 // decodes raw bytes of a target address space; casts, pointers and offsets are bounded by the explicit lengths
53 // checked here.
54 RELOCO_BEGIN_UNSAFE_CODE
55 void format(const remote_memory_diff_view &diff, const sink &out) const noexcept {
56 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
57
58 // Scratch buffer must be at least large enough to hold two rows (old and new chunks)
59 const size_t row_size = (diff.bytes_per_row > 0) ? size_t{diff.bytes_per_row} : size_t{16};
60 if (diff.scratch.size() < row_size * 2) {
61 out.write(string_view("[ERROR: Scratch buffer too small for remote_memory_diff]"));
62 return;
63 }
64
65 std::byte *old_chunk_buf = diff.scratch.data();
66 std::byte *new_chunk_buf = diff.scratch.data() + row_size;
67
68 size_t identical_rows_streak = 0;
69 size_t offset = 0;
70
71 while (offset < diff.size) {
72 const size_t current_chunk_size = std::min(row_size, diff.size - offset);
73
74 // Read remote/target chunks safely using address_space_ref
75 const auto old_read = diff.old_space.read_bytes(diff.old_address + offset, old_chunk_buf, current_chunk_size);
76 const auto new_read = diff.new_space.read_bytes(diff.new_address + offset, new_chunk_buf, current_chunk_size);
77
78 if (!old_read || !new_read) {
79 // Handle read fault gracefully
80 if (identical_rows_streak > 0) {
81 flush_streak(out, identical_rows_streak);
82 identical_rows_streak = 0;
83 }
84 out.write(string_view(" 0x"));
85 detail::format_unsigned<detail::radix::hex>(out, diff.new_address + offset, false, 8);
86 out.write(string_view(": [REMOTE READ FAULT]\n"));
87 offset += row_size;
88 continue;
89 }
90
91 const span<const std::byte> old_chunk(old_chunk_buf, current_chunk_size);
92 const span<const std::byte> new_chunk(new_chunk_buf, current_chunk_size);
93
94 // Compare chunks
95 bool rows_match = true;
96 for (size_t i = 0; i < current_chunk_size; ++i) {
97 if (old_chunk[i] != new_chunk[i]) {
98 rows_match = false;
99 break;
100 }
101 }
102
103 if (rows_match) {
104 identical_rows_streak++;
105 } else {
106 if (identical_rows_streak > 0) {
107 flush_streak(out, identical_rows_streak);
108 identical_rows_streak = 0;
109 }
110
111 // Print Address Header
112 out.write(string_view(" 0x"));
113 detail::format_unsigned<detail::radix::hex>(out, diff.new_address + offset, false, 8);
114 out.write(string_view(": "));
115
116 format_row_bytes(out, old_chunk);
117 out.write(string_view(" -> "));
118 format_row_bytes(out, new_chunk);
119 out.write(string_view("\n"));
120 }
121
122 offset += row_size;
123 }
124
125 if (identical_rows_streak > 0) {
126 flush_streak(out, identical_rows_streak);
127 }
128
129 RELOCO_END_UNSAFE_BUFFER_USAGE;
130 }
132
133private:
134 static void flush_streak(const sink &out, size_t count) noexcept {
135 out.write(string_view(" [... "));
136 detail::format_unsigned<detail::radix::decimal>(out, count, false, 0);
137 out.write(string_view(" identical rows hidden ...]\n"));
138 }
139
140 static void format_row_bytes(const sink &out, span<const std::byte> chunk) noexcept {
141 for (size_t i = 0; i < chunk.size(); ++i) {
142 detail::format_unsigned<detail::radix::hex>(out, static_cast<uint64_t>(chunk[i]), true, 2);
143 out.write(string_view(" ", 1));
144 }
145 }
146};
147
148} // namespace microfmt
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
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132
constexpr remote_memory_diff_view remote_mem_diff(address_space_ref old_space, uintptr_t old_addr, address_space_ref new_space, uintptr_t new_addr, size_t size, span< std::byte > scratch_buffer) noexcept
Creates a remote memory diff view.
Definition remote_memory_diff.hpp:35
constexpr void parse(format_parse_context &ctx) noexcept
Definition remote_memory_diff.hpp:50
RELOCO_BEGIN_UNSAFE_CODE void format(const remote_memory_diff_view &diff, const sink &out) const noexcept
Definition remote_memory_diff.hpp:55
Definition microfmt.hpp:985
Non-owning view representing a byte-level comparison between two remote address spaces.
Definition remote_memory_diff.hpp:20
span< std::byte > scratch
Definition remote_memory_diff.hpp:28
address_space_ref old_space
Definition remote_memory_diff.hpp:21
address_space_ref new_space
Definition remote_memory_diff.hpp:24