You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
6.4 KiB
276 lines
6.4 KiB
// Copyright 2020 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/decoder.h"
|
|
|
|
#include <cstring>
|
|
|
|
#include "pw_varint/varint.h"
|
|
|
|
namespace pw::protobuf {
|
|
|
|
Status Decoder::Next() {
|
|
if (!previous_field_consumed_) {
|
|
if (Status status = SkipField(); !status.ok()) {
|
|
return status;
|
|
}
|
|
}
|
|
if (proto_.empty()) {
|
|
return Status::OutOfRange();
|
|
}
|
|
previous_field_consumed_ = false;
|
|
return FieldSize() == 0 ? Status::DataLoss() : OkStatus();
|
|
}
|
|
|
|
Status Decoder::SkipField() {
|
|
if (proto_.empty()) {
|
|
return Status::OutOfRange();
|
|
}
|
|
|
|
size_t bytes_to_skip = FieldSize();
|
|
if (bytes_to_skip == 0) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
proto_ = proto_.subspan(bytes_to_skip);
|
|
return proto_.empty() ? Status::OutOfRange() : OkStatus();
|
|
}
|
|
|
|
uint32_t Decoder::FieldNumber() const {
|
|
uint64_t key;
|
|
varint::Decode(proto_, &key);
|
|
return key >> kFieldNumberShift;
|
|
}
|
|
|
|
Status Decoder::ReadUint32(uint32_t* out) {
|
|
uint64_t value = 0;
|
|
Status status = ReadUint64(&value);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
if (value > std::numeric_limits<uint32_t>::max()) {
|
|
return Status::OutOfRange();
|
|
}
|
|
*out = value;
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadSint32(int32_t* out) {
|
|
int64_t value = 0;
|
|
Status status = ReadSint64(&value);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
if (value > std::numeric_limits<int32_t>::max()) {
|
|
return Status::OutOfRange();
|
|
}
|
|
*out = value;
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadSint64(int64_t* out) {
|
|
uint64_t value = 0;
|
|
Status status = ReadUint64(&value);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
*out = varint::ZigZagDecode(value);
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadBool(bool* out) {
|
|
uint64_t value = 0;
|
|
Status status = ReadUint64(&value);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
*out = value;
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadString(std::string_view* out) {
|
|
std::span<const std::byte> bytes;
|
|
Status status = ReadDelimited(&bytes);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
*out = std::string_view(reinterpret_cast<const char*>(bytes.data()),
|
|
bytes.size());
|
|
return OkStatus();
|
|
}
|
|
|
|
size_t Decoder::FieldSize() const {
|
|
uint64_t key;
|
|
size_t key_size = varint::Decode(proto_, &key);
|
|
if (key_size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
std::span<const std::byte> remainder = proto_.subspan(key_size);
|
|
WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
|
|
uint64_t value = 0;
|
|
size_t expected_size = 0;
|
|
|
|
switch (wire_type) {
|
|
case WireType::kVarint:
|
|
expected_size = varint::Decode(remainder, &value);
|
|
if (expected_size == 0) {
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WireType::kDelimited:
|
|
// Varint at cursor indicates size of the field.
|
|
expected_size = varint::Decode(remainder, &value);
|
|
if (expected_size == 0) {
|
|
return 0;
|
|
}
|
|
expected_size += value;
|
|
break;
|
|
|
|
case WireType::kFixed32:
|
|
expected_size = sizeof(uint32_t);
|
|
break;
|
|
|
|
case WireType::kFixed64:
|
|
expected_size = sizeof(uint64_t);
|
|
break;
|
|
}
|
|
|
|
if (remainder.size() < expected_size) {
|
|
return 0;
|
|
}
|
|
|
|
return key_size + expected_size;
|
|
}
|
|
|
|
Status Decoder::ConsumeKey(WireType expected_type) {
|
|
uint64_t key;
|
|
size_t bytes_read = varint::Decode(proto_, &key);
|
|
if (bytes_read == 0) {
|
|
return Status::FailedPrecondition();
|
|
}
|
|
|
|
WireType wire_type = static_cast<WireType>(key & kWireTypeMask);
|
|
if (wire_type != expected_type) {
|
|
return Status::FailedPrecondition();
|
|
}
|
|
|
|
// Advance past the key.
|
|
proto_ = proto_.subspan(bytes_read);
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadVarint(uint64_t* out) {
|
|
if (Status status = ConsumeKey(WireType::kVarint); !status.ok()) {
|
|
return status;
|
|
}
|
|
|
|
size_t bytes_read = varint::Decode(proto_, out);
|
|
if (bytes_read == 0) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
// Advance to the next field.
|
|
proto_ = proto_.subspan(bytes_read);
|
|
previous_field_consumed_ = true;
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadFixed(std::byte* out, size_t size) {
|
|
WireType expected_wire_type =
|
|
size == sizeof(uint32_t) ? WireType::kFixed32 : WireType::kFixed64;
|
|
Status status = ConsumeKey(expected_wire_type);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
|
|
if (proto_.size() < size) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
std::memcpy(out, proto_.data(), size);
|
|
proto_ = proto_.subspan(size);
|
|
previous_field_consumed_ = true;
|
|
|
|
return OkStatus();
|
|
}
|
|
|
|
Status Decoder::ReadDelimited(std::span<const std::byte>* out) {
|
|
Status status = ConsumeKey(WireType::kDelimited);
|
|
if (!status.ok()) {
|
|
return status;
|
|
}
|
|
|
|
uint64_t length;
|
|
size_t bytes_read = varint::Decode(proto_, &length);
|
|
if (bytes_read == 0) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
proto_ = proto_.subspan(bytes_read);
|
|
if (proto_.size() < length) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
*out = proto_.first(length);
|
|
proto_ = proto_.subspan(length);
|
|
previous_field_consumed_ = true;
|
|
|
|
return OkStatus();
|
|
}
|
|
|
|
Status CallbackDecoder::Decode(std::span<const std::byte> proto) {
|
|
if (handler_ == nullptr || state_ != kReady) {
|
|
return Status::FailedPrecondition();
|
|
}
|
|
|
|
state_ = kDecodeInProgress;
|
|
decoder_.Reset(proto);
|
|
|
|
// Iterate the proto, calling the handler with each field number.
|
|
while (state_ == kDecodeInProgress) {
|
|
if (Status status = decoder_.Next(); !status.ok()) {
|
|
if (status.IsOutOfRange()) {
|
|
// Reached the end of the proto.
|
|
break;
|
|
}
|
|
|
|
// Proto data is malformed.
|
|
return status;
|
|
}
|
|
|
|
Status status = handler_->ProcessField(*this, decoder_.FieldNumber());
|
|
if (!status.ok()) {
|
|
state_ = status.IsCancelled() ? kDecodeCancelled : kDecodeFailed;
|
|
return status;
|
|
}
|
|
|
|
// The callback function can modify the decoder's state; check that
|
|
// everything is still okay.
|
|
if (state_ == kDecodeFailed) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (state_ != kDecodeInProgress) {
|
|
return Status::DataLoss();
|
|
}
|
|
|
|
state_ = kReady;
|
|
return OkStatus();
|
|
}
|
|
|
|
} // namespace pw::protobuf
|