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 <cstdint> 17 18 #include "pw_bluetooth_sapphire/internal/host/common/assert.h" 19 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h" 20 21 namespace bt { 22 23 // Non-templated base class for PacketView to reduce per-instantiation code size 24 // overhead. This could also be instantiated on <size_t HeaderSize> instead of 25 // storing a |header_size_| field, which would instantiate one class per header 26 // size needed for an insignificant time and stack win. 27 // 28 // MutablePacketView methods are included in this class instead of a separate 29 // class to avoid a diamond inheritance hierarchy. 30 class PacketViewBase { 31 public: data()32 BufferView data() const { return buffer_->view(0, size_); } payload_data()33 BufferView payload_data() const { 34 return buffer_->view(header_size(), size_ - header_size()); 35 } 36 size()37 size_t size() const { return size_; } payload_size()38 size_t payload_size() const { 39 PW_CHECK(size() >= header_size()); 40 return size() - header_size(); 41 } 42 43 template <typename PayloadType> payload()44 const PayloadType& payload() const { 45 PW_CHECK(sizeof(PayloadType) <= payload_size()); 46 return *reinterpret_cast<const PayloadType*>(payload_data().data()); 47 } 48 49 // Adjusts the size of this PacketView to match the given |payload_size|. This 50 // is useful when the exact packet size is not known during construction. 51 // 52 // This performs runtime checks to make sure that the underlying buffer is 53 // appropriately sized. Resize(size_t payload_size)54 void Resize(size_t payload_size) { 55 this->set_size(header_size() + payload_size); 56 } 57 58 protected: PacketViewBase(size_t header_size,const ByteBuffer * buffer,size_t payload_size)59 PacketViewBase(size_t header_size, 60 const ByteBuffer* buffer, 61 size_t payload_size) 62 : header_size_(header_size), 63 buffer_(buffer), 64 size_(header_size_ + payload_size) { 65 PW_CHECK(buffer_); 66 PW_CHECK(buffer_->size() >= size_, 67 "view size %zu exceeds buffer size %zu", 68 size_, 69 buffer_->size()); 70 } 71 72 // Default copy ctor is required for PacketView and MutablePacketView to be 73 // copy-constructed, but it should stay protected to avoid upcasting from 74 // causing issues. 75 PacketViewBase(const PacketViewBase&) = default; 76 77 // Assignment disabled because PacketViewBase doesn't know whether |this| and 78 // the assigned parameter are the same type of PacketView<…>. 79 PacketViewBase& operator=(const PacketViewBase&) = delete; 80 set_size(size_t size)81 void set_size(size_t size) { 82 PW_CHECK(buffer_->size() >= size); 83 PW_CHECK(size >= header_size()); 84 size_ = size; 85 } 86 header_size()87 size_t header_size() const { return header_size_; } 88 buffer()89 const ByteBuffer* buffer() const { return buffer_; } 90 91 // Method for MutableBufferView only mutable_data()92 MutableBufferView mutable_data() const { 93 return mutable_buffer()->mutable_view(0, this->size()); 94 } 95 96 // Method for MutableBufferView only mutable_payload_data()97 MutableBufferView mutable_payload_data() const { 98 return mutable_buffer()->mutable_view(header_size(), 99 this->size() - header_size()); 100 } 101 102 // Method for MutableBufferView only mutable_payload_bytes()103 uint8_t* mutable_payload_bytes() const { 104 return this->payload_size() 105 ? mutable_buffer()->mutable_data() + header_size() 106 : nullptr; 107 } 108 109 private: mutable_buffer()110 MutableByteBuffer* mutable_buffer() const { 111 // For use only by MutableBufferView, which is constructed with a 112 // MutableBufferView*. This restores the mutability that is implicitly 113 // upcasted away when stored in this Base class. 114 return const_cast<MutableByteBuffer*>( 115 static_cast<const MutableByteBuffer*>(this->buffer())); 116 } 117 118 const size_t header_size_; 119 const ByteBuffer* const buffer_; 120 size_t size_; 121 }; 122 123 // Base class-template for generic packets that contain a header and a payload. 124 // A PacketView is a light-weight object that operates over a previously 125 // allocated ByteBuffer without taking ownership of it. The PacketView 126 // class-template provides a read-only view over the underlying buffer while 127 // MutablePacketView allows modification of the underlying buffer. 128 // 129 // Example usage: 130 // 131 // // Allocate a buffer 132 // StaticByteBuffer<512> buffer; 133 // 134 // // Receive some data on the buffer. 135 // foo::WriteMyPacket(buffer.mutable_data(), ...); 136 // 137 // // Read packet header contents: 138 // struct MyHeaderType { 139 // uint8_t field0; 140 // }; 141 // 142 // PacketView<MyHeaderType> packet(&buffer, 0); 143 // std::cout << "My header field is: " << packet.header().field0; 144 // 145 // // If the packet has an expected payload size, pass that into the 146 // // constructor: 147 // struct MyPayloadType { 148 // uint8_t byte_field; 149 // uint16_t uint16_field; 150 // uint8_t array_field[]; 151 // } __attribute__((packed)); 152 // 153 // MutablePacketView<MyHeaderType> packet(&buffer, sizeof(MyPayloadType) + 2); 154 // packet.mutable_payload<MyPayloadType>().byte_field = 0xFF; 155 // packet.mutable_payload<MyPayloadType>().uint16_field = 0xFFFF; 156 // packet.mutable_payload<MyPayloadType>().array_field[0] = 0x00; 157 // packet.mutable_payload<MyPayloadType>().array_field[1] = 0x01; 158 // 159 // MutablePacketView allows itself to be resized at any time. This is useful 160 // when the complete packet payload is unknown prior to reading the header 161 // contents. For example: 162 // 163 // MutablePacketView<MyHeaderType view(&buffer, my_max_payload_length); 164 // view.mutable_data().Write(data); 165 // view.Resize(view.header().payload_size); 166 template <typename HeaderType> 167 class PacketView : public PacketViewBase { 168 public: 169 // Initializes this Packet to operate over |buffer|. |payload_size| is the 170 // size of the packet payload not including the packet header. A 171 // |payload_size| value of 0 indicates that the packet contains no payload. 172 explicit PacketView(const ByteBuffer* buffer, size_t payload_size = 0u) PacketViewBase(sizeof (HeaderType),buffer,payload_size)173 : PacketViewBase(sizeof(HeaderType), buffer, payload_size) {} 174 header()175 HeaderType header() const { return buffer()->template To<HeaderType>(); } 176 }; 177 178 template <typename HeaderType> 179 class MutablePacketView : public PacketView<HeaderType> { 180 public: 181 explicit MutablePacketView(MutableByteBuffer* buffer, 182 size_t payload_size = 0u) 183 : PacketView<HeaderType>(buffer, payload_size) {} 184 185 using PacketViewBase::mutable_data; 186 using PacketViewBase::mutable_payload_bytes; 187 using PacketViewBase::mutable_payload_data; 188 mutable_header()189 HeaderType* mutable_header() const { 190 return reinterpret_cast<HeaderType*>(mutable_data().mutable_data()); 191 } 192 193 template <typename PayloadType> mutable_payload()194 PayloadType* mutable_payload() const { 195 PW_CHECK(sizeof(PayloadType) <= this->payload_size()); 196 return reinterpret_cast<PayloadType*>(mutable_payload_bytes()); 197 } 198 }; 199 200 } // namespace bt 201