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