| /* |
| * |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements a parser for the Weave TLV (Tag-Length-Value) encoding format. |
| * |
| */ |
| |
| #include <stdlib.h> |
| |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Core/WeaveTLV.h> |
| #include <Weave/Support/CodeUtils.h> |
| |
| namespace nl { |
| namespace Weave { |
| namespace TLV { |
| |
| using namespace nl::Weave::Encoding; |
| |
| static const uint8_t sTagSizes[] = { 0, 1, 2, 4, 2, 4, 6, 8 }; |
| |
| /** |
| * @fn uint32_t TLVReader::GetLengthRead() const |
| * |
| * Returns the total number of bytes read since the reader was initialized. |
| * |
| * @return Total number of bytes read since the reader was initialized. |
| */ |
| |
| /** |
| * @fn uint32_t TLVReader::GetRemainingLength() const |
| * |
| * Returns the total number of bytes that can be read until the max read length is reached. |
| * |
| * @return Total number of bytes that can be read until the max read length is reached. |
| */ |
| |
| /** |
| * @fn const uint8_t *TLVReader::GetReadPoint() const |
| * |
| * Gets the point in the underlying input buffer that corresponds to the reader's current position. |
| * |
| * @note Depending on the type of the current element, GetReadPoint() will return a pointer that |
| * is some number of bytes *after* the first byte of the element. For string types (UTF8 and byte |
| * strings), the pointer will point to the first byte of the string's value. For container types |
| * (structures, arrays and paths), the pointer will point to the first member element within the |
| * container. For all other types, the pointer will point to the byte immediately after the element's |
| * encoding. |
| * |
| * @return A pointer into underlying input buffer that corresponds to the reader's current position. |
| */ |
| |
| /** |
| * |
| * @var uint32_t TLVReader::ImplicitProfileId |
| * |
| * The profile id to be used for profile tags encoded in implicit form. |
| * |
| * When the reader encounters a profile-specific tag that has been encoded in implicit form, it |
| * uses the value of the @p ImplicitProfileId property as the assumed profile id for the tag. |
| * |
| * By default, the @p ImplicitProfileId property is set to kProfileIdNotSpecified. When decoding |
| * TLV that contains implicitly-encoded tags, applications must set @p ImplicitProfileId prior |
| * to reading any TLV elements having such tags. The appropriate profile id is usually dependent |
| * on the context of the application or protocol being spoken. |
| * |
| * If an implicitly-encoded tag is encountered while @p ImplicitProfileId is set to |
| * kProfileIdNotSpecified, the reader will return a #WEAVE_ERROR_UNKNOWN_IMPLICIT_TLV_TAG error. |
| */ |
| |
| /** |
| * @var void *TLVReader::AppData |
| * |
| * A pointer field that can be used for application-specific data. |
| */ |
| |
| /** |
| * @typedef WEAVE_ERROR (*TLVReader::GetNextBufferFunct)(TLVReader& reader, uintptr_t& bufHandle, const uint8_t *& bufStart, uint32_t& bufLen) |
| * |
| * A function that can be used to retrieve additional TLV data to be parsed. |
| * |
| * Functions of this type are used to feed input data to a TLVReader. When called, the function is |
| * expected to produce additional data for the reader to parse or signal the reader that no more |
| * data is available. |
| * |
| * @param[in] reader A reference to the TLVReader object that is requesting input data. |
| * @param[in,out] bufHandle A reference to a uintptr_t value that the function can use to store |
| * context data between calls. This value is initialized to 0 |
| * prior to the first call. |
| * @param[in,out] bufStart A reference to a data pointer. On entry to the function, @p bufStart |
| * points to one byte beyond the last TLV data byte consumed by the |
| * reader. On exit, bufStart is expected to point to the first byte |
| * of new TLV data to be parsed. The new pointer value can be within |
| * the same buffer as the previously consumed data, or it can point |
| * to an entirely new buffer. |
| * @param[out] bufLen A reference to an unsigned integer that the function must set to |
| * the number of TLV data bytes being returned. If the end of the |
| * input TLV data has been reached, the function should set this value |
| * to 0. |
| * |
| * @retval #WEAVE_NO_ERROR If the function successfully produced more TLV data, or the end of |
| * the input data was reached (@p bufLen should be set to 0 in this case). |
| * @retval other Other Weave or platform-specific error codes indicating that an error |
| * occurred preventing the function from producing the requested data. |
| * |
| */ |
| |
| /** |
| * @var GetNextBufferFunct TLVReader::GetNextBuffer |
| * |
| * A pointer to a function that will produce input data for the TLVReader object. If set to NULL (the |
| * default value), the reader will assume that no further input data is available. |
| * |
| * GetNextBuffer can be set by an application at any time, but is typically set when the reader |
| * is initialized. |
| * |
| * See the GetNextBufferFunct type definition for additional information on implementing a |
| * GetNextBuffer function. |
| */ |
| |
| /** |
| * Initializes a TLVReader object to read from a single input buffer. |
| * |
| * @param[in] data A pointer to a buffer containing the TLV data to be parsed. |
| * @param[in] dataLen The length of the TLV data to be parsed. |
| * |
| */ |
| void TLVReader::Init(const uint8_t *data, uint32_t dataLen) |
| { |
| mBufHandle = 0; |
| mReadPoint = data; |
| mBufEnd = data + dataLen; |
| mLenRead = 0; |
| mMaxLen = dataLen; |
| ClearElementState(); |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| AppData = NULL; |
| GetNextBuffer = NULL; |
| } |
| |
| /** |
| * Initializes a TLVReader object to read from a single PacketBuffer. |
| * |
| * Parsing begins at the buffer's start position (buf->DataStart()) and continues until the |
| * end of the data in the buffer (as denoted by buf->Datalen()), or maxLen bytes have been parsed. |
| * |
| * @param[in] buf A pointer to an PacketBuffer containing the TLV data to be parsed. |
| * @param[in] maxLen The maximum of bytes to parse. Defaults to the amount of data |
| * in the input buffer. |
| */ |
| void TLVReader::Init(PacketBuffer *buf, uint32_t maxLen) |
| { |
| mBufHandle = (uintptr_t) buf; |
| mReadPoint = buf->Start(); |
| mBufEnd = mReadPoint + buf->DataLength(); |
| mLenRead = 0; |
| mMaxLen = maxLen; |
| ClearElementState(); |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| AppData = NULL; |
| GetNextBuffer = NULL; |
| } |
| |
| /** |
| * Initializes a TLVReader object to read from a one or more PacketBuffers. |
| * |
| * Parsing begins at the initial buffer's start position (buf->DataStart()). If |
| * allowDiscontiguousBuffers is true, the reader will advance through the chain of buffers linked |
| * by their Next() pointers. Parsing continues until all data in the buffer chain has been consumed |
| * (as denoted by buf->Datalen()), or maxLen bytes have been parsed. |
| * |
| * @param[in] buf A pointer to an PacketBuffer containing the TLV data to be parsed. |
| * @param[in] maxLen The maximum of bytes to parse. Defaults to the total amount of data |
| * in the input buffer chain. |
| * @param[in] allowDiscontiguousBuffers |
| * If true, advance to the next buffer in the chain once all data in the |
| * current buffer has been consumed. If false, stop parsing at the end |
| * of the initial buffer. |
| */ |
| void TLVReader::Init(PacketBuffer *buf, uint32_t maxLen, bool allowDiscontiguousBuffers) |
| { |
| mBufHandle = (uintptr_t) buf; |
| mReadPoint = buf->Start(); |
| mBufEnd = mReadPoint + buf->DataLength(); |
| mLenRead = 0; |
| mMaxLen = maxLen; |
| ClearElementState(); |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| AppData = NULL; |
| |
| if (allowDiscontiguousBuffers) |
| { |
| GetNextBuffer = GetNextPacketBuffer; |
| } |
| else |
| { |
| GetNextBuffer = NULL; |
| } |
| } |
| |
| /** |
| * Initializes a TLVReader object from another TLVReader object. |
| * |
| * @param[in] aReader A read-only reference to the TLVReader to initialize |
| * this from. |
| * |
| */ |
| void TLVReader::Init(const TLVReader &aReader) |
| { |
| // Initialize private data members |
| |
| mElemTag = aReader.mElemTag; |
| mElemLenOrVal = aReader.mElemLenOrVal; |
| mBufHandle = aReader.mBufHandle; |
| mReadPoint = aReader.mReadPoint; |
| mBufEnd = aReader.mBufEnd; |
| mLenRead = aReader.mLenRead; |
| mMaxLen = aReader.mMaxLen; |
| mControlByte = aReader.mControlByte; |
| mContainerType = aReader.mContainerType; |
| SetContainerOpen(aReader.IsContainerOpen()); |
| |
| // Initialize public data members |
| |
| ImplicitProfileId = aReader.ImplicitProfileId; |
| AppData = aReader.AppData; |
| GetNextBuffer = aReader.GetNextBuffer; |
| } |
| |
| /** |
| * Returns the type of the current TLV element. |
| * |
| * @return A TLVType value describing the data type of the current TLV element. If the reader |
| * is not positioned on a TLV element, the return value will be kTLVType_NotSpecified. |
| */ |
| TLVType TLVReader::GetType() const |
| { |
| TLVElementType elemType = ElementType(); |
| if (elemType == kTLVElementType_EndOfContainer) |
| return kTLVType_NotSpecified; |
| if (elemType == kTLVElementType_FloatingPointNumber32 || elemType == kTLVElementType_FloatingPointNumber64) |
| return kTLVType_FloatingPointNumber; |
| if (elemType == kTLVElementType_NotSpecified || elemType >= kTLVElementType_Null) |
| return (TLVType) elemType; |
| return (TLVType) (elemType & ~kTLVTypeSizeMask); |
| } |
| |
| /** |
| * Returns the control byte associated with current TLV element. |
| * |
| * Ideally, nobody ever needs to know about the control byte and only the |
| * internal implementation of TLV should have access to it. But, nevertheless, |
| * having access to the control byte is helpful for debugging purposes by the |
| * TLV Debug Utilities (that try to decode the tag control byte when pretty |
| * printing the TLV buffer contents). |
| * |
| * @note Unless you really know what you are doing, please refrain from using |
| * this method and the associated control byte information. |
| * |
| * @return An unsigned integer containing the control byte associated with |
| * the current TLV element. kTLVControlByte_NotSpecified is |
| * returned if the reader is not positioned @em on an element. |
| */ |
| uint16_t TLVReader::GetControlByte() const |
| { |
| return mControlByte; |
| } |
| |
| /** |
| * Returns the tag associated with current TLV element. |
| * |
| * The value returned by GetTag() can be used with the tag utility functions (IsProfileTag(), |
| * IsContextTag(), ProfileIdFromTag(), etc.) to determine the type of tag and to extract various tag |
| * field values. |
| * |
| * @note If the reader is not positioned on a TLV element when GetTag() is called, the return value |
| * is undefined. Therefore whenever the position of the reader is uncertain applications should call |
| * GetType() to determine if the reader is position on an element (GetType() != kTLVType_NotSpecified) |
| * before calling GetTag(). |
| * |
| * @return An unsigned integer containing information about the tag associated with the current |
| * TLV element. |
| */ |
| uint64_t TLVReader::GetTag() const |
| { |
| return mElemTag; |
| } |
| |
| /** |
| * Returns the length of data associated with current TLV element. |
| * |
| * Data length only applies to elements of type UTF8 string or byte string. For UTF8 strings, the |
| * value returned is the number of bytes in the string, not the number of characters. |
| * |
| * @return The length (in bytes) of data associated with the current TLV element, or 0 if the |
| * current element is not a UTF8 string or byte string, or if the reader is not |
| * positioned on an element. |
| */ |
| uint32_t TLVReader::GetLength() const |
| { |
| if (TLVTypeHasLength(ElementType())) |
| return (uint32_t) mElemLenOrVal; |
| else |
| return 0; |
| } |
| |
| /** |
| * Get the value of the current element as a bool type. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV boolean type, or the |
| * reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(bool& v) |
| { |
| TLVElementType elemType = ElementType(); |
| if (elemType == kTLVElementType_BooleanFalse) |
| v = false; |
| else if (elemType == kTLVElementType_BooleanTrue) |
| v = true; |
| else |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Get the value of the current element as an 8-bit signed integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(int8_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 16-bit signed integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(int16_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 32-bit signed integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(int32_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 64-bit signed integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(int64_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as an 8-bit unsigned integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. Similarly, if the encoded integer value is negative, the value will be converted |
| * to unsigned. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(uint8_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 16-bit unsigned integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. Similarly, if the encoded integer value is negative, the value will be converted |
| * to unsigned. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(uint16_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 32-bit unsigned integer. |
| * |
| * If the encoded integer value is larger than the output data type the resultant value will be |
| * truncated. Similarly, if the encoded integer value is negative, the value will be converted |
| * to unsigned. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(uint32_t& v) |
| { |
| uint64_t v64 = 0; |
| WEAVE_ERROR err = Get(v64); |
| v = v64; |
| return err; |
| } |
| |
| /** |
| * Get the value of the current element as a 64-bit unsigned integer. |
| * |
| * If the encoded integer value is negative, the value will be converted to unsigned. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV integer type (signed or |
| * unsigned), or the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(uint64_t& v) |
| { |
| switch (ElementType()) |
| { |
| case kTLVElementType_Int8: |
| v = (int64_t) (int8_t) mElemLenOrVal; |
| break; |
| case kTLVElementType_Int16: |
| v = (int64_t) (int16_t) mElemLenOrVal; |
| break; |
| case kTLVElementType_Int32: |
| v = (int64_t) (int32_t) mElemLenOrVal; |
| break; |
| case kTLVElementType_Int64: |
| case kTLVElementType_UInt8: |
| case kTLVElementType_UInt16: |
| case kTLVElementType_UInt32: |
| case kTLVElementType_UInt64: |
| v = mElemLenOrVal; |
| break; |
| default: |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| } |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Get the value of the current element as a double-precision floating point number. |
| * |
| * @param[out] v Receives the value associated with current TLV element. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV floating point type, or |
| * the reader is not positioned on an element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Get(double& v) |
| { |
| switch (ElementType()) |
| { |
| case kTLVElementType_FloatingPointNumber32: |
| { |
| union |
| { |
| uint32_t u32; |
| float f; |
| } cvt; |
| cvt.u32 = (uint32_t)mElemLenOrVal; |
| v = cvt.f; |
| break; |
| } |
| case kTLVElementType_FloatingPointNumber64: |
| { |
| union |
| { |
| uint64_t u64; |
| double d; |
| } cvt; |
| cvt.u64 = mElemLenOrVal; |
| v = cvt.d; |
| break; |
| } |
| default: |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| } |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Get the value of the current byte or UTF8 string element. |
| * |
| * To determine the required input buffer size, call the GetLength() method before calling GetBytes(). |
| * |
| * @note The data output by this method is NOT null-terminated. |
| * |
| * @param[in] buf A pointer to a buffer to receive the string data. |
| * @param[in] bufSize The size in bytes of the buffer pointed to by @p buf. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or |
| * the reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If the supplied buffer is too small to hold the data associated |
| * with the current element. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::GetBytes(uint8_t *buf, uint32_t bufSize) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| |
| if (mElemLenOrVal > bufSize) |
| return WEAVE_ERROR_BUFFER_TOO_SMALL; |
| |
| WEAVE_ERROR err = ReadData(buf, (uint32_t) mElemLenOrVal); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| mElemLenOrVal = 0; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Get the value of the current byte or UTF8 string element as a null terminated string. |
| * |
| * To determine the required input buffer size, call the GetLength() method before calling GetBytes(). |
| * The input buffer should be at least one byte bigger than the string length to accommodate the null |
| * character. |
| * |
| * @param[in] buf A pointer to a buffer to receive the byte string data. |
| * @param[in] bufSize The size in bytes of the buffer pointed to by @p buf. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or |
| * the reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If the supplied buffer is too small to hold the data associated |
| * with the current element. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::GetString(char *buf, uint32_t bufSize) |
| { |
| if (!TLVTypeIsString(ElementType())) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| |
| if ((mElemLenOrVal + 1) > bufSize) |
| return WEAVE_ERROR_BUFFER_TOO_SMALL; |
| |
| buf[mElemLenOrVal] = 0; |
| |
| return GetBytes((uint8_t *) buf, bufSize - 1); |
| } |
| |
| /** |
| * Allocates and returns a buffer containing the value of the current byte or UTF8 string. |
| * |
| * This method creates a buffer for and returns a copy of the data associated with the byte |
| * or UTF-8 string element at the current position. Memory for the buffer is obtained with |
| * malloc() and should be freed with free() by the caller when it is no longer needed. |
| * |
| * @note The data returned by this method is NOT null-terminated. |
| * |
| * @param[out] buf A reference to a pointer to which a heap-allocated buffer of |
| * @p dataLen bytes will be assigned on success. |
| * @param[out] dataLen A reference to storage for the size, in bytes, of @p buf on |
| * success. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or |
| * the reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_NO_MEMORY If memory could not be allocated for the output buffer. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE |
| * If the target platform does not support malloc() and free(). |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer |
| * is non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::DupBytes(uint8_t *& buf, uint32_t& dataLen) |
| { |
| #if HAVE_MALLOC && HAVE_FREE |
| if (!TLVTypeIsString(ElementType())) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| |
| buf = (uint8_t *) malloc(mElemLenOrVal); |
| if (buf == NULL) |
| return WEAVE_ERROR_NO_MEMORY; |
| |
| WEAVE_ERROR err = ReadData(buf, (uint32_t) mElemLenOrVal); |
| if (err != WEAVE_NO_ERROR) |
| { |
| free(buf); |
| return err; |
| } |
| |
| dataLen = mElemLenOrVal; |
| mElemLenOrVal = 0; |
| |
| return WEAVE_NO_ERROR; |
| #else |
| return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE; |
| #endif // HAVE_MALLOC && HAVE_FREE |
| } |
| |
| /** |
| * Allocates and returns a buffer containing the null-terminated value of the current byte or UTF8 |
| * string. |
| * |
| * This method creates a buffer for and returns a null-terminated copy of the data associated with |
| * the byte or UTF-8 string element at the current position. Memory for the buffer is obtained with |
| * malloc() and should be freed with free() by the caller when it is no longer needed. |
| * |
| * @param[out] buf A reference to a pointer to which a heap-allocated buffer of |
| * will be assigned on success. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or |
| * the reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_NO_MEMORY If memory could not be allocated for the output buffer. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE |
| * If the target platform does not support malloc() and free(). |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer |
| * is non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::DupString(char *& buf) |
| { |
| #if HAVE_MALLOC && HAVE_FREE |
| if (!TLVTypeIsString(ElementType())) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| |
| buf = (char *) malloc(mElemLenOrVal + 1); |
| if (buf == NULL) |
| return WEAVE_ERROR_NO_MEMORY; |
| |
| WEAVE_ERROR err = ReadData((uint8_t *) buf, (uint32_t) mElemLenOrVal); |
| if (err != WEAVE_NO_ERROR) |
| { |
| free(buf); |
| return err; |
| } |
| |
| buf[mElemLenOrVal] = 0; |
| mElemLenOrVal = 0; |
| |
| return err; |
| #else |
| return WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE; |
| #endif // HAVE_MALLOC && HAVE_FREE |
| } |
| |
| /** |
| * Get a pointer to the initial encoded byte of a TLV byte or UTF8 string element. |
| * |
| * This method returns a direct pointer the encoded string value within the underlying input buffer. |
| * To succeed, the method requires that the entirety of the string value be present in a single buffer. |
| * Otherwise the method returns #WEAVE_ERROR_TLV_UNDERRUN. This makes the method of limited use when |
| * reading data from multiple discontiguous buffers. |
| * |
| * @param[out] data A reference to a const pointer that will receive a pointer to |
| * the underlying string data. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV byte or UTF8 string, or the |
| * reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely or the value |
| * of the current string element is not contained within a single |
| * contiguous buffer. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::GetDataPtr(const uint8_t *& data) |
| { |
| WEAVE_ERROR err; |
| |
| if (!TLVTypeIsString(ElementType())) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| |
| err = EnsureData(WEAVE_ERROR_TLV_UNDERRUN); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| uint32_t remainingLen = mBufEnd - mReadPoint; |
| |
| // Verify that the entirety of the data is available in the buffer. |
| // Note that this may not be possible if the reader is reading from a chain of buffers. |
| if (remainingLen < (uint32_t)mElemLenOrVal) |
| return WEAVE_ERROR_TLV_UNDERRUN; |
| |
| data = mReadPoint; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Initializes a new TLVReader object for reading the members of a TLV container element. |
| * |
| * The OpenContainer() method initializes a new TLVReader object for reading the member elements of a |
| * TLV container (a structure, array or path). When OpenContainer() is called, the current TLVReader |
| * object must be positioned on the container element to be read. The method takes as its sole argument |
| * a reference to a new reader that will be initialized to read the container. This reader is known as |
| * the <em>container reader</em> while the reader on which OpenContainer() is called is known as the <em>parent |
| * reader</em>. |
| * |
| * When the OpenContainer() method returns, the container reader is positioned immediately before the |
| * first member of the container. Calling Next() on the container reader will advance through the members |
| * of the collection until the end is reached, at which point the reader will return WEAVE_END_OF_TLV. |
| * |
| * While the container reader is open, applications must not make calls on or otherwise alter the state |
| * of the parent reader. Once an application has finished using the container reader it must close it |
| * by calling CloseContainer() on the parent reader, passing the container reader as an argument. |
| * Applications may close the container reader at any point, with or without reading all elements |
| * contained in the underlying container. After the container reader is closed, applications may |
| * continue their use of the parent reader. |
| * |
| * The container reader inherits various configuration properties from the parent reader. These are: |
| * |
| * @li The implicit profile id (ImplicitProfileId) |
| * @li The application data pointer (AppData) |
| * @li The GetNextBuffer function pointer |
| * |
| * @note The EnterContainer() method can be used as an alternative to OpenContainer() to read a |
| * container element without initializing a new reader object. |
| * |
| * @param[out] containerReader A reference to a TLVReader object that will be initialized for |
| * reading the members of the current container element. Any data |
| * associated with the supplied object is overwritten. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the current element is not positioned on a container element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::OpenContainer(TLVReader& containerReader) |
| { |
| TLVElementType elemType = ElementType(); |
| if (!TLVTypeIsContainer(elemType)) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| containerReader.mBufHandle = mBufHandle; |
| containerReader.mReadPoint = mReadPoint; |
| containerReader.mBufEnd = mBufEnd; |
| containerReader.mLenRead = mLenRead; |
| containerReader.mMaxLen = mMaxLen; |
| containerReader.ClearElementState(); |
| containerReader.mContainerType = (TLVType) elemType; |
| containerReader.SetContainerOpen(false); |
| containerReader.ImplicitProfileId = ImplicitProfileId; |
| containerReader.AppData = AppData; |
| containerReader.GetNextBuffer = GetNextBuffer; |
| |
| SetContainerOpen(true); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Completes the reading of a TLV container after a call to OpenContainer(). |
| * |
| * The CloseContainer() method restores the state of a parent TLVReader object after a call to |
| * OpenContainer(). For every call to OpenContainer() applications must make a corresponding |
| * call to CloseContainer(), passing a reference to the same container reader to both methods. |
| * |
| * When CloseContainer() returns, the parent reader is positioned immediately before the first |
| * element that follows the container. From this point an application can use the Next() method |
| * to advance through any remaining elements. |
| * |
| * Applications can call close CloseContainer() on a parent reader at any point in time, regardless |
| * of whether all elements in the underlying container have been read. After CloseContainer() has |
| * been called, the application should consider the container reader 'de-initialized' and must not |
| * use it further without re-initializing it. |
| * |
| * @param[in] containerReader A reference to the TLVReader object that was supplied to the |
| * OpenContainer() method. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If OpenContainer() has not been called on the reader, or if |
| * the container reader does not match the one passed to the |
| * OpenContainer() method. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV |
| * element type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::CloseContainer(TLVReader& containerReader) |
| { |
| WEAVE_ERROR err; |
| |
| if (!IsContainerOpen()) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| if ((TLVElementType) containerReader.mContainerType != ElementType()) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| err = containerReader.SkipToEndOfContainer(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| mBufHandle = containerReader.mBufHandle; |
| mReadPoint = containerReader.mReadPoint; |
| mBufEnd = containerReader.mBufEnd; |
| mLenRead = containerReader.mLenRead; |
| mMaxLen = containerReader.mMaxLen; |
| ClearElementState(); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Prepares a TLVReader object for reading the members of TLV container element. |
| * |
| * The EnterContainer() method prepares the current TLVReader object to begin reading the member |
| * elements of a TLV container (a structure, array or path). For every call to EnterContainer() |
| * applications must make a corresponding call to ExitContainer(). |
| * |
| * When EnterContainer() is called the TLVReader object must be positioned on the container element |
| * to be read. The method takes as an argument a reference to a TLVType value which will be used |
| * to save the context of the reader while it is reading the container. |
| * |
| * When the EnterContainer() method returns, the reader is positioned immediately @em before the |
| * first member of the container. Repeatedly calling Next() will advance the reader through the members |
| * of the collection until the end is reached, at which point the reader will return WEAVE_END_OF_TLV. |
| * |
| * Once the application has finished reading a container it can continue reading the elements after |
| * the container by calling the ExitContainer() method. |
| * |
| * @param[out] outerContainerType A reference to a TLVType value that will receive the context |
| * of the reader. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If the current element is not positioned on a container element. |
| * |
| */ |
| WEAVE_ERROR TLVReader::EnterContainer(TLVType& outerContainerType) |
| { |
| TLVElementType elemType = ElementType(); |
| if (!TLVTypeIsContainer(elemType)) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| outerContainerType = mContainerType; |
| mContainerType = (TLVType) elemType; |
| |
| ClearElementState(); |
| SetContainerOpen(false); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Completes the reading of a TLV container and prepares a TLVReader object to read elements |
| * after the container. |
| * |
| * The ExitContainer() method restores the state of a TLVReader object after a call to |
| * EnterContainer(). For every call to EnterContainer() applications must make a corresponding |
| * call to ExitContainer(), passing the context value returned by the EnterContainer() method. |
| * |
| * When ExitContainer() returns, the reader is positioned immediately before the first element that |
| * follows the container. From this point an application can use the Next() method to advance |
| * through any remaining elements. |
| * |
| * Once EnterContainer() has been called, applications can call ExitContainer() on a reader at any |
| * point in time, regardless of whether all elements in the underlying container have been read. |
| * |
| * @note Any changes made to the configuration of the reader between the calls to EnterContainer() |
| * and ExitContainer() are NOT undone by the call to ExitContainer(). For example, a change to the |
| * implicit profile id (@p ImplicitProfileId) will not be reversed when a container is exited. Thus |
| * it is the application's responsibility to adjust the configuration accordingly at the appropriate |
| * times. |
| * |
| * @param[in] outerContainerType The TLVType value that was returned by the EnterContainer() method. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE If OpenContainer() has not been called on the reader, or if |
| * the container reader does not match the one passed to the |
| * OpenContainer() method. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV element type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::ExitContainer(TLVType outerContainerType) |
| { |
| WEAVE_ERROR err; |
| |
| err = SkipToEndOfContainer(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| mContainerType = outerContainerType; |
| ClearElementState(); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Verifies that the TVLReader object is at the end of a TLV container. |
| * |
| * The VerifyEndOfContainer() method verifies that there are no further TLV elements to be read |
| * within the current TLV container. This is a convenience method that is equivalent to calling |
| * Next() and checking for a return value of WEAVE_END_OF_TLV. |
| * |
| * @note When there are more TLV elements in the collection, this method will change the position |
| * of the reader. |
| * |
| * @retval #WEAVE_NO_ERROR If there are no further TLV elements to be read. |
| * @retval #WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT |
| * If another TLV element was found in the collection. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::VerifyEndOfContainer() |
| { |
| WEAVE_ERROR err = Next(); |
| if (err == WEAVE_END_OF_TLV) |
| return WEAVE_NO_ERROR; |
| if (err == WEAVE_NO_ERROR) |
| return WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT; |
| return err; |
| } |
| |
| /** |
| * Returns the type of the container within which the TLVReader is currently reading. |
| * |
| * The GetContainerType() method returns the type of the TLV container within which the TLVReader |
| * is reading. If the TLVReader is positioned at the outer-most level of a TLV encoding (i.e. before, |
| * on or after the outer-most TLV element), the method will return kTLVType_NotSpecified. |
| * |
| * @return The TLVType of the current container, or kTLVType_NotSpecified if the TLVReader is not |
| * positioned within a container. |
| */ |
| TLVType TLVReader::GetContainerType() const |
| { |
| return mContainerType; |
| } |
| |
| /** |
| * Advances the TLVReader object to the next TLV element to be read. |
| * |
| * The Next() method positions the reader object on the next element in a TLV encoding that resides |
| * in the same containment context. In particular, if the reader is positioned at the outer-most |
| * level of a TLV encoding, calling Next() will advance the reader to the next, top-most element. |
| * If the reader is positioned within a TLV container element (a structure, array or path), calling |
| * Next() will advance the reader to the next member element of the container. |
| * |
| * Since Next() constrains reader motion to the current containment context, calling Next() when |
| * the reader is positioned on a container element will advance @em over the container, skipping |
| * its member elements (and the members of any nested containers) until it reaches the first element |
| * after the container. |
| * |
| * When there are no further elements within a particular containment context the Next() method will |
| * return a #WEAVE_END_OF_TLV error and the position of the reader will remain unchanged. |
| * |
| * @retval #WEAVE_NO_ERROR If the reader was successfully positioned on a new element. |
| * @retval #WEAVE_END_OF_TLV If no further elements are available. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval #WEAVE_ERROR_UNKNOWN_IMPLICIT_TLV_TAG |
| * If the reader encountered a implicitly-encoded TLV tag for which |
| * the corresponding profile id is unknown. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Next() |
| { |
| WEAVE_ERROR err; |
| TLVElementType elemType = ElementType(); |
| |
| err = Skip(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| err = ReadElement(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| elemType = ElementType(); |
| if (elemType == kTLVElementType_EndOfContainer) |
| return WEAVE_END_OF_TLV; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Advances the TLVReader object to the next TLV element to be read, asserting the type and tag of |
| * the new element. |
| * |
| * The Next(TLVType expectedType, uint64_t expectedTag) method is a convenience method that has the |
| * same behavior as Next(), but also verifies that the type and tag of the new TLV element match |
| * the supplied arguments. |
| * |
| * @param[in] expectedType The expected data type for the next element. |
| * @param[in] expectedTag The expected tag for the next element. |
| * |
| * @retval #WEAVE_NO_ERROR If the reader was successfully positioned on a new element. |
| * @retval #WEAVE_END_OF_TLV If no further elements are available. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the type of the new element does not match the value |
| * of the @p expectedType argument. |
| * @retval #WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT |
| * If the tag associated with the new element does not match the |
| * value of the @p expectedTag argument. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV |
| * element type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Next(TLVType expectedType, uint64_t expectedTag) |
| { |
| WEAVE_ERROR err = Next(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| if (GetType() != expectedType) |
| return WEAVE_ERROR_WRONG_TLV_TYPE; |
| if (mElemTag != expectedTag) |
| return WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT; |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Advances the TLVReader object to immediately after the current TLV element. |
| * |
| * The Skip() method positions the reader object immediately @em after the current TLV element, such |
| * that a subsequent call to Next() will advance the reader to the following element. Like Next(), |
| * if the reader is positioned on a container element at the time of the call, the members of the |
| * container will be skipped. If the reader is not positioned on any element, its position remains |
| * unchanged. |
| * |
| * @retval #WEAVE_NO_ERROR If the reader was successfully positioned on a new element. |
| * @retval #WEAVE_END_OF_TLV If no further elements are available. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the reader encountered an invalid or unsupported TLV |
| * element type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| * |
| */ |
| WEAVE_ERROR TLVReader::Skip() |
| { |
| WEAVE_ERROR err; |
| TLVElementType elemType = ElementType(); |
| |
| if (elemType == kTLVElementType_EndOfContainer) |
| return WEAVE_END_OF_TLV; |
| |
| if (TLVTypeIsContainer(elemType)) |
| { |
| TLVType outerContainerType; |
| err = EnterContainer(outerContainerType); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| err = ExitContainer(outerContainerType); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| } |
| |
| else |
| { |
| err = SkipData(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| ClearElementState(); |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Clear the state of the TLVReader. |
| * This method is used to position the reader before the first TLV, |
| * between TLVs or after the last TLV. |
| */ |
| void TLVReader::ClearElementState(void) |
| { |
| mElemTag = AnonymousTag; |
| mControlByte = kTLVControlByte_NotSpecified; |
| mElemLenOrVal = 0; |
| } |
| |
| /** |
| * Skip any data contained in the current TLV by reading over it without |
| * a destination buffer. |
| * |
| * @retval #WEAVE_NO_ERROR If the reader was successfully positioned at the end of the |
| * data. |
| * @retval other Other Weave or platform error codes returned by the configured |
| * GetNextBuffer() function. Only possible when GetNextBuffer is |
| * non-NULL. |
| */ |
| WEAVE_ERROR TLVReader::SkipData(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| TLVElementType elemType = ElementType(); |
| |
| if (TLVTypeHasLength(elemType)) |
| { |
| err = ReadData(NULL, mElemLenOrVal); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| } |
| |
| return err; |
| } |
| |
| WEAVE_ERROR TLVReader::SkipToEndOfContainer() |
| { |
| WEAVE_ERROR err; |
| TLVType outerContainerType = mContainerType; |
| uint32_t nestLevel = 0; |
| |
| // If the user calls Next() after having called OpenContainer() but before calling |
| // CloseContainer() they're effectively doing a close container by skipping over |
| // the container element. So reset the 'container open' flag here to prevent them |
| // from calling CloseContainer() with the now orphaned container reader. |
| SetContainerOpen(false); |
| |
| while (true) |
| { |
| TLVElementType elemType = ElementType(); |
| |
| if (elemType == kTLVElementType_EndOfContainer) |
| { |
| if (nestLevel == 0) |
| return WEAVE_NO_ERROR; |
| |
| nestLevel--; |
| mContainerType = (nestLevel == 0) ? outerContainerType : kTLVType_UnknownContainer; |
| } |
| |
| else if (TLVTypeIsContainer(elemType)) |
| { |
| nestLevel++; |
| mContainerType = (TLVType)elemType; |
| } |
| |
| err = SkipData(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| err = ReadElement(); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| } |
| } |
| |
| WEAVE_ERROR TLVReader::ReadElement() |
| { |
| WEAVE_ERROR err; |
| uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes |
| const uint8_t *p; |
| TLVElementType elemType; |
| |
| // Make sure we have input data. Return WEAVE_END_OF_TLV if no more data is available. |
| err = EnsureData(WEAVE_END_OF_TLV); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| // Get the element's control byte. |
| mControlByte = *mReadPoint; |
| |
| // Extract the element type from the control byte. Fail if it's invalid. |
| elemType = ElementType(); |
| if (!IsValidTLVType(elemType)) |
| return WEAVE_ERROR_INVALID_TLV_ELEMENT; |
| |
| // Extract the tag control from the control byte. |
| TLVTagControl tagControl = (TLVTagControl)(mControlByte & kTLVTagControlMask); |
| |
| // Determine the number of bytes in the element's tag, if any. |
| uint8_t tagBytes = sTagSizes[tagControl >> kTLVTagControlShift]; |
| |
| // Extract the size of length/value field from the control byte. |
| TLVFieldSize lenOrValFieldSize = GetTLVFieldSize(elemType); |
| |
| // Determine the number of bytes in the length/value field. |
| uint8_t valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); |
| |
| // Determine the number of bytes in the element's 'head'. This includes: the control byte, the tag bytes (if present), the |
| // length bytes (if present), and for elements that don't have a length (e.g. integers), the value bytes. |
| uint8_t elemHeadBytes = 1 + tagBytes + valOrLenBytes; |
| |
| // If the head of the element overlaps the end of the input buffer, read the bytes into the staging buffer |
| // and arrange to parse them from there. Otherwise read them directly from the input buffer. |
| if (elemHeadBytes > (mBufEnd - mReadPoint)) |
| { |
| err = ReadData(stagingBuf, elemHeadBytes); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| p = stagingBuf; |
| } |
| else |
| { |
| p = mReadPoint; |
| mReadPoint += elemHeadBytes; |
| mLenRead += elemHeadBytes; |
| } |
| |
| // Skip over the control byte. |
| p++; |
| |
| // Read the tag field, if present. |
| mElemTag = ReadTag(tagControl, p); |
| |
| // Read the length/value field, if present. |
| switch (lenOrValFieldSize) |
| { |
| case kTLVFieldSize_0Byte: |
| mElemLenOrVal = 0; |
| break; |
| case kTLVFieldSize_1Byte: |
| mElemLenOrVal = Read8(p); |
| break; |
| case kTLVFieldSize_2Byte: |
| mElemLenOrVal = LittleEndian::Read16(p); |
| break; |
| case kTLVFieldSize_4Byte: |
| mElemLenOrVal = LittleEndian::Read32(p); |
| break; |
| case kTLVFieldSize_8Byte: |
| mElemLenOrVal = LittleEndian::Read64(p); |
| break; |
| } |
| |
| return VerifyElement(); |
| } |
| |
| WEAVE_ERROR TLVReader::VerifyElement() |
| { |
| if (ElementType() == kTLVElementType_EndOfContainer) |
| { |
| if (mContainerType == kTLVType_NotSpecified) |
| return WEAVE_ERROR_INVALID_TLV_ELEMENT; |
| if (mElemTag != AnonymousTag) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| } |
| else |
| { |
| if (mElemTag == UnknownImplicitTag) |
| return WEAVE_ERROR_UNKNOWN_IMPLICIT_TLV_TAG; |
| switch (mContainerType) |
| { |
| case kTLVType_NotSpecified: |
| if (IsContextTag(mElemTag)) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_Structure: |
| if (mElemTag == AnonymousTag) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_Array: |
| if (mElemTag != AnonymousTag) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| break; |
| case kTLVType_UnknownContainer: |
| case kTLVType_Path: |
| break; |
| default: |
| return WEAVE_ERROR_INCORRECT_STATE; |
| } |
| } |
| |
| // If the current element encodes a specific length (e.g. a UTF8 string or a byte string), verify |
| // that the purported length fits within the remaining bytes of the encoding (as delineated by mMaxLen). |
| // |
| // Note that this check is not strictly necessary to prevent runtime errors, as any attempt to access |
| // the data of an element with an invalid length will result in an error. However checking the length |
| // here catches the error earlier, and ensures that the application will never see the erroneous length |
| // value. |
| // |
| if (TLVTypeHasLength(ElementType())) |
| { |
| uint32_t overallLenRemaining = mMaxLen - mLenRead; |
| if (overallLenRemaining < (uint32_t)mElemLenOrVal) |
| return WEAVE_ERROR_TLV_UNDERRUN; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| uint64_t TLVReader::ReadTag(TLVTagControl tagControl, const uint8_t *& p) |
| { |
| uint16_t vendorId; |
| uint16_t profileNum; |
| |
| switch (tagControl) |
| { |
| case kTLVTagControl_ContextSpecific: |
| return ContextTag(Read8(p)); |
| case kTLVTagControl_CommonProfile_2Bytes: |
| return CommonTag(LittleEndian::Read16(p)); |
| case kTLVTagControl_CommonProfile_4Bytes: |
| return CommonTag(LittleEndian::Read32(p)); |
| case kTLVTagControl_ImplicitProfile_2Bytes: |
| if (ImplicitProfileId == kProfileIdNotSpecified) |
| return UnknownImplicitTag; |
| return ProfileTag(ImplicitProfileId, LittleEndian::Read16(p)); |
| case kTLVTagControl_ImplicitProfile_4Bytes: |
| if (ImplicitProfileId == kProfileIdNotSpecified) |
| return UnknownImplicitTag; |
| return ProfileTag(ImplicitProfileId, LittleEndian::Read32(p)); |
| case kTLVTagControl_FullyQualified_6Bytes: |
| vendorId = LittleEndian::Read16(p); |
| profileNum = LittleEndian::Read16(p); |
| return ProfileTag(vendorId, profileNum, LittleEndian::Read16(p)); |
| case kTLVTagControl_FullyQualified_8Bytes: |
| vendorId = LittleEndian::Read16(p); |
| profileNum = LittleEndian::Read16(p); |
| return ProfileTag(vendorId, profileNum, LittleEndian::Read32(p)); |
| case kTLVTagControl_Anonymous: |
| default: |
| return AnonymousTag; |
| } |
| } |
| |
| WEAVE_ERROR TLVReader::ReadData(uint8_t *buf, uint32_t len) |
| { |
| WEAVE_ERROR err; |
| |
| while (len > 0) |
| { |
| err = EnsureData(WEAVE_ERROR_TLV_UNDERRUN); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| uint32_t remainingLen = mBufEnd - mReadPoint; |
| |
| uint32_t readLen = len; |
| if (readLen > remainingLen) |
| readLen = remainingLen; |
| |
| if (buf != NULL) |
| { |
| memcpy(buf, mReadPoint, readLen); |
| buf += readLen; |
| } |
| mReadPoint += readLen; |
| mLenRead += readLen; |
| len -= readLen; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR TLVReader::EnsureData(WEAVE_ERROR noDataErr) |
| { |
| WEAVE_ERROR err; |
| |
| if (mReadPoint == mBufEnd) |
| { |
| if (mLenRead == mMaxLen) |
| return noDataErr; |
| |
| if (GetNextBuffer == NULL) |
| return noDataErr; |
| |
| uint32_t bufLen; |
| err = GetNextBuffer(*this, mBufHandle, mReadPoint, bufLen); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| if (bufLen == 0) |
| return noDataErr; |
| |
| // Cap mBufEnd so that we don't read beyond the user's specified maximum length, even |
| // if the underlying buffer is larger. |
| uint32_t overallLenRemaining = mMaxLen - mLenRead; |
| if (overallLenRemaining < bufLen) |
| bufLen = overallLenRemaining; |
| |
| mBufEnd = mReadPoint + bufLen; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * This is a private method used to compute the length of a TLV element head. |
| */ |
| WEAVE_ERROR TLVReader::GetElementHeadLength(uint8_t& elemHeadBytes) const |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| uint8_t tagBytes; |
| uint8_t valOrLenBytes; |
| TLVTagControl tagControl; |
| TLVFieldSize lenOrValFieldSize; |
| TLVElementType elemType = ElementType(); |
| |
| // Verify element is of valid TLVType. |
| VerifyOrExit(IsValidTLVType(elemType), err = WEAVE_ERROR_INVALID_TLV_ELEMENT); |
| |
| // Extract the tag control from the control byte. |
| tagControl = (TLVTagControl)(mControlByte & kTLVTagControlMask); |
| |
| // Determine the number of bytes in the element's tag, if any. |
| tagBytes = sTagSizes[tagControl >> kTLVTagControlShift]; |
| |
| // Extract the size of length/value field from the control byte. |
| lenOrValFieldSize = GetTLVFieldSize(elemType); |
| |
| // Determine the number of bytes in the length/value field. |
| valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); |
| |
| // Determine the number of bytes in the element's 'head'. This includes: the |
| // control byte, the tag bytes (if present), the length bytes (if present), |
| // and for elements that don't have a length (e.g. integers), the value |
| // bytes. |
| elemHeadBytes = 1 + tagBytes + valOrLenBytes; |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * This is a private method that returns the TLVElementType from mControlByte |
| */ |
| TLVElementType TLVReader::ElementType() const |
| { |
| if (mControlByte == (uint16_t) kTLVControlByte_NotSpecified) |
| return kTLVElementType_NotSpecified; |
| else |
| return (TLVElementType) (mControlByte & kTLVTypeMask); |
| } |
| |
| WEAVE_ERROR TLVReader::GetNextPacketBuffer(TLVReader& reader, uintptr_t& bufHandle, const uint8_t *& bufStart, |
| uint32_t& bufLen) |
| { |
| PacketBuffer *& buf = (PacketBuffer *&) bufHandle; |
| |
| if (buf != NULL) |
| buf = buf->Next(); |
| if (buf != NULL) |
| { |
| bufStart = buf->Start(); |
| bufLen = buf->DataLength(); |
| } |
| else |
| { |
| bufStart = NULL; |
| bufLen = 0; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| } // namespace TLV |
| } // namespace Weave |
| } // namespace nl |