blob: 156469af45c7a3f8f4858d674487ca5944956198 [file] [log] [blame]
Alexei Frolovf9ae1892021-04-01 18:24:27 -07001// Copyright 2021 The Pigweed Authors
Alexei Frolovbbf164c2019-12-16 12:51:59 -08002//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
15#include "pw_protobuf/encoder.h"
16
Armando Montaneza095fc22021-05-21 12:27:11 -070017#include <cstddef>
18#include <cstring>
19#include <span>
20
21#include "pw_assert/check.h"
22#include "pw_bytes/span.h"
Yecheng Zhao931f4d52021-09-02 23:15:13 -070023#include "pw_protobuf/serialized_size.h"
Armando Montaneza095fc22021-05-21 12:27:11 -070024#include "pw_protobuf/wire_format.h"
25#include "pw_status/status.h"
26#include "pw_status/try.h"
27#include "pw_stream/memory_stream.h"
28#include "pw_stream/stream.h"
29#include "pw_varint/varint.h"
Alexei Frolovf9ae1892021-04-01 18:24:27 -070030
Alexei Frolovbbf164c2019-12-16 12:51:59 -080031namespace pw::protobuf {
32
Armando Montanez9f3708c2021-08-02 14:37:36 -070033StreamEncoder StreamEncoder::GetNestedEncoder(uint32_t field_number) {
Armando Montaneza095fc22021-05-21 12:27:11 -070034 PW_CHECK(!nested_encoder_open());
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -070035 PW_CHECK(ValidFieldNumber(field_number));
36
Armando Montaneza095fc22021-05-21 12:27:11 -070037 nested_field_number_ = field_number;
38
39 // Pass the unused space of the scratch buffer to the nested encoder to use
40 // as their scratch buffer.
41 size_t key_size =
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -070042 varint::EncodedSize(FieldKey(field_number, WireType::kDelimited));
Armando Montaneza095fc22021-05-21 12:27:11 -070043 size_t reserved_size = key_size + config::kMaxVarintSize;
44 size_t max_size = std::min(memory_writer_.ConservativeWriteLimit(),
45 writer_.ConservativeWriteLimit());
46 // Account for reserved bytes.
47 max_size = max_size > reserved_size ? max_size - reserved_size : 0;
48 // Cap based on max varint size.
49 max_size = std::min(varint::MaxValueInBytes(config::kMaxVarintSize),
50 static_cast<uint64_t>(max_size));
51
52 ByteSpan nested_buffer;
53 if (max_size > 0) {
54 nested_buffer = ByteSpan(
55 memory_writer_.data() + reserved_size + memory_writer_.bytes_written(),
56 max_size);
57 } else {
58 nested_buffer = ByteSpan();
59 }
Armando Montanez9f3708c2021-08-02 14:37:36 -070060 return StreamEncoder(*this, nested_buffer);
Alexei Frolovbbf164c2019-12-16 12:51:59 -080061}
62
Ewout van Bekkum011a4d52021-08-20 20:19:52 -070063StreamEncoder::~StreamEncoder() {
64 // If this was an invalidated StreamEncoder which cannot be used, permit the
65 // object to be cleanly destructed by doing nothing.
66 if (nested_field_number_ == kFirstReservedNumber) {
67 return;
Alexei Frolovf9ae1892021-04-01 18:24:27 -070068 }
69
Ewout van Bekkum011a4d52021-08-20 20:19:52 -070070 PW_CHECK(
71 !nested_encoder_open(),
72 "Tried to destruct a proto encoder with an active submessage encoder");
Alexei Frolovf9ae1892021-04-01 18:24:27 -070073
Ewout van Bekkum011a4d52021-08-20 20:19:52 -070074 if (parent_ != nullptr) {
75 parent_->CloseNestedMessage(*this);
76 }
Armando Montaneza095fc22021-05-21 12:27:11 -070077}
78
Ewout van Bekkum011a4d52021-08-20 20:19:52 -070079void StreamEncoder::CloseNestedMessage(StreamEncoder& nested) {
80 PW_DCHECK_PTR_EQ(nested.parent_,
81 this,
82 "CloseNestedMessage() called on the wrong Encoder parent");
Armando Montaneza095fc22021-05-21 12:27:11 -070083
84 // Make the nested encoder look like it has an open child to block writes for
85 // the remainder of the object's life.
86 nested.nested_field_number_ = kFirstReservedNumber;
87 nested.parent_ = nullptr;
88 // Temporarily cache the field number of the child so we can re-enable
89 // writing to this encoder.
90 uint32_t temp_field_number = nested_field_number_;
91 nested_field_number_ = 0;
92
93 // TODO(amontanez): If a submessage fails, we could optionally discard
94 // it and continue happily. For now, we'll always invalidate the entire
95 // encoder if a single submessage fails.
96 status_.Update(nested.status_);
Ewout van Bekkum011a4d52021-08-20 20:19:52 -070097 if (!status_.ok()) {
98 return;
99 }
Armando Montaneza095fc22021-05-21 12:27:11 -0700100
101 if (varint::EncodedSize(nested.memory_writer_.bytes_written()) >
102 config::kMaxVarintSize) {
103 status_ = Status::OutOfRange();
Ewout van Bekkum011a4d52021-08-20 20:19:52 -0700104 return;
Armando Montaneza095fc22021-05-21 12:27:11 -0700105 }
106
107 status_ = WriteLengthDelimitedField(temp_field_number,
108 nested.memory_writer_.WrittenData());
Armando Montaneza095fc22021-05-21 12:27:11 -0700109}
110
Armando Montanez9f3708c2021-08-02 14:37:36 -0700111Status StreamEncoder::WriteVarintField(uint32_t field_number, uint64_t value) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700112 PW_TRY(UpdateStatusForWrite(
113 field_number, WireType::kVarint, varint::EncodedSize(value)));
114
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -0700115 WriteVarint(FieldKey(field_number, WireType::kVarint))
Adrien Larbanetd1ca56c2021-06-10 14:20:45 +0000116 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
Armando Montaneza095fc22021-05-21 12:27:11 -0700117 return WriteVarint(value);
118}
119
Armando Montanez9f3708c2021-08-02 14:37:36 -0700120Status StreamEncoder::WriteLengthDelimitedField(uint32_t field_number,
121 ConstByteSpan data) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700122 PW_TRY(UpdateStatusForWrite(field_number, WireType::kDelimited, data.size()));
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700123 status_.Update(WriteLengthDelimitedKeyAndLengthPrefix(
124 field_number, data.size(), writer_));
125 PW_TRY(status_);
Armando Montaneza095fc22021-05-21 12:27:11 -0700126 if (Status status = writer_.Write(data); !status.ok()) {
127 status_ = status;
128 }
129 return status_;
130}
131
Yecheng Zhao5d15dbe2021-09-01 20:45:35 -0700132Status StreamEncoder::WriteLengthDelimitedFieldFromStream(
133 uint32_t field_number,
134 stream::Reader& bytes_reader,
135 size_t num_bytes,
136 ByteSpan stream_pipe_buffer) {
137 PW_CHECK_UINT_GT(
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -0700138 stream_pipe_buffer.size(), 0, "Transfer buffer cannot be 0 size");
Yecheng Zhao5d15dbe2021-09-01 20:45:35 -0700139 PW_TRY(UpdateStatusForWrite(field_number, WireType::kDelimited, num_bytes));
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700140 status_.Update(
141 WriteLengthDelimitedKeyAndLengthPrefix(field_number, num_bytes, writer_));
Yecheng Zhao5d15dbe2021-09-01 20:45:35 -0700142 PW_TRY(status_);
143
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -0700144 // Stream data from `bytes_reader` to `writer_`.
Yecheng Zhao5d15dbe2021-09-01 20:45:35 -0700145 // TODO(pwbug/468): move the following logic to pw_stream/copy.h at a later
146 // time.
147 for (size_t bytes_written = 0; bytes_written < num_bytes;) {
148 const size_t chunk_size_bytes =
149 std::min(num_bytes - bytes_written, stream_pipe_buffer.size_bytes());
150 const Result<ByteSpan> read_result =
151 bytes_reader.Read(stream_pipe_buffer.data(), chunk_size_bytes);
152 status_.Update(read_result.status());
153 PW_TRY(status_);
154
155 status_.Update(writer_.Write(read_result.value()));
156 PW_TRY(status_);
157
158 bytes_written += read_result.value().size();
159 }
160
161 return OkStatus();
162}
163
Armando Montanez9f3708c2021-08-02 14:37:36 -0700164Status StreamEncoder::WriteFixed(uint32_t field_number, ConstByteSpan data) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700165 WireType type =
166 data.size() == sizeof(uint32_t) ? WireType::kFixed32 : WireType::kFixed64;
167
168 PW_TRY(UpdateStatusForWrite(field_number, type, data.size()));
169
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -0700170 WriteVarint(FieldKey(field_number, type))
Adrien Larbanetd1ca56c2021-06-10 14:20:45 +0000171 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
Armando Montaneza095fc22021-05-21 12:27:11 -0700172 if (Status status = writer_.Write(data); !status.ok()) {
173 status_ = status;
174 }
175 return status_;
176}
177
Armando Montanez9f3708c2021-08-02 14:37:36 -0700178Status StreamEncoder::WritePackedFixed(uint32_t field_number,
179 std::span<const std::byte> values,
180 size_t elem_size) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700181 if (values.empty()) {
182 return status_;
183 }
184
185 PW_CHECK_NOTNULL(values.data());
186 PW_DCHECK(elem_size == sizeof(uint32_t) || elem_size == sizeof(uint64_t));
187
188 PW_TRY(UpdateStatusForWrite(
189 field_number, WireType::kDelimited, values.size_bytes()));
Alexei Frolov5ebf8ad2021-09-02 16:55:15 -0700190 WriteVarint(FieldKey(field_number, WireType::kDelimited))
Adrien Larbanetd1ca56c2021-06-10 14:20:45 +0000191 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
192 WriteVarint(values.size_bytes())
193 .IgnoreError(); // TODO(pwbug/387): Handle Status properly
Armando Montaneza095fc22021-05-21 12:27:11 -0700194
195 for (auto val_start = values.begin(); val_start != values.end();
196 val_start += elem_size) {
197 // Allocates 8 bytes so both 4-byte and 8-byte types can be encoded as
198 // little-endian for serialization.
199 std::array<std::byte, sizeof(uint64_t)> data;
200 if (std::endian::native == std::endian::little) {
201 std::copy(val_start, val_start + elem_size, std::begin(data));
202 } else {
203 std::reverse_copy(val_start, val_start + elem_size, std::begin(data));
204 }
205 status_.Update(writer_.Write(std::span(data).first(elem_size)));
206 PW_TRY(status_);
207 }
208 return status_;
209}
210
Armando Montanez9f3708c2021-08-02 14:37:36 -0700211Status StreamEncoder::UpdateStatusForWrite(uint32_t field_number,
212 WireType type,
213 size_t data_size) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700214 PW_CHECK(!nested_encoder_open());
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700215 PW_TRY(status_);
216
Armando Montaneza095fc22021-05-21 12:27:11 -0700217 if (!ValidFieldNumber(field_number)) {
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700218 return status_ = Status::InvalidArgument();
Armando Montaneza095fc22021-05-21 12:27:11 -0700219 }
220
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700221 const Result<size_t> field_size = SizeOfField(field_number, type, data_size);
222 status_.Update(field_size.status());
223 PW_TRY(status_);
Armando Montaneza095fc22021-05-21 12:27:11 -0700224
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700225 if (field_size.value() > writer_.ConservativeWriteLimit()) {
Armando Montaneza095fc22021-05-21 12:27:11 -0700226 status_ = Status::ResourceExhausted();
227 }
Yecheng Zhao931f4d52021-09-02 23:15:13 -0700228
Armando Montaneza095fc22021-05-21 12:27:11 -0700229 return status_;
230}
231
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800232} // namespace pw::protobuf