| // 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_ |