text: /*
   *    Copyright (c) 2017, The OpenThread Authors.
   *    All rights reserved.
   *
   *    Redistribution and use in source and binary forms, with or without
   *    modification, are permitted provided that the following conditions are met:
   *    1. Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *    2. Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in the
   *       documentation and/or other materials provided with the distribution.
   *    3. Neither the name of the copyright holder nor the
   *       names of its contributors may be used to endorse or promote products
   *       derived from this software without specific prior written permission.
   *
   *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
   *    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   *    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   *    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
   *    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   *    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   *    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   *    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   *    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   *    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /**
   * @file
   *   This file contains the definitions of a spinel decoder.
   */
  
  #ifndef SPINEL_DECODER_HPP_
  #define SPINEL_DECODER_HPP_
  
  #include <openthread/config.h>
  
  #include <openthread/ncp.h>
  #include <openthread/types.h>
  
  #include "openthread-core-config.h"
  #include "ncp/spinel.h"
  
  namespace ot {
  namespace Ncp {
  
  /**
   * This class defines a spinel decoder.
   *
   */
  class SpinelDecoder
  {
  public:
      /**
       * This constructor initializes a `SpinelDecoder` object
       *
       */
      SpinelDecoder(void);
  
      /**
       * This method initializes the decoder to start decoding a new given spinel encoded frame.
       *
       * It sets the read position to beginning of the frame and also erases/voids any saved positions (see
       * `SavePosition()` and `ResetToSaved()` methods).
       *
       * @param[in] aFrame                Pointer to the buffer containing the frame to be decoded.
       * @param[in] aLength               Length (number of bytes) of the frame.
       *
       */
      void Init(const uint8_t *aFrame, uint16_t aLength);
  
      /**
       * This method returns a pointer to the frame.
       *
       * @returns A pointer to buffer containing current frame being decoded.
       *
       */
      const uint8_t *GetFrame(void) const { return mFrame; }
  
      /**
       * This method returns the total length of current frame being decoded.
       *
       * @returns The length of current frame being decoded.
       *
       */
      uint16_t GetLength(void) const { return mLength; }
  
      /**
       * This method returns the number of bytes that are already read/decoded from the frame.
       *
       * @returns The number of bytes already read from frame.
       *
       */
      uint16_t GetReadLength(void) const { return mIndex; }
  
      /**
       * This method returns the number of remaining (not yet read/decoded) bytes in the frame.
       *
       * @returns The number of remaining unread bytes in the frame.
       *
       */
      uint16_t GetRemainingLength(void) const { return mLength - mIndex; }
  
      /**
       * This method indicates whether or not all the bytes in the frame are read.
       *
       * @returns TRUE if all the bytes in the buffer are read, FALSE otherwise.
       *
       */
      bool IsAllRead(void) const { return (mIndex == mLength); }
  
      /**
       * This method resets the read position to beginning of frame. It will also void/erase any previously saved
       * position using `SavePosition()` method.
       *
       */
      void Reset(void);
  
      /**
       * This method decodes and reads a boolean value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aBool                Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadBool(bool &aBool);
  
      /**
       * This method decodes and reads an `int8_t` value form the frame.
       *
       * On success, the read position get updated.
       *
       * @param[out] aInt8                Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadInt8(int8_t &aInt8);
  
      /**
       * This method decodes and reads an `uint8_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUint8               Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUint8(uint8_t &aUint8);
  
      /**
       * This method decodes and reads an `int16_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aInt16               Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadInt16(int16_t &aInt16);
  
      /**
       * This method decodes and reads an `uint16_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUint16              Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUint16(uint16_t &aUint16);
  
      /**
       * This method decodes and reads an `int32_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aInt32               Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadInt32(int32_t &aInt32);
  
      /**
       * This method decodes and reads an `uint32_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUint32              Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUint32(uint32_t &aUint32);
  
      /**
       * This method decodes and reads an `int64_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aInt64               Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadInt64(int64_t &aInt64);
  
      /**
       * This method decodes and reads an `uint64_t` value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUint64              Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUint64(uint64_t &aUint64);
  
      /**
       * This method decodes (using spinel packed integer format) and reads an unsigned integer value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUint                Reference to variable to output the read value.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUintPacked(unsigned int &aUint);
  
      /**
       * This method decodes and reads an IPv6 address form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aIp6AddrPtr          Reference to IPv6 address pointer to output the value (as `spinel_ipv6addr_t`).
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadIp6Address(const spinel_ipv6addr_t *&aIp6AddrPtr)
      {
          return ReadItem(reinterpret_cast<const uint8_t **>(&aIp6AddrPtr), sizeof(spinel_ipv6addr_t));
      }
  
      /**
       * This method decodes and reads an IPv6 address form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aIp6AddrPtr          Reference to IPv6 address pointer to output the value (as `otIp6Address`).
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadIp6Address(const otIp6Address *&aIp6AddrPtr)
      {
          return ReadItem(reinterpret_cast<const uint8_t **>(&aIp6AddrPtr), sizeof(spinel_ipv6addr_t));
      }
  
      /**
       * This method decodes and reads an IPv6 address form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aIp6AddrBufPtr       Reference to a buffer pointer to output the value.
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadIp6Address(const uint8_t *&aIp6AddrBufPtr)
      {
          return ReadItem(&aIp6AddrBufPtr, sizeof(spinel_ipv6addr_t));
      }
  
      /**
       * This method decodes and reads an IPv6 address form the frame.
       *
       * On success, the read position gets updated and the IP address is copied into the given output variable.
       *
       * @param[out] aIp6Addr             Reference to IPv6 address variable to output the value (as spinel_ipv6addr_t).
       *                                  On success, the address is copied into the output variable.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadIp6Address(spinel_ipv6addr_t &aIp6Addr);
  
      /**
       * This method decodes and reads an IPv6 address form the frame.
       *
       * On success, the read position gets updated and the IP address is copied into the given output variable.
       *
       * @param[out] aIp6Addr             Reference to IPv6 address variable to output the value (as otIp6Address).
       *                                  On success, the address is copied into the output variable.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadIp6Address(otIp6Address &aIp6Addr);
  
      /**
       * This method decodes and reads an EUI64 value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aEui64Ptr            Reference to an EUI64 pointer to output the value (as `spinel_eui64_t`).
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui64(const spinel_eui64_t *&aEui64Ptr)
      {
          return ReadItem(reinterpret_cast<const uint8_t **>(&aEui64Ptr), sizeof(spinel_eui64_t));
      }
  
      /**
       * This method decodes and reads an EUI64 value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aEui64Ptr            Reference to an EUI64 pointer to output the value (as `otExtAddress`).
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui64(const otExtAddress *&aEui64Ptr)
      {
          return ReadItem(reinterpret_cast<const uint8_t **>(&aEui64Ptr), sizeof(spinel_eui64_t));
      }
  
      /**
       * This method decodes and reads an EUI64 value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aEui64BufPtr         Reference to a buffer pointer to output the value.
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui64(const uint8_t *&aEui64BufPtr) { return ReadItem(&aEui64BufPtr, sizeof(spinel_eui64_t)); }
  
      /**
       * This method decodes and reads an EUI64 value form the frame.
       *
       * On success, the read position gets updated and the EUI64 value is copied into the given output variable.
       *
       * @param[out] aEui64               Reference to EUI64 variable to output the value (as spinel_eui64_t).
       *                                  On success, the address is copied into the output variable.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui64(spinel_eui64_t &aEui64);
  
      /**
       * This method decodes and reads an EUI64 value form the frame.
       *
       * On success, the read position gets updated and the EUI64 value is copied into the given output variable.
       *
       * @param[out] aEui64               Reference to EUI64 variable to output the value (as otExtAddress).
       *                                  On success, the address is copied into the output variable.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui64(otExtAddress &aEui64);
  
      /**
       * This method decodes and reads an EUI48 value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aEui48Ptr            Reference to an EUI48 pointer to output the value (as `spinel_eui48_t`).
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui48(const spinel_eui48_t *&aEui48Ptr)
      {
          return ReadItem(reinterpret_cast<const uint8_t **>(&aEui48Ptr), sizeof(spinel_eui48_t));
      }
  
      /**
       * This method decodes and reads an EUI48 value form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aEui48BufPtr         Reference to a buffer pointer to output the value.
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui48(const uint8_t *&aEui48BufPtr) { return ReadItem(&aEui48BufPtr, sizeof(spinel_eui48_t)); }
  
      /**
       * This method decodes and reads an EUI48 value form the frame.
       *
       * On success, the read position gets updated and the EUI48 value is copied into the given output variable.
       *
       * @param[out] aEui48               Reference to EUI48 variable to output the value (as spinel_eui48_t).
       *                                  On success, value is copied into the output variable.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadEui48(spinel_eui48_t &aEui48);
  
      /**
       * This method decodes and reads a UTF8 string form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aUt8                 Reference to a `char` pointer to output the string.
       *                                  On success, the pointer variable is updated.
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadUtf8(const char *&aUt8);
  
      /**
       * This method decodes and reads a data blob (sequence of bytes) form the frame.
       *
       * On success, the read position gets updated.
       *
       * @param[out] aData                Reference to pointer variable to output the data.
       *                                  On success, the pointer variable is updated.
       * @param[out] aDataLength          Reference to variable to out the data length (number of bytes).
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadData(const uint8_t *&aData, uint16_t &aDataLen);
  
      /**
       * This method decodes and reads a data blob (sequence of bytes) with data length.
       *
       * The data length is  assumed to be prepended before the data content (encoded as a `uint16_t`). The size of the
       * length field should not be included in the length value. This method corresponds  to `SPINEL_DATATYPE_DATA_WLEN`
       * type.
       *
       * @param[out] aData                Reference to pointer variable to output the data.
       *                                  On success, the pointer variable is updated.
       * @param[out] aDataLength          Reference to variable to out the data length (number of bytes).
       *
       * @retval OT_ERROR_NONE            Successfully read the value.
       * @retval OT_ERROR_PARSE           Failed to parse/decode the value.
       *
       */
      otError ReadDataWithLen(const uint8_t *&aData, uint16_t &aDataLen);
  
      /**
       * This method opens a struct in the frame.
       *
       * After a successful call to this method, all the subsequent `Read<SomeType>()` methods decode and read the
       * field/value from the current open struct until the struct is closed using `CloseStruct()` method. Structures can
       * be nested. Up to `kMaxNestedStructs` nested structs can be opened at the same time.
       *
       * @retval OT_ERROR_NONE            Successfully opened a struct.
       * @retval OT_ERROR_PARSE           Failed to parse/open a struct.
       * @retval OT_ERROR_INVALID_STATE   Already at the maximum number of nested open structures.
       *
       */
      otError OpenStruct(void);
  
      /**
       * This method closes the most recently opened struct (using `OpenStruct()`) in the frame.
       *
       * On success, the read position is moved to end of the struct skipping any unread bytes within the struct.
       *
       * @retval OT_ERROR_NONE            Successfully closed the struct.
       * @retval OT_ERROR_INVALID_STATE   There is no current open struct to close.
       *
       */
      otError CloseStruct(void);
  
      /**
       * This method returns the number of remaining/unread bytes in the current inner-most open structure.
       *
       * If there is no currently open structure the number of remaining bytes in whole frame is returned instead.
       *
       * @returns The number of remaining unread bytes in the inner-most open structure.
       *
       */
      uint16_t GetRemainingLengthInStruct(void) const { return mEnd - mIndex; }
  
      /**
       * This method indicates whether or not all the bytes in inner-most open structure are read.
       *
       * If there is no currently open structure, the whole frame is considered instead.
       *
       * @returns TRUE if all the bytes are read, FALSE otherwise.
       *
       */
      bool IsAllReadInStruct(void) const { return (mIndex == mEnd); }
  
      /**
       * This method saves the current read position in the frame.
  
       * A subsequent call to `SavePosition()` will overwrite the previously saved position. The saved position can be
       * used to to move the read position back (using `ResetToSaved()`) and re-read the same content.
       *
       * Saved position can be within an open struct, and it remembers its enclosing struct. When the enclosing struct is
       * closed, the saved position will be voided and can no longer be used. This ensures that we cannot jump back to
       * middle an already fully decoded/read and closed struct.
       *
       */
      void SavePosition(void);
  
      /**
       * This method resets/moves the read position to a previously saved position.
       *
       * The saved position remembers its enclosing structure. When `ResetToSaved()` is called, the current open
       * structure will be the same as when position was saved.
       *
       * @retval OT_ERROR_NONE            Successfully reset the read position.
       * @retval OT_ERROR_INVALID_STATE   The saved position is not valid (there is no saved position or the saved
       *                                  position was voided since its enclosing struct was closed).
       *
       */
      otError ResetToSaved(void);
  
  private:
      otError ReadItem(const uint8_t **aPtr, size_t aSize);
      void    ClearSavedPosition(void) { mSavedIndex = mLength; }
      bool    IsSavedPositionValid(void) const { return (mSavedIndex < mLength); }
  
      enum
      {
          kMaxNestedStructs = 4, ///< Maximum number of nested structs.
      };
  
      const uint8_t *mFrame;          // Frame buffer
      uint16_t       mLength;         // Total length of the buffer.
      uint16_t       mIndex;          // Current read index.
      uint16_t       mEnd;            // Current end index (end of struct if in a struct, or end of buffer otherwise).
      uint8_t        mNumOpenStructs; // Number of open structs.
  
      uint8_t  mSavedNumOpenStructs; // Number of open structs when read position was saved.
      uint16_t mSavedIndex;          // Read index when position was saved.
      uint16_t mSavedEnd;            // End index when position was saved.
  
      uint16_t mPrevEnd[kMaxNestedStructs];
  };
  
  } // namespace Ncp
  } // namespace ot
  
  #endif // SPINEL_DECODER_HPP_
  