1 /* 2 * Copyright (c) 2023, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 /** 29 * @file 30 * This file includes definitions for the multiple frame buffer. 31 */ 32 33 #ifndef SPINEL_MULTI_FRAME_BUFFER_HPP_ 34 #define SPINEL_MULTI_FRAME_BUFFER_HPP_ 35 36 #include <assert.h> 37 #include <stdint.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include <openthread/error.h> 42 43 #include "lib/utils/endian.hpp" 44 45 namespace ot { 46 namespace Spinel { 47 48 /** 49 * Defines a frame write pointer. 50 * 51 * Defines the minimum set of APIs used by `Encoder/Decoder` for writing an encoded/decoded frame. It is 52 * simply a wrapper over a pointer into a buffer indicating where next byte should be written. Along with a write 53 * pointer, this class stores a remaining length variable indicating number of remaining bytes that can be written into 54 * the buffer. 55 * 56 * @note This class does NOT define the underlying buffer space or how it is being managed. 57 * 58 * Two template sub-class `FrameBuffer` and `MultiFrameBuffer` are defined which respectively allow storing a single 59 * frame or multiple frames (FIFO queue of frame) in a buffer of a given size. 60 */ 61 class FrameWritePointer 62 { 63 public: 64 /** 65 * Indicates whether there is buffer space available to write @p aWriteLength bytes. 66 * 67 * param[in] aWriteLength Number of bytes to write. 68 * 69 * @retval TRUE Enough buffer space is available to write the requested number of bytes. 70 * @retval FALSE Insufficient buffer space to write the requested number of bytes. 71 */ CanWrite(uint16_t aWriteLength) const72 bool CanWrite(uint16_t aWriteLength) const { return (mRemainingLength >= aWriteLength); } 73 74 /** 75 * Writes a byte into the buffer and updates the write pointer (if space is available). 76 * 77 * @param[in] aByte A byte to be written to the buffer. 78 * 79 * @retval OT_ERROR_NONE Successfully wrote the byte and updated the pointer. 80 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to write the byte. 81 */ WriteByte(uint8_t aByte)82 otError WriteByte(uint8_t aByte) 83 { 84 return CanWrite(sizeof(uint8_t)) ? (*mWritePointer++ = aByte, mRemainingLength--, OT_ERROR_NONE) 85 : OT_ERROR_NO_BUFS; 86 } 87 88 /** 89 * Undoes the last @p aUndoLength writes, removing them from frame. 90 * 91 * @note Caller should ensure that @p aUndoLength is less than or equal to the number of previously written bytes 92 * into the frame. This method does not perform any checks and its behavior is undefined if @p aUndoLength is 93 * larger than the number of bytes previously written into the frame. 94 * 95 * @param[in] aUndoLength Number of bytes to remove (number of last `WriteByte()` calls to undo). 96 */ UndoLastWrites(uint16_t aUndoLength)97 void UndoLastWrites(uint16_t aUndoLength) 98 { 99 mWritePointer -= aUndoLength; 100 mRemainingLength += aUndoLength; 101 } 102 103 protected: FrameWritePointer(void)104 FrameWritePointer(void) 105 : mWritePointer(nullptr) 106 , mRemainingLength(0) 107 { 108 } 109 110 uint8_t *mWritePointer; ///< A pointer to current write position in the buffer. 111 uint16_t mRemainingLength; ///< Number of remaining bytes available to write. 112 }; 113 114 /** 115 * Defines a template frame buffer of a given size for storing a single frame. 116 * 117 * @tparam kSize The size of the frame buffer. 118 */ 119 template <uint16_t kSize> class FrameBuffer : public FrameWritePointer 120 { 121 public: 122 /** 123 * Initializes the `FrameBuffer` object. 124 */ FrameBuffer(void)125 FrameBuffer(void) 126 : FrameWritePointer() 127 { 128 Clear(); 129 } 130 131 /** 132 * Clears the buffer, moving the write pointer to the beginning of the buffer. 133 */ Clear(void)134 void Clear(void) 135 { 136 mWritePointer = mBuffer; 137 mRemainingLength = sizeof(mBuffer); 138 } 139 140 /** 141 * Indicates whether the buffer is empty or contains a frame. 142 * 143 * @retval TRUE Buffer is empty. 144 * @retval FALSE Buffer contains a frame. 145 */ IsEmpty(void) const146 bool IsEmpty(void) const { return (mWritePointer == mBuffer); } 147 148 /** 149 * Gets the length (number of bytes) in the frame. 150 * 151 * @returns The length (number of bytes) in the frame. 152 */ GetLength(void) const153 uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - mBuffer); } 154 155 /** 156 * Gets a pointer to start of the frame. 157 * 158 * @returns A pointer to start of the frame. 159 */ GetFrame(void)160 uint8_t *GetFrame(void) { return mBuffer; } 161 162 private: 163 uint8_t mBuffer[kSize]; 164 }; 165 166 /** 167 * Defines a template frame buffer of a given size for storing multiple frames. 168 * 169 * Unlike `FrameBuffer` class where a single frame can be stored, this class is capable of saving multiple frames 170 * in a FIFO queue format. 171 * 172 * @tparam kSize The total size of the buffer. 173 */ 174 template <uint16_t kSize> class MultiFrameBuffer : public FrameWritePointer 175 { 176 public: 177 /** 178 * Initializes the `MultiFrameBuffer` object. 179 */ MultiFrameBuffer(void)180 MultiFrameBuffer(void) 181 : FrameWritePointer() 182 { 183 Clear(); 184 } 185 186 /** 187 * Clears the buffer, removing current frame and all previously saved frames. 188 * 189 * It moves the write pointer to the beginning of the buffer. 190 */ Clear(void)191 void Clear(void) 192 { 193 mWriteFrameStart = mBuffer; 194 mWritePointer = mBuffer + kHeaderSize; 195 mRemainingLength = kSize - kHeaderSize; 196 197 IgnoreError(SetSkipLength(0)); 198 } 199 200 /** 201 * Indicates whether the current frame (being written) is empty or not. 202 * 203 * @retval TRUE Current frame is empty. 204 * @retval FALSE Current frame is not empty. 205 */ HasFrame(void) const206 bool HasFrame(void) const { return (mWritePointer != GetFrame()); } 207 208 /** 209 * Sets the length (number of bytes) of the current frame being written. 210 * 211 * param[in] aLength The length of current frame. 212 * 213 * @retval OT_ERROR_NONE Successfully set the length of the current frame. 214 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a frame of length @p aLength. 215 */ SetLength(uint16_t aLength)216 otError SetLength(uint16_t aLength) 217 { 218 otError error = OT_ERROR_NO_BUFS; 219 220 if (GetFrame() + aLength <= GetArrayEnd(mBuffer)) 221 { 222 mWritePointer = GetFrame() + aLength; 223 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 224 error = OT_ERROR_NONE; 225 } 226 227 return error; 228 } 229 230 /** 231 * Gets the length (number of bytes) in the current frame being written into the buffer. 232 * 233 * @returns The length (number of bytes) in the frame. 234 */ GetLength(void) const235 uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - GetFrame()); } 236 237 /** 238 * Sets the length (number of bytes) of reserved buffer in front of the current frame being written. 239 * 240 * param[in] aSkipLength The length of reserved buffer. 241 * 242 * @retval OT_ERROR_NONE Successfully set the length of reserved buffer. 243 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a reserved buffer of length @p aLength. 244 */ SetSkipLength(uint16_t aSkipLength)245 otError SetSkipLength(uint16_t aSkipLength) 246 { 247 otError error = OT_ERROR_NO_BUFS; 248 249 if (mWriteFrameStart + kHeaderSize + aSkipLength <= GetArrayEnd(mBuffer)) 250 { 251 Lib::Utils::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); 252 mWritePointer = GetFrame(); 253 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 254 error = OT_ERROR_NONE; 255 } 256 257 return error; 258 } 259 260 /** 261 * Gets the length (number of bytes) of reserved buffer in front of the current frame being written. 262 * 263 * @returns The length (number of bytes) of the reserved buffer. 264 */ GetSkipLength(void) const265 uint16_t GetSkipLength(void) const 266 { 267 return Lib::Utils::LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset); 268 } 269 270 /** 271 * Gets a pointer to the start of the current frame. 272 * 273 * @returns A pointer to the start of the frame. 274 */ GetFrame(void) const275 uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); } 276 277 /** 278 * Gets the maximum length of the current frame. 279 * 280 * @returns The maximum length of the current frame. 281 */ GetFrameMaxLength(void) const282 uint16_t GetFrameMaxLength(void) const { return static_cast<uint16_t>(mBuffer + kSize - GetFrame()); } 283 284 /** 285 * Saves the current frame and prepares the write pointer for a next frame to be written into the 286 * buffer. 287 * 288 * Saved frame can be retrieved later using `GetNextSavedFrame()`. 289 * 290 * @retval OT_ERROR_NONE Successfully saved the buffer and prepared the write pointer for the next frame. 291 * @retval OT_ERROR_NO_BUFS Insufficient buffer space. 292 */ SaveFrame(void)293 otError SaveFrame(void) 294 { 295 otError error = OT_ERROR_NONE; 296 297 // If the next header will overflow the buffer, we can't save the frame. 298 if (!CanWrite(kHeaderSize)) 299 { 300 error = OT_ERROR_NO_BUFS; 301 } 302 else 303 { 304 Lib::Utils::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), 305 mWriteFrameStart + kHeaderTotalLengthOffset); 306 mWriteFrameStart = mWritePointer; 307 IgnoreError(SetSkipLength(0)); 308 } 309 310 return error; 311 } 312 313 /** 314 * Discards the current frame and prepares the write pointer for a next frame to be written into the 315 * buffer. 316 */ DiscardFrame(void)317 void DiscardFrame(void) 318 { 319 IgnoreError(SetSkipLength(0)); 320 321 mWritePointer = GetFrame(); 322 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 323 } 324 325 /** 326 * Indicates whether there are any saved frames in the buffer. 327 * 328 * @retval TRUE There is at least one saved frame in the buffer. 329 * @retval FALSE There is no saved frame in the buffer. 330 */ HasSavedFrame(void) const331 bool HasSavedFrame(void) const { return (mWriteFrameStart != mBuffer); } 332 333 /** 334 * Iterates through previously saved frames in the buffer, getting a next frame in the queue. 335 * 336 * @param[in,out] aFrame On entry, should point to a previous saved frame or nullptr to get the first frame. 337 * On exit, the pointer variable is updated to next frame or set to nullptr if there are 338 * none. 339 * @param[in,out] aLength On entry, should be a reference to the frame length of the previous saved frame. 340 * On exit, the reference is updated to the frame length (number of bytes) of next frame. 341 * 342 * @retval OT_ERROR_NONE Updated @aFrame and @aLength successfully with the next saved frame. 343 * @retval OT_ERROR_NOT_FOUND No more saved frame in the buffer. 344 */ GetNextSavedFrame(uint8_t * & aFrame,uint16_t & aLength)345 otError GetNextSavedFrame(uint8_t *&aFrame, uint16_t &aLength) 346 { 347 otError error = OT_ERROR_NONE; 348 349 assert(aFrame == nullptr || (mBuffer <= aFrame && aFrame < GetArrayEnd(mBuffer))); 350 351 aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength; 352 353 if (HasSavedFrame() && (aFrame != mWriteFrameStart)) 354 { 355 uint16_t totalLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); 356 uint16_t skipLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); 357 358 aLength = totalLength - skipLength; 359 aFrame += kHeaderSize + skipLength; 360 } 361 else 362 { 363 aLength = 0; 364 aFrame = nullptr; 365 error = OT_ERROR_NOT_FOUND; 366 } 367 368 return error; 369 } 370 371 /** 372 * Clears all saved frames from the buffer and adjusts all the pointers. 373 * 374 * @note This method moves the pointers into the buffer and also copies the content. Any previously retrieved 375 * pointer to buffer (from `GetFrame()` or `GetNextSavedFrame()`) should be considered invalid after calling this 376 * method. 377 */ ClearSavedFrames(void)378 void ClearSavedFrames(void) 379 { 380 uint16_t len = static_cast<uint16_t>(mWriteFrameStart - mBuffer); 381 382 if (len > 0) 383 { 384 memmove(mBuffer, mWriteFrameStart, static_cast<uint16_t>(mWritePointer - mWriteFrameStart)); 385 mWritePointer -= len; 386 mWriteFrameStart -= len; 387 mRemainingLength += len; 388 } 389 } 390 391 private: 392 /* 393 * The diagram below illustrates the format of a saved frame. 394 * 395 * +---------+-------------+------------+----------------+----------------------------+ 396 * | Octets: | 2 | 2 | SkipLength | TotalLength - SkipLength | 397 * +---------+-------------+------------+----------------+----------------------------+ 398 * | Fields: | TotalLength | SkipLength | ReservedBuffer | FrameBuffer | 399 * +---------+-------------+------------+----------------+----------------------------+ 400 * 401 * - "TotalLength" : The total length of the `ReservedBuffer` and `FrameBuffer`. It is stored in header bytes 402 * as a `uint16_t` value using little-endian encoding. 403 * - "SkipLength" : The length of the `ReservedBuffer`. It is stored in header bytes as a `uint16_t` value 404 * using little-endian encoding. 405 * - "ReservedBuffer": A reserved buffer in front of `FrameBuffer`. User can use it to store extra header, etc. 406 * - "FrameBuffer" : Frame buffer. 407 * 408 * The diagram below illustrates how the frames are saved in the buffer. 409 * 410 * The diagram shows `mBuffer` and different pointers into the buffer. It represents buffer state when there are 411 * two saved frames in the buffer. 412 * 413 * Saved frame #1 Saved frame #2 Current frame being written 414 * / \ / \ / \ 415 * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ 416 * | header #1 | ... | header #2 | ... | header | ... | ... | 417 * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ 418 * ^ ^ ^\ /^ 419 * | | | mRemainingLength | 420 * mBuffer[0] mWriteFrameStart | | 421 * | mBuffer[kSize] 422 * mWritePointer 423 */ 424 425 enum 426 { 427 kHeaderTotalLengthOffset = 0, 428 kHeaderSkipLengthOffset = sizeof(uint16_t), 429 kHeaderSize = sizeof(uint16_t) + sizeof(uint16_t), 430 }; 431 GetArrayEnd(Type (& aArray)[kArrayLength])432 template <typename Type, uint16_t kArrayLength> Type *GetArrayEnd(Type (&aArray)[kArrayLength]) 433 { 434 return &aArray[kArrayLength]; 435 } 436 GetArrayEnd(const Type (& aArray)[kArrayLength])437 template <typename Type, uint16_t kArrayLength> const Type *GetArrayEnd(const Type (&aArray)[kArrayLength]) 438 { 439 return &aArray[kArrayLength]; 440 } 441 IgnoreError(otError aError)442 static void IgnoreError(otError aError) { (void)(aError); } 443 444 uint8_t mBuffer[kSize]; 445 uint8_t *mWriteFrameStart; // Pointer to start of current frame being written. 446 }; 447 448 } // namespace Spinel 449 } // namespace ot 450 #endif // SPINEL_MULTI_FRAME_BUFFER_HPP_ 451