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 int64_t value = bytes::ReadInOrder<int64_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(). Returns: 221 // 222 // OK if no errors have occurred 223 // RESOURCE_EXHAUSTED if output to the ByteBuilder was truncated 224 // INVALID_ARGUMENT if printf-style formatting failed 225 // OUT_OF_RANGE if an operation outside the buffer was attempted 226 // status()227 Status status() const { return status_; } 228 229 // Returns status() and size() as a StatusWithSize. status_with_size()230 StatusWithSize status_with_size() const { 231 return StatusWithSize(status_, size_); 232 } 233 234 // True if status() is OkStatus(). ok()235 bool ok() const { return status_.ok(); } 236 237 // True if the bytes builder is empty. empty()238 bool empty() const { return size() == 0u; } 239 240 // Returns the current length of the bytes. size()241 size_t size() const { return size_; } 242 243 // Returns the maximum length of the bytes. max_size()244 size_t max_size() const { return buffer_.size(); } 245 246 // Clears the bytes and resets its error state. clear()247 void clear() { 248 size_ = 0; 249 status_ = OkStatus(); 250 } 251 252 // Sets the statuses to OkStatus(); clear_status()253 void clear_status() { status_ = OkStatus(); } 254 255 // Appends a single byte. Sets the status to RESOURCE_EXHAUSTED if the 256 // byte cannot be added because the buffer is full. push_back(std::byte b)257 void push_back(std::byte b) { append(1, b); } 258 259 // Removes the last byte. Sets the status to OUT_OF_RANGE if the buffer 260 // is empty (in which case the unsigned overflow is intentional). pop_back()261 void pop_back() PW_NO_SANITIZE("unsigned-integer-overflow") { 262 resize(size() - 1); 263 } 264 265 // Root of bytebuffer wrapped in iterator type begin()266 const_iterator begin() const { return iterator(data()); } cbegin()267 const_iterator cbegin() const { return begin(); } 268 269 // End of bytebuffer wrapped in iterator type end()270 const_iterator end() const { return iterator(data() + size()); } cend()271 const_iterator cend() const { return end(); } 272 273 // Front and Back C++ container functions front()274 const std::byte& front() const { return buffer_[0]; } back()275 const std::byte& back() const { return buffer_[size() - 1]; } 276 277 // Appends the provided byte count times. 278 ByteBuilder& append(size_t count, std::byte b); 279 280 // Appends count bytes from 'bytes' to the end of the ByteBuilder. If count 281 // exceeds the remaining space in the ByteBuffer, no bytes will be appended 282 // and the status is set to RESOURCE_EXHAUSTED. 283 ByteBuilder& append(const void* bytes, size_t count); 284 285 // Appends bytes from a byte span that calls the pointer/length version. append(ConstByteSpan bytes)286 ByteBuilder& append(ConstByteSpan bytes) { 287 return append(bytes.data(), bytes.size()); 288 } 289 290 // Sets the ByteBuilder's size. This function only truncates; if 291 // new_size > size(), it sets status to OUT_OF_RANGE and does nothing. 292 void resize(size_t new_size); 293 294 // Put methods for inserting different 8-bit ints PutUint8(uint8_t val)295 ByteBuilder& PutUint8(uint8_t val) { return WriteInOrder(val); } 296 PutInt8(int8_t val)297 ByteBuilder& PutInt8(int8_t val) { return WriteInOrder(val); } 298 299 // Put methods for inserting different 16-bit ints 300 ByteBuilder& PutUint16(uint16_t value, endian order = endian::little) { 301 return WriteInOrder(bytes::ConvertOrderTo(order, value)); 302 } 303 304 ByteBuilder& PutInt16(int16_t value, endian order = endian::little) { 305 return PutUint16(static_cast<uint16_t>(value), order); 306 } 307 308 // Put methods for inserting different 32-bit ints 309 ByteBuilder& PutUint32(uint32_t value, endian order = endian::little) { 310 return WriteInOrder(bytes::ConvertOrderTo(order, value)); 311 } 312 313 ByteBuilder& PutInt32(int32_t value, endian order = endian::little) { 314 return PutUint32(static_cast<uint32_t>(value), order); 315 } 316 317 // Put methods for inserting different 64-bit ints 318 ByteBuilder& PutUint64(uint64_t value, endian order = endian::little) { 319 return WriteInOrder(bytes::ConvertOrderTo(order, value)); 320 } 321 322 ByteBuilder& PutInt64(int64_t value, endian order = endian::little) { 323 return PutUint64(static_cast<uint64_t>(value), order); 324 } 325 326 protected: 327 // Functions to support ByteBuffer copies. ByteBuilder(const ByteSpan & buffer,const ByteBuilder & other)328 constexpr ByteBuilder(const ByteSpan& buffer, const ByteBuilder& other) 329 : buffer_(buffer), size_(other.size_), status_(other.status_) {} 330 CopySizeAndStatus(const ByteBuilder & other)331 void CopySizeAndStatus(const ByteBuilder& other) { 332 size_ = other.size_; 333 status_ = other.status_; 334 } 335 336 private: 337 template <typename T> WriteInOrder(T value)338 ByteBuilder& WriteInOrder(T value) { 339 return append(&value, sizeof(value)); 340 } 341 size_t ResizeForAppend(size_t bytes_to_append); 342 343 const ByteSpan buffer_; 344 345 size_t size_; 346 Status status_; 347 }; 348 349 // ByteBuffers declare a buffer along with a ByteBuilder. 350 template <size_t kSizeBytes> 351 class ByteBuffer : public ByteBuilder { 352 public: ByteBuffer()353 ByteBuffer() : ByteBuilder(buffer_) {} 354 355 // ByteBuffers of the same size may be copied and assigned into one another. ByteBuffer(const ByteBuffer & other)356 ByteBuffer(const ByteBuffer& other) : ByteBuilder(buffer_, other) { 357 CopyContents(other); 358 } 359 360 // A smaller ByteBuffer may be copied or assigned into a larger one. 361 template <size_t kOtherSizeBytes> ByteBuffer(const ByteBuffer<kOtherSizeBytes> & other)362 ByteBuffer(const ByteBuffer<kOtherSizeBytes>& other) 363 : ByteBuilder(buffer_, other) { 364 static_assert(ByteBuffer<kOtherSizeBytes>::max_size() <= max_size(), 365 "A ByteBuffer cannot be copied into a smaller buffer"); 366 CopyContents(other); 367 } 368 369 template <size_t kOtherSizeBytes> 370 ByteBuffer& operator=(const ByteBuffer<kOtherSizeBytes>& other) { 371 assign<kOtherSizeBytes>(other); 372 return *this; 373 } 374 375 ByteBuffer& operator=(const ByteBuffer& other) { 376 assign<kSizeBytes>(other); 377 return *this; 378 } 379 380 template <size_t kOtherSizeBytes> assign(const ByteBuffer<kOtherSizeBytes> & other)381 ByteBuffer& assign(const ByteBuffer<kOtherSizeBytes>& other) { 382 static_assert(ByteBuffer<kOtherSizeBytes>::max_size() <= max_size(), 383 "A ByteBuffer cannot be copied into a smaller buffer"); 384 CopySizeAndStatus(other); 385 CopyContents(other); 386 return *this; 387 } 388 389 // Returns the maximum length of the bytes that can be inserted in the bytes 390 // buffer. max_size()391 static constexpr size_t max_size() { return kSizeBytes; } 392 393 // Returns a ByteBuffer<kSizeBytes>& instead of a generic ByteBuilder& for 394 // append calls. 395 template <typename... Args> append(Args &&...args)396 ByteBuffer& append(Args&&... args) { 397 ByteBuilder::append(std::forward<Args>(args)...); 398 return *this; 399 } 400 401 private: 402 template <size_t kOtherSize> CopyContents(const ByteBuffer<kOtherSize> & other)403 void CopyContents(const ByteBuffer<kOtherSize>& other) { 404 std::memcpy(buffer_.data(), other.data(), other.size()); 405 } 406 407 std::array<std::byte, kSizeBytes> buffer_; 408 }; 409 410 constexpr ByteBuilder::iterator operator+(int n, ByteBuilder::iterator it) { 411 return it + n; 412 } 413 414 } // namespace pw 415