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