1 // Copyright 2023 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 15 #pragma once 16 #include <cpp-type/member_pointer_traits.h> 17 #include <cpp-type/to_std_array.h> 18 19 #include <array> 20 #include <cstdint> 21 #include <cstring> 22 #include <limits> 23 #include <memory> 24 #include <string> 25 #include <string_view> 26 #include <tuple> 27 #include <type_traits> 28 #include <vector> 29 30 #include "pw_bluetooth_sapphire/internal/host/common/assert.h" 31 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 32 #include "pw_span/span.h" 33 34 namespace bt { 35 36 class BufferView; 37 class MutableBufferView; 38 class MutableByteBuffer; 39 40 // Interface for buffer implementations with various allocation schemes. 41 class ByteBuffer { 42 public: 43 using const_iterator = const uint8_t*; 44 using iterator = const_iterator; 45 using value_type = uint8_t; 46 47 virtual ~ByteBuffer() = default; 48 49 // Returns a pointer to the beginning of this buffer. The return value is 50 // undefined if the buffer has size 0. 51 virtual const uint8_t* data() const = 0; 52 53 // Returns the number of bytes contained in this buffer. 54 virtual size_t size() const = 0; 55 56 // Returns a BufferView that points to the region of this buffer starting at 57 // |pos| of |size| bytes. If |size| is larger than the size of this BufferView 58 // then the returned region will contain all bytes in this buffer starting at 59 // |pos|. 60 // 61 // For example: 62 // 63 // // Get a view of all of |my_buffer|. 64 // const BufferView view = my_buffer.view(); 65 // 66 // // Get a view of the first 5 bytes in |my_buffer| (assuming |my_buffer| is 67 // // large enough). 68 // view = my_buffer.view(0, 5); 69 // 70 // // Get a view of |my_buffer| starting at the second byte. 71 // view = my_buffer.view(2); 72 // 73 // 74 // WARNING: 75 // 76 // A BufferView is only valid as long as the buffer that it points to is 77 // valid. Care should be taken to ensure that a BufferView does not outlive 78 // its backing buffer. 79 BufferView view(size_t pos = 0, 80 size_t size = std::numeric_limits<std::size_t>::max()) const; 81 82 // Same as view(), but returns a span instead of a BufferView. 83 pw::span<const std::byte> subspan( 84 size_t pos = 0, 85 size_t size = std::numeric_limits<std::size_t>::max()) const; 86 87 // Copies all bytes of this buffer into |out_buffer|. |out_buffer| must be 88 // large enough to accommodate the result of this operation. 89 void Copy(MutableByteBuffer* out_buffer) const; 90 91 // Copies |size| bytes of this buffer into |out_buffer| starting at offset 92 // |pos|. |out_buffer| must be large enough to accommodate the result of this 93 // operation. 94 void Copy(MutableByteBuffer* out_buffer, size_t pos, size_t size) const; 95 96 // Creates a new std::string that contains a printable representation of a 97 // range of this buffer starting at |pos|. The string is checked to see if it 98 // is UTF-8. If not, each byte in the range to be converted is checked to see 99 // if it is printable ASCII. If so, the character is used as is. If not, it is 100 // replaced by '.'. The returned std::string will have size |size| + 1 to fit 101 // a terminating '\0'. 102 std::string Printable(size_t pos, size_t size) const; 103 104 // Iterator functions. begin()105 iterator begin() const { return cbegin(); } end()106 iterator end() const { return cend(); } 107 virtual const_iterator cbegin() const = 0; 108 virtual const_iterator cend() const = 0; 109 110 // Read-only random access operator. 111 inline const uint8_t& operator[](size_t pos) const { 112 BT_ASSERT_MSG(pos < size(), "invalid offset (pos = %zu)", pos); 113 return data()[pos]; 114 } 115 116 // Creates an object of type T with the first sizeof(T) bytes of the buffer as 117 // its representation (per definition at ISO/IEC 14882:2017(E) § 6.9 118 // [basic.types] ¶ 4.4). The user is responsible for checking that the first 119 // sizeof(T) bytes represent a valid instance of T. If T is an array type, the 120 // return value will be a std::array with the same element type and extents. 121 // 122 // This or ReadMember should always be used in place of reinterpret_cast on 123 // raw pointers because of dangerous UB related to object lifetimes and 124 // alignment issues (see fxbug.dev/42123294). Moreover, this will perform 125 // bounds checking on the data being read. 126 template <typename T> To()127 [[nodiscard]] auto To() const { 128 static_assert(std::is_trivially_copyable_v<T>, 129 "unsafe to copy representation"); 130 static_assert(std::is_default_constructible_v<T>); 131 using OutType = std::remove_cv_t<bt_lib_cpp_type::ToStdArrayT<T>>; 132 133 // This is value-initialized in order to construct objects that have const 134 // members. The consideration for modifying the object through its 135 // representation even if the constituent types are cv-qualified is based on 136 // the potent rules for memcpy'ing "underlying bytes" at ISO/IEC 137 // 14882:2017(E) § 6.9 [basic.types] ¶ 4.2–4.3. 138 OutType out{}; 139 CopyRaw(/*dst_data=*/std::addressof(out), 140 /*dst_capacity=*/sizeof(out), 141 /*src_offset=*/0, 142 /*copy_size=*/sizeof(out)); 143 return out; 144 } 145 146 // Given a pointer to a member of a class, interpret the underlying buffer as 147 // a representation of the class and return a copy of the member, with bounds 148 // checking for reading the representation. Array elements (including 149 // multi-dimensional) will be returned as std::array. The buffer is allowed to 150 // be larger than T. The user is responsible for checking that the first 151 // sizeof(T) bytes represent a valid instance of T. 152 // 153 // Example: 154 // struct Foo { float bar[3]; int baz; char qux[]; }; 155 // buffer.ReadMember<&Foo::bar>(); // OK, returns std::array<float, 3> 156 // buffer.ReadMember<&Foo::baz>(); // OK, returns int 157 // buffer.ReadMember<&Foo::qux>(); // Asserts, use 158 // ReadMember<&Foo::qux>(index) instead 159 // 160 // This functions similarly to C-style type punning at address 161 // |buffer.data() + offsetof(Foo, bar)| 162 template <auto PointerToMember> ReadMember()163 auto ReadMember() const { 164 using ClassT = typename bt_lib_cpp_type::MemberPointerTraits< 165 PointerToMember>::ClassType; 166 BT_ASSERT_MSG(sizeof(ClassT) <= this->size(), 167 "insufficient buffer (class size: %zu, buffer size: %zu)", 168 sizeof(ClassT), 169 this->size()); 170 using MemberT = typename bt_lib_cpp_type::MemberPointerTraits< 171 PointerToMember>::MemberType; 172 if constexpr (std::is_array_v<MemberT>) { 173 static_assert( 174 std::extent_v<MemberT> > 0, 175 "use indexed overload of ReadMember for flexible array members"); 176 } 177 using ReturnType = std::remove_cv_t<bt_lib_cpp_type::ToStdArrayT<MemberT>>; 178 179 // std::array is required to be an aggregate that's list-initialized per 180 // ISO/IEC 14882:2017(E) § 26.3.7.1 [array.overview] ¶ 2, so its layout's 181 // initial run is identical to a raw array. 182 static_assert(sizeof(MemberT) <= sizeof(ReturnType)); 183 static_assert(std::is_trivially_copyable_v<MemberT>, 184 "unsafe to copy representation"); 185 static_assert(std::is_trivially_copyable_v<ReturnType>, 186 "unsafe to copy representation"); 187 ReturnType out{}; 188 const size_t offset = 189 bt_lib_cpp_type::MemberPointerTraits<PointerToMember>::offset(); 190 CopyRaw(/*dst_data=*/std::addressof(out), 191 /*dst_capacity=*/sizeof(out), 192 /*src_offset=*/offset, 193 /*copy_size=*/sizeof(MemberT)); 194 return out; 195 } 196 197 // Given a pointer to an array (or smart array) member of a class, interpret 198 // the underlying buffer as a representation of the class and return a copy of 199 // the member's |index - 1|-th element, with bounds checking for the indexing 200 // and reading representation bytes. Multi-dimensional arrays will return 201 // array elements as std::array. The buffer is allowed to be larger than T. 202 // The user is responsible for checking that the first sizeof(T) bytes 203 // represent a valid instance of T. 204 // 205 // Example: 206 // struct Foo { float bar[3]; int baz; char qux[]; }; 207 // buffer.ReadMember<&Foo::bar>(2); // OK 208 // buffer.ReadMember<&Foo::qux>(3); // OK, checked against buffer.size() 209 // buffer.ReadMember<&Foo::bar>(3); // Asserts because out-of-bounds on 210 // Foo::bar 211 // 212 // This functions similarly to C-style type punning at address 213 // |buffer.data() + offsetof(Foo, bar) + index * sizeof(bar[0])| 214 // but performs bounds checking and returns a valid type-punned object. 215 template <auto PointerToMember> ReadMember(size_t index)216 auto ReadMember(size_t index) const { 217 // From the ReadMember<&Foo::bar>(2) example, ClassT = Foo 218 using ClassT = typename bt_lib_cpp_type::MemberPointerTraits< 219 PointerToMember>::ClassType; 220 BT_ASSERT_MSG(sizeof(ClassT) <= this->size(), 221 "insufficient buffer (class size: %zu, buffer size: %zu)", 222 sizeof(ClassT), 223 this->size()); 224 225 // From the ReadMember<&Foo::bar>(2) example, MemberT = float[3] 226 using MemberT = typename bt_lib_cpp_type::MemberPointerTraits< 227 PointerToMember>::MemberType; 228 static_assert(std::is_trivially_copyable_v<MemberT>, 229 "unsafe to copy representation"); 230 231 // From the ReadMember<&Foo::bar>(2) example, MemberAsStdArrayT = 232 // std::array<float, 3> 233 using MemberAsStdArrayT = bt_lib_cpp_type::ToStdArrayT<MemberT>; 234 235 // Check array bounds 236 constexpr size_t kArraySize = std::tuple_size_v<MemberAsStdArrayT>; 237 const size_t base_offset = 238 bt_lib_cpp_type::MemberPointerTraits<PointerToMember>::offset(); 239 if constexpr (kArraySize > 0) { 240 // std::array is required to be an aggregate that's list-initialized per 241 // ISO/IEC 14882:2017(E) § 26.3.7.1 [array.overview] ¶ 2, so we can rely 242 // on the initial run of its layout, but in the technically possible but 243 // unlikely case that it contains additional bytes, we can't use its size 244 // for array indexing calculations. 245 static_assert(sizeof(MemberAsStdArrayT) == sizeof(MemberT)); 246 BT_ASSERT_MSG(index < kArraySize, 247 "index past array bounds (index: %zu, array size: %zu)", 248 index, 249 kArraySize); 250 } else { 251 // Allow flexible array members (at the end of structs) that have zero 252 // length 253 BT_ASSERT_MSG(base_offset == sizeof(ClassT), 254 "read from zero-length array"); 255 } 256 257 // From the ReadMember<&Foo::bar>(2) example, ElementT = float 258 using ElementT = std::remove_cv_t<typename MemberAsStdArrayT::value_type>; 259 static_assert(std::is_trivially_copyable_v<ElementT>, 260 "unsafe to copy representation"); 261 const size_t offset = base_offset + index * sizeof(ElementT); 262 ElementT element{}; 263 CopyRaw(/*dst_data=*/std::addressof(element), 264 /*dst_capacity=*/sizeof(ElementT), 265 /*src_offset=*/offset, 266 /*copy_size=*/sizeof(ElementT)); 267 return element; 268 } 269 270 bool operator==(const ByteBuffer& other) const { 271 if (size() != other.size()) { 272 return false; 273 } 274 return (memcmp(data(), other.data(), size()) == 0); 275 } 276 277 // Returns the contents of this buffer as a C++ string-like object without 278 // copying its contents. 279 std::string_view AsString() const; 280 281 // Returns the contents of this buffer as a C++ string after copying its 282 // contents. 283 std::string ToString() const; 284 285 // Returns a copy of the contents of this buffer in a std::vector. 286 std::vector<uint8_t> ToVector() const; 287 288 private: 289 void CopyRaw(void* dst_data, 290 size_t dst_capacity, 291 size_t src_offset, 292 size_t copy_size) const; 293 }; 294 295 using ByteBufferPtr = std::unique_ptr<ByteBuffer>; 296 297 // Mutable extension to the ByteBuffer interface. This provides methods that 298 // allows direct mutable access to the underlying buffer. 299 class MutableByteBuffer : public ByteBuffer { 300 public: 301 ~MutableByteBuffer() override = default; 302 303 // Returns a pointer to the beginning of this buffer. The return value is 304 // undefined if the buffer has size 0. 305 virtual uint8_t* mutable_data() = 0; 306 307 // Random access operator that allows mutations. 308 inline uint8_t& operator[](size_t pos) { 309 BT_ASSERT_MSG(pos < size(), "invalid offset (pos = %zu)", pos); 310 return mutable_data()[pos]; 311 } 312 313 // Read-only random access operator. Required because there is no overload 314 // resolution from derived to base classes - without this, |const 315 // MutableByteBuffer|s cannot use operator[]. 316 uint8_t operator[](size_t pos) const { return ByteBuffer::operator[](pos); } 317 318 // Converts the underlying buffer to a mutable reference to the given type, 319 // with bounds checking. The buffer is allowed to be larger than T. The user 320 // is responsible for checking that the first sizeof(T) bytes represents a 321 // valid instance of T. 322 template <typename T> AsMutable()323 T* AsMutable() { 324 static_assert(std::is_trivially_copyable_v<T>); 325 BT_ASSERT(size() >= sizeof(T)); 326 return reinterpret_cast<T*>(mutable_data()); 327 } 328 329 // Writes the contents of |data| into this buffer starting at |pos|. 330 inline void Write(const ByteBuffer& data, size_t pos = 0) { 331 Write(data.data(), data.size(), pos); 332 } 333 334 // Writes |size| octets of data starting from |data| into this buffer starting 335 // at |pos|. |data| must point to a valid piece of memory if |size| is 336 // non-zero. If |size| is zero, then this operation is a NOP. 337 void Write(const uint8_t* data, size_t size, size_t pos = 0); 338 339 // Writes the byte interpretation of |data| at |pos|, overwriting the octets 340 // from pos to pos + sizeof(T). 341 // There must be enough space in the buffer to write T. 342 // If T is an array of known bounds, the entire array will be written. 343 template <typename T> 344 void WriteObj(const T& data, size_t pos = 0) { 345 // ByteBuffers are (mostly?) not TriviallyCopyable, but check this first for 346 // the error to be useful. 347 static_assert(!std::is_base_of_v<ByteBuffer, T>, 348 "ByteBuffer passed to WriteObj; use Write"); 349 static_assert(!std::is_pointer_v<T>, 350 "Pointer passed to WriteObj, deref or use Write"); 351 static_assert(std::is_trivially_copyable_v<T>, 352 "Unsafe to peek byte representation"); 353 Write(reinterpret_cast<const uint8_t*>(&data), sizeof(T), pos); 354 } 355 356 // Behaves exactly like ByteBuffer::View but returns the result in a 357 // MutableBufferView instead. 358 // 359 // WARNING: 360 // 361 // A BufferView is only valid as long as the buffer that it points to is 362 // valid. Care should be taken to ensure that a BufferView does not outlive 363 // its backing buffer. 364 MutableBufferView mutable_view( 365 size_t pos = 0, size_t size = std::numeric_limits<std::size_t>::max()); 366 367 // Same as mutable_view(), but returns a mutable span instead of a 368 // MutableBufferView. 369 pw::span<std::byte> mutable_subspan( 370 size_t pos = 0, size_t size = std::numeric_limits<std::size_t>::max()); 371 372 // Sets the contents of the buffer to 0s. SetToZeros()373 void SetToZeros() { Fill(0); } 374 375 // Fills the contents of the buffer with the given value. 376 virtual void Fill(uint8_t value) = 0; 377 }; 378 379 using MutableByteBufferPtr = std::unique_ptr<MutableByteBuffer>; 380 381 // A ByteBuffer with static storage duration. Instances of this class are 382 // copyable. Due to the static buffer storage duration, move semantics work the 383 // same way as copy semantics, i.e. moving an instance will copy the buffer 384 // contents. 385 template <size_t BufferSize> 386 class StaticByteBuffer : public MutableByteBuffer { 387 public: 388 // Create a buffer of size |BufferSize|. The buffer bytes will be initialized 389 // to 0x00. StaticByteBuffer()390 StaticByteBuffer() { 391 static_assert(BufferSize, "|BufferSize| must be non-zero"); 392 } 393 ~StaticByteBuffer() override = default; 394 395 // Variadic template constructor to initialize a StaticByteBuffer using a 396 // parameter pack e.g.: 397 // 398 // StaticByteBuffer foo(0x00, 0x01, 0x02); 399 // StaticByteBuffer<3> foo(0x00, 0x01, 0x02); 400 // 401 // The class's |BufferSize| template parameter, if explicitly provided, will 402 // be checked against the number of initialization elements provided. 403 // 404 // All types castable to uint8_t can be used without casting (including class 405 // enums) for brevity but care must be taken not to exceed uint8_t range 406 // limits. 407 // 408 // StaticByteBuffer foo(-257); // -257 has type int and will likely convert 409 // to uint8_t{0xff} 410 template <typename... T> StaticByteBuffer(T...bytes)411 constexpr explicit StaticByteBuffer(T... bytes) 412 : buffer_{{static_cast<uint8_t>(bytes)...}} { 413 static_assert(BufferSize, "|BufferSize| must be non-zero"); 414 static_assert(BufferSize == sizeof...(T), 415 "|BufferSize| must match initializer list count"); 416 417 // Check that arguments are within byte range. Restrict checking to smaller 418 // inputs to limit compile time impact and because clang considers fold 419 // expressions "nested" (i.e. subject to a default 256 depth limit). 420 if constexpr (sizeof...(bytes) <= 256) { 421 constexpr auto is_byte_storable = [](auto value) { 422 if constexpr (sizeof(value) > sizeof(uint8_t)) { 423 return static_cast<std::make_unsigned_t<decltype(value)>>(value) <= 424 std::numeric_limits<uint8_t>::max(); 425 } 426 return true; 427 }; 428 429 // This is a runtime assert because this class was written to work with 430 // non-constant values but most uses of StaticByteBuffer are in tests so 431 // this is an acceptable cost. 432 BT_DEBUG_ASSERT((is_byte_storable(bytes) && ...)); 433 } 434 } 435 436 // ByteBuffer overrides data()437 const uint8_t* data() const override { return buffer_.data(); } size()438 size_t size() const override { return buffer_.size(); } cbegin()439 const_iterator cbegin() const override { return buffer_.data(); } cend()440 const_iterator cend() const override { 441 return buffer_.data() + buffer_.size(); 442 } 443 444 // MutableByteBuffer overrides: mutable_data()445 uint8_t* mutable_data() override { return buffer_.data(); } Fill(uint8_t value)446 void Fill(uint8_t value) override { buffer_.fill(value); } 447 448 private: 449 // Value-initialize to 0. 450 std::array<uint8_t, BufferSize> buffer_{}; 451 }; 452 453 // Template deduction guide for the |BufferSize| class template parameter using 454 // the number of parameters passed into the templated parameter pack 455 // constructor. This allows |BufferSize| to be omitted when it should be deduced 456 // from the initializer: 457 // 458 // StaticByteBuffer buffer(0x00, 0x01, 0x02); 459 // 460 template <typename... T> 461 StaticByteBuffer(T... bytes) -> StaticByteBuffer<sizeof...(T)>; 462 463 // A ByteBuffer with dynamic storage duration. The underlying buffer is 464 // allocated using malloc. Instances of this class are move-only. 465 class DynamicByteBuffer : public MutableByteBuffer { 466 public: 467 // The default constructor creates an empty buffer with size 0. 468 DynamicByteBuffer(); 469 ~DynamicByteBuffer() override = default; 470 471 // Allocates a new buffer with |buffer_size| bytes. The buffer bytes will be 472 // initialized to 0x00. 473 explicit DynamicByteBuffer(size_t buffer_size); 474 475 // Copies the contents of |buffer|. 476 explicit DynamicByteBuffer(const ByteBuffer& buffer); 477 DynamicByteBuffer(const DynamicByteBuffer& buffer); 478 // Copies the contensts of |string|. 479 explicit DynamicByteBuffer(const std::string& buffer); 480 481 // Takes ownership of |buffer| and avoids allocating a new buffer. Since this 482 // constructor performs a simple assignment, the caller must make sure that 483 // the buffer pointed to by |buffer| actually contains |buffer_size| bytes. 484 DynamicByteBuffer(size_t buffer_size, std::unique_ptr<uint8_t[]> buffer); 485 486 // Move constructor and assignment operator 487 DynamicByteBuffer(DynamicByteBuffer&& other); 488 DynamicByteBuffer& operator=(DynamicByteBuffer&& other); 489 490 // Copy assignment is prohibited. 491 DynamicByteBuffer& operator=(const DynamicByteBuffer&) = delete; 492 493 // ByteBuffer overrides: 494 const uint8_t* data() const override; 495 size_t size() const override; 496 const_iterator cbegin() const override; 497 const_iterator cend() const override; 498 499 // MutableByteBuffer overrides: 500 uint8_t* mutable_data() override; 501 void Fill(uint8_t value) override; 502 503 private: 504 // Pointer to the underlying buffer, which is owned and managed by us. 505 size_t buffer_size_ = 0u; 506 std::unique_ptr<uint8_t[]> buffer_; 507 }; 508 509 // A ByteBuffer that does not own the memory that it points to but rather 510 // provides an immutable view over it. 511 // 512 // WARNING: 513 // 514 // A BufferView is only valid as long as the buffer that it points to is 515 // valid. Care should be taken to ensure that a BufferView does not outlive 516 // its backing buffer. 517 class BufferView final : public ByteBuffer { 518 public: 519 BufferView(const void* bytes, size_t size); 520 ~BufferView() override = default; 521 522 explicit BufferView(const ByteBuffer& buffer, 523 size_t size = std::numeric_limits<std::size_t>::max()); 524 explicit BufferView(std::string_view string); 525 explicit BufferView(const std::vector<uint8_t>& vec); 526 explicit BufferView(pw::span<const std::byte> bytes); 527 528 // The default constructor initializes this to an empty buffer. 529 BufferView(); 530 531 // ByteBuffer overrides: 532 const uint8_t* data() const override; 533 size_t size() const override; 534 const_iterator cbegin() const override; 535 const_iterator cend() const override; 536 537 private: 538 size_t size_ = 0u; 539 const uint8_t* bytes_ = nullptr; 540 }; 541 542 // A ByteBuffer that does not own the memory that it points to but rather 543 // provides a mutable view over it. 544 // 545 // WARNING: 546 // 547 // A BufferView is only valid as long as the buffer that it points to is 548 // valid. Care should be taken to ensure that a BufferView does not outlive 549 // its backing buffer. 550 class MutableBufferView final : public MutableByteBuffer { 551 public: 552 explicit MutableBufferView(MutableByteBuffer* buffer); 553 MutableBufferView(void* bytes, size_t size); 554 ~MutableBufferView() override = default; 555 556 // The default constructor initializes this to an empty buffer. 557 MutableBufferView(); 558 559 // ByteBuffer overrides: 560 const uint8_t* data() const override; 561 size_t size() const override; 562 const_iterator cbegin() const override; 563 const_iterator cend() const override; 564 565 // MutableByteBuffer overrides: 566 uint8_t* mutable_data() override; 567 void Fill(uint8_t value) override; 568 569 private: 570 size_t size_ = 0u; 571 uint8_t* bytes_ = nullptr; 572 }; 573 574 } // namespace bt 575