1 // Copyright 2019 The Pigweed Authors
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
7 // https://www.apache.org/licenses/LICENSE-2.0
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
20 #include "pw_bytes/span.h"
21 #include "pw_protobuf/wire_format.h"
22 #include "pw_result/result.h"
23 #include "pw_status/status.h"
24 #include "pw_varint/varint.h"
26 namespace pw::protobuf {
28 // A streaming protobuf encoder which encodes to a user-specified buffer.
31 // TODO(frolv): Right now, only one intermediate size is supported. However,
32 // this can be wasteful, as it requires 4 or 8 bytes of space per nested
33 // message. This can be templated to minimize the overhead.
34 using SizeType = size_t;
36 constexpr Encoder(ByteSpan buffer,
37 std::span<SizeType*> locations,
38 std::span<SizeType*> stack)
40 cursor_(buffer.data()),
41 blob_locations_(locations),
45 encode_status_(OkStatus()) {}
47 // Disallow copy/assign to avoid confusion about who owns the buffer.
48 Encoder(const Encoder& other) = delete;
49 Encoder& operator=(const Encoder& other) = delete;
51 // Per the protobuf specification, valid field numbers range between 1 and
52 // 2**29 - 1, inclusive. The numbers 19000-19999 are reserved for internal
54 constexpr static uint32_t kMaxFieldNumber = (1u << 29) - 1;
55 constexpr static uint32_t kFirstReservedNumber = 19000;
56 constexpr static uint32_t kLastReservedNumber = 19999;
58 // Writes a proto uint32 key-value pair.
59 Status WriteUint32(uint32_t field_number, uint32_t value) {
60 return WriteUint64(field_number, value);
63 // Writes a repeated uint32 using packed encoding.
64 Status WritePackedUint32(uint32_t field_number,
65 std::span<const uint32_t> values) {
66 return WritePackedVarints(field_number, values, /*zigzag=*/false);
69 // Writes a proto uint64 key-value pair.
70 Status WriteUint64(uint32_t field_number, uint64_t value);
72 // Writes a repeated uint64 using packed encoding.
73 Status WritePackedUint64(uint64_t field_number,
74 std::span<const uint64_t> values) {
75 return WritePackedVarints(field_number, values, /*zigzag=*/false);
78 // Writes a proto int32 key-value pair.
79 Status WriteInt32(uint32_t field_number, int32_t value) {
80 return WriteUint64(field_number, value);
83 // Writes a repeated int32 using packed encoding.
84 Status WritePackedInt32(uint32_t field_number,
85 std::span<const int32_t> values) {
86 return WritePackedVarints(
88 std::span(reinterpret_cast<const uint32_t*>(values.data()),
93 // Writes a proto int64 key-value pair.
94 Status WriteInt64(uint32_t field_number, int64_t value) {
95 return WriteUint64(field_number, value);
98 // Writes a repeated int64 using packed encoding.
99 Status WritePackedInt64(uint32_t field_number,
100 std::span<const int64_t> values) {
101 return WritePackedVarints(
103 std::span(reinterpret_cast<const uint64_t*>(values.data()),
108 // Writes a proto sint32 key-value pair.
109 Status WriteSint32(uint32_t field_number, int32_t value) {
110 return WriteUint64(field_number, varint::ZigZagEncode(value));
113 // Writes a repeated sint32 using packed encoding.
114 Status WritePackedSint32(uint32_t field_number,
115 std::span<const int32_t> values) {
116 return WritePackedVarints(
118 std::span(reinterpret_cast<const uint32_t*>(values.data()),
123 // Writes a proto sint64 key-value pair.
124 Status WriteSint64(uint32_t field_number, int64_t value) {
125 return WriteUint64(field_number, varint::ZigZagEncode(value));
128 // Writes a repeated sint64 using packed encoding.
129 Status WritePackedSint64(uint32_t field_number,
130 std::span<const int64_t> values) {
131 return WritePackedVarints(
133 std::span(reinterpret_cast<const uint64_t*>(values.data()),
138 // Writes a proto bool key-value pair.
139 Status WriteBool(uint32_t field_number, bool value) {
140 return WriteUint32(field_number, static_cast<uint32_t>(value));
143 // Writes a proto fixed32 key-value pair.
144 Status WriteFixed32(uint32_t field_number, uint32_t value) {
145 std::byte* original_cursor = cursor_;
146 WriteFieldKey(field_number, WireType::kFixed32);
147 Status status = WriteRawBytes(value);
148 IncreaseParentSize(cursor_ - original_cursor);
152 // Writes a repeated fixed32 field using packed encoding.
153 Status WritePackedFixed32(uint32_t field_number,
154 std::span<const uint32_t> values) {
155 return WriteBytes(field_number, std::as_bytes(values));
158 // Writes a proto fixed64 key-value pair.
159 Status WriteFixed64(uint32_t field_number, uint64_t value) {
160 std::byte* original_cursor = cursor_;
161 WriteFieldKey(field_number, WireType::kFixed64);
162 Status status = WriteRawBytes(value);
163 IncreaseParentSize(cursor_ - original_cursor);
167 // Writes a repeated fixed64 field using packed encoding.
168 Status WritePackedFixed64(uint32_t field_number,
169 std::span<const uint64_t> values) {
170 return WriteBytes(field_number, std::as_bytes(values));
173 // Writes a proto sfixed32 key-value pair.
174 Status WriteSfixed32(uint32_t field_number, int32_t value) {
175 return WriteFixed32(field_number, static_cast<uint32_t>(value));
178 // Writes a repeated sfixed32 field using packed encoding.
179 Status WritePackedSfixed32(uint32_t field_number,
180 std::span<const int32_t> values) {
181 return WriteBytes(field_number, std::as_bytes(values));
184 // Writes a proto sfixed64 key-value pair.
185 Status WriteSfixed64(uint32_t field_number, int64_t value) {
186 return WriteFixed64(field_number, static_cast<uint64_t>(value));
189 // Writes a repeated sfixed64 field using packed encoding.
190 Status WritePackedSfixed64(uint32_t field_number,
191 std::span<const int64_t> values) {
192 return WriteBytes(field_number, std::as_bytes(values));
195 // Writes a proto float key-value pair.
196 Status WriteFloat(uint32_t field_number, float value) {
197 static_assert(sizeof(float) == sizeof(uint32_t),
198 "Float and uint32_t are not the same size");
199 std::byte* original_cursor = cursor_;
200 WriteFieldKey(field_number, WireType::kFixed32);
201 Status status = WriteRawBytes(value);
202 IncreaseParentSize(cursor_ - original_cursor);
206 // Writes a repeated float field using packed encoding.
207 Status WritePackedFloat(uint32_t field_number,
208 std::span<const float> values) {
209 return WriteBytes(field_number, std::as_bytes(values));
212 // Writes a proto double key-value pair.
213 Status WriteDouble(uint32_t field_number, double value) {
214 static_assert(sizeof(double) == sizeof(uint64_t),
215 "Double and uint64_t are not the same size");
216 std::byte* original_cursor = cursor_;
217 WriteFieldKey(field_number, WireType::kFixed64);
218 Status status = WriteRawBytes(value);
219 IncreaseParentSize(cursor_ - original_cursor);
223 // Writes a repeated double field using packed encoding.
224 Status WritePackedDouble(uint32_t field_number,
225 std::span<const double> values) {
226 return WriteBytes(field_number, std::as_bytes(values));
229 // Writes a proto bytes key-value pair.
230 Status WriteBytes(uint32_t field_number, ConstByteSpan value) {
231 std::byte* original_cursor = cursor_;
232 WriteFieldKey(field_number, WireType::kDelimited);
233 WriteVarint(value.size_bytes());
234 Status status = WriteRawBytes(value.data(), value.size_bytes());
235 IncreaseParentSize(cursor_ - original_cursor);
239 // Writes a proto string key-value pair.
240 Status WriteString(uint32_t field_number, const char* value, size_t size) {
241 return WriteBytes(field_number, std::as_bytes(std::span(value, size)));
244 Status WriteString(uint32_t field_number, const char* value) {
245 return WriteString(field_number, value, strlen(value));
248 // Begins writing a sub-message with a specified field number.
249 Status Push(uint32_t field_number);
251 // Finishes writing a sub-message.
254 // Returns the total encoded size of the proto message.
255 size_t EncodedSize() const { return cursor_ - buffer_.data(); }
257 // Returns the number of bytes remaining in the buffer.
258 size_t RemainingSize() const { return buffer_.size() - EncodedSize(); }
260 // Resets write index to the start of the buffer. This invalidates any spans
261 // obtained from Encode().
263 cursor_ = buffer_.data();
264 encode_status_ = OkStatus();
269 // Runs a final encoding pass over the intermediary data and returns the
270 // encoded protobuf message.
271 Result<ConstByteSpan> Encode();
273 // DEPRECATED. Use Encode() instead.
274 // TODO(frolv): Remove this after all references to it are updated.
275 Status Encode(ConstByteSpan* out) {
276 Result result = Encode();
278 return result.status();
280 *out = result.value();
285 constexpr bool ValidFieldNumber(uint32_t field_number) const {
286 return field_number != 0 && field_number <= kMaxFieldNumber &&
287 !(field_number >= kFirstReservedNumber &&
288 field_number <= kLastReservedNumber);
291 // Encodes the key for a proto field consisting of its number and wire type.
292 Status WriteFieldKey(uint32_t field_number, WireType wire_type) {
293 if (!ValidFieldNumber(field_number)) {
294 encode_status_ = Status::InvalidArgument();
295 return encode_status_;
298 return WriteVarint(MakeKey(field_number, wire_type));
301 Status WriteVarint(uint64_t value);
303 Status WriteZigzagVarint(int64_t value) {
304 return WriteVarint(varint::ZigZagEncode(value));
307 template <typename T>
308 Status WriteRawBytes(const T& value) {
309 return WriteRawBytes(reinterpret_cast<const std::byte*>(&value),
313 Status WriteRawBytes(const std::byte* ptr, size_t size);
315 // Writes a list of varints to the buffer in length-delimited packed encoding.
316 // If zigzag is true, zig-zag encodes each of the varints.
317 template <typename T>
318 Status WritePackedVarints(uint32_t field_number,
321 if (Status status = Push(field_number); !status.ok()) {
325 std::byte* original_cursor = cursor_;
326 for (T value : values) {
328 WriteZigzagVarint(static_cast<std::make_signed_t<T>>(value));
333 IncreaseParentSize(cursor_ - original_cursor);
338 // Adds to the parent proto's size field in the buffer.
339 void IncreaseParentSize(size_t bytes) {
341 *blob_stack_[depth_ - 1] += bytes;
345 // Returns the size of `n` encoded as a varint.
346 size_t VarintSizeBytes(uint64_t n) {
347 size_t size_bytes = 1;
355 // Do the actual (potentially partial) encoding. Also used in Pop().
356 Result<ConstByteSpan> EncodeFrom(size_t blob);
358 // The buffer into which the proto is encoded.
362 // List of pointers to sub-messages' delimiting size fields.
363 std::span<SizeType*> blob_locations_;
366 // Stack of current nested message size locations. Push() operations add a new
367 // entry to this stack and Pop() operations remove one.
368 std::span<SizeType*> blob_stack_;
371 Status encode_status_;
374 // A proto encoder with its own blob stack.
375 template <size_t kMaxNestedDepth = 1, size_t kMaxBlobs = kMaxNestedDepth>
376 class NestedEncoder : public Encoder {
378 NestedEncoder(ByteSpan buffer) : Encoder(buffer, blobs_, stack_) {}
380 // Disallow copy/assign to avoid confusion about who owns the buffer.
381 NestedEncoder(const NestedEncoder& other) = delete;
382 NestedEncoder& operator=(const NestedEncoder& other) = delete;
385 std::array<size_t*, kMaxBlobs> blobs_;
386 std::array<size_t*, kMaxNestedDepth> stack_;
389 // Explicit template argument deduction to hide warnings.
390 NestedEncoder()->NestedEncoder<>;
392 } // namespace pw::protobuf