1308 lines
37 KiB
C++
1308 lines
37 KiB
C++
// 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 <array>
|
|
|
|
#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',
|
|
// type=sfixed32, k=7, v=-50
|
|
0x3d, 0xce, 0xff, 0xff, 0xff,
|
|
// type=sfixed64, k=8, v=-1647993274
|
|
0x41, 0x46, 0x9e, 0xc5, 0x9d, 0xff, 0xff, 0xff, 0xff,
|
|
// type=float, k=9, v=2.718
|
|
0x4d, 0xb6, 0xf3, 0x2d, 0x40,
|
|
};
|
|
// 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(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 7u);
|
|
Result<int32_t> sfixed32 = decoder.ReadSfixed32();
|
|
ASSERT_EQ(sfixed32.status(), OkStatus());
|
|
EXPECT_EQ(sfixed32.value(), -50);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 8u);
|
|
Result<int64_t> sfixed64 = decoder.ReadSfixed64();
|
|
ASSERT_EQ(sfixed64.status(), OkStatus());
|
|
EXPECT_EQ(sfixed64.value(), -1647993274);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 9u);
|
|
Result<float> flt = decoder.ReadFloat();
|
|
ASSERT_EQ(flt.status(), OkStatus());
|
|
EXPECT_EQ(flt.value(), 2.718f);
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(StreamDecoder, RepeatedField) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32, k=1, v=0
|
|
0x08, 0x00,
|
|
// type=uint32, k=1, v=50
|
|
0x08, 0x32,
|
|
// type=uint32, k=1, v=100
|
|
0x08, 0x64,
|
|
// type=uint32, k=1, v=150
|
|
0x08, 0x96, 0x01,
|
|
// type=uint32, k=1, v=200
|
|
0x08, 0xc8, 0x01
|
|
};
|
|
// 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<uint32_t> uint32 = decoder.ReadUint32();
|
|
ASSERT_EQ(uint32.status(), OkStatus());
|
|
EXPECT_EQ(uint32.value(), 0u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
uint32 = decoder.ReadUint32();
|
|
ASSERT_EQ(uint32.status(), OkStatus());
|
|
EXPECT_EQ(uint32.value(), 50u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
uint32 = decoder.ReadUint32();
|
|
ASSERT_EQ(uint32.status(), OkStatus());
|
|
EXPECT_EQ(uint32.value(), 100u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
uint32 = decoder.ReadUint32();
|
|
ASSERT_EQ(uint32.status(), OkStatus());
|
|
EXPECT_EQ(uint32.value(), 150u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
uint32 = decoder.ReadUint32();
|
|
ASSERT_EQ(uint32.status(), OkStatus());
|
|
EXPECT_EQ(uint32.value(), 200u);
|
|
|
|
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
|
|
}
|
|
|
|
TEST(StreamDecoder, RepeatedFieldVector) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32, k=1, v=0
|
|
0x08, 0x00,
|
|
// type=uint32, k=1, v=50
|
|
0x08, 0x32,
|
|
// type=uint32, k=1, v=100
|
|
0x08, 0x64,
|
|
// type=uint32, k=1, v=150
|
|
0x08, 0x96, 0x01,
|
|
// type=uint32, k=1, v=200
|
|
0x08, 0xc8, 0x01
|
|
};
|
|
// clang-format on
|
|
|
|
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
|
|
StreamDecoder decoder(reader);
|
|
|
|
pw::Vector<uint32_t, 8> uint32{};
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
Status status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 1u);
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 2u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 3u);
|
|
EXPECT_EQ(uint32[2], 100u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 4u);
|
|
EXPECT_EQ(uint32[3], 150u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 5u);
|
|
EXPECT_EQ(uint32[4], 200u);
|
|
|
|
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
|
|
}
|
|
|
|
TEST(StreamDecoder, RepeatedFieldVectorFull) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32, k=1, v=0
|
|
0x08, 0x00,
|
|
// type=uint32, k=1, v=50
|
|
0x08, 0x32,
|
|
// type=uint32, k=1, v=100
|
|
0x08, 0x64,
|
|
// type=uint32, k=1, v=150
|
|
0x08, 0x96, 0x01,
|
|
// type=uint32, k=1, v=200
|
|
0x08, 0xc8, 0x01
|
|
};
|
|
// clang-format on
|
|
|
|
stream::MemoryReader reader(std::as_bytes(std::span(encoded_proto)));
|
|
StreamDecoder decoder(reader);
|
|
|
|
pw::Vector<uint32_t, 2> uint32{};
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
Status status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 1u);
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 2u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 1u);
|
|
status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, Status::ResourceExhausted());
|
|
EXPECT_EQ(uint32.size(), 2u);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedVarint) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x07,
|
|
0x00,
|
|
0x32,
|
|
0x64,
|
|
0x96, 0x01,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
std::array<uint32_t, 8> uint32{};
|
|
StatusWithSize size = decoder.ReadPackedUint32(uint32);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 5u);
|
|
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
EXPECT_EQ(uint32[2], 100u);
|
|
EXPECT_EQ(uint32[3], 150u);
|
|
EXPECT_EQ(uint32[4], 200u);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedVarintInsufficientSpace) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x07,
|
|
0x00,
|
|
0x32,
|
|
0x64,
|
|
0x96, 0x01,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
std::array<uint32_t, 2> uint32{};
|
|
StatusWithSize size = decoder.ReadPackedUint32(uint32);
|
|
ASSERT_EQ(size.status(), Status::ResourceExhausted());
|
|
EXPECT_EQ(size.size(), 2u);
|
|
|
|
// Still returns values in case of error.
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedVarintVector) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x07,
|
|
0x00,
|
|
0x32,
|
|
0x64,
|
|
0x96, 0x01,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
pw::Vector<uint32_t, 8> uint32{};
|
|
Status status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(uint32.size(), 5u);
|
|
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
EXPECT_EQ(uint32[2], 100u);
|
|
EXPECT_EQ(uint32[3], 150u);
|
|
EXPECT_EQ(uint32[4], 200u);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedVarintVectorFull) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=uint32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x07,
|
|
0x00,
|
|
0x32,
|
|
0x64,
|
|
0x96, 0x01,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
pw::Vector<uint32_t, 2> uint32{};
|
|
Status status = decoder.ReadRepeatedUint32(uint32);
|
|
ASSERT_EQ(status, Status::ResourceExhausted());
|
|
EXPECT_EQ(uint32.size(), 2u);
|
|
|
|
// Still returns values in case of error.
|
|
EXPECT_EQ(uint32[0], 0u);
|
|
EXPECT_EQ(uint32[1], 50u);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedZigZag) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=sint32[], k=1, v={-100, -25, -1, 0, 1, 25, 100}
|
|
0x0a, 0x09,
|
|
0xc7, 0x01,
|
|
0x31,
|
|
0x01,
|
|
0x00,
|
|
0x02,
|
|
0x32,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
std::array<int32_t, 8> sint32{};
|
|
StatusWithSize size = decoder.ReadPackedSint32(sint32);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 7u);
|
|
|
|
EXPECT_EQ(sint32[0], -100);
|
|
EXPECT_EQ(sint32[1], -25);
|
|
EXPECT_EQ(sint32[2], -1);
|
|
EXPECT_EQ(sint32[3], 0);
|
|
EXPECT_EQ(sint32[4], 1);
|
|
EXPECT_EQ(sint32[5], 25);
|
|
EXPECT_EQ(sint32[6], 100);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedZigZagVector) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=sint32[], k=1, v={-100, -25, -1, 0, 1, 25, 100}
|
|
0x0a, 0x09,
|
|
0xc7, 0x01,
|
|
0x31,
|
|
0x01,
|
|
0x00,
|
|
0x02,
|
|
0x32,
|
|
0xc8, 0x01
|
|
};
|
|
// 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);
|
|
pw::Vector<int32_t, 8> sint32{};
|
|
Status status = decoder.ReadRepeatedSint32(sint32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(sint32.size(), 7u);
|
|
|
|
EXPECT_EQ(sint32[0], -100);
|
|
EXPECT_EQ(sint32[1], -25);
|
|
EXPECT_EQ(sint32[2], -1);
|
|
EXPECT_EQ(sint32[3], 0);
|
|
EXPECT_EQ(sint32[4], 1);
|
|
EXPECT_EQ(sint32[5], 25);
|
|
EXPECT_EQ(sint32[6], 100);
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedFixed) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=fixed32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x14,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x32, 0x00, 0x00, 0x00,
|
|
0x64, 0x00, 0x00, 0x00,
|
|
0x96, 0x00, 0x00, 0x00,
|
|
0xc8, 0x00, 0x00, 0x00,
|
|
// type=fixed64[], v=2, v={0x0102030405060708}
|
|
0x12, 0x08,
|
|
0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
|
|
// type=sfixed32[], k=3, v={0, -50, 100, -150, 200}
|
|
0x1a, 0x14,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0xce, 0xff, 0xff, 0xff,
|
|
0x64, 0x00, 0x00, 0x00,
|
|
0x6a, 0xff, 0xff, 0xff,
|
|
0xc8, 0x00, 0x00, 0x00,
|
|
// type=sfixed64[], v=4, v={-1647993274}
|
|
0x22, 0x08,
|
|
0x46, 0x9e, 0xc5, 0x9d, 0xff, 0xff, 0xff, 0xff,
|
|
// type=double[], k=5, v=3.14159
|
|
0x2a, 0x08,
|
|
0x6e, 0x86, 0x1b, 0xf0, 0xf9, 0x21, 0x09, 0x40,
|
|
// type=float[], k=6, v=2.718
|
|
0x32, 0x04,
|
|
0xb6, 0xf3, 0x2d, 0x40,
|
|
};
|
|
// 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);
|
|
std::array<uint32_t, 8> fixed32{};
|
|
StatusWithSize size = decoder.ReadPackedFixed32(fixed32);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 5u);
|
|
|
|
EXPECT_EQ(fixed32[0], 0u);
|
|
EXPECT_EQ(fixed32[1], 50u);
|
|
EXPECT_EQ(fixed32[2], 100u);
|
|
EXPECT_EQ(fixed32[3], 150u);
|
|
EXPECT_EQ(fixed32[4], 200u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 2u);
|
|
std::array<uint64_t, 8> fixed64{};
|
|
size = decoder.ReadPackedFixed64(fixed64);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 1u);
|
|
|
|
EXPECT_EQ(fixed64[0], 0x0102030405060708u);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 3u);
|
|
std::array<int32_t, 8> sfixed32{};
|
|
size = decoder.ReadPackedSfixed32(sfixed32);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 5u);
|
|
|
|
EXPECT_EQ(sfixed32[0], 0);
|
|
EXPECT_EQ(sfixed32[1], -50);
|
|
EXPECT_EQ(sfixed32[2], 100);
|
|
EXPECT_EQ(sfixed32[3], -150);
|
|
EXPECT_EQ(sfixed32[4], 200);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 4u);
|
|
std::array<int64_t, 8> sfixed64{};
|
|
size = decoder.ReadPackedSfixed64(sfixed64);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 1u);
|
|
|
|
EXPECT_EQ(sfixed64[0], -1647993274);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 5u);
|
|
std::array<double, 8> dbl{};
|
|
size = decoder.ReadPackedDouble(dbl);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 1u);
|
|
|
|
EXPECT_EQ(dbl[0], 3.14159);
|
|
|
|
EXPECT_EQ(decoder.Next(), OkStatus());
|
|
ASSERT_EQ(decoder.FieldNumber().value(), 6u);
|
|
std::array<float, 8> flt{};
|
|
size = decoder.ReadPackedFloat(flt);
|
|
ASSERT_EQ(size.status(), OkStatus());
|
|
EXPECT_EQ(size.size(), 1u);
|
|
|
|
EXPECT_EQ(flt[0], 2.718f);
|
|
|
|
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedFixedInsufficientSpace) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=fixed32[], k=1, v={0, 50, 100, 150, 200}
|
|
0x0a, 0x14,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x32, 0x00, 0x00, 0x00,
|
|
0x64, 0x00, 0x00, 0x00,
|
|
0x96, 0x00, 0x00, 0x00,
|
|
0xc8, 0x00, 0x00, 0x00,
|
|
};
|
|
// 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);
|
|
std::array<uint32_t, 2> fixed32{};
|
|
StatusWithSize size = decoder.ReadPackedFixed32(fixed32);
|
|
ASSERT_EQ(size.status(), Status::ResourceExhausted());
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedFixedVector) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=sfixed32[], k=1, v={0, -50, 100, -150, 200}
|
|
0x0a, 0x14,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0xce, 0xff, 0xff, 0xff,
|
|
0x64, 0x00, 0x00, 0x00,
|
|
0x6a, 0xff, 0xff, 0xff,
|
|
0xc8, 0x00, 0x00, 0x00,
|
|
};
|
|
// 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);
|
|
pw::Vector<int32_t, 8> sfixed32{};
|
|
Status status = decoder.ReadRepeatedSfixed32(sfixed32);
|
|
ASSERT_EQ(status, OkStatus());
|
|
EXPECT_EQ(sfixed32.size(), 5u);
|
|
|
|
EXPECT_EQ(sfixed32[0], 0);
|
|
EXPECT_EQ(sfixed32[1], -50);
|
|
EXPECT_EQ(sfixed32[2], 100);
|
|
EXPECT_EQ(sfixed32[3], -150);
|
|
EXPECT_EQ(sfixed32[4], 200);
|
|
|
|
EXPECT_EQ(decoder.Next(), Status::OutOfRange());
|
|
}
|
|
|
|
TEST(StreamDecoder, PackedFixedVectorFull) {
|
|
// clang-format off
|
|
constexpr uint8_t encoded_proto[] = {
|
|
// type=sfixed32[], k=1, v={0, -50, 100, -150, 200}
|
|
0x0a, 0x14,
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0xce, 0xff, 0xff, 0xff,
|
|
0x64, 0x00, 0x00, 0x00,
|
|
0x6a, 0xff, 0xff, 0xff,
|
|
0xc8, 0x00, 0x00, 0x00,
|
|
};
|
|
// 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);
|
|
pw::Vector<int32_t, 2> sfixed32{};
|
|
Status status = decoder.ReadRepeatedSfixed32(sfixed32);
|
|
ASSERT_EQ(status, Status::ResourceExhausted());
|
|
EXPECT_EQ(sfixed32.size(), 0u);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace pw::protobuf
|