1 /*
2  *    Copyright (c) 2017, 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 the definitions of a spinel encoder.
31  */
32 
33 #ifndef SPINEL_ENCODER_HPP_
34 #define SPINEL_ENCODER_HPP_
35 
36 #include "openthread-spinel-config.h"
37 
38 #include <openthread/ip6.h>
39 #include <openthread/message.h>
40 #include <openthread/ncp.h>
41 
42 #include "spinel.h"
43 #include "spinel_buffer.hpp"
44 
45 namespace ot {
46 namespace Spinel {
47 
48 /**
49  * Defines a spinel encoder.
50  */
51 class Encoder
52 {
53 public:
54     /**
55      * Initializes a `Encoder` object.
56      *
57      * @param[in] aNcpBuffer   A reference to a `Spinel::Buffer` where the frames are written.
58      */
Encoder(Spinel::Buffer & aNcpBuffer)59     explicit Encoder(Spinel::Buffer &aNcpBuffer)
60         : mNcpBuffer(aNcpBuffer)
61         , mNumOpenStructs(0)
62         , mSavedNumOpenStructs(0)
63     {
64     }
65 
66     /**
67      * Begins a new frame to be added/written to the frame buffer.
68      *
69      * If there is a previous frame being written (for which `EndFrame()` has not yet been called), calling
70      * `BeginFrame()` will discard and clear the previous unfinished frame.
71      *
72      * @param[in] aPriority             Priority level of the new input frame.
73      *
74      * @retval OT_ERROR_NONE            Successfully started a new frame.
75      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to start a new frame.
76      */
77     otError BeginFrame(Spinel::Buffer::Priority aPriority);
78 
79     /**
80      * Begins a new spinel command frame to be added/written to the frame buffer.
81      *
82      * If there is a previous frame being written (for which `EndFrame()` has not yet been called), calling
83      * `BeginFrame()` will discard and clear the previous unfinished frame.
84      *
85      * The spinel transaction ID (TID) in the given spinel header is used to determine the priority level of the new
86      * frame. Non-zero TID value indicates that the frame is a response and therefore it uses higher priority level.
87      *
88      * @param[in] aHeader               Spinel header for new the command frame.
89      * @param[in] aCommand              Spinel command.
90      *
91      * @retval OT_ERROR_NONE            Successfully started a new frame.
92      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to start a new frame.
93      */
94     otError BeginFrame(uint8_t aHeader, unsigned int aCommand);
95 
96     /**
97      * Begins a new spinel property update command frame to be added/written to the frame buffer.
98      *
99      * If there is a previous frame being written (for which `EndFrame()` has not yet been called), calling
100      * `BeginFrame()` will discard and clear the previous unfinished frame.
101      *
102      * The spinel transaction ID (TID) in the given spinel header is used to determine the priority level of the new
103      * frame. Non-zero TID value indicates that the frame is a response and therefore it uses higher priority level.
104      *
105      * Saves the write position before the property key (see also `SavePosition()`) so that if fetching the
106      * property fails and the property key should be switched to `LAST_STATUS` with an error status, the saved
107      * position can be used to update the property key in the frame (see also `OverwriteWithLastStatusError()`)
108      *
109      * @param[in] aHeader               Spinel header for new the command frame.
110      * @param[in] aCommand              Spinel command.
111      * @param[in] aKey                  Spinel property key
112      *
113      * @retval OT_ERROR_NONE            Successfully started a new frame.
114      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to start a new frame.
115      */
116     otError BeginFrame(uint8_t aHeader, unsigned int aCommand, spinel_prop_key_t aKey);
117 
118     /**
119      * Overwrites the property key with `LAST_STATUS` in a property update command frame.
120      *
121      * Should be only used after a successful `BeginFrame(aHeader, aCommand, aPropertyKey)`, otherwise, its
122      * behavior is undefined.
123      *
124      * Moves the write position back to saved position by `BeginFrame()` and replaces the property key
125      * `SPINEL_PROP_LAST_STATUS` and writes the given spinel status error.
126      *
127      * @param[in] aStatus               Spinel error status
128      *
129      * @retval OT_ERROR_NONE            Successfully updated the frame.
130      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to update the frame.
131      */
132     otError OverwriteWithLastStatusError(spinel_status_t aStatus);
133 
134     /**
135      * Finalizes/ends the current frame being written to the buffer.
136      *
137      * Before using this method `BeginFrame()` must be called to start and prepare a new frame. Otherwise, this method
138      * does nothing and returns error status `OT_ERROR_INVALID_STATE`.
139      *
140      * If no buffer space is available, this method will discard and clear the frame and return error status
141      * `OT_ERROR_NO_BUFS`.
142      *
143      * Ensures to close any open structure (previously opened using `OpenStruct()` but not closed using
144      * `CloseStruct()`).
145      *
146      * @retval OT_ERROR_NONE            Successfully ended the input frame.
147      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add message.
148      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
149      */
150     otError EndFrame(void);
151 
152     /**
153      * Encodes and writes a boolean value to current input frame.
154      *
155      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
156      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
157      *
158      * If no buffer space is available, this method will discard and clear the current input frame and return the
159      * error status `OT_ERROR_NO_BUFS`.
160      *
161      * @param[in]  aBool                The boolean value to add to input frame.
162      *
163      * @retval OT_ERROR_NONE            Successfully added given byte to the frame.
164      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
165      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
166      */
WriteBool(bool aBool)167     otError WriteBool(bool aBool) { return mNcpBuffer.InFrameFeedByte(aBool ? 0x01 : 0x00); }
168 
169     /**
170      * Encodes and writes a `uint8_t` value to current input frame.
171      *
172      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
173      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
174      *
175      * If no buffer space is available, this method will discard and clear the current input frame and return the
176      * error status `OT_ERROR_NO_BUFS`.
177      *
178      * @param[in]  aUint8               The value to add to input frame.
179      *
180      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
181      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
182      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
183      */
WriteUint8(uint8_t aUint8)184     otError WriteUint8(uint8_t aUint8) { return mNcpBuffer.InFrameFeedByte(aUint8); }
185 
186     /**
187      * Encodes and writes an `int8_t` value to current input frame.
188      *
189      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
190      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
191      *
192      * If no buffer space is available, this method will discard and clear the current input frame and return the
193      * error status `OT_ERROR_NO_BUFS`.
194      *
195      * @param[in]  aInt8                The value to add to input frame.
196      *
197      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
198      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
199      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
200      */
WriteInt8(int8_t aInt8)201     otError WriteInt8(int8_t aInt8) { return WriteUint8(static_cast<uint8_t>(aInt8)); }
202 
203     /**
204      * Encodes and writes a `uint16_t` value to current input frame.
205      *
206      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
207      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
208      *
209      * If no buffer space is available, this method will discard and clear the current input frame and return the
210      * error status `OT_ERROR_NO_BUFS`.
211      *
212      * @param[in]  aUint16              The value to add to input frame.
213      *
214      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
215      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
216      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
217      */
218     otError WriteUint16(uint16_t aUint16);
219 
220     /**
221      * Encodes and writes an `int16_t` value to current input frame.
222      *
223      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
224      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
225      *
226      * If no buffer space is available, this method will discard and clear the current input frame and return the
227      * error status `OT_ERROR_NO_BUFS`.
228      *
229      * @param[in]  aInt16              The value to add to input frame.
230      *
231      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
232      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
233      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
234      */
WriteInt16(int16_t aInt16)235     otError WriteInt16(int16_t aInt16) { return WriteUint16(static_cast<uint16_t>(aInt16)); }
236 
237     /**
238      * Encodes and writes a `uint32_t` value to current input frame.
239      *
240      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
241      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
242      *
243      * If no buffer space is available, this method will discard and clear the current input frame and return the
244      * error status `OT_ERROR_NO_BUFS`.
245      *
246      * @param[in]  aUint32              The value to add to input frame.
247      *
248      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
249      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
250      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
251      */
252     otError WriteUint32(uint32_t aUint32);
253 
254     /**
255      * Encodes and writes an `int32_t` value to current input frame.
256      *
257      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
258      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
259      *
260      * If no buffer space is available, this method will discard and clear the current input frame and return the
261      * error status `OT_ERROR_NO_BUFS`.
262      *
263      * @param[in]  aInt32               The value to add to input frame.
264      *
265      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
266      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
267      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
268      */
WriteInt32(int32_t aInt32)269     otError WriteInt32(int32_t aInt32) { return WriteUint32(static_cast<uint32_t>(aInt32)); }
270 
271     /**
272      * Encodes and writes a `uint64_t` value to current input frame.
273      *
274      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
275      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
276      *
277      * If no buffer space is available, this method will discard and clear the current input frame and return the
278      * error status `OT_ERROR_NO_BUFS`.
279      *
280      * @param[in]  aUint64              The value to add to input frame.
281      *
282      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
283      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
284      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
285      */
286     otError WriteUint64(uint64_t aUint64);
287 
288     /**
289      * Encodes and writes an `int64_t` value to current input frame.
290      *
291      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
292      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
293      *
294      * If no buffer space is available, this method will discard and clear the current input frame and return the
295      * error status `OT_ERROR_NO_BUFS`.
296      *
297      * @param[in]  aInt64               The value to add to input frame.
298      *
299      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
300      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
301      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
302      */
WriteInt64(int64_t aInt64)303     otError WriteInt64(int64_t aInt64) { return WriteUint64(static_cast<uint64_t>(aInt64)); }
304 
305     /**
306      * Encodes (using spinel packed integer format) and writes a value to current input frame.
307      *
308      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
309      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
310      *
311      * If no buffer space is available, this method will discard and clear the current input frame and return the
312      * error status `OT_ERROR_NO_BUFS`.
313      *
314      * @param[in]  aUint                The value to add to input frame.
315      *
316      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
317      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the value.
318      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
319      */
320     otError WriteUintPacked(unsigned int aUint);
321 
322     /**
323      * Encodes and writes an IPv6 address to current input frame.
324      *
325      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
326      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
327      *
328      * If no buffer space is available, this method will discard and clear the current input frame and return the
329      * error status `OT_ERROR_NO_BUFS`.
330      *
331      * @param[in]  aIp6Addr             A reference to the IPv6 address to be added (as `spinel_ipv6addr_t`)
332      *
333      * @retval OT_ERROR_NONE            Successfully added given address to the frame.
334      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the IP address.
335      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
336      */
WriteIp6Address(const spinel_ipv6addr_t & aIp6Addr)337     otError WriteIp6Address(const spinel_ipv6addr_t &aIp6Addr) { return WriteIp6Address(aIp6Addr.bytes); }
338 
339     /**
340      * Encodes and writes an IPv6 address to current input frame.
341      *
342      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
343      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
344      *
345      * If no buffer space is available, this method will discard and clear the current input frame and return the
346      * error status `OT_ERROR_NO_BUFS`.
347      *
348      * @param[in]  aIp6Addr             A reference to the IPv6 address to be added (as `otIp6Address`)
349      *
350      * @retval OT_ERROR_NONE            Successfully added given address to the frame.
351      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the IP address.
352      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
353      */
WriteIp6Address(const otIp6Address & aIp6Addr)354     otError WriteIp6Address(const otIp6Address &aIp6Addr) { return WriteIp6Address(aIp6Addr.mFields.m8); }
355 
356     /**
357      * Encodes and writes an IPv6 address to current input frame.
358      *
359      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
360      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
361      *
362      * If no buffer space is available, this method will discard and clear the current input frame and return the
363      * error status `OT_ERROR_NO_BUFS`.
364      *
365      * @param[in]  aIp6AddrBuf          A pointer to a buffer containing the IPv6 address.
366      *
367      * @retval OT_ERROR_NONE            Successfully added given address to the frame.
368      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the IP address.
369      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
370      */
WriteIp6Address(const uint8_t * aIp6AddrBuf)371     otError WriteIp6Address(const uint8_t *aIp6AddrBuf) { return WriteData(aIp6AddrBuf, sizeof(spinel_ipv6addr_t)); }
372 
373     /**
374      * Encodes and writes an EUI64 value to current input frame.
375      *
376      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
377      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
378      *
379      * If no buffer space is available, this method will discard and clear the current input frame and return the
380      * error status `OT_ERROR_NO_BUFS`.
381      *
382      * @param[in]  aEui64               A reference to the EUI64 value as a `spinel_eui64_t` type.
383      *
384      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
385      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI64 value.
386      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
387      */
WriteEui64(const spinel_eui64_t & aEui64)388     otError WriteEui64(const spinel_eui64_t &aEui64) { return WriteEui64(aEui64.bytes); }
389 
390     /**
391      * Encodes and writes an EUI64 value to current input frame.
392      *
393      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
394      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
395      *
396      * If no buffer space is available, this method will discard and clear the current input frame and return the
397      * error status `OT_ERROR_NO_BUFS`.
398      *
399      * @param[in]  aExtAddress          A reference to an `otExtAddress`
400      *
401      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
402      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI64 value.
403      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
404      */
WriteEui64(const otExtAddress & aExtAddress)405     otError WriteEui64(const otExtAddress &aExtAddress) { return WriteEui64(aExtAddress.m8); }
406 
407     /**
408      * Encodes and writes an EUI64 value to current input frame.
409      *
410      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
411      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
412      *
413      * If no buffer space is available, this method will discard and clear the current input frame and return the
414      * error status `OT_ERROR_NO_BUFS`.
415      *
416      * @param[in]  aEui64               A pointer to a buffer containing the EUI64 value.
417      *
418      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
419      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI64 value.
420      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
421      */
WriteEui64(const uint8_t * aEui64)422     otError WriteEui64(const uint8_t *aEui64) { return WriteData(aEui64, sizeof(spinel_eui64_t)); }
423 
424     /**
425      * Encodes and writes an EUI48 value to current input frame.
426      *
427      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
428      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
429      *
430      * If no buffer space is available, this method will discard and clear the current input frame and return the
431      * error status `OT_ERROR_NO_BUFS`.
432      *
433      * @param[in]  aEui48               A reference to the EUI48 value.
434      *
435      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
436      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI48 value.
437      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
438      */
WriteEui48(const spinel_eui48_t & aEui48)439     otError WriteEui48(const spinel_eui48_t &aEui48) { return WriteEui48(aEui48.bytes); }
440 
441     /**
442      * Encodes and writes an EUI48 value to current input frame.
443      *
444      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
445      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
446      *
447      * If no buffer space is available, this method will discard and clear the current input frame and return the
448      * error status `OT_ERROR_NO_BUFS`.
449      *
450      * @param[in]  aEui48               A pointer to a buffer containing the EUI64 value.
451      *
452      * @retval OT_ERROR_NONE            Successfully added given value to the frame.
453      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the EUI48 value.
454      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
455      */
WriteEui48(const uint8_t * aEui48)456     otError WriteEui48(const uint8_t *aEui48) { return WriteData(aEui48, sizeof(spinel_eui48_t)); }
457 
458     /**
459      * Encodes and writes a UTF8 string to current input frame.
460      *
461      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
462      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
463      *
464      * If no buffer space is available, this method will discard and clear the current input frame and return the
465      * error status `OT_ERROR_NO_BUFS`.
466      *
467      * @param[in]  aUtf8                A const character pointer (C string).
468      *
469      * @retval OT_ERROR_NONE            Successfully added given string to the frame.
470      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the string.
471      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
472      */
473     otError WriteUtf8(const char *aUtf8);
474 
475     /**
476      * Encodes and writes a sequence of bytes to current input frame.
477      *
478      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
479      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
480      *
481      * If no buffer space is available, this method will discard and clear the current input frame and return the
482      * error status `OT_ERROR_NO_BUFS`.
483      *
484      * @param[in]  aData                A pointer to data buffer.
485      * @param[in]  aDataLen             The length (number of bytes) in the data buffer.
486      *
487      * @retval OT_ERROR_NONE            Successfully added given data to the frame.
488      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the byte.
489      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
490      */
WriteData(const uint8_t * aData,uint16_t aDataLen)491     otError WriteData(const uint8_t *aData, uint16_t aDataLen) { return mNcpBuffer.InFrameFeedData(aData, aDataLen); }
492 
493     /**
494      * Encodes and writes a data blob (sequence of bytes) with its length prepended before the data.
495      *
496      * The length of the data (in bytes) is prepended (with the length encoded as a `uint16`). The size of the length
497      * field is not included in the length. This is similar to `SPINEL_DATATYPE_DATA_WLEN` type.
498      *
499      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
500      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
501      *
502      * If no buffer space is available, this method will discard and clear the current input frame and return the
503      * error status `OT_ERROR_NO_BUFS`.
504      *
505      * @param[in]  aData                A pointer to data buffer.
506      * @param[in]  aDataLen             The length (number of bytes) in the data buffer.
507      *
508      * @retval OT_ERROR_NONE            Successfully added given data to the frame.
509      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the byte.
510      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
511      */
512     otError WriteDataWithLen(const uint8_t *aData, uint16_t aDataLen);
513 
514 #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE
515     /**
516      * Adds a message to the current input frame.
517      *
518      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
519      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
520      *
521      * If no buffer space is available, this method will discard and clear the frame and return error status
522      * `OT_ERROR_NO_BUFS`.
523      *
524      * The ownership of the passed-in message @p aMessage changes to underlying `Spinel::Buffer` ONLY when the entire
525      * frame is successfully finished (i.e., with a successful call to `EndFrame()` for the current frame being
526      * written), and in this case the `otMessage` instance will be freed once the frame is removed from the
527      * `Spinel::Buffer`. However, if the frame gets discarded before it is finished (e.g., running out of buffer space),
528      * the  `otMessage` instance remains unchanged.
529      *
530      * @param[in] aMessage              A message to be added to current frame.
531      *
532      * @retval OT_ERROR_NONE            Successfully added the message to the frame.
533      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the message.
534      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
535      * @retval OT_ERROR_INVALID_ARGS    If @p aMessage is nullptr.
536      */
WriteMessage(otMessage * aMessage)537     otError WriteMessage(otMessage *aMessage) { return mNcpBuffer.InFrameFeedMessage(aMessage); }
538 #endif
539 
540     /**
541      * Encodes and writes a set of variables to the current input frame using a given spinel packing format
542      * string.
543      *
544      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
545      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
546      *
547      * If no buffer space is available, this method will discard and clear the current input frame and return the
548      * error status `OT_ERROR_NO_BUFS`.
549      *
550      * Note that the encoded buffer should fit in `kPackFormatBufferSize` bytes.
551      *
552      * @param[in]  aPackFormat          A string giving the spinel packing format.
553      * @param[in]  ...                  Variable arguments corresponding to the types given in @p aPackFormat (see
554      *                                  `spinel_datatype_pack()`).
555      *
556      * @retval OT_ERROR_NONE            Successfully added given data to the frame.
557      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the byte.
558      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
559      */
560     otError WritePacked(const char *aPackFormat, ...);
561 
562     /**
563      * Encodes and writes a set of variables to the current input frame using a given spinel packing format
564      * string.
565      *
566      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
567      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
568      *
569      * If no buffer space is available, this method will discard and clear the current input frame and return the
570      * error status `OT_ERROR_NO_BUFS`.
571      *
572      * Note that the encoded buffer should fit in `kPackFormatBufferSize` bytes.
573      *
574      * @param[in]  aPackFormat          A string giving the spinel packing format.
575      * @param[in]  aArgs                Variable arguments corresponding to the types given in @p aPackFormat (see
576      *                                  `spinel_datatype_pack()`).
577      *
578      * @retval OT_ERROR_NONE            Successfully added given data to the frame.
579      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to add the byte.
580      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
581      */
582     otError WriteVPacked(const char *aPackFormat, va_list aArgs);
583 
584     /**
585      * Opens a struct in the current input frame.
586      *
587      * After a successful call to this method, all the subsequent `Write<SomeType>()` methods add the field/value to
588      * the current open struct until the struct is closed using `CloseStruct()` method. Structures can be nested. Up to
589      * `kMaxNestedStructs` nested structs can be opened at the same time.
590      *
591      * Before using this method `BeginFrame()` must be called to start and prepare a new input frame. Otherwise, this
592      * method does nothing and returns error status `OT_ERROR_INVALID_STATE`.
593      *
594      * If no buffer space is available, this method will discard and clear the frame and return error status
595      * `OT_ERROR_NO_BUFS`.
596      *
597      * @retval OT_ERROR_NONE            Successfully opened the struct.
598      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to open the struct.
599      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame or if we reached
600      *                                  the maximum number of nested open structures.
601      */
602     otError OpenStruct(void);
603 
604     /**
605      * Closes the most recently opened struct (using `OpenStruct()`) in the current input frame.
606      *
607      * Each call to `CloseStruct()` must correspond to an earlier successfully opened struct. If a frame is ended using
608      * `EndFrame()` with remaining open structs, the `EndFrame()` method will close all the remaining structs.
609      *
610      * If no buffer space is available, this method will discard and clear the frame and return error status
611      * `OT_ERROR_NO_BUFS`.
612      *
613      * @retval OT_ERROR_NONE            Successfully closed the most recently opened struct.
614      * @retval OT_ERROR_NO_BUFS         Insufficient buffer space available to open the struct.
615      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame or if there is no
616      *                                  open struct to close
617      */
618     otError CloseStruct(void);
619 
620     /**
621      * Saves the current write position in the input frame.
622      *
623      * The saved position can later be used to discard a portion of written/encoded frame and move the write pointer
624      * back to the saved position (using `ResetToSaved()`).
625      *
626      * @retval OT_ERROR_NONE            Successfully saved current write position in @p aPosition.
627      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
628      */
629     otError SavePosition(void);
630 
631     /**
632      * Resets the write position of input frame back to a previously saved position. Any added content
633      * after the write position is discarded.
634      *
635      * The saved position must belong to the same input frame saved earlier with `SavePosition()`. This method cannot
636      * be used if the input frame has an added `otMessage`.
637      *
638      * @retval OT_ERROR_NONE            Successfully reset the write position of current input frame.
639      * @retval OT_ERROR_INVALID_STATE   `BeginFrame()` has not been called earlier to start the frame.
640      * @retval OT_ERROR_INVALID_ARGS    The saved position is not valid (does not belong to same input frame), or
641      *                                  the input frame has an added `otMessage`.
642      */
643     otError ResetToSaved(void);
644 
645     /**
646      * Clear NCP buffer on reset command.
647      */
648     void ClearNcpBuffer(void);
649 
650 private:
651     enum
652     {
653         kPackFormatBufferSize = 96, ///< Size of buffer used when encoding using `WritePacked()` or `WriteVPacked()`.
654         kMaxNestedStructs     = 4,  ///< Maximum number of nested structs.
655     };
656 
657     Spinel::Buffer               &mNcpBuffer;
658     Spinel::Buffer::WritePosition mStructPosition[kMaxNestedStructs];
659     uint8_t                       mNumOpenStructs;
660 
661     uint8_t                       mSavedNumOpenStructs;
662     Spinel::Buffer::WritePosition mSavedPosition;
663 };
664 
665 } // namespace Spinel
666 } // namespace ot
667 
668 #endif // SPINEL_ENCODER_HPP_
669