• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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