-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrrcp_message.hpp
More file actions
239 lines (206 loc) · 6.35 KB
/
Copy pathrrcp_message.hpp
File metadata and controls
239 lines (206 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//
// rrcp_message.hpp
// ~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2024 Christopher M. Kohlhoff
// Moderniced from Claus Klein and ChatGPT
//
#ifndef RRCP_MESSAGE_HPP
#define RRCP_MESSAGE_HPP
#include <fmt/format.h>
#include <algorithm>
#include <array>
#include <cstdlib>
#include <cstring>
#include <string>
#include <string_view>
#include "rrcp_helper.hpp"
using namespace rrcp;
/**
* @class rrcp_message
* @brief Encapsulates an RRCP message with methods for encoding, decoding, and accessing message content.
*
* Each message consists of a 4-byte header (hex-encoded length) and an escaped string payload.
*/
class rrcp_message
{
public:
/// Number of bytes used for the fixed-size header.
static constexpr std::size_t HEADER_LENGTH = 4;
/// Maximum message body length in bytes.
static constexpr std::size_t MAX_MSG_LENGTH = MAX_MU_LENGTH;
/**
* @brief Default constructor.
*/
rrcp_message() = default;
/**
* @brief Construct a message from a string view.
* @param msg The message content.
*
* The message is encoded and the header is generated. Validity is tracked.
*/
explicit rrcp_message(std::string_view msg)
{
valid_ = set_msg(msg);
if (!valid_)
{
clear();
}
}
/**
* @brief Checks if the message is in a valid state.
* @return True if valid, false otherwise.
*/
[[nodiscard]] auto is_valid() const -> bool { return valid_; }
/**
* @brief Returns a const pointer to the start of the raw message buffer.
* @return Pointer to buffer (includes header and body).
*/
[[nodiscard]] auto data() const -> const char* { return data_.data(); }
/**
* @brief Returns a mutable pointer to the start of the raw message buffer.
* @return Pointer to buffer (includes header and body).
*/
[[nodiscard]] auto data() -> char* { return data_.data(); }
/**
* @brief Returns the total length of the message (header + body).
* @return Total length in bytes.
*/
[[nodiscard]] auto length() const -> std::size_t { return HEADER_LENGTH + msg_length_; }
/**
* @brief Returns a view over the full message buffer.
* @return Message as a std::string_view.
*/
[[nodiscard]] auto get_data() const -> std::string_view { return {data(), length()}; }
/**
* @brief Returns a const pointer to the message body.
* @return Pointer to the body (excludes header).
*/
[[nodiscard]] auto body() const -> const char* { return data_.data() + HEADER_LENGTH; }
/**
* @brief Returns a mutable pointer to the message body.
* @return Pointer to the body (excludes header).
*/
[[nodiscard]] auto body() -> char* { return data_.data() + HEADER_LENGTH; }
/**
* @brief Returns the length of the message body in bytes.
* @return Length of the body.
*/
[[nodiscard]] auto body_length() const -> std::size_t { return msg_length_; }
/**
* @brief Returns a view of the message body.
* @return Message body as a std::string_view.
*/
[[nodiscard]] auto get_body() const -> std::string_view { return {body(), body_length()}; }
/**
* @brief Returns the decoded message string (after escaping is removed).
* @return Decoded message.
*/
[[nodiscard]] auto get_msg() const -> std::string { return esc2char(std::string(body(), body_length())); }
/**
* @brief Sets the message body using the input string, escaping it as needed.
* @param msg The input message.
* @return True if header encoding is successful, false otherwise.
*/
[[nodiscard]] auto set_msg(std::string_view msg) -> bool
{
std::string data = char2esc(std::string{msg});
body_length(data.length());
if (data.length() > MAX_MSG_LENGTH)
{
#ifdef DEBUG
fmt::print(stderr, "{}: {} to long!\n", __func__, msg_length_);
#endif
clear();
return false;
}
std::memcpy(body(), data.c_str(), msg_length_);
encode_header();
return valid_;
}
/**
* @brief Sets the body length, clamping it to the maximum allowed length.
* @param new_length Desired length of body.
*/
void body_length(std::size_t new_length)
{
msg_length_ = std::min(new_length, MAX_MSG_LENGTH);
#ifdef DEBUG
fmt::print(stderr, "body_length({})\n", msg_length_);
#endif
}
/**
* @brief Decodes the escaped content in the body.
* @return True if decoding succeeds, false if result is empty.
*/
auto decode_body() -> bool
{
// TODO(CK): only if (msg_length != 0 && not yet done)!
const std::string result = esc2char(std::string(body(), msg_length_));
if (result.length() != msg_length_)
{
#ifdef DEBUG
fmt::print(stderr, "{}: {}\n", __func__, result);
#endif
body_length(result.length());
std::memcpy(body(), result.c_str(), msg_length_);
}
return !result.empty();
}
/**
* @brief Parses the 4-byte header to determine body length.
* @return True if the header is valid, false if the length is out of bounds.
*/
auto decode_header() -> bool
{
// TODO(CK): only if msg_length != 0
const std::string header(data_.data(), HEADER_LENGTH);
msg_length_ = std::stoul(header, nullptr, 16);
if (msg_length_ > MAX_MSG_LENGTH)
{
#ifdef DEBUG
fmt::print(stderr, "{}: Invalid msg_length {}!\n", __func__, header);
#endif
msg_length_ = 0;
return false;
}
return true;
}
/**
* @brief Encodes the body to escaped format and adjusts body length.
*/
void encode_body()
{
// TODO(CK): only if (msg_length != 0 && not yet done)!
std::string msg = char2esc(std::string(body(), msg_length_));
if (msg.length() != msg_length_)
{
body_length(msg.length());
std::memcpy(body(), msg.c_str(), msg_length_);
}
}
/**
* @brief Encodes the message header from the current body length.
*/
void encode_header()
{
// TODO(CK): only if msg_length != 0
std::string header = fmt::format("{:04x}", static_cast< uint16_t >(msg_length_));
std::memcpy(data_.data(), header.data(), HEADER_LENGTH);
valid_ = true;
}
/**
* @brief Clears the message buffer and resets internal state.
*/
void clear()
{
msg_length_ = 0;
valid_ = false;
data_.fill('\0');
}
private:
std::array< char, HEADER_LENGTH + MAX_MSG_LENGTH > data_{}; ///< Internal buffer for message (header + body).
std::size_t msg_length_{0}; ///< Length of message body.
bool valid_{false}; ///< Flag indicating whether the message is valid.
};
#endif // RRCP_MESSAGE_HPP