blob: 1cedc6046a5e9247970a5feeb42b1c3a9eee1157 [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_PACKET_VIEW_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_PACKET_VIEW_H_
#include <zircon/assert.h>
#include <cstdint>
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
namespace bt {
// Non-templated base class for PacketView to reduce per-instantiation code size overhead. This
// could also be instantiated on <size_t HeaderSize> instead of storing a |header_size_| field,
// which would instantiate one class per header size needed for an insignificant time and stack win.
//
// MutablePacketView methods are included in this class instead of a separate class to avoid a
// diamond inheritance hierarchy.
class PacketViewBase {
public:
BufferView data() const { return buffer_->view(0, size_); }
BufferView payload_data() const { return buffer_->view(header_size(), size_ - header_size()); }
size_t size() const { return size_; }
size_t payload_size() const {
ZX_ASSERT(size() >= header_size());
return size() - header_size();
}
template <typename PayloadType>
const PayloadType& payload() const {
ZX_ASSERT(sizeof(PayloadType) <= payload_size());
return *reinterpret_cast<const PayloadType*>(payload_data().data());
}
// Adjusts the size of this PacketView to match the given |payload_size|. This is useful when the
// exact packet size is not known during construction.
//
// This performs runtime checks to make sure that the underlying buffer is appropriately sized.
void Resize(size_t payload_size) { this->set_size(header_size() + payload_size); }
protected:
PacketViewBase(size_t header_size, const ByteBuffer* buffer, size_t payload_size)
: header_size_(header_size), buffer_(buffer), size_(header_size_ + payload_size) {
ZX_ASSERT(buffer_);
ZX_ASSERT_MSG(buffer_->size() >= size_, "view size %zu exceeds buffer size %zu", size_,
buffer_->size());
}
// Default copy ctor is required for PacketView and MutablePacketView to be copy-constructed, but
// it should stay protected to avoid upcasting from causing issues.
PacketViewBase(const PacketViewBase&) = default;
// Assignment disabled because PacketViewBase doesn't know whether |this| and the assigned
// parameter are the same type of PacketView<…>.
PacketViewBase& operator=(const PacketViewBase&) = delete;
void set_size(size_t size) {
ZX_ASSERT(buffer_->size() >= size);
ZX_ASSERT(size >= header_size());
size_ = size;
}
size_t header_size() const { return header_size_; }
const ByteBuffer* buffer() const { return buffer_; }
// Method for MutableBufferView only
MutableBufferView mutable_data() const { return mutable_buffer()->mutable_view(0, this->size()); }
// Method for MutableBufferView only
MutableBufferView mutable_payload_data() const {
return mutable_buffer()->mutable_view(header_size(), this->size() - header_size());
}
// Method for MutableBufferView only
uint8_t* mutable_payload_bytes() const {
return this->payload_size() ? mutable_buffer()->mutable_data() + header_size() : nullptr;
}
private:
MutableByteBuffer* mutable_buffer() const {
// For use only by MutableBufferView, which is constructed with a MutableBufferView*. This
// restores the mutability that is implicitly upcasted away when stored in this Base class.
return const_cast<MutableByteBuffer*>(static_cast<const MutableByteBuffer*>(this->buffer()));
}
const size_t header_size_;
const ByteBuffer* const buffer_;
size_t size_;
};
// Base class-template for generic packets that contain a header and a payload.
// A PacketView is a light-weight object that operates over a previously
// allocated ByteBuffer without taking ownership of it. The PacketView
// class-template provides a read-only view over the underlying buffer while
// MutablePacketView allows modification of the underlying buffer.
//
// Example usage:
//
// // Allocate a buffer
// StaticByteBuffer<512> buffer;
//
// // Receive some data on the buffer.
// foo::WriteMyPacket(buffer.mutable_data(), ...);
//
// // Read packet header contents:
// struct MyHeaderType {
// uint8_t field0;
// };
//
// PacketView<MyHeaderType> packet(&buffer, 0);
// std::cout << "My header field is: " << packet.header().field0;
//
// // If the packet has an expected payload size, pass that into the
// // constructor:
// struct MyPayloadType {
// uint8_t byte_field;
// uint16_t uint16_field;
// uint8_t array_field[];
// } __PACKED;
//
// MutablePacketView<MyHeaderType> packet(&buffer, sizeof(MyPayloadType) + 2);
// packet.mutable_payload<MyPayloadType>().byte_field = 0xFF;
// packet.mutable_payload<MyPayloadType>().uint16_field = 0xFFFF;
// packet.mutable_payload<MyPayloadType>().array_field[0] = 0x00;
// packet.mutable_payload<MyPayloadType>().array_field[1] = 0x01;
//
// MutablePacketView allows itself to be resized at any time. This is useful
// when the complete packet payload is unknown prior to reading the header
// contents. For example:
//
// MutablePacketView<MyHeaderType view(&buffer, my_max_payload_length);
// view.mutable_data().Write(data);
// view.Resize(view.header().payload_size);
template <typename HeaderType>
class PacketView : public PacketViewBase {
public:
// Initializes this Packet to operate over |buffer|. |payload_size| is the size of the packet
// payload not including the packet header. A |payload_size| value of 0 indicates that the packet
// contains no payload.
explicit PacketView(const ByteBuffer* buffer, size_t payload_size = 0u)
: PacketViewBase(sizeof(HeaderType), buffer, payload_size) {}
HeaderType header() const { return buffer()->template To<HeaderType>(); }
};
template <typename HeaderType>
class MutablePacketView : public PacketView<HeaderType> {
public:
explicit MutablePacketView(MutableByteBuffer* buffer, size_t payload_size = 0u)
: PacketView<HeaderType>(buffer, payload_size) {}
using PacketViewBase::mutable_data;
using PacketViewBase::mutable_payload_bytes;
using PacketViewBase::mutable_payload_data;
HeaderType* mutable_header() const {
return reinterpret_cast<HeaderType*>(mutable_data().mutable_data());
}
template <typename PayloadType>
PayloadType* mutable_payload() const {
ZX_ASSERT(sizeof(PayloadType) <= this->payload_size());
return reinterpret_cast<PayloadType*>(mutable_payload_bytes());
}
};
} // namespace bt
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_PACKET_VIEW_H_