6#include "../microfmt.hpp"
7#include "../reloco.hpp"
18 explicit constexpr gdb_packet_writer(span<char> buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
19 : m_buf(buffer), m_pos(0), m_checksum(0), m_finalized(
false) {
21 if (m_buf.size() > 0) {
31 return sink{
this, [](
void *ctx, string_view sv)
noexcept {
33 if (self->m_finalized)
37 if (self->m_pos < self->m_buf.size()) {
38 self->m_buf[self->m_pos++] = c;
39 self->m_checksum +=
static_cast<uint8_t
>(c);
49 [[nodiscard]] string_view
finalize() noexcept RELOCO_LIFETIMEBOUND {
51 return string_view(m_buf.data(), m_total_len);
55 if (m_pos + 3 > m_buf.size()) {
62 m_buf[m_pos++] = microfmt::detail::hex_digits_lower[(m_checksum >> 4) & 0x0F];
63 m_buf[m_pos++] = microfmt::detail::hex_digits_lower[m_checksum & 0x0F];
67 return string_view(m_buf.data(), m_total_len);
70 [[nodiscard]]
constexpr bool is_finalized() const noexcept {
return m_finalized; }
75 size_t m_total_len{0};
76 uint8_t m_checksum{0};
77 bool m_finalized{
false};
104 span<char> payload_scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
105 : m_buf(payload_scratch) {
115 m_expected_checksum = 0;
118 m_state = state::waiting_for_start;
129 RELOCO_BEGIN_UNSAFE_CODE
132 case state::waiting_for_start:
137 m_state = state::reading_payload;
140 return status::in_progress;
142 case state::reading_payload:
144 m_state = state::reading_checksum_1;
146 m_expected_checksum = 0;
147 return status::in_progress;
152 m_state = state::reading_payload;
153 return status::in_progress;
157 m_running_sum +=
static_cast<uint8_t
>(c);
160 const char decoded =
static_cast<char>(c ^ 0x20);
161 if (m_pos >= m_buf.size()) {
162 m_state = state::waiting_for_start;
163 return status::error_overflow;
165 m_buf[m_pos++] = decoded;
167 }
else if (c ==
'}') {
170 if (m_pos >= m_buf.size()) {
171 m_state = state::waiting_for_start;
172 return status::error_overflow;
176 return status::in_progress;
178 case state::reading_checksum_1:
179 case state::reading_checksum_2: {
181 if (!parse_hex_nibble(c, nibble)) {
182 m_state = state::waiting_for_start;
183 return status::error_format;
185 m_expected_checksum =
static_cast<uint8_t
>((m_expected_checksum << 4) | nibble);
187 if (m_state == state::reading_checksum_1) {
188 m_state = state::reading_checksum_2;
191 m_state = state::waiting_for_start;
192 if (m_running_sum != m_expected_checksum) {
193 return status::error_checksum;
195 return status::ready;
197 return status::in_progress;
201 return status::in_progress;
209 [[nodiscard]]
constexpr string_view
payload() const noexcept RELOCO_LIFETIMEBOUND {
210 return string_view(m_buf.data(), m_pos);
214 enum class state : uint8_t { waiting_for_start, reading_payload, reading_checksum_1, reading_checksum_2 };
218 uint8_t m_running_sum{0};
219 uint8_t m_expected_checksum{0};
220 uint8_t m_nibble_count{0};
221 bool m_escaped{
false};
222 state m_state{state::waiting_for_start};
226 RELOCO_BEGIN_UNSAFE_CODE
227 [[nodiscard]]
static constexpr bool parse_hex_nibble(
char c, uint8_t &out_val)
noexcept {
228 if (c >=
'0' && c <=
'9') {
229 out_val =
static_cast<uint8_t
>(c -
'0');
232 if (c >=
'a' && c <=
'f') {
233 out_val =
static_cast<uint8_t
>(c -
'a' + 10);
236 if (c >=
'A' && c <=
'F') {
237 out_val =
static_cast<uint8_t
>(c -
'A' + 10);
Zero-allocation GDB Remote Serial Protocol packet builder sink.
Definition gdb_stream.hpp:16
string_view finalize() noexcept RELOCO_LIFETIMEBOUND
Finalizes the packet by appending #XX checksum.
Definition gdb_stream.hpp:49
sink as_sink() noexcept RELOCO_LIFETIMEBOUND
Creates a type-erased sink adapter for formatting.
Definition gdb_stream.hpp:30
constexpr gdb_packet_writer(span< char > buffer RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition gdb_stream.hpp:18
constexpr bool is_finalized() const noexcept
Definition gdb_stream.hpp:70
Stateful streaming decoder for incoming GDB Remote Serial Protocol packets.
Definition gdb_stream.hpp:86
constexpr gdb_streaming_decoder(span< char > payload_scratch RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Constructs a streaming decoder over a caller-provided scratch span.
Definition gdb_stream.hpp:103
RELOCO_BEGIN_UNSAFE_CODE constexpr status feed(char c) noexcept
Feeds a single incoming character into the stateful decoder.
Definition gdb_stream.hpp:130
status
Status returned after feeding a byte into the decoder.
Definition gdb_stream.hpp:91
constexpr void reset() noexcept
Resets the decoder state machine back to waiting for a new packet start ('$').
Definition gdb_stream.hpp:112
RELOCO_END_UNSAFE_CODE constexpr string_view payload() const noexcept RELOCO_LIFETIMEBOUND
Returns a non-owning string view over the fully unescaped packet payload. Valid only when feed() retu...
Definition gdb_stream.hpp:209
#define MICROFMT_API_CLASS
Definition compat.hpp:181
RELOCO_BEGIN_UNSAFE_CODE constexpr bool parse_hex_nibble(char c, uint8_t &val) noexcept
Definition gdb_decoders.hpp:21
Definition gdb_decoders.hpp:15
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132