xref: /aosp_15_r20/external/pigweed/pw_bytes/public/pw_bytes/byte_builder.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2020 The Pigweed Authors
2 //
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 #pragma once
15 
16 #include <algorithm>
17 #include <array>
18 #include <cstddef>
19 #include <cstring>
20 
21 #include "pw_bytes/bit.h"
22 #include "pw_bytes/endian.h"
23 #include "pw_bytes/span.h"
24 #include "pw_containers/iterator.h"
25 #include "pw_preprocessor/compiler.h"
26 #include "pw_status/status.h"
27 #include "pw_status/status_with_size.h"
28 
29 namespace pw {
30 
31 /// ByteBuilder facilitates building bytes in a fixed-size buffer.
32 /// BytesBuilders never overflow. Status is tracked for each operation and
33 /// an overall status is maintained, which reflects the most recent error.
34 ///
35 /// A ByteBuilder does not own the buffer it writes to. It can be used to write
36 /// bytes to any buffer. The ByteBuffer template class, defined below,
37 /// allocates a buffer alongside a ByteBuilder.
38 class ByteBuilder {
39  public:
40   /// iterator class will allow users of ByteBuilder and ByteBuffer to access
41   /// the data stored in the buffer. It has the functionality of C++'s
42   /// random access iterator.
43   class iterator {
44    public:
45     using difference_type = ptrdiff_t;
46     using value_type = std::byte;
47     using element_type = const std::byte;
48     using pointer = const std::byte*;
49     using reference = const std::byte&;
50     using iterator_category = containers::contiguous_iterator_tag;
51 
52     explicit constexpr iterator(const std::byte* byte_ptr = nullptr)
byte_(byte_ptr)53         : byte_(byte_ptr) {}
54 
55     constexpr iterator& operator++() {
56       byte_ += 1;
57       return *this;
58     }
59 
60     constexpr iterator operator++(int) {
61       iterator previous(byte_);
62       operator++();
63       return previous;
64     }
65 
66     constexpr iterator& operator--() {
67       byte_ -= 1;
68       return *this;
69     }
70 
71     constexpr iterator operator--(int) {
72       iterator previous(byte_);
73       operator--();
74       return previous;
75     }
76 
77     constexpr iterator& operator+=(int n) {
78       byte_ += n;
79       return *this;
80     }
81 
82     constexpr iterator operator+(int n) const { return iterator(byte_ + n); }
83 
84     constexpr iterator& operator-=(int n) { return operator+=(-n); }
85 
86     constexpr iterator operator-(int n) const { return iterator(byte_ - n); }
87 
88     constexpr difference_type operator-(const iterator& rhs) const {
89       return byte_ - rhs.byte_;
90     }
91 
92     constexpr reference operator*() const { return *byte_; }
93 
94     constexpr pointer operator->() const { return byte_; }
95 
96     constexpr reference operator[](int index) const { return byte_[index]; }
97 
98     constexpr bool operator==(const iterator& rhs) const {
99       return byte_ == rhs.byte_;
100     }
101 
102     constexpr bool operator!=(const iterator& rhs) const {
103       return byte_ != rhs.byte_;
104     }
105 
106     constexpr bool operator<(const iterator& rhs) const {
107       return byte_ < rhs.byte_;
108     }
109 
110     constexpr bool operator>(const iterator& rhs) const {
111       return byte_ > rhs.byte_;
112     }
113 
114     constexpr bool operator<=(const iterator& rhs) const {
115       return !operator>(rhs);
116     }
117 
118     constexpr bool operator>=(const iterator& rhs) const {
119       return !operator<(rhs);
120     }
121 
122     /// The Peek methods will retreive ordered (Little/Big Endian) values
123     /// located at the iterator position without moving the iterator forward.
PeekInt8()124     int8_t PeekInt8() const { return static_cast<int8_t>(PeekUint8()); }
125 
PeekUint8()126     uint8_t PeekUint8() const {
127       return bytes::ReadInOrder<uint8_t>(endian::little, byte_);
128     }
129 
130     int16_t PeekInt16(endian order = endian::little) const {
131       return static_cast<int16_t>(PeekUint16(order));
132     }
133 
134     uint16_t PeekUint16(endian order = endian::little) const {
135       return bytes::ReadInOrder<uint16_t>(order, byte_);
136     }
137 
138     int32_t PeekInt32(endian order = endian::little) const {
139       return static_cast<int32_t>(PeekUint32(order));
140     }
141 
142     uint32_t PeekUint32(endian order = endian::little) const {
143       return bytes::ReadInOrder<uint32_t>(order, byte_);
144     }
145 
146     int64_t PeekInt64(endian order = endian::little) const {
147       return static_cast<int64_t>(PeekUint64(order));
148     }
149 
150     uint64_t PeekUint64(endian order = endian::little) const {
151       return bytes::ReadInOrder<uint64_t>(order, byte_);
152     }
153 
154     /// The Read methods will retreive ordered (Little/Big Endian) values
155     /// located at the iterator position and move the iterator forward by
156     /// sizeof(value) positions forward.
ReadInt8()157     int8_t ReadInt8() { return static_cast<int8_t>(ReadUint8()); }
158 
ReadUint8()159     uint8_t ReadUint8() {
160       uint8_t value = bytes::ReadInOrder<uint8_t>(endian::little, byte_);
161       byte_ += 1;
162       return value;
163     }
164 
165     int16_t ReadInt16(endian order = endian::little) {
166       return static_cast<int16_t>(ReadUint16(order));
167     }
168 
169     uint16_t ReadUint16(endian order = endian::little) {
170       uint16_t value = bytes::ReadInOrder<uint16_t>(order, byte_);
171       byte_ += 2;
172       return value;
173     }
174 
175     int32_t ReadInt32(endian order = endian::little) {
176       return static_cast<int32_t>(ReadUint32(order));
177     }
178 
179     uint32_t ReadUint32(endian order = endian::little) {
180       uint32_t value = bytes::ReadInOrder<uint32_t>(order, byte_);
181       byte_ += 4;
182       return value;
183     }
184 
185     int64_t ReadInt64(endian order = endian::little) {
186       return static_cast<int64_t>(ReadUint64(order));
187     }
188 
189     uint64_t ReadUint64(endian order = endian::little) {
190       uint64_t value = bytes::ReadInOrder<uint64_t>(order, byte_);
191       byte_ += 8;
192       return value;
193     }
194 
195    private:
196     const std::byte* byte_;
197   };
198 
199   using element_type = const std::byte;
200   using value_type = std::byte;
201   using pointer = std::byte*;
202   using reference = std::byte&;
203   using iterator = iterator;
204   using const_iterator = iterator;
205 
206   /// Creates an empty ByteBuilder.
ByteBuilder(ByteSpan buffer)207   constexpr ByteBuilder(ByteSpan buffer) : buffer_(buffer), size_(0) {}
208 
209   /// Disallow copy/assign to avoid confusion about where the bytes is actually
210   /// stored. ByteBuffers may be copied into one another.
211   ByteBuilder(const ByteBuilder&) = delete;
212 
213   ByteBuilder& operator=(const ByteBuilder&) = delete;
214 
215   /// Returns the contents of the bytes buffer.
data()216   const std::byte* data() const { return buffer_.data(); }
217 
218   /// Returns the ByteBuilder's status, which reflects the most recent error
219   /// that occurred while updating the bytes. After an update fails, the status
220   /// remains non-OK until it is cleared with clear() or clear_status().
221   ///
222   /// @returns @rst
223   ///
224   /// .. pw-status-codes::
225   ///
226   ///    OK: No errors have occurred.
227   ///
228   ///    RESOURCE_EXHAUSTED: Output to the ``ByteBuilder`` was truncated.
229   ///
230   ///    INVALID_ARGUMENT: ``printf``-style formatting failed.
231   ///
232   ///    OUT_OF_RANGE: An operation outside the buffer was attempted.
233   ///
234   /// @endrst
status()235   Status status() const { return status_; }
236 
237   /// Returns status() and size() as a StatusWithSize.
status_with_size()238   StatusWithSize status_with_size() const {
239     return StatusWithSize(status_, size_);
240   }
241 
242   /// True if status() is OkStatus().
ok()243   bool ok() const { return status_.ok(); }
244 
245   /// True if the bytes builder is empty.
empty()246   bool empty() const { return size() == 0u; }
247 
248   /// Returns the current length of the bytes.
size()249   size_t size() const { return size_; }
250 
251   /// Returns the maximum length of the bytes.
max_size()252   size_t max_size() const { return buffer_.size(); }
253 
254   /// Clears the bytes and resets its error state.
clear()255   void clear() {
256     size_ = 0;
257     status_ = OkStatus();
258   }
259 
260   /// Sets the statuses to OkStatus();
clear_status()261   void clear_status() { status_ = OkStatus(); }
262 
263   /// Appends a single byte. Sets the status to RESOURCE_EXHAUSTED if the
264   /// byte cannot be added because the buffer is full.
push_back(std::byte b)265   void push_back(std::byte b) { append(1, b); }
266 
267   /// Removes the last byte. Sets the status to OUT_OF_RANGE if the buffer
268   /// is empty (in which case the unsigned overflow is intentional).
pop_back()269   void pop_back() PW_NO_SANITIZE("unsigned-integer-overflow") {
270     resize(size() - 1);
271   }
272 
273   /// Root of bytebuffer wrapped in iterator type
begin()274   const_iterator begin() const { return iterator(data()); }
cbegin()275   const_iterator cbegin() const { return begin(); }
276 
277   /// End of bytebuffer wrapped in iterator type
end()278   const_iterator end() const { return iterator(data() + size()); }
cend()279   const_iterator cend() const { return end(); }
280 
281   /// Front and Back C++ container functions
front()282   const std::byte& front() const { return buffer_[0]; }
back()283   const std::byte& back() const { return buffer_[size() - 1]; }
284 
285   /// Appends the provided byte count times.
286   ByteBuilder& append(size_t count, std::byte b);
287 
288   /// Appends count bytes from 'bytes' to the end of the ByteBuilder. If count
289   /// exceeds the remaining space in the ByteBuffer, no bytes will be appended
290   /// and the status is set to RESOURCE_EXHAUSTED.
291   ByteBuilder& append(const void* bytes, size_t count);
292 
293   /// Appends bytes from a byte span that calls the pointer/length version.
append(ConstByteSpan bytes)294   ByteBuilder& append(ConstByteSpan bytes) {
295     return append(bytes.data(), bytes.size());
296   }
297 
298   /// Sets the ByteBuilder's size. This function only truncates; if
299   /// new_size > size(), it sets status to OUT_OF_RANGE and does nothing.
300   void resize(size_t new_size);
301 
302   /// Put methods for inserting different 8-bit ints
PutUint8(uint8_t val)303   ByteBuilder& PutUint8(uint8_t val) { return WriteInOrder(val); }
304 
PutInt8(int8_t val)305   ByteBuilder& PutInt8(int8_t val) { return WriteInOrder(val); }
306 
307   /// Put methods for inserting different 16-bit ints
308   ByteBuilder& PutUint16(uint16_t value, endian order = endian::little) {
309     return WriteInOrder(bytes::ConvertOrderTo(order, value));
310   }
311 
312   ByteBuilder& PutInt16(int16_t value, endian order = endian::little) {
313     return PutUint16(static_cast<uint16_t>(value), order);
314   }
315 
316   /// Put methods for inserting different 32-bit ints
317   ByteBuilder& PutUint32(uint32_t value, endian order = endian::little) {
318     return WriteInOrder(bytes::ConvertOrderTo(order, value));
319   }
320 
321   ByteBuilder& PutInt32(int32_t value, endian order = endian::little) {
322     return PutUint32(static_cast<uint32_t>(value), order);
323   }
324 
325   /// Put methods for inserting different 64-bit ints
326   ByteBuilder& PutUint64(uint64_t value, endian order = endian::little) {
327     return WriteInOrder(bytes::ConvertOrderTo(order, value));
328   }
329 
330   ByteBuilder& PutInt64(int64_t value, endian order = endian::little) {
331     return PutUint64(static_cast<uint64_t>(value), order);
332   }
333 
334  protected:
335   /// Functions to support ByteBuffer copies.
ByteBuilder(const ByteSpan & buffer,const ByteBuilder & other)336   constexpr ByteBuilder(const ByteSpan& buffer, const ByteBuilder& other)
337       : buffer_(buffer), size_(other.size_), status_(other.status_) {}
338 
CopySizeAndStatus(const ByteBuilder & other)339   void CopySizeAndStatus(const ByteBuilder& other) {
340     size_ = other.size_;
341     status_ = other.status_;
342   }
343 
344  private:
345   template <typename T>
WriteInOrder(T value)346   ByteBuilder& WriteInOrder(T value) {
347     return append(&value, sizeof(value));
348   }
349   size_t ResizeForAppend(size_t bytes_to_append);
350 
351   const ByteSpan buffer_;
352 
353   size_t size_;
354   Status status_;
355 };
356 
357 /// ByteBuffers declare a buffer along with a ByteBuilder.
358 template <size_t kSizeBytes>
359 class ByteBuffer : public ByteBuilder {
360  public:
ByteBuffer()361   ByteBuffer() : ByteBuilder(buffer_) {}
362 
363   // Implicit copy constructors are not provided in order to prevent
364   // accidental copies of data when passing around ByteByffers.
365   //
366   // Copy assignment, however, is provided, as it requires the user to
367   // explicitly declare a separate local.
368   ByteBuffer(ByteBuffer& other) = delete;
369 
370   template <size_t kOtherSizeBytes>
371   ByteBuffer& operator=(const ByteBuffer<kOtherSizeBytes>& other) {
372     assign<kOtherSizeBytes>(other);
373     return *this;
374   }
375 
376   ByteBuffer& operator=(const ByteBuffer& other) {
377     assign<kSizeBytes>(other);
378     return *this;
379   }
380 
381   template <size_t kOtherSizeBytes>
assign(const ByteBuffer<kOtherSizeBytes> & other)382   ByteBuffer& assign(const ByteBuffer<kOtherSizeBytes>& other) {
383     static_assert(ByteBuffer<kOtherSizeBytes>::max_size() <= max_size(),
384                   "A ByteBuffer cannot be copied into a smaller buffer");
385     CopySizeAndStatus(other);
386     CopyContents(other);
387     return *this;
388   }
389 
390   /// ByteBuffers are not movable: the underlying data must be copied.
391   ByteBuffer(ByteBuffer&& other) = delete;
392 
393   /// ByteBuffers are not movable: the underlying data must be copied.
394   ByteBuffer& operator=(ByteBuffer&& other) = delete;
395 
396   /// Returns the maximum length of the bytes that can be inserted in the bytes
397   /// buffer.
max_size()398   static constexpr size_t max_size() { return kSizeBytes; }
399 
400   /// Returns a ByteBuffer<kSizeBytes>& instead of a generic ByteBuilder& for
401   /// append calls.
402   template <typename... Args>
append(Args &&...args)403   ByteBuffer& append(Args&&... args) {
404     ByteBuilder::append(std::forward<Args>(args)...);
405     return *this;
406   }
407 
408  private:
409   template <size_t kOtherSize>
CopyContents(const ByteBuffer<kOtherSize> & other)410   void CopyContents(const ByteBuffer<kOtherSize>& other) {
411     std::memcpy(buffer_.data(), other.data(), other.size());
412   }
413 
414   std::array<std::byte, kSizeBytes> buffer_;
415 };
416 
417 constexpr ByteBuilder::iterator operator+(int n, ByteBuilder::iterator it) {
418   return it + n;
419 }
420 
421 }  // namespace pw
422