blob: f1385c4cabe85e1a277a5407ee4147da486ca069 [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#include "pw_protobuf/stream_decoder.h"
#include "gtest/gtest.h"
#include "pw_status/status.h"
#include "pw_status/status_with_size.h"
#include "pw_stream/memory_stream.h"
#include "pw_stream/stream.h"
namespace pw::protobuf {
namespace {
// Non-seekable wrapper for MemoryReader for testing behavior when seeking is
// not available.
class NonSeekableMemoryReader : public stream::NonSeekableReader {
public:
explicit NonSeekableMemoryReader(stream::MemoryReader& reader)
: reader_(reader) {}
size_t bytes_read() const { return reader_.bytes_read(); }
const std::byte* data() const { return reader_.data(); }
private:
virtual StatusWithSize DoRead(ByteSpan destination) override {
const pw::Result<pw::ByteSpan> result = reader_.Read(destination);
if (!result.ok()) {
return StatusWithSize(result.status(), 0);
}
return StatusWithSize(result.value().size_bytes());
}
stream::MemoryReader& reader_;
};
TEST(StreamDecoder, Decode) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// type=sint32, k=2, v=-13
0x10, 0x19,
// type=bool, k=3, v=false
0x18, 0x00,
// type=double, k=4, v=3.14159
0x21, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
// type=fixed32, k=5, v=0xdeadbeef
0x2d, 0xef, 0xbe, 0xad, 0xde,
// type=string, k=6, v="Hello world"
0x32, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
Result<int32_t> int32 = decoder.ReadInt32();
ASSERT_EQ(int32.status(), OkStatus());
EXPECT_EQ(int32.value(), 42);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 2u);
Result<int32_t> sint32 = decoder.ReadSint32();
ASSERT_EQ(sint32.status(), OkStatus());
EXPECT_EQ(sint32.value(), -13);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 3u);
Result<bool> boolean = decoder.ReadBool();
ASSERT_EQ(boolean.status(), OkStatus());
EXPECT_FALSE(boolean.value());
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 4u);
Result<double> dbl = decoder.ReadDouble();
ASSERT_EQ(dbl.status(), OkStatus());
EXPECT_EQ(dbl.value(), 3.14159);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 5u);
Result<uint32_t> fixed32 = decoder.ReadFixed32();
ASSERT_EQ(fixed32.status(), OkStatus());
EXPECT_EQ(fixed32.value(), 0xdeadbeef);
char buffer[16];
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 6u);
StatusWithSize sws = decoder.ReadString(buffer);
ASSERT_EQ(sws.status(), OkStatus());
buffer[sws.size()] = '\0';
EXPECT_STREQ(buffer, "Hello world");
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_SkipsUnusedFields) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// type=sint32, k=2, v=-13
0x10, 0x19,
// type=bool, k=3, v=false
0x18, 0x00,
// type=double, k=4, v=3.14159
0x21, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
// type=fixed32, k=5, v=0xdeadbeef
0x2d, 0xef, 0xbe, 0xad, 0xde,
// type=string, k=6, v="Hello world"
0x32, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
// Don't process any fields except for the fourth. Next should still iterate
// correctly despite field values not being consumed.
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 4u);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_NonSeekable_SkipsUnusedFields) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// type=sint32, k=2, v=-13
0x10, 0x19,
// type=bool, k=3, v=false
0x18, 0x00,
// type=double, k=4, v=3.14159
0x21, 0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
// type=fixed32, k=5, v=0xdeadbeef
0x2d, 0xef, 0xbe, 0xad, 0xde,
// type=string, k=6, v="Hello world"
0x32, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
};
// clang-format on
// Test with a non-seekable memory reader
stream::MemoryReader wrapped_reader(std::as_bytes(std::span(encoded_proto)));
NonSeekableMemoryReader reader(wrapped_reader);
StreamDecoder decoder(reader);
// Don't process any fields except for the fourth. Next should still iterate
// correctly despite field values not being consumed.
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 4u);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_BadData) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// type=sint32, k=2, value... missing
0x10,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
Result<int32_t> int32 = decoder.ReadInt32();
ASSERT_EQ(int32.status(), OkStatus());
EXPECT_EQ(int32.value(), 42);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 2u);
EXPECT_EQ(decoder.ReadSint32().status(), Status::DataLoss());
EXPECT_EQ(decoder.Next(), Status::DataLoss());
}
TEST(Decoder, Decode_SkipsBadFieldNumbers) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// type=int32, k=19001, v=42 (invalid field number)
0xc8, 0xa3, 0x09, 0x2a,
// type=bool, k=3, v=false
0x18, 0x00,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 1u);
Result<int32_t> int32 = decoder.ReadInt32();
ASSERT_EQ(int32.status(), OkStatus());
EXPECT_EQ(int32.value(), 42);
// Bad field.
EXPECT_EQ(decoder.Next(), Status::DataLoss());
EXPECT_EQ(decoder.FieldNumber().status(), Status::FailedPrecondition());
EXPECT_EQ(decoder.Next(), Status::DataLoss());
}
TEST(StreamDecoder, Decode_Nested) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// Submessage (bytes) key=8, length=4
0x32, 0x04,
// type=uint32, k=1, v=2
0x08, 0x02,
// type=uint32, k=2, v=7
0x10, 0x07,
// End submessage
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
Result<int32_t> int32 = decoder.ReadInt32();
ASSERT_EQ(int32.status(), OkStatus());
EXPECT_EQ(int32.value(), 42);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 6u);
{
StreamDecoder nested = decoder.GetNestedDecoder();
EXPECT_EQ(nested.Next(), OkStatus());
ASSERT_EQ(*nested.FieldNumber(), 1u);
Result<uint32_t> uint32 = nested.ReadUint32();
ASSERT_EQ(uint32.status(), OkStatus());
EXPECT_EQ(uint32.value(), 2u);
EXPECT_EQ(nested.Next(), OkStatus());
ASSERT_EQ(*nested.FieldNumber(), 2u);
uint32 = nested.ReadUint32();
ASSERT_EQ(uint32.status(), OkStatus());
EXPECT_EQ(uint32.value(), 7u);
ASSERT_EQ(nested.Next(), Status::OutOfRange());
}
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 2u);
Result<int32_t> sint32 = decoder.ReadSint32();
ASSERT_EQ(sint32.status(), OkStatus());
EXPECT_EQ(sint32.value(), -13);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_Nested_SeeksToNextFieldOnDestruction) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// Submessage (bytes) key=8, length=4
0x32, 0x04,
// type=uint32, k=1, v=2
0x08, 0x02,
// type=uint32, k=2, v=7
0x10, 0x07,
// End submessage
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
// Create a nested encoder for the nested field, but don't use it.
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 6u);
{ StreamDecoder nested = decoder.GetNestedDecoder(); }
// The root decoder should still advance to the next field after the nested
// decoder is closed.
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 2u);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder,
Decode_Nested_NonSeekable_AdvancesToNextFieldOnDestruction) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// Submessage (bytes) key=8, length=4
0x32, 0x04,
// type=uint32, k=1, v=2
0x08, 0x02,
// type=uint32, k=2, v=7
0x10, 0x07,
// End submessage
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
// Test with a non-seekable memory reader
stream::MemoryReader wrapped_reader(std::as_bytes(std::span(encoded_proto)));
NonSeekableMemoryReader reader(wrapped_reader);
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
// Create a nested encoder for the nested field, but don't use it.
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 6u);
{ StreamDecoder nested = decoder.GetNestedDecoder(); }
// The root decoder should still advance to the next field after the nested
// decoder is closed.
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 2u);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_Nested_LastField) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// Submessage (bytes) key=8, length=4
0x32, 0x04,
// type=uint32, k=1, v=2
0x08, 0x02,
// type=uint32, k=2, v=7
0x10, 0x07,
// End submessage and proto
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
// Create a nested encoder for the nested field, which is the last field in
// the root proto.
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 6u);
{ StreamDecoder nested = decoder.GetNestedDecoder(); }
// Root decoder should correctly terminate after the nested decoder is closed.
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_Nested_MultiLevel) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// Submessage key=1, length=4
0x0a, 0x04,
// Sub-submessage key=1, length=2
0x0a, 0x02,
// type=uint32, k=2, v=7
0x10, 0x07,
// End sub-submessage
// End submessage
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
{
StreamDecoder nested = decoder.GetNestedDecoder();
EXPECT_EQ(nested.Next(), OkStatus());
ASSERT_EQ(*nested.FieldNumber(), 1u);
{
StreamDecoder double_nested = nested.GetNestedDecoder();
EXPECT_EQ(double_nested.Next(), OkStatus());
ASSERT_EQ(*double_nested.FieldNumber(), 2u);
Result<uint32_t> result = double_nested.ReadUint32();
ASSERT_EQ(result.status(), OkStatus());
EXPECT_EQ(result.value(), 7u);
EXPECT_EQ(double_nested.Next(), Status::OutOfRange());
}
EXPECT_EQ(nested.Next(), Status::OutOfRange());
}
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_Nested_InvalidField) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// Submessage key=1, length=4
0x0a, 0x04,
// Oops. No data!
};
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
{
StreamDecoder nested = decoder.GetNestedDecoder();
EXPECT_EQ(nested.Next(), Status::DataLoss());
}
EXPECT_EQ(decoder.Next(), Status::DataLoss());
}
TEST(StreamDecoder, Decode_BytesReader) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=14
0x0a, 0x0e,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 1u);
{
StreamDecoder::BytesReader bytes = decoder.GetBytesReader();
EXPECT_EQ(bytes.field_size(), 14u);
std::byte buffer[7];
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 2, sizeof(buffer)), 0);
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 9, sizeof(buffer)), 0);
EXPECT_EQ(bytes.Read(buffer).status(), Status::OutOfRange());
}
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_BytesReader_Seek) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=14
0x0a, 0x0e,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 1u);
{
StreamDecoder::BytesReader bytes = decoder.GetBytesReader();
std::byte buffer[2];
ASSERT_EQ(bytes.Seek(3), OkStatus());
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 5, sizeof(buffer)), 0);
// Bad seek offset (absolute).
ASSERT_EQ(bytes.Seek(15), Status::OutOfRange());
// Seek back from current position.
ASSERT_EQ(bytes.Seek(-4, stream::Stream::kCurrent), OkStatus());
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 3, sizeof(buffer)), 0);
// Bad seek offset (relative).
ASSERT_EQ(bytes.Seek(-4, stream::Stream::kCurrent), Status::OutOfRange());
// Seek from the end of the bytes field.
ASSERT_EQ(bytes.Seek(-2, stream::Stream::kEnd), OkStatus());
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 14, sizeof(buffer)), 0);
// Bad seek offset (end).
ASSERT_EQ(bytes.Seek(-15, stream::Stream::kEnd), Status::OutOfRange());
}
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_BytesReader_Close) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=14
0x0a, 0x0e,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d,
// End bytes
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 1u);
{
// Partially consume the bytes field.
StreamDecoder::BytesReader bytes = decoder.GetBytesReader();
std::byte buffer[2];
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 2, sizeof(buffer)), 0);
}
// Continue reading the top-level message.
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 2u);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_BytesReader_NonSeekable_Close) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=14
0x0a, 0x0e,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d,
// End bytes
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
// Test with a non-seekable memory reader
stream::MemoryReader wrapped_reader(std::as_bytes(std::span(encoded_proto)));
NonSeekableMemoryReader reader(wrapped_reader);
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 1u);
{
// Partially consume the bytes field.
StreamDecoder::BytesReader bytes = decoder.GetBytesReader();
std::byte buffer[2];
EXPECT_EQ(bytes.Read(buffer).status(), OkStatus());
EXPECT_EQ(std::memcmp(buffer, encoded_proto + 2, sizeof(buffer)), 0);
}
// Continue reading the top-level message.
EXPECT_EQ(decoder.Next(), OkStatus());
EXPECT_EQ(*decoder.FieldNumber(), 2u);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_BytesReader_InvalidField) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=4
0x0a, 0x04,
// Oops. No data!
};
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(*decoder.FieldNumber(), 1u);
{
StreamDecoder::BytesReader bytes = decoder.GetBytesReader();
EXPECT_EQ(bytes.Seek(0), Status::DataLoss());
std::byte buffer[2];
EXPECT_EQ(bytes.Read(buffer).status(), Status::DataLoss());
}
EXPECT_EQ(decoder.Next(), Status::DataLoss());
}
TEST(StreamDecoder, GetLengthDelimitedPayloadBounds) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// bytes key=1, length=14
0x0a, 0x0e,
0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d,
// End bytes
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
ASSERT_EQ(OkStatus(), decoder.Next());
Result<StreamDecoder::Bounds> field_bound =
decoder.GetLengthDelimitedPayloadBounds();
ASSERT_EQ(OkStatus(), field_bound.status());
ASSERT_EQ(field_bound.value().low, 2ULL);
ASSERT_EQ(field_bound.value().high, 16ULL);
ASSERT_EQ(OkStatus(), decoder.Next());
ASSERT_EQ(Status::NotFound(),
decoder.GetLengthDelimitedPayloadBounds().status());
}
TEST(StreamDecoder, ReadDelimitedField_DoesntOverConsume) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=string, k=1, v="Hello world"
0x0a, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
// type=int32, k=2, v=42
0x10, 0x2a,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader);
ASSERT_EQ(OkStatus(), decoder.Next());
// This buffer is much larger than the string.
char buffer[128];
const StatusWithSize size = decoder.ReadString(buffer);
EXPECT_EQ(size.status(), OkStatus());
EXPECT_EQ(size.size(), 11u);
// Make sure we can still read the next field.
ASSERT_EQ(OkStatus(), decoder.Next());
const pw::Result<int32_t> result = decoder.ReadInt32();
EXPECT_EQ(result.status(), OkStatus());
EXPECT_EQ(result.value(), 42);
}
TEST(StreamDecoder, Decode_WithLength) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=int32, k=1, v=42
0x08, 0x2a,
// This field is beyond the range of the protobuf:
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
StreamDecoder decoder(reader, /*length=*/2u);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
Result<int32_t> int32 = decoder.ReadInt32();
ASSERT_EQ(int32.status(), OkStatus());
EXPECT_EQ(int32.value(), 42);
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
}
TEST(StreamDecoder, Decode_WithLength_SkipsToEnd) {
// clang-format off
constexpr uint8_t encoded_proto[] = {
// type=string, k=1, v="Hello world"
0x08, 0x0b, 'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd',
// This field is beyond the range of the protobuf:
// type=sint32, k=2, v=-13
0x10, 0x19,
};
// clang-format on
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
{
StreamDecoder decoder(reader, /*length=*/13u);
EXPECT_EQ(decoder.Next(), OkStatus());
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
// Don't read the value out, or advance further. Destructing the object
// should advance to the end of the length given.
}
EXPECT_EQ(reader.Tell(), 13u);
}
} // namespace
} // namespace pw::protobuf