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