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