1 /*
2  *    Copyright (c) 2016, 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 contains definitions for the NCP frame buffer class.
31  */
32 
33 #ifndef NCP_FRAME_BUFFER_HPP_
34 #define NCP_FRAME_BUFFER_HPP_
35 
36 #include "openthread-spinel-config.h"
37 
38 #include <openthread/message.h>
39 
40 namespace ot {
41 namespace Spinel {
42 
43 /**
44  * This class implements a buffer/queue for storing Ncp frames.
45  *
46  * A frame can consist of a sequence of data bytes and/or the content of an `otMessage` or a combination of the two.
47  * `Buffer` implements priority FIFO logic for storing and reading frames. Two priority levels of high and low
48  * are supported. Within same priority level first-in-first-out order is preserved. High priority frames are read
49  * ahead of any low priority ones.
50  *
51  */
52 class Buffer
53 {
54     friend class Encoder;
55 
56 public:
57     /**
58      * Defines the priority of a frame. High priority frames are read before low priority frames. Within same priority
59      * level FIFO order is preserved.
60      *
61      */
62     enum Priority
63     {
64         kPriorityLow  = 0, //< Indicates low/normal priority for a frame.
65         kPriorityHigh = 1, //< Indicates high priority for a frame.
66     };
67 
68     /**
69      * Defines the (abstract) frame tag type. The tag is a unique value (within currently queued frames) associated
70      * with a frame in the `Buffer`. Frame tags can be compared with one another using operator `==`.
71      *
72      */
73     typedef const void *FrameTag;
74 
75     /**
76      * Defines the tag value indicating an invalid tag (e.g., when there is no frame).
77      *
78      */
79     static const FrameTag kInvalidTag;
80 
81     /**
82      * Defines the structure to hold a write position for an input frame (frame being written).
83      *
84      * It should be considered as an opaque data structure to users of `Buffer`.
85      *
86      */
87     struct WritePosition
88     {
89     public:
90         /**
91          * The constructor for a `WritePosition` object.
92          *
93          */
WritePositionot::Spinel::Buffer::WritePosition94         WritePosition(void)
95             : mPosition(nullptr)
96             , mSegmentHead(nullptr)
97         {
98         }
99 
100     private:
101         uint8_t *mPosition;    //< Pointer into buffer corresponding to saved write position.
102         uint8_t *mSegmentHead; //< Pointer to segment head.
103 
104         friend class Buffer;
105     };
106 
107     /**
108      * Defines a function pointer callback which is invoked to inform a change in `Buffer` either when a new
109      * frame is added/written to `Buffer` or when a frame is removed from `Buffer`.
110      *
111      * @param[in] aContext              A pointer to arbitrary context information.
112      * @param[in] aTag                  The tag associated with the frame which is added or removed.
113      * @param[in] aPriority             The priority of frame.
114      * @param[in] aBuffer               A pointer to the `Buffer`.
115      *
116      */
117     typedef void (*BufferCallback)(void *aContext, FrameTag aTag, Priority aPriority, Buffer *aBuffer);
118 
119     /**
120      * This constructor initializes an NCP frame buffer.
121      *
122      * @param[in] aBuffer               A pointer to a buffer which will be used by NCP frame buffer.
123      * @param[in] aBufferLength         The buffer size (in bytes).
124      *
125      */
126     Buffer(uint8_t *aBuffer, uint16_t aBufferLength);
127 
128     /**
129      * This method clears the NCP frame buffer. All the frames are cleared/removed.
130      *
131      */
132     void Clear(void);
133 
134     /**
135      * This method sets the FrameAdded callback and its context.
136      *
137      * Subsequent calls to this method will overwrite the previous callback and its context.
138      *
139      * @param[in] aFrameAddedCallback   Callback invoked when a new frame is successfully added to buffer.
140      * @param[in] aFrameAddedContext    A pointer to arbitrary context used with frame added callback.
141      *
142      */
143     void SetFrameAddedCallback(BufferCallback aFrameAddedCallback, void *aFrameAddedContext);
144 
145     /**
146      * This method sets the FrameRemoved callback and its context.
147      *
148      * Subsequent calls to this method will overwrite the previous callback and its context.
149      *
150      * @param[in] aFrameRemovedCallback Callback invoked when a frame is removed from buffer.
151      * @param[in] aFrameRemovedContext  A pointer to arbitrary context used with frame removed callback.
152      *
153      */
154     void SetFrameRemovedCallback(BufferCallback aFrameRemovedCallback, void *aFrameRemovedContext);
155 
156     /**
157      * This method begins a new input frame (InFrame) to be added/written to the frame buffer.
158 
159      * If there is a previous frame being written (for which `InFrameEnd()` has not yet been called), calling
160      * `InFrameBegin()` will discard and clear the previous unfinished frame.
161      *
162      * @param[in] aPriority             Priority level of the new input frame.
163      *
164      */
165     void InFrameBegin(Priority aPriority);
166 
167     /**
168      * This method adds a single byte to current input frame.
169      *
170      * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
171      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
172      *
173      * If no buffer space is available, this method will discard and clear the current input frame and return the
174      * error status `OT_ERROR_NO_BUFS`.
175      *
176      * @param[in]  aByte                The byte value to add to input frame.
177      *
178      * @retval OT_ERROR_NONE            Successfully added given byte to the frame.
179      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the byte.
180      * @retval OT_ERROR_INVALID_STATE   `InFrameBegin()` has not been called earlier to start the frame.
181      *
182      */
183     otError InFrameFeedByte(uint8_t aByte);
184 
185     /**
186      * This method adds data to the current input frame.
187      *
188      * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
189      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
190      *
191      * If no buffer space is available, this method will discard and clear the current input frame and return the
192      * error status `OT_ERROR_NO_BUFS`.
193      *
194      * @param[in]  aDataBuffer          A pointer to data buffer.
195      * @param[in]  aDataBufferLength    The length of the data buffer.
196      *
197      * @retval OT_ERROR_NONE            Successfully added new data to the frame.
198      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add data.
199      * @retval OT_ERROR_INVALID_STATE   `InFrameBegin()` has not been called earlier to start the frame.
200      *
201      */
202     otError InFrameFeedData(const uint8_t *aDataBuffer, uint16_t aDataBufferLength);
203 
204 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
205     /**
206      * This method adds a message to the current input frame.
207      *
208      * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
209      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
210      *
211      * If no buffer space is available, this method will discard and clear the frame and return error status
212      * `OT_ERROR_NO_BUFS`.
213      *
214      * The ownership of the passed-in message @p aMessage changes to `Buffer` ONLY when the entire frame is
215      * successfully finished (i.e., with a successful call to `InFrameEnd()` for the current input frame), and in this
216      * case the `otMessage` instance will be freed once the frame is removed (using `OutFrameRemove()`) from NCP buffer.
217      * However, if the input frame gets discarded before it is finished (e.g., running out of buffer space), the
218      * `otMessage` instance remains unchanged.
219      *
220      * @param[in] aMessage              A message to be added to current frame.
221      *
222      * @retval OT_ERROR_NONE            Successfully added the message to the frame.
223      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the message.
224      * @retval OT_ERROR_INVALID_STATE   `InFrameBegin()` has not been called earlier to start the frame.
225      * @retval OT_ERROR_INVALID_ARGS    If @p aMessage is nullptr.
226      *
227      */
228     otError InFrameFeedMessage(otMessage *aMessage);
229 #endif
230 
231     /**
232      * This method gets the current write position in the input frame.
233      *
234      * The write position is returned in @p aPosition. The saved position can later be used to overwrite the frame
235      * content (using `InFrameOverwrite()`) or discard a portion of written frame and move the write pointer back to
236      * the saved position (using `InFrameReset()`).
237      *
238      * @param[out] aPosition            A reference to a `WritePosition` to save the current write position.
239      *
240      * @retval OT_ERROR_NONE            Successfully saved current write position in @p aPosition.
241      * @retval OT_ERROR_INVALID_STATE   `InFrameBegin()` has not been called earlier to start the frame.
242      *
243      */
244     otError InFrameGetPosition(WritePosition &aPosition);
245 
246     /**
247      * This method overwrites the previously written content in the current input frame at a given write position.
248      *
249      * The write position @p aPostion must belong to the same input frame saved earlier with `InFrameGetPosition()`.
250      * This method does not allow writing beyond the current end of the input frame (i.e., it can only write over
251      * previously added content). If writing @p aDataBufferLength bytes from write position @p aPosition goes beyond
252      * the end, this method does not change the input frame and returns error status `OT_ERROR_INVALID_ARGS`.
253      * This method cannot be used if the input frame has an added `otMessage` (i.e., a previous call to
254      * `InFrameFeedMessage()`).
255      *
256      * @param[in] aPosition             A reference to the write position.
257      * @param[in] aDataBuffer           A pointer to data buffer.
258      * @param[in] aDataBufferLength     The length of the data buffer.
259      *
260      * @retval OT_ERROR_NONE            Successfully overwrote the data at the given write position.
261      * @retval OT_ERROR_INVALID_STATE   No input frame (`InFrameBegin()` has not been called).
262      * @retval OT_ERROR_INVALID_ARGS    The given write position is not valid (i.e., if it does not belong to same
263      *                                  input frame), or the input frame has an added `otMessage`, or the write
264      *                                  operation will go beyond the current end of the input frame.
265      *
266      */
267     otError InFrameOverwrite(const WritePosition &aPosition, const uint8_t *aDataBuffer, uint16_t aDataBufferLength);
268 
269     /**
270      * This method resets the write position of input frame back to a previously saved position. Any previously
271      * added content after the write position is discarded.
272      *
273      * The write position @p aPosition must belong to the same input frame saved earlier with `InFrameGetPosition()`.
274      * This method cannot be used if the input frame has an added `otMessage` (i.e., a previous call to
275      * `InFrameFeedMessage()`).
276      *
277      * @param[in] aPosition             A reference to write position
278      *
279      * @retval OT_ERROR_NONE            Successfully reset the write position of current input frame..
280      * @retval OT_ERROR_INVALID_STATE   No input frame (`InFrameBegin()` has not been called).
281      * @retval OT_ERROR_INVALID_ARGS    The given write position is not valid (does not belong to same input frame), or
282      *                                  the input frame has an added `otMessage`.
283      *
284      */
285     otError InFrameReset(const WritePosition &aPosition);
286 
287     /**
288      * This method gets the distance (number of bytes) from a given saved position to current end of frame.
289      *
290      * The write position @p aPosition must belong to the same input frame saved earlier with `InFrameGetPosition()`.
291      * This method cannot be used if the input frame has an added `otMessage` (i.e., a previous call to
292      * `InFrameFeedMessage()`). In case of invalid argument, this method returns zero.
293      *
294      * @param[in] aPosition             A reference to write position
295      *
296      * @returns The distance (number of bytes) from a write position to current end of frame, or zero for invalid
297      *          arguments.
298      *
299      */
300     uint16_t InFrameGetDistance(const WritePosition &aPosition) const;
301 
302     /**
303      * This method finalizes/ends the current input frame being written to the buffer.
304      *
305      * Before using this method `InFrameBegin()` must be called to start and prepare a new input frame. Otherwise, this
306      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
307      *
308      * If no buffer space is available, this method will discard and clear the frame and return error status
309      * `OT_ERROR_NO_BUFS`.
310      *
311      * @retval OT_ERROR_NONE            Successfully ended the input frame.
312      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add message.
313      * @retval OT_ERROR_INVALID_STATE   `InFrameBegin()` has not been called earlier to start the frame.
314      *
315      */
316     otError InFrameEnd(void);
317 
318     /**
319      * This method returns the tag assigned to last successfully written/added frame to NcpBuffer (i.e., last input
320      * frame for which `InFrameEnd()` was called and returned success status). The tag is a unique value (within
321      * currently queued frames) associated with a frame in the `Buffer`. The tag can be used to identify the
322      * same frame when it is read and removed from the NcpBuffer. Tags can be compared using operator `==`.
323      *
324      * @returns The tag of the last successfully written frame, or `kInvalidTag` if no frame is written so far.
325      */
326     FrameTag InFrameGetLastTag(void) const;
327 
328     /**
329      * This method checks if the buffer is empty. A non-empty buffer contains at least one full frame for reading.
330      *
331      * @retval TRUE                     Buffer is not empty and contains at least one full frame for reading.
332      * @retval FALSE                    Buffer is empty and contains no frame for reading.
333      *
334      */
335     bool IsEmpty(void) const;
336 
337     /**
338      * This method begins/prepares an output frame to be read from the frame buffer if there is no current active output
339      * frame, or resets the read offset if there is a current active output frame.
340      *
341      * The NCP buffer maintains a read offset for the current frame being read. Before reading any bytes from the frame
342      * this method should be called to prepare the frame and set the read offset.
343      *
344      * If part or even all of current frame has been read, a sub-sequent call to this method  will reset the read
345      * offset back to beginning of current output frame (note that the current output frame will remain unchanged even
346      * in case where a higher priority frame was written to buffer while reading current output frame). A prepared
347      * output frame will stay active as current output frame until it is explicitly removed using `OutFrameRemove()`.
348      *
349      * @retval OT_ERROR_NONE            Successfully started/prepared a new output frame for reading.
350      * @retval OT_ERROR_NOT_FOUND       No frame available in buffer for reading.
351      *
352      */
353     otError OutFrameBegin(void);
354 
355     /**
356      * This method checks if the current output frame (being read) has ended.
357      *
358      * The NCP buffer maintains a read offset for the current output frame being read. This method returns `true` if
359      * the read offset has reached the end of the frame and there are no more bytes available to read, or if there is
360      * no currently active output frame.
361      *
362      * @retval TRUE                     Frame has ended (no more bytes available to read from current output frame), or
363      *                                  there is currently no prepared/active output frame.
364      * @retval FALSE                    Frame still has more data to read.
365      *
366      */
367     bool OutFrameHasEnded(void);
368 
369     /**
370      * This method reads and returns the next byte from the current output frame.
371      *
372      * The NCP buffer maintains a read offset for the current output frame being read. This method reads and returns
373      * the next byte from the current frame and moves the read offset forward. If read offset is already at the end
374      * current output frame, this method returns zero.
375      *
376      * @returns The next byte from the current output frame or zero if current output frame has ended or there is
377      * prepared/active output from.
378      *
379      */
380     uint8_t OutFrameReadByte(void);
381 
382     /**
383      * This method reads and copies bytes from the current output frame into a given buffer.
384      *
385      * The NCP buffer maintains a read offset for the current output frame being read. This method attempts to read
386      * the given number of bytes (@p aReadLength) from the current frame and copies the bytes into the given
387      * data buffer (@p aDataBuffer). It also moves the read offset forward accordingly. If there are fewer bytes
388      * remaining in current frame than the requested @p aReadLength, the available bytes are read/copied. This methods
389      * returns the number of bytes read from frame and copied into @p aDataBuffer.
390      *
391      * @param[in]   aReadLength          Number of bytes to read.
392      * @param[out]  aDataBuffer          A pointer to a data buffer.
393      *
394      * @returns The number of bytes read and copied into data buffer.
395      *
396      */
397     uint16_t OutFrameRead(uint16_t aReadLength, uint8_t *aDataBuffer);
398 
399     /**
400      * This method removes the current or front output frame from the buffer.
401      *
402      * If there is an active output from being read (an output frame was prepared earlier with a successful call to
403      * `OutFrameBegin()`), this method removes the current active output frame. If there is no current active frame,
404      * the front frame in the queue (the next frame which would have been read) will be removed.
405      *
406      * When a frame is removed all its associated messages will be freed.
407      *
408      * If the remove operation is successful, this method will invoke the `FrameRemovedCallback` (if not nullptr) before
409      * returning the success state.
410      *
411      * @retval OT_ERROR_NONE            Successfully removed the front frame.
412      * @retval OT_ERROR_NOT_FOUND       No frame available in NCP frame buffer to remove.
413      *
414      */
415     otError OutFrameRemove(void);
416 
417     /**
418      * This method returns the number of bytes (length) of current or front frame in the NCP frame buffer.
419      *
420      * If there is an active output from being read (an output frame was prepared earlier with successful call to
421      * `OutFrameBegin()`), this method returns the length of the current output frame. If there is no current active
422      * frame, the length of the front frame in the queue (the next frame which would have been read) will be returned.
423      *
424      * If there is no frame in buffer, this method returns zero.
425      *
426      * @returns The number of bytes (length) of current/front frame, or zero if no frame in buffer.
427      *
428      */
429     uint16_t OutFrameGetLength(void);
430 
431     /**
432      * This method returns the tag value associated to current or front frame in the NCP frame buffer.
433      *
434      * If there is an active output from being read (an output frame was prepared earlier with successful call to
435      * `OutFrameBegin()`), this method returns the tag associated with current output frame. If there is no current
436      * active frame, the tag associated with the front frame in the queue (the next frame which would have been read)
437      * will be returned.
438      *
439      * If there is no frame in buffer, this method returns `kInvalidTag`.
440      *
441      * @returns The tag assigned to the current/from output frame, or `kInvalidTag` if no frame in buffer.
442      *
443      */
444     FrameTag OutFrameGetTag(void);
445 
446 private:
447     /*
448      * `Buffer` Implementation
449      * -------------------------------
450      *
451      * `Buffer` internally stores a frame as a sequence of data segments. Each segment stores a portion of
452      * frame. The data segments are stored in the main buffer `mBuffer`. `mBuffer` is utilized as a circular buffer.
453 
454      * The content of messages (which are added using `InFrameFeedMessage()`) are not directly copied in the `mBuffer`
455      * but instead they are enqueued in a message queue `mMessageQueue`.
456      *
457      * Every data segments starts with a header before the data portion. The header is 2 bytes long with the following
458      * format:
459      *
460      *    Bit 0-13: Give the length of the data segment (max segment len is 2^14 = 16,384 bytes).
461      *    Bit 14:   Flag bit set to indicate that this segment has an associated `Message` (appended to its end).
462      *    Bit 15:   Flag bit set to indicate that this segment defines the start of a new frame.
463      *
464      *        Bit  15         Bit 14                     Bits: 0 - 13
465      *    +--------------+--------------+--------------------------------------------------------+
466      *    |   New Frame  |  Has Message |  Length of segment (excluding the header)              |
467      *    +--------------+--------------+--------------------------------------------------------+
468      *
469      * The header is encoded in big-endian (msb first) style.
470 
471      * Consider the following calls to create a frame:
472      *
473      *    ncpBuffer.InFrameBegin();
474      *    ncpBuffer.InFrameFeedData("Hello", 5);
475      *    ncpBuffer.InFrameFeedData("There", 5);
476      *    ncpBuffer.InFrameFeedMessage(*someMessage);
477      *    ncpBuffer.InFrameFeedData("Bye", 3);
478      *    ncpBuffer.InFrameEnd();
479      *
480      * This frame is stored as two segments:
481      *
482      *    - Segment #1 contains "HelloThere" with a header value `0xC00A` which shows that this segment contains 10
483      *      data bytes, and it starts a new frame, and also includes an appended message from the message queue.
484      *
485      *    - Segment #2 contains "Bye" with a header value of `0x0003` showing length of 3 and no appended message.
486      *
487      *    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
488      *    | C0  | 0A  | 'H' | 'e' | 'l' | 'l' | 'o' | 'T' | 'h' | 'e' | 'r' | 'e' | 00  | 03  | 'B' | 'y' | 'e' |
489      *    +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
490      *     \         /                                                             \         /
491      *   Segment #1 Header                                                      Segment #2 Header
492      *
493      *
494      * `Buffer` uses the `mBuffer` as a circular/ring buffer. To support two frame priorities the buffer is
495      * divided into two high-priority and low-priority regions. The high priority frames are stored in buffer in
496      * backward direction while the low-priority frames use the buffer in forward direction. This model ensures the
497      * available buffer space is utilized efficiently between all frame types.
498      *
499      *                                       mReadFrameStart[kPriorityLow]
500      *                                                 |
501      *                                                 |                   mWriteFrameStart[kPriorityLow]
502      *                                                 |                              |
503      *                                                 V    Low Priority Frames       V
504      *   --------------+------------------------------+------------------------------+------------------
505      *             ... |        <--------             |         -------->            | ...
506      *   --------------+------------------------------+------------------------------+------------------
507      *                ^       High Priority Frames   ^
508      *                |                              |
509      *                |                    mReadFrameStart[kPriorityHigh]
510      *                |
511      *          mWriteFrameStart[kPriorityHigh]
512      *
513      *
514      *
515      * When frames are removed, if possible, the `mReadFrameStart` and `mWriteFrameStart` pointers of the two priority
516      * levels are moved closer to avoid gaps.
517      *
518      * For an output frame (frame being read), Buffer maintains a `ReadState` along with a set of pointers
519      * into the buffer:
520      *
521      *             mReadFrameStart[priority]: Start of the current/front frame.
522      *             |
523      *             |            mReadSegmentHead: Start of the current segment.
524      *             |            |
525      *             |            |              mReadPointer: Pointer to the next byte to read.
526      *             |            |              |
527      *             |            |              |           mReadSegmentTail: End of the current segment.
528      *             |            |              |           |
529      *             V            V              V           V
530      *   ---------+------------+--------------------------+------+----------------+-----------------------------------
531      *       ...  | Segment 1  | Segment  2               | ...  | Last Segment   | ... (possible) next frame
532      *   ---------+------------+--------------------------+------+----------------+-----------------------------------
533      *             \           |                         |                       /
534      *              |          \------------v-----------/                       |
535      *              |                   Current Segment                         |
536      *              |                                                           |
537      *               \---------------------------V-----------------------------/
538      *                              Current OutFrame (being read)
539      *
540      * Note that the diagram above shows the pointers for a low-priority frame (with pointers increasing in forward
541      * direction).
542      *
543      * The `ReadState` indicates the state of current output frame and its read offset (e.g., if read offset is in
544      * middle of a segment or if it is is middle of an appended message, or if we are done with entire frame).
545      *
546      * For an input frame (frame being written), the following pointers are maintained:
547      *
548      *            mWriteFrameWrite[priority]: Start of the current/next frame being written.
549      *                       |
550      *                       |      mWriteSegmentHead: Start of the current segment of the active input frame.
551      *                       |                 |
552      *                       |                 |   mWriteSegmentTail: Pointer to the next byte to write.
553      *                       |                 |                       |
554      *                       |                 |                       |
555      *                       |                 |                       |
556      *                       V                 V                       V
557      *    ------------------+------------------+-------------------------------------------------------------------
558      *    Previous Frames   | Segment 1        | Segment  2            : . . .
559      *    ------------------+------------------+-------------------------------------------------------------------
560      *                       \                                        /
561      *                        |                                      |
562      *                         \------------------V-----------------/
563      *                             Current InFrame (being written)
564      *
565      *
566      */
567 
568     enum
569     {
570         kReadByteAfterFrameHasEnded = 0,      // Value returned by ReadByte() when frame has ended.
571         kMessageReadBufferSize      = 16,     // Size of message buffer array `mMessageBuffer`.
572         kUnknownFrameLength         = 0xffff, // Value used when frame length is unknown.
573         kSegmentHeaderSize          = 2,      // Length of the segment header.
574         kSegmentHeaderLengthMask    = 0x3fff, // Bit mask to get the length from the segment header
575         kMaxSegments                = 10,     // Max number of segments allowed in a frame
576 
577         kSegmentHeaderNoFlag               = 0,         // No flags are set.
578         kSegmentHeaderNewFrameFlag         = (1 << 15), // Indicates that this segment starts a new frame.
579         kSegmentHeaderMessageIndicatorFlag = (1 << 14), // Indicates this segment ends with a Message.
580 
581         kNumPrios = (kPriorityHigh + 1), // Number of priorities.
582     };
583 
584     enum ReadState
585     {
586         kReadStateNotActive, // No current prepared output frame.
587         kReadStateInSegment, // In middle of a data segment while reading current frame.
588         kReadStateInMessage, // In middle of a message while reading current frame.
589         kReadStateDone,      // Current output frame is read fully.
590     };
591 
592     enum Direction
593     {
594         kForward  = kPriorityLow,
595         kBackward = kPriorityHigh,
596         kUnknown,
597     };
598 
599     uint8_t *GetUpdatedBufPtr(uint8_t *aBufPtr, uint16_t aOffset, Direction aDirection) const;
600     uint16_t GetDistance(const uint8_t *aStartPtr, const uint8_t *aEndPtr, Direction aDirection) const;
601 
602     uint16_t ReadUint16At(uint8_t *aBufPtr, Direction aDirection);
603     void     WriteUint16At(uint8_t *aBufPtr, uint16_t aValue, Direction aDirection);
604 
605     bool HasFrame(Priority aPriority) const;
606     void UpdateReadWriteStartPointers(void);
607 
608     otError InFrameAppend(uint8_t aByte);
609     otError InFrameBeginSegment(void);
610     void    InFrameEndSegment(uint16_t aSegmentHeaderFlags);
611     void    InFrameDiscard(void);
612     bool    InFrameIsWriting(Priority aPriority) const;
613 
614     void    OutFrameSelectReadDirection(void);
615     otError OutFramePrepareSegment(void);
616     void    OutFrameMoveToNextSegment(void);
617 
618 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
619     otError OutFramePrepareMessage(void);
620     otError OutFrameFillMessageBuffer(void);
621 #endif
622 
623     uint8_t *const mBuffer;       // Pointer to the buffer used to store the data.
624     uint8_t *const mBufferEnd;    // Points to after the end of buffer.
625     const uint16_t mBufferLength; // Length of the buffer.
626 
627     BufferCallback mFrameAddedCallback;   // Callback to signal when a new frame is added
628     void *         mFrameAddedContext;    // Context passed to `mFrameAddedCallback`.
629     BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed.
630     void *         mFrameRemovedContext;  // Context passed to `mFrameRemovedCallback`.
631 
632     Direction mWriteDirection;             // Direction (priority) for current frame being read.
633     uint8_t * mWriteFrameStart[kNumPrios]; // Pointer to start of current frame being written.
634     uint8_t * mWriteSegmentHead;           // Pointer to start of current segment in the frame being written.
635     uint8_t * mWriteSegmentTail;           // Pointer to end of current segment in the frame being written.
636     FrameTag  mWriteFrameTag;              // Tag associated with last successfully written frame.
637 
638     Direction mReadDirection;   // Direction (priority) for current frame being read.
639     ReadState mReadState;       // Read state.
640     uint16_t  mReadFrameLength; // Length of current frame being read.
641 
642     uint8_t *mReadFrameStart[kNumPrios]; // Pointer to start of current frame being read.
643     uint8_t *mReadSegmentHead;           // Pointer to start of current segment in the frame being read.
644     uint8_t *mReadSegmentTail;           // Pointer to end of current segment in the frame being read.
645     uint8_t *mReadPointer;               // Pointer to next byte to read (either in segment or in msg buffer).
646 
647 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
648     otMessageQueue mWriteFrameMessageQueue;                // Message queue for the current frame being written.
649     otMessageQueue mMessageQueue[kNumPrios];               // Main message queues.
650     otMessage *    mReadMessage;                           // Current Message in the frame being read.
651     uint16_t       mReadMessageOffset;                     // Offset within current message being read.
652     uint8_t        mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read.
653     uint8_t *      mReadMessageTail;                       // Pointer to end of current part in mMessageBuffer.
654 #endif
655 };
656 
657 } // namespace Spinel
658 } // namespace ot
659 
660 #endif // NCP_FRAME_BUFFER_HPP_
661