blob: f85896f04e1c93b22db130c0966e2301233bc125 [file] [log] [blame]
// 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 SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_BYTE_BUFFER_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_BYTE_BUFFER_H_
#include <zircon/assert.h>
#include <zircon/syscalls.h>
#include <array>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <fbl/macros.h>
namespace bt {
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.
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 accommodate 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. The user is responsible for checking that the first sizeof(T) bytes
// represents a valid instance of T.
template <typename T>
const T& As() const {
// std::is_trivial_v would be a stronger guarantee that the buffer contains a valid T object,
// but would disallow casting to types that have useful constructors, which might instead cause
// uninitialized field(s) bugs for data encoding/decoding structs.
static_assert(std::is_trivially_copyable_v<T>, "Can not reinterpret bytes");
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.
std::string_view AsString() const;
// Returns the contents of this buffer as a C++ string after copying its
// contents.
std::string ToString() const;
// Returns a copy of the contents of this buffer in a std::vector.
std::vector<uint8_t> ToVector() const;
};
using ByteBufferPtr = std::unique_ptr<ByteBuffer>;
// Mutable extension to the ByteBuffer interface. This provides methods that
// allows direct 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];
}
// Converts the underlying buffer to a mutable reference to the given type, with bounds checking.
// The buffer is allowed to be larger than T. The user is responsible for checking that the first
// sizeof(T) bytes represents a valid instance of T.
template <typename T>
T& AsMutable() {
static_assert(std::is_trivially_copyable_v<T>, "Can not reinterpret bytes");
ZX_ASSERT(size() >= sizeof(T));
return *reinterpret_cast<T*>(mutable_data());
}
// 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) {
// ByteBuffers are (mostly?) not TriviallyCopyable, but check this first for the error to be
// useful.
static_assert(!std::is_base_of_v<ByteBuffer, T>, "ByteBuffer passed to WriteObj; use Write");
static_assert(!std::is_pointer_v<T>, "Pointer passed to WriteObj, deref or use Write");
static_assert(std::is_trivially_copyable_v<T>, "Unsafe to peek byte representation");
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 a parameter pack e.g.:
//
// StaticByteBuffer<3> foo{0x00, 0x01, 0x02};
// StaticByteBuffer<3> bar({0x00, 0x01, 0x02});
//
// The class's |BufferSize| template parameter, if explicitly provided, will be checked against
// the number of initialization elements provided.
template <typename... T>
explicit 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_;
};
// Template deduction guide for the |BufferSize| class template parameter using the number of
// parameters passed into the templated parameter pack constructor. This allows |BufferSize| to be
// omitted when it should be deduced from the initializer:
//
// StaticByteBuffer buffer(0x00, 0x01, 0x02);
//
template <typename... T>
StaticByteBuffer(T... bytes) -> StaticByteBuffer<sizeof...(T)>;
// 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 = 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);
DynamicByteBuffer(const DynamicByteBuffer& 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);
// Copy assignment is prohibited.
DynamicByteBuffer& operator=(const DynamicByteBuffer&) = delete;
// 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_ = 0u;
std::unique_ptr<uint8_t[]> buffer_;
};
// 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(std::string_view string);
explicit BufferView(const std::vector<uint8_t>& vec);
// 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_ = 0u;
const uint8_t* bytes_ = nullptr;
};
// 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_ = 0u;
uint8_t* bytes_ = nullptr;
};
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_BYTE_BUFFER_H_