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 */ 62 class FrameWritePointer 63 { 64 public: 65 /** 66 * Indicates whether there is buffer space available to write @p aWriteLength bytes. 67 * 68 * param[in] aWriteLength Number of bytes to write. 69 * 70 * @retval TRUE Enough buffer space is available to write the requested number of bytes. 71 * @retval FALSE Insufficient buffer space to write the requested number of bytes. 72 * 73 */ CanWrite(uint16_t aWriteLength) const74 bool CanWrite(uint16_t aWriteLength) const { return (mRemainingLength >= aWriteLength); } 75 76 /** 77 * Writes a byte into the buffer and updates the write pointer (if space is available). 78 * 79 * @param[in] aByte A byte to be written to the buffer. 80 * 81 * @retval OT_ERROR_NONE Successfully wrote the byte and updated the pointer. 82 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to write the byte. 83 * 84 */ WriteByte(uint8_t aByte)85 otError WriteByte(uint8_t aByte) 86 { 87 return CanWrite(sizeof(uint8_t)) ? (*mWritePointer++ = aByte, mRemainingLength--, OT_ERROR_NONE) 88 : OT_ERROR_NO_BUFS; 89 } 90 91 /** 92 * Undoes the last @p aUndoLength writes, removing them from frame. 93 * 94 * @note Caller should ensure that @p aUndoLength is less than or equal to the number of previously written bytes 95 * into the frame. This method does not perform any checks and its behavior is undefined if @p aUndoLength is 96 * larger than the number of bytes previously written into the frame. 97 * 98 * @param[in] aUndoLength Number of bytes to remove (number of last `WriteByte()` calls to undo). 99 * 100 */ UndoLastWrites(uint16_t aUndoLength)101 void UndoLastWrites(uint16_t aUndoLength) 102 { 103 mWritePointer -= aUndoLength; 104 mRemainingLength += aUndoLength; 105 } 106 107 protected: FrameWritePointer(void)108 FrameWritePointer(void) 109 : mWritePointer(nullptr) 110 , mRemainingLength(0) 111 { 112 } 113 114 uint8_t *mWritePointer; ///< A pointer to current write position in the buffer. 115 uint16_t mRemainingLength; ///< Number of remaining bytes available to write. 116 }; 117 118 /** 119 * Defines a template frame buffer of a given size for storing a single frame. 120 * 121 * @tparam kSize The size of the frame buffer. 122 * 123 */ 124 template <uint16_t kSize> class FrameBuffer : public FrameWritePointer 125 { 126 public: 127 /** 128 * Initializes the `FrameBuffer` object. 129 * 130 */ FrameBuffer(void)131 FrameBuffer(void) 132 : FrameWritePointer() 133 { 134 Clear(); 135 } 136 137 /** 138 * Clears the buffer, moving the write pointer to the beginning of the buffer. 139 * 140 */ Clear(void)141 void Clear(void) 142 { 143 mWritePointer = mBuffer; 144 mRemainingLength = sizeof(mBuffer); 145 } 146 147 /** 148 * Indicates whether the buffer is empty or contains a frame. 149 * 150 * @retval TRUE Buffer is empty. 151 * @retval FALSE Buffer contains a frame. 152 * 153 */ IsEmpty(void) const154 bool IsEmpty(void) const { return (mWritePointer == mBuffer); } 155 156 /** 157 * Gets the length (number of bytes) in the frame. 158 * 159 * @returns The length (number of bytes) in the frame. 160 * 161 */ GetLength(void) const162 uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - mBuffer); } 163 164 /** 165 * Gets a pointer to start of the frame. 166 * 167 * @returns A pointer to start of the frame. 168 * 169 */ GetFrame(void)170 uint8_t *GetFrame(void) { return mBuffer; } 171 172 private: 173 uint8_t mBuffer[kSize]; 174 }; 175 176 /** 177 * Defines a template frame buffer of a given size for storing multiple frames. 178 * 179 * Unlike `FrameBuffer` class where a single frame can be stored, this class is capable of saving multiple frames 180 * in a FIFO queue format. 181 * 182 * @tparam kSize The total size of the buffer. 183 * 184 */ 185 template <uint16_t kSize> class MultiFrameBuffer : public FrameWritePointer 186 { 187 public: 188 /** 189 * Initializes the `MultiFrameBuffer` object. 190 * 191 */ MultiFrameBuffer(void)192 MultiFrameBuffer(void) 193 : FrameWritePointer() 194 { 195 Clear(); 196 } 197 198 /** 199 * Clears the buffer, removing current frame and all previously saved frames. 200 * 201 * It moves the write pointer to the beginning of the buffer. 202 * 203 */ Clear(void)204 void Clear(void) 205 { 206 mWriteFrameStart = mBuffer; 207 mWritePointer = mBuffer + kHeaderSize; 208 mRemainingLength = kSize - kHeaderSize; 209 210 IgnoreError(SetSkipLength(0)); 211 } 212 213 /** 214 * Indicates whether the current frame (being written) is empty or not. 215 * 216 * @retval TRUE Current frame is empty. 217 * @retval FALSE Current frame is not empty. 218 * 219 */ HasFrame(void) const220 bool HasFrame(void) const { return (mWritePointer != GetFrame()); } 221 222 /** 223 * Sets the length (number of bytes) of the current frame being written. 224 * 225 * param[in] aLength The length of current frame. 226 * 227 * @retval OT_ERROR_NONE Successfully set the length of the current frame. 228 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a frame of length @p aLength. 229 * 230 */ SetLength(uint16_t aLength)231 otError SetLength(uint16_t aLength) 232 { 233 otError error = OT_ERROR_NO_BUFS; 234 235 if (GetFrame() + aLength <= GetArrayEnd(mBuffer)) 236 { 237 mWritePointer = GetFrame() + aLength; 238 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 239 error = OT_ERROR_NONE; 240 } 241 242 return error; 243 } 244 245 /** 246 * Gets the length (number of bytes) in the current frame being written into the buffer. 247 * 248 * @returns The length (number of bytes) in the frame. 249 * 250 */ GetLength(void) const251 uint16_t GetLength(void) const { return static_cast<uint16_t>(mWritePointer - GetFrame()); } 252 253 /** 254 * Sets the length (number of bytes) of reserved buffer in front of the current frame being written. 255 * 256 * param[in] aSkipLength The length of reserved buffer. 257 * 258 * @retval OT_ERROR_NONE Successfully set the length of reserved buffer. 259 * @retval OT_ERROR_NO_BUFS Insufficient buffer space to hold a reserved buffer of length @p aLength. 260 * 261 */ SetSkipLength(uint16_t aSkipLength)262 otError SetSkipLength(uint16_t aSkipLength) 263 { 264 otError error = OT_ERROR_NO_BUFS; 265 266 if (mWriteFrameStart + kHeaderSize + aSkipLength <= GetArrayEnd(mBuffer)) 267 { 268 Lib::Utils::LittleEndian::WriteUint16(aSkipLength, mWriteFrameStart + kHeaderSkipLengthOffset); 269 mWritePointer = GetFrame(); 270 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 271 error = OT_ERROR_NONE; 272 } 273 274 return error; 275 } 276 277 /** 278 * Gets the length (number of bytes) of reserved buffer in front of the current frame being written. 279 * 280 * @returns The length (number of bytes) of the reserved buffer. 281 * 282 */ GetSkipLength(void) const283 uint16_t GetSkipLength(void) const 284 { 285 return Lib::Utils::LittleEndian::ReadUint16(mWriteFrameStart + kHeaderSkipLengthOffset); 286 } 287 288 /** 289 * Gets a pointer to the start of the current frame. 290 * 291 * @returns A pointer to the start of the frame. 292 * 293 */ GetFrame(void) const294 uint8_t *GetFrame(void) const { return mWriteFrameStart + kHeaderSize + GetSkipLength(); } 295 296 /** 297 * Gets the maximum length of the current frame. 298 * 299 * @returns The maximum length of the current frame. 300 * 301 */ GetFrameMaxLength(void) const302 uint16_t GetFrameMaxLength(void) const { return static_cast<uint16_t>(mBuffer + kSize - GetFrame()); } 303 304 /** 305 * Saves the current frame and prepares the write pointer for a next frame to be written into the 306 * buffer. 307 * 308 * Saved frame can be retrieved later using `GetNextSavedFrame()`. 309 * 310 * @retval OT_ERROR_NONE Successfully saved the buffer and prepared the write pointer for the next frame. 311 * @retval OT_ERROR_NO_BUFS Insufficient buffer space. 312 */ SaveFrame(void)313 otError SaveFrame(void) 314 { 315 otError error = OT_ERROR_NONE; 316 317 // If the next header will overflow the buffer, we can't save the frame. 318 if (!CanWrite(kHeaderSize)) 319 { 320 error = OT_ERROR_NO_BUFS; 321 } 322 else 323 { 324 Lib::Utils::LittleEndian::WriteUint16(GetSkipLength() + GetLength(), 325 mWriteFrameStart + kHeaderTotalLengthOffset); 326 mWriteFrameStart = mWritePointer; 327 IgnoreError(SetSkipLength(0)); 328 } 329 330 return error; 331 } 332 333 /** 334 * Discards the current frame and prepares the write pointer for a next frame to be written into the 335 * buffer. 336 * 337 */ DiscardFrame(void)338 void DiscardFrame(void) 339 { 340 IgnoreError(SetSkipLength(0)); 341 342 mWritePointer = GetFrame(); 343 mRemainingLength = static_cast<uint16_t>(mBuffer + kSize - mWritePointer); 344 } 345 346 /** 347 * Indicates whether there are any saved frames in the buffer. 348 * 349 * @retval TRUE There is at least one saved frame in the buffer. 350 * @retval FALSE There is no saved frame in the buffer. 351 * 352 */ HasSavedFrame(void) const353 bool HasSavedFrame(void) const { return (mWriteFrameStart != mBuffer); } 354 355 /** 356 * Iterates through previously saved frames in the buffer, getting a next frame in the queue. 357 * 358 * @param[in,out] aFrame On entry, should point to a previous saved frame or nullptr to get the first frame. 359 * On exit, the pointer variable is updated to next frame or set to nullptr if there are 360 * none. 361 * @param[in,out] aLength On entry, should be a reference to the frame length of the previous saved frame. 362 * On exit, the reference is updated to the frame length (number of bytes) of next frame. 363 * 364 * @retval OT_ERROR_NONE Updated @aFrame and @aLength successfully with the next saved frame. 365 * @retval OT_ERROR_NOT_FOUND No more saved frame in the buffer. 366 * 367 */ GetNextSavedFrame(uint8_t * & aFrame,uint16_t & aLength)368 otError GetNextSavedFrame(uint8_t *&aFrame, uint16_t &aLength) 369 { 370 otError error = OT_ERROR_NONE; 371 372 assert(aFrame == nullptr || (mBuffer <= aFrame && aFrame < GetArrayEnd(mBuffer))); 373 374 aFrame = (aFrame == nullptr) ? mBuffer : aFrame + aLength; 375 376 if (HasSavedFrame() && (aFrame != mWriteFrameStart)) 377 { 378 uint16_t totalLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderTotalLengthOffset); 379 uint16_t skipLength = Lib::Utils::LittleEndian::ReadUint16(aFrame + kHeaderSkipLengthOffset); 380 381 aLength = totalLength - skipLength; 382 aFrame += kHeaderSize + skipLength; 383 } 384 else 385 { 386 aLength = 0; 387 aFrame = nullptr; 388 error = OT_ERROR_NOT_FOUND; 389 } 390 391 return error; 392 } 393 394 /** 395 * Clears all saved frames from the buffer and adjusts all the pointers. 396 * 397 * @note This method moves the pointers into the buffer and also copies the content. Any previously retrieved 398 * pointer to buffer (from `GetFrame()` or `GetNextSavedFrame()`) should be considered invalid after calling this 399 * method. 400 * 401 */ ClearSavedFrames(void)402 void ClearSavedFrames(void) 403 { 404 uint16_t len = static_cast<uint16_t>(mWriteFrameStart - mBuffer); 405 406 if (len > 0) 407 { 408 memmove(mBuffer, mWriteFrameStart, static_cast<uint16_t>(mWritePointer - mWriteFrameStart)); 409 mWritePointer -= len; 410 mWriteFrameStart -= len; 411 mRemainingLength += len; 412 } 413 } 414 415 private: 416 /* 417 * The diagram below illustrates the format of a saved frame. 418 * 419 * +---------+-------------+------------+----------------+----------------------------+ 420 * | Octets: | 2 | 2 | SkipLength | TotalLength - SkipLength | 421 * +---------+-------------+------------+----------------+----------------------------+ 422 * | Fields: | TotalLength | SkipLength | ReservedBuffer | FrameBuffer | 423 * +---------+-------------+------------+----------------+----------------------------+ 424 * 425 * - "TotalLength" : The total length of the `ReservedBuffer` and `FrameBuffer`. It is stored in header bytes 426 * as a `uint16_t` value using little-endian encoding. 427 * - "SkipLength" : The length of the `ReservedBuffer`. It is stored in header bytes as a `uint16_t` value 428 * using little-endian encoding. 429 * - "ReservedBuffer": A reserved buffer in front of `FrameBuffer`. User can use it to store extra header, etc. 430 * - "FrameBuffer" : Frame buffer. 431 * 432 * The diagram below illustrates how the frames are saved in the buffer. 433 * 434 * The diagram shows `mBuffer` and different pointers into the buffer. It represents buffer state when there are 435 * two saved frames in the buffer. 436 * 437 * Saved frame #1 Saved frame #2 Current frame being written 438 * / \ / \ / \ 439 * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ 440 * | header #1 | ... | header #2 | ... | header | ... | ... | 441 * +-----------+-------------+-----------+------------+---------+--------------------------------------------+ 442 * ^ ^ ^\ /^ 443 * | | | mRemainingLength | 444 * mBuffer[0] mWriteFrameStart | | 445 * | mBuffer[kSize] 446 * mWritePointer 447 */ 448 449 enum 450 { 451 kHeaderTotalLengthOffset = 0, 452 kHeaderSkipLengthOffset = sizeof(uint16_t), 453 kHeaderSize = sizeof(uint16_t) + sizeof(uint16_t), 454 }; 455 456 template <typename Type, uint16_t kArrayLength> Type *GetArrayEnd(Type (&aArray)[kArrayLength]) 457 { 458 return &aArray[kArrayLength]; 459 } 460 GetArrayEnd(const Type (& aArray)[kArrayLength])461 template <typename Type, uint16_t kArrayLength> const Type *GetArrayEnd(const Type (&aArray)[kArrayLength]) 462 { 463 return &aArray[kArrayLength]; 464 } 465 IgnoreError(otError aError)466 static void IgnoreError(otError aError) { (void)(aError); } 467 468 uint8_t mBuffer[kSize]; 469 uint8_t *mWriteFrameStart; // Pointer to start of current frame being written. 470 }; 471 472 } // namespace Spinel 473 } // namespace ot 474 #endif // SPINEL_MULTI_FRAME_BUFFER_HPP_ 475