|
| 1 | +#include "plain_text_protocol.hpp" |
| 2 | +#include <google/protobuf/io/coded_stream.h> |
| 3 | +#include <google/protobuf/io/zero_copy_stream.h> |
| 4 | +#include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| 5 | +#include "api_options.pb.h" |
| 6 | +#include "make_unexpected_result.hpp" |
| 7 | + |
| 8 | +namespace cppesphomeapi |
| 9 | +{ |
| 10 | +Result<void> plain_text_decode(std::span<const std::uint8_t> received_data, ::google::protobuf::Message &message) |
| 11 | +{ |
| 12 | + if (received_data.size() < 3) |
| 13 | + { |
| 14 | + return make_unexpected_result(ApiErrorCode::ParseError, |
| 15 | + "response does not contain enough bytes for the header"); |
| 16 | + } |
| 17 | + google::protobuf::io::CodedInputStream stream{received_data.data(), static_cast<int>(received_data.size())}; |
| 18 | + std::uint32_t preamble{}; |
| 19 | + if (not stream.ReadVarint32(&preamble) or preamble != 0x00) |
| 20 | + { |
| 21 | + return make_unexpected_result(ApiErrorCode::ParseError, "response does contain an invalid preamble"); |
| 22 | + } |
| 23 | + |
| 24 | + std::uint32_t message_size{}; |
| 25 | + if (not stream.ReadVarint32(&message_size)) |
| 26 | + { |
| 27 | + return make_unexpected_result(ApiErrorCode::ParseError, "got no message size"); |
| 28 | + } |
| 29 | + |
| 30 | + std::uint32_t message_type{}; |
| 31 | + if (not stream.ReadVarint32(&message_type)) |
| 32 | + { |
| 33 | + return make_unexpected_result(ApiErrorCode::ParseError, "got no message type"); |
| 34 | + } |
| 35 | + |
| 36 | + const auto expected_message_id = message.GetDescriptor()->options().GetExtension(proto::id); |
| 37 | + if (expected_message_id != message_type) |
| 38 | + { |
| 39 | + return make_unexpected_result( |
| 40 | + ApiErrorCode::UnexpectedMessage, |
| 41 | + std::format("Expected {} but got message id {}", message.GetDescriptor()->name(), message_type)); |
| 42 | + } |
| 43 | + if (stream.BytesUntilLimit() != message_size) |
| 44 | + { |
| 45 | + return make_unexpected_result( |
| 46 | + ApiErrorCode::ParseError, |
| 47 | + std::format("Received message size does not match the remaining bytes. Expected {} bytes got {} bytes.", |
| 48 | + message_size, |
| 49 | + stream.BytesUntilLimit())); |
| 50 | + } |
| 51 | + const bool parsed = message.ParseFromCodedStream(std::addressof(stream)); |
| 52 | + if (not parsed) |
| 53 | + { |
| 54 | + return make_unexpected_result( |
| 55 | + ApiErrorCode::ParseError, |
| 56 | + std::format("Could not parse message \"{}\" from bytes.", message.GetDescriptor()->name())); |
| 57 | + } |
| 58 | + return {}; |
| 59 | +} |
| 60 | + |
| 61 | +Result<std::vector<std::uint8_t>> plain_text_serialize(const ::google::protobuf::Message &message) |
| 62 | +{ |
| 63 | + constexpr std::uint8_t kPlainTextPreamble = 0x00; |
| 64 | + auto &&msg_options = message.GetDescriptor()->options(); |
| 65 | + if (not msg_options.HasExtension(proto::id)) |
| 66 | + { |
| 67 | + return make_unexpected_result( |
| 68 | + ApiErrorCode::SerializeError, |
| 69 | + std::format("message \"{}\" does not contain the id field", message.GetDescriptor()->name())); |
| 70 | + } |
| 71 | + |
| 72 | + constexpr auto kMaxHeaderLen = 5; |
| 73 | + std::vector<std::uint8_t> buffer; |
| 74 | + buffer.resize(message.ByteSizeLong() + kMaxHeaderLen); |
| 75 | + |
| 76 | + buffer[0] = kPlainTextPreamble; |
| 77 | + |
| 78 | + google::protobuf::io::ArrayOutputStream zstream{std::next(buffer.data(), 1), static_cast<int>(buffer.size() - 1)}; |
| 79 | + google::protobuf::io::CodedOutputStream output_stream{std::addressof(zstream)}; |
| 80 | + |
| 81 | + output_stream.WriteVarint32(message.ByteSizeLong()); |
| 82 | + output_stream.WriteVarint32(msg_options.GetExtension(proto::id)); |
| 83 | + |
| 84 | + if(output_stream.ByteCount() != (kMaxHeaderLen - 1)) { |
| 85 | + buffer.resize(1 + message.ByteSizeLong() + output_stream.ByteCount()); |
| 86 | + } |
| 87 | + |
| 88 | + const bool serialized = message.SerializeToCodedStream(std::addressof(output_stream)); |
| 89 | + if (not serialized) |
| 90 | + { |
| 91 | + return make_unexpected_result( |
| 92 | + ApiErrorCode::SerializeError, |
| 93 | + std::format("could not serialize message \"{}\"", message.GetDescriptor()->name())); |
| 94 | + } |
| 95 | + return buffer; |
| 96 | +} |
| 97 | +} // namespace cppesphomeapi |
0 commit comments