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