| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef GARNET_DRIVERS_BLUETOOTH_LIB_COMMON_BYTE_BUFFER_H_ |
| #define GARNET_DRIVERS_BLUETOOTH_LIB_COMMON_BYTE_BUFFER_H_ |
| |
| #include <array> |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| |
| #include <zircon/assert.h> |
| #include <zircon/syscalls.h> |
| |
| #include "lib/fxl/macros.h" |
| #include "lib/fxl/strings/string_view.h" |
| |
| namespace btlib { |
| namespace common { |
| |
| class BufferView; |
| class MutableBufferView; |
| class MutableByteBuffer; |
| |
| // Interface for buffer implementations with various allocation schemes. |
| class ByteBuffer { |
| public: |
| using const_iterator = const uint8_t*; |
| using iterator = const_iterator; |
| |
| virtual ~ByteBuffer() = default; |
| |
| // Returns a pointer to the beginning of this buffer. The return value is |
| // undefined if the buffer has size 0. |
| virtual const uint8_t* data() const = 0; |
| |
| // Returns the number of bytes contained in this packet. |
| virtual size_t size() const = 0; |
| |
| // Returns a BufferView that points to the region of this buffer starting at |
| // |pos| of |size| bytes. If |size| is larger than the size of this BufferView |
| // then the returned region will contain all bytes in this buffer starting at |
| // |pos|. |
| // |
| // For example: |
| // |
| // // Get a view of all of |my_buffer|. |
| // const BufferView view = my_buffer.view(); |
| // |
| // // Get a view of the first 5 bytes in |my_buffer| (assuming |my_buffer| is |
| // // large enough). |
| // view = my_buffer.view(0, 5); |
| // |
| // // Get a view of |my_buffer| starting at the second byte. |
| // view = my_buffer.view(2); |
| // |
| // |
| // WARNING: |
| // |
| // A BufferView is only valid as long as the buffer that it points to is |
| // valid. Care should be taken to ensure that a BufferView does not outlive |
| // its backing buffer. |
| const BufferView view( |
| size_t pos = 0, |
| size_t size = std::numeric_limits<std::size_t>::max()) const; |
| |
| // Copies |size| bytes of this buffer into |out_buffer| starting at offset |
| // |pos| and returns the number of bytes that were copied. |out_buffer| must |
| // be large enough to accomodate the result of this operation. |
| size_t Copy(MutableByteBuffer* out_buffer, |
| size_t pos = 0, |
| size_t size = std::numeric_limits<std::size_t>::max()) const; |
| |
| // Iterator functions. |
| iterator begin() const { return cbegin(); } |
| iterator end() const { return cend(); } |
| virtual const_iterator cbegin() const = 0; |
| virtual const_iterator cend() const = 0; |
| |
| // Read-only random access operator. |
| inline const uint8_t& operator[](size_t pos) const { |
| ZX_ASSERT_MSG(pos < size(), "invalid offset (pos = %zu)", pos); |
| return data()[pos]; |
| } |
| |
| // Converts the underlying buffer to the given type with bounds checking. The |
| // buffer is allowed to be larger than T. |
| template <typename T> |
| const T& As() const { |
| ZX_ASSERT(size() >= sizeof(T)); |
| return *reinterpret_cast<const T*>(data()); |
| } |
| |
| bool operator==(const ByteBuffer& other) const { |
| if (size() != other.size()) { |
| return false; |
| } |
| return (memcmp(data(), other.data(), size()) == 0); |
| } |
| |
| // Returns the contents of this buffer as a C++ string-like object without |
| // copying its contents. |
| fxl::StringView AsString() const; |
| |
| // Returns the contents of this buffer as a C++ string after copying its |
| // contents. |
| std::string ToString() const; |
| }; |
| |
| using ByteBufferPtr = std::unique_ptr<ByteBuffer>; |
| |
| // Mutable extension to the ByteBuffer interface. This provides methods that |
| // allows durect mutable access to the underlying buffer. |
| class MutableByteBuffer : public ByteBuffer { |
| public: |
| ~MutableByteBuffer() override = default; |
| |
| // Returns a pointer to the beginning of this buffer. The return value is |
| // undefined if the buffer has size 0. |
| virtual uint8_t* mutable_data() = 0; |
| |
| // Random access operator that allows mutations. |
| inline uint8_t& operator[](size_t pos) { |
| ZX_ASSERT_MSG(pos < size(), "invalid offset (pos = %zu)", pos); |
| return mutable_data()[pos]; |
| } |
| |
| // Writes the contents of |data| into this buffer starting at |pos|. |
| inline void Write(const ByteBuffer& data, size_t pos = 0) { |
| Write(data.data(), data.size(), pos); |
| } |
| |
| // Writes |size| octets of data starting from |data| into this buffer starting |
| // at |pos|. |data| must point to a valid piece of memory if |size| is |
| // non-zero. If |size| is zero, then this operation is a NOP. |
| void Write(const uint8_t* data, size_t size, size_t pos = 0); |
| |
| // Writes the byte interpretation of |data| at |pos|, overwriting the octets |
| // from pos to pos + sizeof(T). |
| // There must be enough space in the buffer to write T. |
| // If T is an array of known bounds, the entire array will be written. |
| template <typename T> |
| void WriteObj(const T& data, size_t pos = 0) { |
| static_assert(!std::is_pointer<T>::value, |
| "Pointer passed to WriteObj, deref or use Write"); |
| Write(reinterpret_cast<const uint8_t*>(&data), sizeof(T), pos); |
| } |
| |
| // Behaves exactly like ByteBuffer::View but returns the result in a |
| // MutableBufferView instead. |
| // |
| // WARNING: |
| // |
| // A BufferView is only valid as long as the buffer that it points to is |
| // valid. Care should be taken to ensure that a BufferView does not outlive |
| // its backing buffer. |
| MutableBufferView mutable_view( |
| size_t pos = 0, |
| size_t size = std::numeric_limits<std::size_t>::max()); |
| |
| // Sets the contents of the buffer to 0s. |
| void SetToZeros() { Fill(0); } |
| |
| // Fills the contents of the buffer with random bytes. |
| void FillWithRandomBytes(); |
| |
| // Fills the contents of the buffer with the given value. |
| virtual void Fill(uint8_t value) = 0; |
| }; |
| |
| using MutableByteBufferPtr = std::unique_ptr<MutableByteBuffer>; |
| |
| // A ByteBuffer with static storage duration. Instances of this class are |
| // copyable. Due to the static buffer storage duration, move semantics work the |
| // same way as copy semantics, i.e. moving an instance will copy the buffer |
| // contents. |
| template <size_t BufferSize> |
| class StaticByteBuffer : public MutableByteBuffer { |
| public: |
| StaticByteBuffer() { |
| static_assert(BufferSize, "|BufferSize| must be non-zero"); |
| } |
| ~StaticByteBuffer() override = default; |
| |
| // Variadic template constructor to initialize a StaticByteBuffer using an |
| // initializer_list e.g.: |
| // |
| // StaticByteBuffer<3> foo{0x00, 0x01, 0x02}; |
| // StaticByteBuffer<3> bar({0x00, 0x01, 0x02}); |
| // |
| template <typename... T> |
| StaticByteBuffer(T... bytes) : buffer_{{static_cast<uint8_t>(bytes)...}} { |
| static_assert(BufferSize, "|BufferSize| must be non-zero"); |
| static_assert(BufferSize == sizeof...(T), |
| "|BufferSize| must match initializer list count"); |
| } |
| |
| // ByteBuffer overrides |
| const uint8_t* data() const override { return buffer_.data(); } |
| size_t size() const override { return buffer_.size(); } |
| const_iterator cbegin() const override { return buffer_.cbegin(); } |
| const_iterator cend() const override { return buffer_.cend(); } |
| |
| // MutableByteBuffer overrides: |
| uint8_t* mutable_data() override { return buffer_.data(); } |
| void Fill(uint8_t value) override { buffer_.fill(value); } |
| |
| private: |
| std::array<uint8_t, BufferSize> buffer_; |
| }; |
| |
| // Wrapper for the variadic template StaticByteBuffer constructor that deduces |
| // the value of the |BufferSize| template parameter from the given input. This |
| // way one can construct a StaticByteBuffer without hard-coding the size of the |
| // buffer like so: |
| // |
| // auto buffer = common::CreateStaticByteBuffer(0x01, 0x02, 0x03); |
| // |
| template <typename... T> |
| StaticByteBuffer<sizeof...(T)> CreateStaticByteBuffer(T... bytes) { |
| return StaticByteBuffer<sizeof...(T)>{bytes...}; |
| } |
| |
| // A ByteBuffer with dynamic storage duration. The underlying buffer is |
| // allocated using malloc. Instances of this class are move-only. |
| class DynamicByteBuffer : public MutableByteBuffer { |
| public: |
| // The default constructor creates an empty buffer with size 0. |
| DynamicByteBuffer(); |
| ~DynamicByteBuffer() override = default; |
| |
| // Allocates a new buffer with |buffer_size| bytes. |
| explicit DynamicByteBuffer(size_t buffer_size); |
| |
| // Copies the contents of |buffer|. |
| explicit DynamicByteBuffer(const ByteBuffer& buffer); |
| |
| // Takes ownership of |buffer| and avoids allocating a new buffer. Since this |
| // constructor performs a simple assignment, the caller must make sure that |
| // the buffer pointed to by |buffer| actually contains |buffer_size| bytes. |
| DynamicByteBuffer(size_t buffer_size, std::unique_ptr<uint8_t[]> buffer); |
| |
| // Move constructor and assignment operator |
| DynamicByteBuffer(DynamicByteBuffer&& other); |
| DynamicByteBuffer& operator=(DynamicByteBuffer&& other); |
| |
| // ByteBuffer overrides: |
| const uint8_t* data() const override; |
| size_t size() const override; |
| const_iterator cbegin() const override; |
| const_iterator cend() const override; |
| |
| // MutableByteBuffer overrides: |
| uint8_t* mutable_data() override; |
| void Fill(uint8_t value) override; |
| |
| private: |
| // Pointer to the underlying buffer, which is owned and managed by us. |
| size_t buffer_size_; |
| std::unique_ptr<uint8_t[]> buffer_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(DynamicByteBuffer); |
| }; |
| |
| // A ByteBuffer that does not own the memory that it points to but rather |
| // provides an immutable view over it. |
| // |
| // WARNING: |
| // |
| // A BufferView is only valid as long as the buffer that it points to is |
| // valid. Care should be taken to ensure that a BufferView does not outlive |
| // its backing buffer. |
| class BufferView final : public ByteBuffer { |
| public: |
| BufferView(const void* bytes, size_t size); |
| ~BufferView() override = default; |
| |
| explicit BufferView(const ByteBuffer& buffer, |
| size_t size = std::numeric_limits<std::size_t>::max()); |
| explicit BufferView(const fxl::StringView& string); |
| |
| // The default constructor initializes this to an empty buffer. |
| BufferView(); |
| |
| // ByteBuffer overrides: |
| const uint8_t* data() const override; |
| size_t size() const override; |
| const_iterator cbegin() const override; |
| const_iterator cend() const override; |
| |
| private: |
| size_t size_; |
| const uint8_t* bytes_; |
| }; |
| |
| // A ByteBuffer that does not own the memory that it points to but rather |
| // provides a mutable view over it. |
| // |
| // WARNING: |
| // |
| // A BufferView is only valid as long as the buffer that it points to is |
| // valid. Care should be taken to ensure that a BufferView does not outlive |
| // its backing buffer. |
| class MutableBufferView final : public MutableByteBuffer { |
| public: |
| explicit MutableBufferView(MutableByteBuffer* buffer); |
| MutableBufferView(void* bytes, size_t size); |
| ~MutableBufferView() override = default; |
| |
| // The default constructor initializes this to an empty buffer. |
| MutableBufferView(); |
| |
| // ByteBuffer overrides: |
| const uint8_t* data() const override; |
| size_t size() const override; |
| const_iterator cbegin() const override; |
| const_iterator cend() const override; |
| |
| // MutableByteBuffer overrides: |
| uint8_t* mutable_data() override; |
| void Fill(uint8_t value) override; |
| |
| private: |
| size_t size_; |
| uint8_t* bytes_; |
| }; |
| |
| } // namespace common |
| } // namespace btlib |
| |
| #endif // GARNET_DRIVERS_BLUETOOTH_LIB_COMMON_BYTE_BUFFER_H_ |