• 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 <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