blob: 6ca6f6df2721d9aab7272b087e155f7ad939d1ac [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
Alexei Frolovf9ae1892021-04-01 18:24:27 -070017#include <limits>
18
Alexei Frolovbbf164c2019-12-16 12:51:59 -080019namespace pw::protobuf {
20
21Status Encoder::WriteUint64(uint32_t field_number, uint64_t value) {
Alexei Frolov311c6202020-01-06 11:16:20 -080022 std::byte* original_cursor = cursor_;
Alexei Frolovbbf164c2019-12-16 12:51:59 -080023 WriteFieldKey(field_number, WireType::kVarint);
Alexei Frolovf9ae1892021-04-01 18:24:27 -070024 WriteVarint(value);
25 return IncreaseParentSize(cursor_ - original_cursor);
Alexei Frolovbbf164c2019-12-16 12:51:59 -080026}
27
28// Encodes a base-128 varint to the buffer.
Rob Mohr85cf08f2019-12-20 11:54:08 -080029Status Encoder::WriteVarint(uint64_t value) {
Alexei Frolovbbf164c2019-12-16 12:51:59 -080030 if (!encode_status_.ok()) {
31 return encode_status_;
32 }
33
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070034 std::span varint_buf = buffer_.last(RemainingSize());
Alexei Frolovbbf164c2019-12-16 12:51:59 -080035 if (varint_buf.empty()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070036 encode_status_ = Status::ResourceExhausted();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080037 return encode_status_;
38 }
39
Rob Mohr85cf08f2019-12-20 11:54:08 -080040 size_t written = pw::varint::EncodeLittleEndianBase128(value, varint_buf);
Alexei Frolovbbf164c2019-12-16 12:51:59 -080041 if (written == 0) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070042 encode_status_ = Status::ResourceExhausted();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080043 return encode_status_;
44 }
45
46 cursor_ += written;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080047 return OkStatus();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080048}
49
50Status Encoder::WriteRawBytes(const std::byte* ptr, size_t size) {
51 if (!encode_status_.ok()) {
52 return encode_status_;
53 }
54
55 if (size > RemainingSize()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070056 encode_status_ = Status::ResourceExhausted();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080057 return encode_status_;
58 }
59
Alexei Frolov33a1e8f2020-05-26 08:39:32 -070060 // Memmove the value into place as it's possible that it shares the encode
61 // buffer on a memory-constrained system.
62 std::memmove(cursor_, ptr, size);
63
Alexei Frolovbbf164c2019-12-16 12:51:59 -080064 cursor_ += size;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -080065 return OkStatus();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080066}
67
68Status Encoder::Push(uint32_t field_number) {
69 if (!encode_status_.ok()) {
70 return encode_status_;
71 }
72
73 if (blob_count_ == blob_locations_.size() || depth_ == blob_stack_.size()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070074 encode_status_ = Status::ResourceExhausted();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080075 return encode_status_;
76 }
77
78 // Write the key for the nested field.
Alexei Frolov311c6202020-01-06 11:16:20 -080079 std::byte* original_cursor = cursor_;
Alexei Frolovbbf164c2019-12-16 12:51:59 -080080 if (Status status = WriteFieldKey(field_number, WireType::kDelimited);
81 !status.ok()) {
82 encode_status_ = status;
83 return status;
84 }
85
86 if (sizeof(SizeType) > RemainingSize()) {
87 // Rollback if there isn't enough space.
88 cursor_ = original_cursor;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070089 encode_status_ = Status::ResourceExhausted();
Alexei Frolovbbf164c2019-12-16 12:51:59 -080090 return encode_status_;
91 }
92
93 // Update parent size with the written key.
Alexei Frolovf9ae1892021-04-01 18:24:27 -070094 PW_TRY(IncreaseParentSize(cursor_ - original_cursor));
Alexei Frolovbbf164c2019-12-16 12:51:59 -080095
96 union {
Alexei Frolov311c6202020-01-06 11:16:20 -080097 std::byte* cursor;
Alexei Frolovbbf164c2019-12-16 12:51:59 -080098 SizeType* size_cursor;
99 };
100
101 // Create a size entry for the new blob and append it to both the nesting
102 // stack and location list.
103 cursor = cursor_;
104 *size_cursor = 0;
105 blob_locations_[blob_count_++] = size_cursor;
106 blob_stack_[depth_++] = size_cursor;
107
108 cursor_ += sizeof(*size_cursor);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800109 return OkStatus();
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800110}
111
112Status Encoder::Pop() {
113 if (!encode_status_.ok()) {
114 return encode_status_;
115 }
116
117 if (depth_ == 0) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700118 encode_status_ = Status::FailedPrecondition();
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800119 return encode_status_;
120 }
121
122 // Update the parent's size with how much total space the child will take
123 // after its size field is varint encoded.
124 SizeType child_size = *blob_stack_[--depth_];
Alexei Frolovf9ae1892021-04-01 18:24:27 -0700125 PW_TRY(IncreaseParentSize(child_size + VarintSizeBytes(child_size)));
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800126
Paul Mathieua762d172020-12-01 16:52:09 -0800127 // Encode the child
128 if (Status status = EncodeFrom(blob_count_ - 1).status(); !status.ok()) {
129 encode_status_ = status;
130 return encode_status_;
131 }
132 blob_count_--;
133
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800134 return OkStatus();
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800135}
136
Paul Mathieua762d172020-12-01 16:52:09 -0800137Result<ConstByteSpan> Encoder::Encode() { return EncodeFrom(0); }
138
139Result<ConstByteSpan> Encoder::EncodeFrom(size_t blob) {
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800140 if (!encode_status_.ok()) {
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800141 return encode_status_;
142 }
143
Paul Mathieua762d172020-12-01 16:52:09 -0800144 if (blob >= blob_count_) {
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800145 // If there are no nested blobs, the buffer already contains a valid proto.
Alexei Frolov13c7c4f2020-10-02 09:41:01 -0700146 return Result<ConstByteSpan>(buffer_.first(EncodedSize()));
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800147 }
148
149 union {
Alexei Frolov311c6202020-01-06 11:16:20 -0800150 std::byte* read_cursor;
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800151 SizeType* size_cursor;
152 };
153
154 // Starting from the first blob, encode each size field as a varint and
155 // shift all subsequent data downwards.
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800156 size_cursor = blob_locations_[blob];
Alexei Frolov311c6202020-01-06 11:16:20 -0800157 std::byte* write_cursor = read_cursor;
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800158
159 while (read_cursor < cursor_) {
160 SizeType nested_size = *size_cursor;
161
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700162 std::span<std::byte> varint_buf(write_cursor, sizeof(*size_cursor));
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800163 size_t varint_size =
164 pw::varint::EncodeLittleEndianBase128(nested_size, varint_buf);
165
166 // Place the write cursor after the encoded varint and the read cursor at
167 // the location of the next proto field.
168 write_cursor += varint_size;
169 read_cursor += varint_buf.size();
170
171 size_t to_copy;
172
173 if (blob == blob_count_ - 1) {
174 to_copy = cursor_ - read_cursor;
175 } else {
Alexei Frolov311c6202020-01-06 11:16:20 -0800176 std::byte* end = reinterpret_cast<std::byte*>(blob_locations_[blob + 1]);
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800177 to_copy = end - read_cursor;
178 }
179
Alexei Frolov33a1e8f2020-05-26 08:39:32 -0700180 std::memmove(write_cursor, read_cursor, to_copy);
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800181 write_cursor += to_copy;
182 read_cursor += to_copy;
183
184 ++blob;
185 }
186
187 // Point the cursor to the end of the encoded proto.
188 cursor_ = write_cursor;
Alexei Frolov13c7c4f2020-10-02 09:41:01 -0700189 return Result<ConstByteSpan>(buffer_.first(EncodedSize()));
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800190}
191
Alexei Frolovf9ae1892021-04-01 18:24:27 -0700192Status Encoder::IncreaseParentSize(size_t size_bytes) {
193 if (!encode_status_.ok()) {
194 return encode_status_;
195 }
196
197 if (depth_ == 0) {
198 return OkStatus();
199 }
200
201 size_t current_size = *blob_stack_[depth_ - 1];
202
203 constexpr size_t max_size =
204 std::min(varint::MaxValueInBytes(sizeof(SizeType)),
205 static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()));
206
207 if (size_bytes > max_size || current_size > max_size - size_bytes) {
208 encode_status_ = Status::OutOfRange();
209 return encode_status_;
210 }
211
212 *blob_stack_[depth_ - 1] = current_size + size_bytes;
213 return OkStatus();
214}
215
Alexei Frolovbbf164c2019-12-16 12:51:59 -0800216} // namespace pw::protobuf