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: A BufferView is only valid as long as the buffer that it points to
75   // is valid. Care should be taken to ensure that a BufferView does not outlive
76   // its backing buffer.
77   BufferView view(size_t pos = 0,
78                   size_t size = std::numeric_limits<std::size_t>::max()) const;
79 
80   // Same as view(), but returns a span instead of a BufferView.
81   pw::span<const std::byte> subspan(
82       size_t pos = 0,
83       size_t size = std::numeric_limits<std::size_t>::max()) const;
84 
85   // Copies all bytes of this buffer into |out_buffer|. |out_buffer| must be
86   // large enough to accommodate the result of this operation.
87   void Copy(MutableByteBuffer* out_buffer) const;
88 
89   // Copies |size| bytes of this buffer into |out_buffer| starting at offset
90   // |pos|. |out_buffer| must be large enough to accommodate the result of this
91   // operation.
92   void Copy(MutableByteBuffer* out_buffer, size_t pos, size_t size) const;
93 
94   // Creates a new std::string that contains a printable representation of a
95   // range of this buffer starting at |pos|. The string is checked to see if it
96   // is UTF-8. If not, each byte in the range to be converted is checked to see
97   // if it is printable ASCII. If so, the character is used as is. If not, it is
98   // replaced by '.'. The returned std::string will have size |size| + 1 to fit
99   // a terminating '\0'.
100   std::string Printable(size_t pos, size_t size) const;
101 
102   // Iterator functions.
begin()103   iterator begin() const { return cbegin(); }
end()104   iterator end() const { return cend(); }
105   virtual const_iterator cbegin() const = 0;
106   virtual const_iterator cend() const = 0;
107 
108   // Read-only random access operator.
109   inline const uint8_t& operator[](size_t pos) const {
110     PW_CHECK(pos < size(), "invalid offset (pos = %zu)", pos);
111     return data()[pos];
112   }
113 
114   // Creates an object of type T with the first sizeof(T) bytes of the buffer as
115   // its representation (per definition at ISO/IEC 14882:2017(E) § 6.9
116   // [basic.types] ¶ 4.4). The user is responsible for checking that the first
117   // sizeof(T) bytes represent a valid instance of T. If T is an array type, the
118   // return value will be a std::array with the same element type and extents.
119   //
120   // This or ReadMember should always be used in place of reinterpret_cast on
121   // raw pointers because of dangerous UB related to object lifetimes and
122   // alignment issues (see fxbug.dev/42123294). Moreover, this will perform
123   // bounds checking on the data being read.
124   template <typename T>
To()125   [[nodiscard]] auto To() const {
126     static_assert(std::is_trivially_copyable_v<T>,
127                   "unsafe to copy representation");
128     static_assert(std::is_default_constructible_v<T>);
129     using OutType = std::remove_cv_t<bt_lib_cpp_type::ToStdArrayT<T>>;
130 
131     // This is value-initialized in order to construct objects that have const
132     // members. The consideration for modifying the object through its
133     // representation even if the constituent types are cv-qualified is based on
134     // the potent rules for memcpy'ing "underlying bytes" at ISO/IEC
135     // 14882:2017(E) § 6.9 [basic.types] ¶ 4.2–4.3.
136     OutType out{};
137     CopyRaw(/*dst_data=*/std::addressof(out),
138             /*dst_capacity=*/sizeof(out),
139             /*src_offset=*/0,
140             /*copy_size=*/sizeof(out));
141     return out;
142   }
143 
144   // Given a pointer to a member of a class, interpret the underlying buffer as
145   // a representation of the class and return a copy of the member, with bounds
146   // checking for reading the representation. Array elements (including
147   // multi-dimensional) will be returned as std::array. The buffer is allowed to
148   // be larger than T. The user is responsible for checking that the first
149   // sizeof(T) bytes represent a valid instance of T.
150   //
151   // Example:
152   //   struct Foo { float bar[3]; int baz; char qux[]; };
153   //   buffer.ReadMember<&Foo::bar>();  // OK, returns std::array<float, 3>
154   //   buffer.ReadMember<&Foo::baz>();  // OK, returns int
155   //   buffer.ReadMember<&Foo::qux>();  // Asserts, use
156   //   ReadMember<&Foo::qux>(index) instead
157   //
158   // This functions similarly to C-style type punning at address
159   //   |buffer.data() + offsetof(Foo, bar)|
160   template <auto PointerToMember>
ReadMember()161   auto ReadMember() const {
162     using ClassT = typename bt_lib_cpp_type::MemberPointerTraits<
163         PointerToMember>::ClassType;
164     PW_CHECK(sizeof(ClassT) <= this->size(),
165              "insufficient buffer (class size: %zu, buffer size: %zu)",
166              sizeof(ClassT),
167              this->size());
168     using MemberT = typename bt_lib_cpp_type::MemberPointerTraits<
169         PointerToMember>::MemberType;
170     if constexpr (std::is_array_v<MemberT>) {
171       static_assert(
172           std::extent_v<MemberT> > 0,
173           "use indexed overload of ReadMember for flexible array members");
174     }
175     using ReturnType = std::remove_cv_t<bt_lib_cpp_type::ToStdArrayT<MemberT>>;
176 
177     // std::array is required to be an aggregate that's list-initialized per
178     // ISO/IEC 14882:2017(E) § 26.3.7.1 [array.overview] ¶ 2, so its layout's
179     // initial run is identical to a raw array.
180     static_assert(sizeof(MemberT) <= sizeof(ReturnType));
181     static_assert(std::is_trivially_copyable_v<MemberT>,
182                   "unsafe to copy representation");
183     static_assert(std::is_trivially_copyable_v<ReturnType>,
184                   "unsafe to copy representation");
185     ReturnType out{};
186     const size_t offset =
187         bt_lib_cpp_type::MemberPointerTraits<PointerToMember>::offset();
188     CopyRaw(/*dst_data=*/std::addressof(out),
189             /*dst_capacity=*/sizeof(out),
190             /*src_offset=*/offset,
191             /*copy_size=*/sizeof(MemberT));
192     return out;
193   }
194 
195   // Given a pointer to an array (or smart array) member of a class, interpret
196   // the underlying buffer as a representation of the class and return a copy of
197   // the member's |index - 1|-th element, with bounds checking for the indexing
198   // and reading representation bytes. Multi-dimensional arrays will return
199   // array elements as std::array. The buffer is allowed to be larger than T.
200   // The user is responsible for checking that the first sizeof(T) bytes
201   // represent a valid instance of T.
202   //
203   // Example:
204   //   struct Foo { float bar[3]; int baz; char qux[]; };
205   //   buffer.ReadMember<&Foo::bar>(2);  // OK
206   //   buffer.ReadMember<&Foo::qux>(3);  // OK, checked against buffer.size()
207   //   buffer.ReadMember<&Foo::bar>(3);  // Asserts because out-of-bounds on
208   //   Foo::bar
209   //
210   // This functions similarly to C-style type punning at address
211   //   |buffer.data() + offsetof(Foo, bar) + index * sizeof(bar[0])|
212   // but performs bounds checking and returns a valid type-punned object.
213   template <auto PointerToMember>
ReadMember(size_t index)214   auto ReadMember(size_t index) const {
215     // From the ReadMember<&Foo::bar>(2) example, ClassT = Foo
216     using ClassT = typename bt_lib_cpp_type::MemberPointerTraits<
217         PointerToMember>::ClassType;
218     PW_CHECK(sizeof(ClassT) <= this->size(),
219              "insufficient buffer (class size: %zu, buffer size: %zu)",
220              sizeof(ClassT),
221              this->size());
222 
223     // From the ReadMember<&Foo::bar>(2) example, MemberT = float[3]
224     using MemberT = typename bt_lib_cpp_type::MemberPointerTraits<
225         PointerToMember>::MemberType;
226     static_assert(std::is_trivially_copyable_v<MemberT>,
227                   "unsafe to copy representation");
228 
229     // From the ReadMember<&Foo::bar>(2) example, MemberAsStdArrayT =
230     // std::array<float, 3>
231     using MemberAsStdArrayT = bt_lib_cpp_type::ToStdArrayT<MemberT>;
232 
233     // Check array bounds
234     constexpr size_t kArraySize = std::tuple_size_v<MemberAsStdArrayT>;
235     const size_t base_offset =
236         bt_lib_cpp_type::MemberPointerTraits<PointerToMember>::offset();
237     if constexpr (kArraySize > 0) {
238       // std::array is required to be an aggregate that's list-initialized per
239       // ISO/IEC 14882:2017(E) § 26.3.7.1 [array.overview] ¶ 2, so we can rely
240       // on the initial run of its layout, but in the technically possible but
241       // unlikely case that it contains additional bytes, we can't use its size
242       // for array indexing calculations.
243       static_assert(sizeof(MemberAsStdArrayT) == sizeof(MemberT));
244       PW_CHECK(index < kArraySize,
245                "index past array bounds (index: %zu, array size: %zu)",
246                index,
247                kArraySize);
248     } else {
249       // Allow flexible array members (at the end of structs) that have zero
250       // length
251       PW_CHECK(base_offset == sizeof(ClassT), "read from zero-length array");
252     }
253 
254     // From the ReadMember<&Foo::bar>(2) example, ElementT = float
255     using ElementT = std::remove_cv_t<typename MemberAsStdArrayT::value_type>;
256     static_assert(std::is_trivially_copyable_v<ElementT>,
257                   "unsafe to copy representation");
258     const size_t offset = base_offset + index * sizeof(ElementT);
259     ElementT element{};
260     CopyRaw(/*dst_data=*/std::addressof(element),
261             /*dst_capacity=*/sizeof(ElementT),
262             /*src_offset=*/offset,
263             /*copy_size=*/sizeof(ElementT));
264     return element;
265   }
266 
267   bool operator==(const ByteBuffer& other) const {
268     if (size() != other.size()) {
269       return false;
270     }
271     return (memcmp(data(), other.data(), size()) == 0);
272   }
273 
274   // Returns the contents of this buffer as a C++ string-like object without
275   // copying its contents.
276   std::string_view AsString() const;
277 
278   // Returns a string in hexadecimal format.
279   std::string AsHexadecimal() const;
280 
281   // Returns the contents of this buffer as a C++ string after copying its
282   // contents.
283   std::string ToString(bool as_hex = false) 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     PW_CHECK(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     PW_CHECK(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: A BufferView is only valid as long as the buffer that it points to
360   // is valid. Care should be taken to ensure that a BufferView does not outlive
361   // its backing buffer.
362   MutableBufferView mutable_view(
363       size_t pos = 0, size_t size = std::numeric_limits<std::size_t>::max());
364 
365   // Same as mutable_view(), but returns a mutable span instead of a
366   // MutableBufferView.
367   pw::span<std::byte> mutable_subspan(
368       size_t pos = 0, size_t size = std::numeric_limits<std::size_t>::max());
369 
370   // Sets the contents of the buffer to 0s.
SetToZeros()371   void SetToZeros() { Fill(0); }
372 
373   // Fills the contents of the buffer with the given value.
374   virtual void Fill(uint8_t value) = 0;
375 };
376 
377 using MutableByteBufferPtr = std::unique_ptr<MutableByteBuffer>;
378 
379 // A ByteBuffer with static storage duration. Instances of this class are
380 // copyable. Due to the static buffer storage duration, move semantics work the
381 // same way as copy semantics, i.e. moving an instance will copy the buffer
382 // contents.
383 template <size_t BufferSize>
384 class StaticByteBuffer : public MutableByteBuffer {
385  public:
386   // Create a buffer of size |BufferSize|. The buffer bytes will be initialized
387   // to 0x00.
StaticByteBuffer()388   StaticByteBuffer() {
389     static_assert(BufferSize, "|BufferSize| must be non-zero");
390   }
391   ~StaticByteBuffer() override = default;
392 
393   // Variadic template constructor to initialize a StaticByteBuffer using a
394   // parameter pack e.g.:
395   //
396   //   StaticByteBuffer foo(0x00, 0x01, 0x02);
397   //   StaticByteBuffer<3> foo(0x00, 0x01, 0x02);
398   //
399   // The class's |BufferSize| template parameter, if explicitly provided, will
400   // be checked against the number of initialization elements provided.
401   //
402   // All types castable to uint8_t can be used without casting (including class
403   // enums) for brevity but care must be taken not to exceed uint8_t range
404   // limits.
405   //
406   //   StaticByteBuffer foo(-257);  // -257 has type int and will likely convert
407   //   to uint8_t{0xff}
408   template <typename... T>
StaticByteBuffer(T...bytes)409   constexpr explicit StaticByteBuffer(T... bytes)
410       : buffer_{{static_cast<uint8_t>(bytes)...}} {
411     static_assert(BufferSize, "|BufferSize| must be non-zero");
412     static_assert(BufferSize == sizeof...(T),
413                   "|BufferSize| must match initializer list count");
414 
415     // Check that arguments are within byte range. Restrict checking to smaller
416     // inputs to limit compile time impact and because clang considers fold
417     // expressions "nested" (i.e. subject to a default 256 depth limit).
418     if constexpr (sizeof...(bytes) <= 256) {
419       constexpr auto is_byte_storable = [](auto value) {
420         if constexpr (sizeof(value) > sizeof(uint8_t)) {
421           return static_cast<std::make_unsigned_t<decltype(value)>>(value) <=
422                  std::numeric_limits<uint8_t>::max();
423         }
424         return true;
425       };
426 
427       // This is a runtime assert because this class was written to work with
428       // non-constant values but most uses of StaticByteBuffer are in tests so
429       // this is an acceptable cost.
430       PW_DASSERT((is_byte_storable(bytes) && ...));
431     }
432   }
433 
434   // ByteBuffer overrides
data()435   const uint8_t* data() const override { return buffer_.data(); }
size()436   size_t size() const override { return buffer_.size(); }
cbegin()437   const_iterator cbegin() const override { return &*buffer_.cbegin(); }
cend()438   const_iterator cend() const override { return &*buffer_.cend(); }
439 
440   // MutableByteBuffer overrides:
mutable_data()441   uint8_t* mutable_data() override { return buffer_.data(); }
Fill(uint8_t value)442   void Fill(uint8_t value) override { buffer_.fill(value); }
443 
444  private:
445   // Value-initialize to 0.
446   std::array<uint8_t, BufferSize> buffer_{};
447 };
448 
449 // Template deduction guide for the |BufferSize| class template parameter using
450 // the number of parameters passed into the templated parameter pack
451 // constructor. This allows |BufferSize| to be omitted when it should be deduced
452 // from the initializer:
453 //
454 //   StaticByteBuffer buffer(0x00, 0x01, 0x02);
455 //
456 template <typename... T>
457 StaticByteBuffer(T... bytes) -> StaticByteBuffer<sizeof...(T)>;
458 
459 // A ByteBuffer with dynamic storage duration. The underlying buffer is
460 // allocated using malloc. Instances of this class are move-only.
461 class DynamicByteBuffer : public MutableByteBuffer {
462  public:
463   // The default constructor creates an empty buffer with size 0.
464   DynamicByteBuffer();
465   ~DynamicByteBuffer() override = default;
466 
467   // Allocates a new buffer with |buffer_size| bytes. The buffer bytes will be
468   // initialized to 0x00.
469   explicit DynamicByteBuffer(size_t buffer_size);
470 
471   // Copies the contents of |buffer|.
472   DynamicByteBuffer(const DynamicByteBuffer& buffer);
473   explicit DynamicByteBuffer(const ByteBuffer& buffer);
474   explicit DynamicByteBuffer(const std::string& buffer);
475 
476   // Takes ownership of |buffer| and avoids allocating a new buffer. Since this
477   // constructor performs a simple assignment, the caller must make sure that
478   // the buffer pointed to by |buffer| actually contains |buffer_size| bytes.
479   DynamicByteBuffer(size_t buffer_size, std::unique_ptr<uint8_t[]> buffer);
480 
481   // Move constructor and assignment operator
482   DynamicByteBuffer(DynamicByteBuffer&& other);
483   DynamicByteBuffer& operator=(DynamicByteBuffer&& other);
484 
485   // Copy assignment is prohibited.
486   DynamicByteBuffer& operator=(const DynamicByteBuffer&) = delete;
487 
488   // ByteBuffer overrides:
489   const uint8_t* data() const override;
490   size_t size() const override;
491   const_iterator cbegin() const override;
492   const_iterator cend() const override;
493 
494   // MutableByteBuffer overrides:
495   uint8_t* mutable_data() override;
496   void Fill(uint8_t value) override;
497 
498   // Allocates a new buffer of the given size and copies all underlying data to
499   // the new buffer. This method is meant only to grow the underlying buffer to
500   // fit in more data. Returns false if the new buffer size is less than the
501   // current buffer size.
502   bool expand(size_t new_buffer_size);
503 
504  private:
505   // Pointer to the underlying buffer, which is owned and managed by us.
506   size_t buffer_size_ = 0u;
507   std::unique_ptr<uint8_t[]> buffer_;
508 };
509 
510 // A ByteBuffer that does not own the memory that it points to but rather
511 // provides an immutable view over it.
512 //
513 // WARNING: A BufferView is only valid as long as the buffer that it points to
514 // is valid. Care should be taken to ensure that a BufferView does not outlive
515 // its backing buffer.
516 class BufferView final : public ByteBuffer {
517  public:
518   BufferView(const void* bytes, size_t size);
519   ~BufferView() override = default;
520 
521   explicit BufferView(const ByteBuffer& buffer,
522                       size_t size = std::numeric_limits<std::size_t>::max());
523   explicit BufferView(std::string_view string);
524   explicit BufferView(const std::vector<uint8_t>& vec);
525   explicit BufferView(pw::span<const std::byte> bytes);
526 
527   // The default constructor initializes this to an empty buffer.
528   BufferView();
529 
530   // ByteBuffer overrides:
531   const uint8_t* data() const override;
532   size_t size() const override;
533   const_iterator cbegin() const override;
534   const_iterator cend() const override;
535 
536  private:
537   size_t size_ = 0u;
538   const uint8_t* bytes_ = nullptr;
539 };
540 
541 // A ByteBuffer that does not own the memory that it points to but rather
542 // provides a mutable view over it.
543 //
544 // WARNING: A BufferView is only valid as long as the buffer that it points to
545 // is valid. Care should be taken to ensure that a BufferView does not outlive
546 // its backing buffer.
547 class MutableBufferView final : public MutableByteBuffer {
548  public:
549   explicit MutableBufferView(MutableByteBuffer* buffer);
550   MutableBufferView(void* bytes, size_t size);
551   ~MutableBufferView() override = default;
552 
553   // The default constructor initializes this to an empty buffer.
554   MutableBufferView();
555 
556   // ByteBuffer overrides:
557   const uint8_t* data() const override;
558   size_t size() const override;
559   const_iterator cbegin() const override;
560   const_iterator cend() const override;
561 
562   // MutableByteBuffer overrides:
563   uint8_t* mutable_data() override;
564   void Fill(uint8_t value) override;
565 
566  private:
567   size_t size_ = 0u;
568   uint8_t* bytes_ = nullptr;
569 };
570 
571 }  // namespace bt
572