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