| 222 | }; |
| 223 | |
| 224 | class MessageDecoder { |
| 225 | public: |
| 226 | explicit MessageDecoder(ITransport& transport); |
| 227 | |
| 228 | template <PrimitiveType T> MessageDecoder& read(T& value) { |
| 229 | // Safe mutable byte access to object representation |
| 230 | read_impl(std::as_writable_bytes(std::span{&value, 1})); |
| 231 | |
| 232 | return *this; |
| 233 | } |
| 234 | |
| 235 | // Overloads for non-primitive types - defined inline so templates can see |
| 236 | // them |
| 237 | MessageDecoder& read(std::vector<std::byte>& value) { |
| 238 | const auto container_size = [&] { |
| 239 | auto size = std::size_t{}; |
| 240 | read(size); |
| 241 | return size; |
| 242 | }(); |
| 243 | |
| 244 | value.resize(container_size); |
| 245 | read_impl(std::span{value.data(), container_size}); |
| 246 | |
| 247 | return *this; |
| 248 | } |
| 249 | |
| 250 | MessageDecoder& read(std::string& value) { |
| 251 | const auto symbol_length = [&] { |
| 252 | auto length = std::size_t{}; |
| 253 | read(length); |
| 254 | return length; |
| 255 | }(); |
| 256 | |
| 257 | value.resize(symbol_length); |
| 258 | // std::string uses char* internally, convert to std::byte* for |
| 259 | // read_impl |
| 260 | read_impl(std::span{reinterpret_cast<std::byte*>(value.data()), |
| 261 | symbol_length}); |
| 262 | |
| 263 | return *this; |
| 264 | } |
| 265 | |
| 266 | template <typename StringContainer, typename StringType> |
| 267 | MessageDecoder& read(StringContainer& symbol_container) { |
| 268 | const auto number_symbols = [&] { |
| 269 | auto num{std::size_t{}}; |
| 270 | read(num); |
| 271 | return num; |
| 272 | }(); |
| 273 | |
| 274 | for (int s = 0; s < static_cast<int>(number_symbols); ++s) { |
| 275 | const auto symbol_value = [&] { |
| 276 | auto value{StringType{}}; |
| 277 | read(value); |
| 278 | return value; |
| 279 | }(); |
| 280 | |
| 281 | symbol_container.push_back(symbol_value); |