| /* |
| * |
| * 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 an encoder for the Weave TLV (Tag-Length-Value) encoding format. |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| #include <stdint.h> |
| #include <stdarg.h> |
| |
| #include <Weave/Core/WeaveCore.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Core/WeaveTLV.h> |
| #include <Weave/Support/CodeUtils.h> |
| #include <stdlib.h> |
| |
| namespace nl { |
| namespace Weave { |
| namespace TLV { |
| |
| using namespace nl::Weave::Encoding; |
| |
| |
| /** |
| * @var uint32_t TLVWriter::ImplicitProfileId |
| * |
| * The profile id of tags that should be encoded in implicit form. |
| * |
| * When a writer is asked to encode a new element, if the profile id of the tag associated with the |
| * new element matches the value of the @p ImplicitProfileId member, the writer will encode the tag |
| * in implicit form, omitting the profile id in the process. |
| * |
| * By default, the @p ImplicitProfileId property is set to kProfileIdNotSpecified, which instructs |
| * the writer not to emit implicitly encoded tags. Applications can set @p ImplicitProfileId at any |
| * time to enable encoding tags in implicit form starting at the current point in the encoding. The |
| * appropriate profile id to set is usually dependent on the context of the application or protocol |
| * being spoken. |
| * |
| * @note The value of the @p ImplicitProfileId member affects the encoding of profile-specific |
| * tags only; the encoding of context-specific tags is unchanged. |
| */ |
| |
| /** |
| * @var void *TLVWriter::AppData |
| * |
| * A pointer field that can be used for application-specific data. |
| */ |
| |
| /** |
| * @typedef WEAVE_ERROR (*TLVWriter::GetNewBufferFunct)(TLVWriter& writer, uintptr_t& bufHandle, uint8_t *& bufStart, uint32_t& bufLen) |
| * |
| * A function that supplies new output buffer space to a TLVWriter. |
| * |
| * Functions of this type are used to prepare new buffer space for a TLVWriter to write to. When called, |
| * the function is expected to return a pointer to a memory location where new data should be written, |
| * along with an associated maximum length. The function can supply write space either by allocating |
| * a new buffer to hold the data or by clearing out previously written data from an existing buffer. |
| * |
| * @param[in] writer A reference to the TLVWriter object that is requesting new buffer |
| * space. |
| * @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 the beginning of the current output buffer. On exit, @p bufStart |
| * is expected to point to the beginning of the new output buffer. |
| * The new pointer value can be the same as the previous value (e.g. |
| * if the function copied the existing data elsewhere), or it can point |
| * to an entirely new location. |
| * @param[in,out] bufLen A reference to an unsigned integer. On entry to the function, |
| * @p bufLen contains the number of byte of @em unused space in the |
| * current buffer. On exit, @p bufLen is expected to contain the maximum |
| * number of bytes that can be written to the new output buffer. |
| * |
| * @retval #WEAVE_NO_ERROR If the function was able to supply more buffer space for the writer. |
| * @retval other Other Weave or platform-specific error codes indicating that an error |
| * occurred preventing the function from producing additional buffer |
| * space. |
| * |
| */ |
| |
| /** |
| * @var GetNewBufferFunct TLVWriter::GetNewBuffer |
| * |
| * A pointer to a function that will supply new output buffer space to a TLVWriter. |
| * |
| * A TLVWriter object will call the GetNewBuffer function whenever an attempt is made to write data |
| * that exceeds the size of the current output buffer. If set to NULL (the default value), the |
| * writer will return a WEAVE_ERROR_NO_MEMORY if the output data overflows the current buffer. |
| * |
| * GetNewBuffer can be set by an application at any time, but is typically set when the writer |
| * is initialized. |
| * |
| * See the GetNewBufferFunct type definition for additional information on implementing a |
| * GetNewBuffer function. |
| */ |
| |
| /** |
| * @typedef WEAVE_ERROR (*TLVWriter::FinalizeBufferFunct)(TLVWriter& writer, uintptr_t bufHandle, uint8_t *bufStart, uint32_t bufLen) |
| * |
| * A function used to perform finalization of the output from a TLVWriter object. |
| * |
| * Functions of this type are called when a TLVWriter's Finalize() method is called. The function is |
| * expected to perform any necessary clean-up or finalization related to consuming the output of the |
| * writer object. Examples of this include such things as recording the final length of the encoding, |
| * or closing a file descriptor. |
| * |
| * @param[in] writer A reference to the TLVWriter object that is being finalized. |
| * @param[in,out] bufHandle A uintptr_t context value that was set by previous calls to the |
| * @p GetNewBuffer function. |
| * @param[in,out] bufStart A pointer to the beginning of the current (and final) output buffer. |
| * @param[in,out] bufLen The number of bytes contained in the buffer pointed to by @p bufStart. |
| * |
| * @retval #WEAVE_NO_ERROR If finalization was successful. |
| * @retval other Other Weave or platform-specific error codes indicating that an error |
| * occurred during finalization. |
| * |
| */ |
| |
| /** |
| * @var FinalizeBufferFunct TLVWriter::FinalizeBuffer |
| * |
| * A pointer to a function that will be called when the TLVWriter is finalized. |
| * |
| * A TLVWriter object will call the FinalizeBuffer function whenever its Finalize() method is |
| * called. Applications can set the function pointer at any point prior to calling Finalize(). |
| * By default the pointer is set to NULL, which causes the Finalize() method to forego calling |
| * the function. |
| * |
| * See the FinalizeBufferFunct type definition for additional information on implementing a |
| * FinalizeBuffer function. |
| */ |
| |
| /** |
| * Initializes a TLVWriter object to write into a single output buffer. |
| * |
| * @note Applications must call Finalize() on the writer before using the contents of the output |
| * buffer. |
| * |
| * @param[in] buf A pointer to the buffer into which TLV should be written. |
| * @param[in] maxLen The maximum number of bytes that should be written to the output buffer. |
| * |
| */ |
| __attribute__((noinline)) |
| void TLVWriter::Init(uint8_t *buf, uint32_t maxLen) |
| { |
| mBufHandle = 0; |
| mBufStart = mWritePoint = buf; |
| mRemainingLen = maxLen; |
| mLenWritten = 0; |
| mMaxLen = maxLen; |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| SetCloseContainerReserved(true); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| GetNewBuffer = NULL; |
| FinalizeBuffer = NULL; |
| } |
| |
| /** |
| * Initializes a TLVWriter object to write into a dynamic buffer. |
| * |
| * @param[in] buf A reference to a pointer that will receive the allocated buffer. |
| * @param[in] maxLen The maximum number of bytes that should be written to the output buffer. |
| * @param[in] initialBufSize |
| * The initial number of bytes that should be allocated to the buffer. |
| */ |
| void TLVWriter::InitMalloced(uint8_t *& outBuf, uint32_t initialBufSize, uint32_t maxLen) |
| { |
| mBufHandle = (uintptr_t)&outBuf; |
| mBufStart = mWritePoint = outBuf = (uint8_t *)malloc(initialBufSize); |
| mMaxLen = maxLen; |
| mRemainingLen = initialBufSize; |
| mLenWritten = 0; |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| SetCloseContainerReserved(true); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| FinalizeBuffer = NULL; |
| GetNewBuffer = GetNewBuffer_Malloced; |
| } |
| |
| /** |
| * Initializes a TLVWriter object to write into a single PacketBuffer. |
| * |
| * Writing begins immediately after the last byte of existing data in the supplied buffer. |
| * |
| * @note If a chain of buffers is given, data will only be written to the first buffer. |
| * |
| * @note Applications must call Finalize() on the writer before using the contents of the buffer. |
| * |
| * @param[in] buf A pointer to an PacketBuffer into which TLV should be written. |
| * @param[in] maxLen The maximum number of bytes that should be written to the output buffer. |
| * |
| */ |
| void TLVWriter::Init(PacketBuffer *buf, uint32_t maxLen) |
| { |
| mBufHandle = (uintptr_t) buf; |
| mBufStart = mWritePoint = buf->Start() + buf->DataLength(); |
| mRemainingLen = buf->AvailableDataLength(); |
| if (mRemainingLen > maxLen) |
| mRemainingLen = maxLen; |
| mLenWritten = 0; |
| mMaxLen = maxLen; |
| mContainerType = kTLVType_NotSpecified; |
| SetContainerOpen(false); |
| SetCloseContainerReserved(true); |
| |
| ImplicitProfileId = kProfileIdNotSpecified; |
| GetNewBuffer = NULL; |
| FinalizeBuffer = FinalizePacketBuffer; |
| } |
| |
| /** |
| * Initializes a TLVWriter object to write into one or more PacketBuffers |
| * |
| * Writing begins immediately after the last byte of existing data in the specified buffer. If |
| * @p allowDiscontiguousBuffers is true, additional PacketBuffers will be allocated and chained to |
| * the supplied buffer as needed to accommodate the amount of data written. If the specified output |
| * buffer is already the head of a chain of buffers, output will be written to the subsequent buffers |
| * in the chain before any new buffers are allocated. |
| * |
| * @note Applications must call Finalize() on the writer before using the contents of the output |
| * buffer(s). |
| * |
| * @param[in] buf A pointer to an PacketBuffer into which TLV data should be written. |
| * @param[in] maxLen The maximum number of bytes that should be written to the output buffer(s). |
| * @param[in] allowDiscontiguousBuffers |
| * If true, write data to a chain of PacketBuffers, allocating new buffers as |
| * needed to store the data written. If false, writing will fail with |
| * WEAVE_ERROR_BUFFER_TOO_SMALL if the written data exceeds the space available |
| * in the initial output buffer. |
| * |
| */ |
| void TLVWriter::Init(PacketBuffer *buf, uint32_t maxLen, bool allowDiscontiguousBuffers) |
| { |
| Init(buf, maxLen); |
| |
| if (allowDiscontiguousBuffers) |
| { |
| GetNewBuffer = GetNewPacketBuffer; |
| } |
| else |
| { |
| GetNewBuffer = NULL; |
| } |
| } |
| |
| /** |
| * Returns the total number of bytes written since the writer was initialized. |
| * |
| * @return Total number of bytes written since the writer was initialized. |
| */ |
| uint32_t TLVWriter::GetLengthWritten() |
| { |
| return mLenWritten; |
| } |
| |
| /** |
| * Finish the writing of a TLV encoding. |
| * |
| * The Finalize() method completes the process of writing a TLV encoding to the underlying output |
| * buffer. The method must be called by the application before it uses the contents of the buffer. |
| * Finalize() can only be called when there are no container writers open for the current writer. |
| * (See @p OpenContainer()). |
| * |
| * @retval #WEAVE_NO_ERROR If the encoding was finalized successfully. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * FinalizeBuffer() function. |
| */ |
| WEAVE_ERROR TLVWriter::Finalize() |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| if (IsContainerOpen()) |
| return WEAVE_ERROR_TLV_CONTAINER_OPEN; |
| if (FinalizeBuffer != NULL) |
| err = FinalizeBuffer(*this, mBufHandle, mBufStart, mWritePoint - mBufStart); |
| return err; |
| } |
| |
| /** |
| * Encodes a TLV boolean value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutBoolean(uint64_t tag, bool v) |
| { |
| return WriteElementHead((v) ? kTLVElementType_BooleanTrue : kTLVElementType_BooleanFalse, tag, 0); |
| } |
| |
| /** |
| * Encodes a TLV unsigned integer value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v) |
| { |
| return Put(tag, static_cast<uint64_t>(v)); |
| } |
| |
| /** |
| * Encodes a TLV unsigned integer value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * @param[in] preserveSize True if the value should be encoded in the same number of bytes as |
| * at the input type. False if value should be encoded in the minimum |
| * number of bytes necessary to represent the value. Note: Applications |
| * are strongly encouraged to set this parameter to false. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_UInt8, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint16_t v) |
| { |
| return Put(tag, static_cast<uint64_t>(v)); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint16_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_UInt16, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint32_t v) |
| { |
| return Put(tag, static_cast<uint64_t>(v)); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint32_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_UInt32, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint64_t v) |
| { |
| TLVElementType elemType; |
| if (v <= UINT8_MAX) |
| elemType = kTLVElementType_UInt8; |
| else if (v <= UINT16_MAX) |
| elemType = kTLVElementType_UInt16; |
| else if (v <= UINT32_MAX) |
| elemType = kTLVElementType_UInt32; |
| else |
| elemType = kTLVElementType_UInt64; |
| return WriteElementHead(elemType, tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, uint64_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_UInt64, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * Encodes a TLV signed integer value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v) |
| { |
| return Put(tag, static_cast<int64_t>(v)); |
| } |
| |
| /** |
| * Encodes a TLV signed integer value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * @param[in] preserveSize True if the value should be encoded in the same number of bytes as |
| * at the input type. False if value should be encoded in the minimum |
| * number of bytes necessary to represent the value. Note: Applications |
| * are strongly encouraged to set this parameter to false. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_Int8, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int16_t v) |
| { |
| return Put(tag, static_cast<int64_t>(v)); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int16_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_Int16, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int32_t v) |
| { |
| return Put(tag, static_cast<int64_t>(v)); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int32_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_Int32, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int64_t v) |
| { |
| TLVElementType elemType; |
| if (v >= INT8_MIN && v <= INT8_MAX) |
| elemType = kTLVElementType_Int8; |
| else if (v >= INT16_MIN && v <= INT16_MAX) |
| elemType = kTLVElementType_Int16; |
| else if (v >= INT32_MIN && v <= INT32_MAX) |
| elemType = kTLVElementType_Int32; |
| else |
| elemType = kTLVElementType_Int64; |
| return WriteElementHead(elemType, tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, int8_t v, bool preserveSize) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, int64_t v, bool preserveSize) |
| { |
| if (preserveSize) |
| return WriteElementHead(kTLVElementType_Int64, tag, v); |
| else |
| return Put(tag, v); |
| } |
| |
| /** |
| * @overload WEAVE_ERROR TLVWriter::Put(uint64_t tag, double v) |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, float v) |
| { |
| union |
| { |
| float f; |
| uint32_t u32; |
| } cvt; |
| cvt.f = v; |
| return WriteElementHead(kTLVElementType_FloatingPointNumber32, tag, cvt.u32); |
| } |
| |
| /** |
| * Encodes a TLV floating point value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] v The value to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::Put(uint64_t tag, double v) |
| { |
| union |
| { |
| double d; |
| uint64_t u64; |
| } cvt; |
| cvt.d = v; |
| return WriteElementHead(kTLVElementType_FloatingPointNumber64, tag, cvt.u64); |
| } |
| |
| /** |
| * Encodes a TLV byte string value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] buf A pointer to a buffer containing the bytes string to be encoded. |
| * @param[in] len The number of bytes to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutBytes(uint64_t tag, const uint8_t *buf, uint32_t len) |
| { |
| return WriteElementWithData(kTLVType_ByteString, tag, (const uint8_t *) buf, len); |
| } |
| |
| /** |
| * Encodes a TLV byte string in multiple chunks. This should be used with ContinuePutBytes. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] totalLen The total number of bytes to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::StartPutBytes(uint64_t tag, uint32_t totalLen) |
| { |
| TLVFieldSize lenFieldSize; |
| TLVType type = kTLVType_ByteString; |
| |
| if (totalLen <= UINT8_MAX) |
| lenFieldSize = kTLVFieldSize_1Byte; |
| else if (totalLen <= UINT16_MAX) |
| lenFieldSize = kTLVFieldSize_2Byte; |
| else |
| lenFieldSize = kTLVFieldSize_4Byte; |
| |
| WEAVE_ERROR err = WriteElementHead((TLVElementType) (type | lenFieldSize), tag, totalLen); |
| |
| return err; |
| } |
| |
| /** |
| * Encodes a TLV byte string value. This should be used with StartPutBytes. |
| * |
| * @param[in] buf A pointer to a buffer containing the bytes string to be encoded. |
| * @param[in] len The number of bytes to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::ContinuePutBytes(const uint8_t *buf, uint32_t len) |
| { |
| return WriteData(buf, len); |
| } |
| |
| /** |
| * Encodes a TLV UTF8 string value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] buf A pointer to the null-terminated UTF-8 string to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutString(uint64_t tag, const char *buf) |
| { |
| return PutString(tag, buf, strlen(buf)); |
| } |
| |
| /** |
| * Encodes a TLV UTF8 string value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] buf A pointer to the UTF-8 string to be encoded. |
| * @param[in] len The length (in bytes) of the string to be encoded. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutString(uint64_t tag, const char *buf, uint32_t len) |
| { |
| return WriteElementWithData(kTLVType_UTF8String, tag, (const uint8_t *) buf, len); |
| } |
| |
| /** |
| * @brief |
| * Encode the string output formatted according to the format in the TLV element. |
| * |
| * PutStringF is an analog of a sprintf where the output is stored in |
| * a TLV element as opposed to a character buffer. When extended |
| * printf functionality is available, the function is able to output |
| * the result string into a discontinuous underlying storage. The |
| * implementation supports the following printf enhancements: |
| * |
| * -- The platform supplies a callback-based `vcbprintf` that provides |
| * the ability to call a custom callback in place of putchar. |
| * |
| * -- The platform supplies a variant of `vsnprintf` called |
| * `vsnprintf_ex`, that behaves exactly like vsnprintf except it |
| * has provisions for omitting the first `n` characters of the |
| * output. |
| * |
| * Note that while the callback-based function may be the simplest and |
| * use the least amount of code, the `vsprintf_ex` variety of |
| * functions will consume less stack. |
| * |
| * If neither of the above is available, but platform provides |
| * `malloc` the function will allocate a temporary buffer to hold the |
| * output. When the platform supplies neither enhancement to the |
| * printf family nor malloc, the output is truncated such that it fits |
| * in the continuous state in the current TLV storage |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p |
| * AnonymousTag if the value should be encoded without |
| * a tag. Tag values should be constructed with one of |
| * the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * |
| * @param[in] fmt The format string used to format the argument list. |
| * Follows the same syntax and rules as the format |
| * string for `printf` family of functions. |
| * |
| * @param[in] ... A list of arguments to be formatted in the output value |
| * according to fmt. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * |
| * @retval other If underlying calls to TLVWriter methods -- |
| * `WriteElementHead` or `GetNewBuffer` -- failed, their |
| * error is immediately forwarded up the call stack. |
| */ |
| WEAVE_ERROR TLVWriter::PutStringF(uint64_t tag, const char *fmt, ...) |
| { |
| WEAVE_ERROR err; |
| va_list ap; |
| |
| va_start(ap, fmt); |
| |
| err = VPutStringF(tag, fmt, ap); |
| |
| va_end(ap); |
| |
| return err; |
| } |
| |
| |
| #if CONFIG_HAVE_VCBPRINTF |
| // We have a variant of the printf function that takes a callback that |
| // emits a single character. The callback performs a function |
| // identical to putchar. |
| |
| void TLVWriter::WeaveTLVWriterPutcharCB(uint8_t c, void *appState) |
| { |
| TLVWriter *w = static_cast<TLVWriter *>(appState); |
| w->WriteData(&c, sizeof(c)); |
| } |
| #endif |
| |
| /** |
| * @brief |
| * Encode the string output formatted according to the format in the TLV element. |
| * |
| * PutStringF is an analog of a sprintf where the output is stored in |
| * a TLV element as opposed to a character buffer. When extended |
| * printf functionality is available, the function is able to output |
| * the result string into a discontinuous underlying storage. The |
| * implementation supports the following printf enhancements: |
| * |
| * -- The platform supplies a callback-based `vcbprintf` that provides |
| * the ability to call a custom callback in place of putchar. |
| * |
| * -- The platform supplies a variant of `vsnprintf` called |
| * `vsnprintf_ex`, that behaves exactly like vsnprintf except it |
| * has provisions for omitting the first `n` characters of the |
| * output. |
| * |
| * Note that while the callback-based function may be the simplest and |
| * use the least amount of code, the `vsprintf_ex` variety of |
| * functions will consume less stack. |
| * |
| * If neither of the above is available, but platform provides |
| * `malloc` the function will allocate a temporary buffer to hold the |
| * output. When the platform supplies neither enhancement to the |
| * printf family nor malloc, the output is truncated such that it fits |
| * in the continuous state in the current TLV storage |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p |
| * AnonymousTag if the value should be encoded without |
| * a tag. Tag values should be constructed with one of |
| * the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * |
| * @param[in] fmt The format string used to format the argument list. |
| * Follows the same syntax and rules as the format |
| * string for `printf` family of functions. |
| * |
| * @param[in] ap A list of arguments to be formatted in the output value |
| * according to fmt. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * |
| * @retval other If underlying calls to TLVWriter methods -- |
| * `WriteElementHead` or `GetNewBuffer` -- failed, their |
| * error is immediately forwarded up the call stack. |
| */ |
| WEAVE_ERROR TLVWriter::VPutStringF(uint64_t tag, const char *fmt, va_list ap) |
| { |
| va_list aq; |
| size_t dataLen; |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| TLVFieldSize lenFieldSize; |
| #if CONFIG_HAVE_VSNPRINTF_EX |
| size_t skipLen; |
| size_t writtenBytes; |
| #elif CONFIG_HAVE_VCBPRINTF |
| #elif HAVE_MALLOC |
| char *tmpBuf; |
| #else |
| size_t maxLen; |
| #endif |
| va_copy(aq, ap); |
| |
| dataLen = vsnprintf(NULL, 0, fmt, aq); |
| |
| va_end(aq); |
| |
| if (dataLen <= UINT8_MAX) |
| lenFieldSize = kTLVFieldSize_1Byte; |
| else if (dataLen <= UINT16_MAX) |
| lenFieldSize = kTLVFieldSize_2Byte; |
| else |
| lenFieldSize = kTLVFieldSize_4Byte; |
| |
| #if !(CONFIG_HAVE_VCBPRINTF) && !(CONFIG_HAVE_VSNPRINTF_EX) && !(HAVE_MALLOC) |
| // no facilities for splitting the stream across multiple buffers, |
| // just write however much fits in the current buffer. |
| // assume conservative tag length at this time (8 bytes) |
| size_t elementHeadLen = (1 + 8 + (1 << static_cast<uint8_t>(lenFieldSize)) + 1); // 1 : control byte, 8 : tag length, stringLen + 1 for null termination |
| VerifyOrExit(mRemainingLen >= elementHeadLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| |
| maxLen = mRemainingLen - elementHeadLen; |
| if (maxLen < dataLen) |
| dataLen = maxLen; |
| #endif |
| |
| // write length. |
| err = WriteElementHead((TLVElementType) (kTLVType_UTF8String | lenFieldSize), tag, dataLen); |
| SuccessOrExit(err); |
| |
| VerifyOrExit((mLenWritten + dataLen) <= mMaxLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| |
| // write data |
| #if CONFIG_HAVE_VSNPRINTF_EX |
| |
| skipLen = 0; |
| |
| do |
| { |
| va_copy(aq, ap); |
| |
| vsnprintf_ex(reinterpret_cast<char*>(mWritePoint), mRemainingLen, skipLen, fmt, aq); |
| |
| va_end(aq); |
| |
| writtenBytes = (mRemainingLen >= (dataLen-skipLen))? dataLen-skipLen : mRemainingLen; |
| skipLen += writtenBytes; |
| mWritePoint += writtenBytes; |
| mRemainingLen -= writtenBytes; |
| mLenWritten += writtenBytes; |
| if (skipLen < dataLen) |
| { |
| VerifyOrExit(GetNewBuffer != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| if (FinalizeBuffer != NULL) |
| { |
| err = FinalizeBuffer(*this, mBufHandle, mBufStart, mWritePoint - mBufStart); |
| SuccessOrExit(err); |
| } |
| |
| err = GetNewBuffer(*this, mBufHandle, mBufStart, mRemainingLen); |
| SuccessOrExit(err); |
| |
| mWritePoint = mBufStart; |
| } |
| |
| } while (skipLen < dataLen); |
| |
| #elif CONFIG_HAVE_VCBPRINTF |
| |
| va_copy(aq, ap); |
| |
| vcbprintf(WeaveTLVWriterPutcharCB, this, dataLen, fmt, aq); |
| |
| va_end(aq); |
| |
| #elif HAVE_MALLOC |
| |
| tmpBuf = static_cast<char*>(malloc(dataLen+1)); |
| VerifyOrExit(tmpBuf != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| va_copy(aq, ap); |
| |
| vsnprintf(tmpBuf, dataLen+1, fmt, aq); |
| |
| va_end(aq); |
| |
| err = WriteData(reinterpret_cast<uint8_t*>(tmpBuf), dataLen); |
| free(tmpBuf); |
| |
| #else // CONFIG_HAVE_VSNPRINTF_EX |
| |
| va_copy(aq, ap); |
| |
| vsnprintf(reinterpret_cast<char *>(mWritePoint), dataLen+1, fmt, aq); |
| |
| va_end(aq); |
| |
| mWritePoint += dataLen; |
| mRemainingLen -= dataLen; |
| mLenWritten += dataLen; |
| |
| #endif // CONFIG_HAVE_VSNPRINTF_EX |
| |
| exit: |
| |
| return err; |
| } |
| |
| |
| /** |
| * Encodes a TLV null value. |
| * |
| * @param[in] tag The TLV tag to be encoded with the value, or @p AnonymousTag if the |
| * value should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutNull(uint64_t tag) |
| { |
| return WriteElementHead(kTLVElementType_Null, tag, 0); |
| } |
| |
| /** |
| * Copies a TLV element from a reader object into the writer. |
| * |
| * The CopyElement() method encodes a new TLV element whose type, tag and value are taken from a TLVReader |
| * object. When the method is called, the supplied reader object is expected to be positioned on the |
| * source TLV element. The newly encoded element will have the same type, tag and contents as the input |
| * container. If the supplied element is a TLV container (structure, array or path), the entire contents |
| * of the container will be copied. |
| * |
| * @note This method requires the supplied TVLReader object to be reading from a single, contiguous |
| * input buffer that contains the entirety of the underlying TLV encoding. Supplying a reader in any |
| * other mode has undefined behavior. |
| * |
| * @param[in] reader A reference to a TLVReader object identifying a pre-encoded TLV |
| * element that should be copied. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If the supplied reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN |
| * If the underlying TLV encoding associated with the supplied reader ended |
| * prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the supplied reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the supplied reader encountered a TLV tag in an invalid context, |
| * or if the supplied tag is invalid or inappropriate in the context in |
| * which the new container is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions, or by the GetNextBuffer() |
| * function associated with the reader object. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::CopyElement(TLVReader& reader) |
| { |
| return CopyElement(reader.GetTag(), reader); |
| } |
| |
| /** |
| * Copies a TLV element from a reader object into the writer. |
| * |
| * The CopyElement() method encodes a new TLV element whose type and value are taken from a TLVReader |
| * object. When the method is called, the supplied reader object is expected to be positioned on the |
| * source TLV element. The newly encoded element will have the same type and contents as the input |
| * container, however the tag will be set to the specified argument. If the supplied element is a |
| * TLV container (structure, array or path), the entire contents of the container will be copied. |
| * |
| * @note This method requires the supplied TVLReader object to be reading from a single, contiguous |
| * input buffer that contains the entirety of the underlying TLV encoding. Supplying a reader in any |
| * other mode has undefined behavior. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] reader A reference to a TLVReader object identifying a pre-encoded TLV |
| * element whose type and value should be copied. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If the supplied reader is not positioned on an element. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN |
| * If the underlying TLV encoding associated with the supplied reader ended |
| * prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the supplied reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the supplied reader encountered a TLV tag in an invalid context, |
| * or if the supplied tag is invalid or inappropriate in the context in |
| * which the new container is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions, or by the GetNextBuffer() |
| * function associated with the reader object. |
| * |
| */ |
| |
| |
| const size_t kWeaveTLVCopyChunkSize = 16; |
| |
| WEAVE_ERROR TLVWriter::CopyElement(uint64_t tag, TLVReader& reader) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| TLVElementType elemType = reader.ElementType(); |
| uint64_t elemLenOrVal = reader.mElemLenOrVal; |
| TLVReader readerHelper; // used to figure out the length of the element and read data of the element |
| uint32_t copyDataLen; |
| uint8_t chunk[kWeaveTLVCopyChunkSize]; |
| |
| VerifyOrExit(elemType != kTLVElementType_NotSpecified && elemType != kTLVElementType_EndOfContainer, err = WEAVE_ERROR_INCORRECT_STATE); |
| |
| // Initialize the helper |
| readerHelper.Init(reader); |
| |
| // Skip to the end of the element. |
| err = reader.Skip(); |
| SuccessOrExit(err); |
| |
| // Compute the amount of value data to copy from the reader. |
| copyDataLen = reader.GetLengthRead() - readerHelper.GetLengthRead(); |
| |
| // Write the head of the new element with the same type and length/value, but using the |
| // specified tag. |
| err = WriteElementHead(elemType, tag, elemLenOrVal); |
| SuccessOrExit(err); |
| |
| while (copyDataLen > 0) |
| { |
| uint32_t chunkSize = copyDataLen > kWeaveTLVCopyChunkSize ? kWeaveTLVCopyChunkSize : copyDataLen; |
| err = readerHelper.ReadData(chunk, chunkSize); |
| SuccessOrExit(err); |
| |
| err = WriteData(chunk, chunkSize); |
| SuccessOrExit(err); |
| |
| copyDataLen -= chunkSize; |
| } |
| |
| exit: |
| return err; |
| |
| } |
| |
| /** |
| * Initializes a new TLVWriter object for writing the members of a TLV container element. |
| * |
| * The OpenContainer() method is used to write TLV container elements (structure, arrays or paths) |
| * to an encoding. The method takes the type and tag (if any) of the new container, and a reference |
| * to a new writer object (the <em>container writer</em>) that will be initialized for the purpose |
| * of writing the container's elements. Applications write the members of the new container using |
| * the container writer and then call CloseContainer() to complete the container encoding. |
| * |
| * While the container writer is open, applications must not make calls on or otherwise alter the state |
| * of the parent writer. |
| * |
| * The container writer inherits various configuration properties from the parent writer. These are: |
| * |
| * @li The implicit profile id (ImplicitProfileId) |
| * @li The application data pointer (AppData) |
| * @li The GetNewBuffer and FinalizeBuffer function pointers |
| * |
| * @note The StartContainer() method can be used as an alternative to OpenContainer() to write a |
| * container element without initializing a new writer object. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] containerType The type of container to encode. Must be one of @p kTLVType_Structure, |
| * @p kTLVType_Array or @p kTLVType_Path. |
| * @param[out] containerWriter A reference to a TLVWriter object that will be initialized for |
| * writing the members of the new container element. Any data |
| * associated with the supplied object is overwritten. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE |
| * If the value specified for containerType is incorrect. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::OpenContainer(uint64_t tag, TLVType containerType, TLVWriter& containerWriter) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(TLVTypeIsContainer(containerType), err = WEAVE_ERROR_WRONG_TLV_TYPE); |
| |
| if (IsCloseContainerReserved()) |
| { |
| VerifyOrExit(mMaxLen >= kEndOfContainerMarkerSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| mMaxLen -= kEndOfContainerMarkerSize; |
| } |
| err = WriteElementHead((TLVElementType) containerType, tag, 0); |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| // undo the space reservation, as the container is not actually open |
| if (IsCloseContainerReserved()) |
| mMaxLen += kEndOfContainerMarkerSize; |
| |
| ExitNow(); |
| } |
| |
| containerWriter.mBufHandle = mBufHandle; |
| containerWriter.mBufStart = mBufStart; |
| containerWriter.mWritePoint = mWritePoint; |
| containerWriter.mRemainingLen = mRemainingLen; |
| containerWriter.mLenWritten = 0; |
| containerWriter.mMaxLen = mMaxLen - mLenWritten; |
| containerWriter.mContainerType = containerType; |
| containerWriter.SetContainerOpen(false); |
| containerWriter.SetCloseContainerReserved(IsCloseContainerReserved()); |
| containerWriter.ImplicitProfileId = ImplicitProfileId; |
| containerWriter.GetNewBuffer = GetNewBuffer; |
| containerWriter.FinalizeBuffer = FinalizeBuffer; |
| |
| SetContainerOpen(true); |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * Completes the writing of a TLV container after a call to OpenContainer(). |
| * |
| * The CloseContainer() method restores the state of a parent TLVWriter 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 writer to both methods. |
| * |
| * When CloseContainer() returns, applications may continue to use the parent writer to write |
| * additional TLV elements that appear after the container element. At this point the supplied |
| * container writer should be considered 'de-initialized' and must not be used without |
| * re-initialization. |
| * |
| * @param[in] containerWriter A reference to the TLVWriter object that was supplied to the |
| * OpenContainer() method. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If the supplied container writer is not in the correct state. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If another container writer has been opened on the supplied |
| * container writer and not yet closed. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If completing the encoding of the container would exceed the |
| * limit on the maximum number of bytes specified when the writer |
| * was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack |
| * of memory. |
| * @retval other Other Weave or platform-specific errors returned by the |
| * configured GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::CloseContainer(TLVWriter& containerWriter) |
| { |
| if (!TLVTypeIsContainer(containerWriter.mContainerType)) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| if (containerWriter.IsContainerOpen()) |
| return WEAVE_ERROR_TLV_CONTAINER_OPEN; |
| |
| mBufHandle = containerWriter.mBufHandle; |
| mBufStart = containerWriter.mBufStart; |
| mWritePoint = containerWriter.mWritePoint; |
| mRemainingLen = containerWriter.mRemainingLen; |
| mLenWritten += containerWriter.mLenWritten; |
| |
| if (IsCloseContainerReserved()) |
| mMaxLen += kEndOfContainerMarkerSize; |
| |
| SetContainerOpen(false); |
| |
| // Reset the container writer so that it can't accidentally be used again. |
| containerWriter.Init((uint8_t *) NULL, 0); |
| |
| return WriteElementHead(kTLVElementType_EndOfContainer, AnonymousTag, 0); |
| } |
| |
| /** |
| * Begins encoding a new TLV container element. |
| * |
| * The StartContainer() method is used to write TLV container elements (structure, arrays or paths) |
| * to an encoding. The method takes the type and tag (if any) of the new container, and a reference |
| * to a TLVType value which will be used to save the current context of the writer while it is being |
| * used to write the container. |
| * |
| * Once the StartContainer() method returns, the application should use the current TLVWriter object to |
| * write the elements of the container. When finish, the application must call the EndContainer() |
| * method to finish the encoding of the container. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] containerType The type of container to encode. Must be one of @p kTLVType_Structure, |
| * @p kTLVType_Array or @p kTLVType_Path. |
| * @param[out] outerContainerType |
| * A reference to a TLVType value that will receive the context of the |
| * writer. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE |
| * If the value specified for containerType is incorrect. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::StartContainer(uint64_t tag, TLVType containerType, TLVType& outerContainerType) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit(TLVTypeIsContainer(containerType), err = WEAVE_ERROR_WRONG_TLV_TYPE); |
| |
| if (IsCloseContainerReserved()) |
| { |
| VerifyOrExit(mMaxLen >= kEndOfContainerMarkerSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| mMaxLen -= kEndOfContainerMarkerSize; |
| } |
| |
| err = WriteElementHead((TLVElementType) containerType, tag, 0); |
| if (err != WEAVE_NO_ERROR) |
| { |
| // undo the space reservation, as the container is not actually open |
| if (IsCloseContainerReserved()) |
| mMaxLen += kEndOfContainerMarkerSize; |
| |
| ExitNow(); |
| } |
| outerContainerType = mContainerType; |
| mContainerType = containerType; |
| |
| SetContainerOpen(false); |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * Completes the encoding of a TLV container element. |
| * |
| * The EndContainer() method completes the encoding of a TLV container element and restores the state |
| * of a TLVWrite object after an earlier call to StartContainer(). For every call to StartContainer() |
| * applications must make a corresponding call to EndContainer(), passing the TLVType value returned |
| * by the StartContainer() call. When EndContainer() returns, the writer object can be used to write |
| * additional TLV elements that follow the container element. |
| * |
| * @note Any changes made to the configuration of the writer between the calls to StartContainer() |
| * and EndContainer() are NOT undone by the call to EndContainer(). For example, a change to the |
| * implicit profile id (@p ImplicitProfileId) will not be reversed when a container is ended. 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 StartContainer() method. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If a corresponding StartContainer() call was not made. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::EndContainer(TLVType outerContainerType) |
| { |
| if (!TLVTypeIsContainer(mContainerType)) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| mContainerType = outerContainerType; |
| |
| if (IsCloseContainerReserved()) |
| mMaxLen += kEndOfContainerMarkerSize; |
| |
| return WriteElementHead(kTLVElementType_EndOfContainer, AnonymousTag, 0); |
| } |
| |
| /** |
| * Encodes a TLV container element from a pre-encoded set of member elements |
| * |
| * The PutPreEncodedContainer() method encodes a new TLV container element (a structure, array or path) |
| * containing a set of member elements taken from a pre-encoded buffer. The input buffer is expected to |
| * contain zero or more full-encoded TLV elements, with tags that conform to the rules associated with |
| * the specified container type (e.g. structure members must have tags, while array members must not). |
| * |
| * The method encodes the entirety of the container element in one call. When PutPreEncodedContainer() |
| * returns, the writer object can be used to write additional TLV elements following the container element. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] containerType The type of container to encode. Must be one of @p kTLVType_Structure, |
| * @p kTLVType_Array or @p kTLVType_Path. |
| * @param[in] data A pointer to a buffer containing zero of more encoded TLV elements that |
| * will become the members of the new container. |
| * @param[in] dataLen The number of bytes in the @p data buffer. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_WRONG_TLV_TYPE |
| * If the value specified for containerType is incorrect. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the specified tag value is invalid or inappropriate in the context |
| * in which the value is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::PutPreEncodedContainer(uint64_t tag, TLVType containerType, const uint8_t *data, uint32_t dataLen) |
| { |
| if (!TLVTypeIsContainer(containerType)) |
| return WEAVE_ERROR_INVALID_ARGUMENT; |
| |
| WEAVE_ERROR err = WriteElementHead((TLVElementType)containerType, tag, 0); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| return WriteData(data, dataLen); |
| } |
| |
| /** |
| * Copies a TLV container element from TLVReader object |
| * |
| * The CopyContainer() encodes a new TLV container element by copying a pre-encoded container element |
| * located at the current position of a TLVReader object. The method writes the entirety of the new |
| * container element in one call, copying the container's type, tag and elements from the source |
| * encoding. When the method returns, the writer object can be used to write additional TLV elements |
| * following the container element. |
| * |
| * @note This method requires the supplied TVLReader object to be reading from a single, contiguous |
| * input buffer that contains the entirety of the underlying TLV encoding. Supplying a reader in any |
| * other mode has undefined behavior. |
| * |
| * @param[in] container A reference to a TLVReader object identifying the pre-encoded TLV |
| * container to be copied. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If the supplied reader is not positioned on a container element. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN |
| * If the underlying TLV encoding associated with the supplied reader ended |
| * prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the supplied reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the supplied reader encountered a TLV tag in an invalid context, |
| * or if the tag associated with the source container is invalid or |
| * inappropriate in the context in which the new container is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions, or by the GetNextBuffer() |
| * function associated with the reader object. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::CopyContainer(TLVReader& container) |
| { |
| return CopyContainer(container.GetTag(), container); |
| } |
| |
| /** |
| * Encodes a TLV container element from a pre-encoded set of member elements |
| * |
| * The CopyContainer() method encodes a new TLV container element (a structure, array or path) |
| * containing a set of member elements taken from a TLVReader object. When the method is called, the |
| * supplied reader object is expected to be positioned on a TLV container element. The newly encoded |
| * container will have the same type and members as the input container. The tag for the new |
| * container is specified as an input parameter. |
| * |
| * When the method returns, the writer object can be used to write additional TLV elements following |
| * the container element. |
| * |
| * @note This method requires the supplied TVLReader object to be reading from a single, contiguous |
| * input buffer that contains the entirety of the underlying TLV encoding. Supplying a reader in any |
| * other mode has undefined behavior. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] container A reference to a TLVReader object identifying a pre-encoded TLV |
| * container whose type and members should be copied. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_INCORRECT_STATE |
| * If the supplied reader is not positioned on a container element. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN |
| * If the underlying TLV encoding associated with the supplied reader ended |
| * prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the supplied reader encountered an invalid or unsupported TLV element |
| * type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the supplied reader encountered a TLV tag in an invalid context, |
| * or if the supplied tag is invalid or inappropriate in the context in |
| * which the new container is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions, or by the GetNextBuffer() |
| * function associated with the reader object. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::CopyContainer(uint64_t tag, TLVReader& container) |
| { |
| // NOTE: This function MUST be used with a TVLReader that is reading from a contiguous buffer. |
| WEAVE_ERROR err; |
| TLVType containerType, outerContainerType; |
| const uint8_t *containerStart; |
| |
| containerType = container.GetType(); |
| |
| err = container.EnterContainer(outerContainerType); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| containerStart = container.GetReadPoint(); |
| |
| err = container.ExitContainer(outerContainerType); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| return PutPreEncodedContainer(tag, containerType, containerStart, container.GetReadPoint() - containerStart); |
| } |
| |
| /** |
| * Encodes a TLV container element that contains member elements from a pre-encoded container |
| * |
| * The CopyContainer() method encodes a new TLV container element (a structure, array or path) |
| * containing a set of member elements taken from the contents of a supplied pre-encoded container. |
| * When the method is called, data in the supplied input buffer is parsed as a TLV container element |
| * an a new container is written that has the same type and members as the input container. The tag |
| * for the new container is specified as an input parameter. |
| * |
| * When the method returns, the writer object can be used to write additional TLV elements following |
| * the container element. |
| * |
| * @param[in] tag The TLV tag to be encoded with the container, or @p AnonymousTag if |
| * the container should be encoded without a tag. Tag values should be |
| * constructed with one of the tag definition functions ProfileTag(), |
| * ContextTag() or CommonTag(). |
| * @param[in] encodedContainer A buffer containing a pre-encoded TLV container whose type and members |
| * should be copied. |
| * @param[in] encodedContainerLen The length in bytes of the pre-encoded container. |
| * |
| * @retval #WEAVE_NO_ERROR If the method succeeded. |
| * @retval #WEAVE_ERROR_TLV_CONTAINER_OPEN |
| * If a container writer has been opened on the current writer and not |
| * yet closed. |
| * @retval #WEAVE_ERROR_TLV_UNDERRUN |
| * If the encoded container ended prematurely. |
| * @retval #WEAVE_ERROR_INVALID_TLV_ELEMENT |
| * If the encoded container contained an invalid or unsupported TLV element type. |
| * @retval #WEAVE_ERROR_INVALID_TLV_TAG |
| * If the encoded container contained a TLV tag in an invalid context, |
| * or if the supplied tag is invalid or inappropriate in the context in |
| * which the new container is being written. |
| * @retval #WEAVE_ERROR_BUFFER_TOO_SMALL |
| * If writing the value would exceed the limit on the maximum number of |
| * bytes specified when the writer was initialized. |
| * @retval #WEAVE_ERROR_NO_MEMORY |
| * If an attempt to allocate an output buffer failed due to lack of |
| * memory. |
| * @retval other Other Weave or platform-specific errors returned by the configured |
| * GetNewBuffer() or FinalizeBuffer() functions, or by the GetNextBuffer() |
| * function associated with the reader object. |
| * |
| */ |
| WEAVE_ERROR TLVWriter::CopyContainer(uint64_t tag, const uint8_t * encodedContainer, uint16_t encodedContainerLen) |
| { |
| WEAVE_ERROR err; |
| TLVReader reader; |
| |
| reader.Init(encodedContainer, encodedContainerLen); |
| |
| err = reader.Next(); |
| SuccessOrExit(err); |
| |
| err = PutPreEncodedContainer(tag, reader.GetType(), reader.GetReadPoint(), reader.GetRemainingLength()); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * Returns the type of container within which the TLVWriter is currently writing. |
| * |
| * The GetContainerType() method returns the type of the TLV container within which the TLVWriter |
| * is currently writing. If the TLVWriter is not writing elements within a container (i.e. if writing |
| * at the outer-most level of an encoding) the method returns kTLVType_NotSpecified. |
| * |
| * @return The TLVType of the current container, or kTLVType_NotSpecified if the TLVWriter is not |
| * writing elements within a container. |
| */ |
| TLVType TLVWriter::GetContainerType() const |
| { |
| return mContainerType; |
| } |
| |
| WEAVE_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, uint64_t tag, uint64_t lenOrVal) |
| { |
| uint8_t *p; |
| uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes |
| |
| if (IsContainerOpen()) |
| return WEAVE_ERROR_TLV_CONTAINER_OPEN; |
| |
| uint32_t tagNum = TagNumFromTag(tag); |
| |
| if ((mRemainingLen >= sizeof(stagingBuf)) && (mMaxLen >= sizeof(stagingBuf))) |
| p = mWritePoint; |
| else |
| p = stagingBuf; |
| |
| if (IsSpecialTag(tag)) |
| { |
| if (tagNum < 256) |
| { |
| if (mContainerType != kTLVType_Structure && mContainerType != kTLVType_Path) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| |
| Write8(p, kTLVTagControl_ContextSpecific | elemType); |
| Write8(p, (uint8_t) tagNum); |
| } |
| else |
| { |
| if (elemType != kTLVElementType_EndOfContainer && mContainerType != kTLVType_NotSpecified |
| && mContainerType != kTLVType_Array && mContainerType != kTLVType_Path) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| |
| Write8(p, kTLVTagControl_Anonymous | elemType); |
| } |
| } |
| else |
| { |
| uint32_t profileId = ProfileIdFromTag(tag); |
| |
| if (mContainerType != kTLVType_NotSpecified && mContainerType != kTLVType_Structure |
| && mContainerType != kTLVType_Path) |
| return WEAVE_ERROR_INVALID_TLV_TAG; |
| |
| if (profileId == kCommonProfileId) |
| { |
| if (tagNum < 65536) |
| { |
| Write8(p, kTLVTagControl_CommonProfile_2Bytes | elemType); |
| LittleEndian::Write16(p, (uint16_t) tagNum); |
| } |
| else |
| { |
| Write8(p, kTLVTagControl_CommonProfile_4Bytes | elemType); |
| LittleEndian::Write32(p, tagNum); |
| } |
| } |
| else if (profileId == ImplicitProfileId) |
| { |
| if (tagNum < 65536) |
| { |
| Write8(p, kTLVTagControl_ImplicitProfile_2Bytes | elemType); |
| LittleEndian::Write16(p, (uint16_t) tagNum); |
| } |
| else |
| { |
| Write8(p, kTLVTagControl_ImplicitProfile_4Bytes | elemType); |
| LittleEndian::Write32(p, tagNum); |
| } |
| } |
| else |
| { |
| uint16_t vendorId = (uint16_t) (profileId >> 16); |
| uint16_t profileNum = (uint16_t) profileId; |
| |
| if (tagNum < 65536) |
| { |
| Write8(p, kTLVTagControl_FullyQualified_6Bytes | elemType); |
| LittleEndian::Write16(p, vendorId); |
| LittleEndian::Write16(p, profileNum); |
| LittleEndian::Write16(p, (uint16_t) tagNum); |
| } |
| else |
| { |
| Write8(p, kTLVTagControl_FullyQualified_8Bytes | elemType); |
| LittleEndian::Write16(p, vendorId); |
| LittleEndian::Write16(p, profileNum); |
| LittleEndian::Write32(p, tagNum); |
| } |
| } |
| } |
| |
| switch (GetTLVFieldSize(elemType)) |
| { |
| case kTLVFieldSize_0Byte: |
| break; |
| case kTLVFieldSize_1Byte: |
| Write8(p, (uint8_t) lenOrVal); |
| break; |
| case kTLVFieldSize_2Byte: |
| LittleEndian::Write16(p, (uint16_t) lenOrVal); |
| break; |
| case kTLVFieldSize_4Byte: |
| LittleEndian::Write32(p, (uint32_t) lenOrVal); |
| break; |
| case kTLVFieldSize_8Byte: |
| LittleEndian::Write64(p, lenOrVal); |
| break; |
| } |
| |
| if ((mRemainingLen >= sizeof(stagingBuf)) && (mMaxLen >= sizeof(stagingBuf))) |
| { |
| uint32_t len = p - mWritePoint; |
| mWritePoint = p; |
| mRemainingLen -= len; |
| mLenWritten += len; |
| return WEAVE_NO_ERROR; |
| } |
| else |
| return WriteData(stagingBuf, p - stagingBuf); |
| } |
| |
| WEAVE_ERROR TLVWriter::WriteElementWithData(TLVType type, uint64_t tag, const uint8_t *data, uint32_t dataLen) |
| { |
| TLVFieldSize lenFieldSize; |
| |
| if (dataLen <= UINT8_MAX) |
| lenFieldSize = kTLVFieldSize_1Byte; |
| else if (dataLen <= UINT16_MAX) |
| lenFieldSize = kTLVFieldSize_2Byte; |
| else |
| lenFieldSize = kTLVFieldSize_4Byte; |
| |
| WEAVE_ERROR err = WriteElementHead((TLVElementType) (type | lenFieldSize), tag, dataLen); |
| if (err != WEAVE_NO_ERROR) |
| return err; |
| |
| return WriteData(data, dataLen); |
| } |
| |
| WEAVE_ERROR TLVWriter::WriteData(const uint8_t *p, uint32_t len) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| VerifyOrExit((mLenWritten + len) <= mMaxLen, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| |
| while (len > 0) |
| { |
| if (mRemainingLen == 0) |
| { |
| VerifyOrExit(GetNewBuffer != NULL, err = WEAVE_ERROR_NO_MEMORY); |
| |
| if (FinalizeBuffer != NULL) |
| { |
| err = FinalizeBuffer(*this, mBufHandle, mBufStart, mWritePoint - mBufStart); |
| SuccessOrExit(err); |
| } |
| |
| err = GetNewBuffer(*this, mBufHandle, mBufStart, mRemainingLen); |
| SuccessOrExit(err); |
| |
| mWritePoint = mBufStart; |
| |
| if (mRemainingLen > (mMaxLen - mLenWritten)) |
| mRemainingLen = (mMaxLen - mLenWritten); |
| } |
| |
| uint32_t writeLen = len; |
| if (writeLen > mRemainingLen) |
| writeLen = mRemainingLen; |
| |
| memmove(mWritePoint, p, writeLen); |
| mWritePoint += writeLen; |
| mRemainingLen -= writeLen; |
| mLenWritten += writeLen; |
| p += writeLen; |
| len -= writeLen; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| /** |
| * An implementation of a TLVWriter GetNewBuffer function for writing to a chain of PacketBuffers. |
| * |
| * The GetNewPacketBuffer() function supplies new output space to a TLVWriter by allocating a chain |
| * of one or more PacketBuffers as needed to store the encoding. The function is designed to be |
| * assigned to the TLVWriter GetNewBuffer function pointer. |
| * |
| * Note that when using the GetNewPacketBuffer with a TLVWriter, the corresponding FinalizePacketBuffer() |
| * function (or an equivalent) should also be used to finalize the buffer chain. |
| * |
| * See the GetNewBufferFunct type definition for additional information on the API of the |
| * GetNewPacketBuffer() function. |
| */ |
| WEAVE_ERROR TLVWriter::GetNewPacketBuffer(TLVWriter& writer, uintptr_t& bufHandle, uint8_t *& bufStart, uint32_t& bufLen) |
| { |
| PacketBuffer *buf = (PacketBuffer *) bufHandle; |
| |
| PacketBuffer *newBuf = buf->Next(); |
| if (newBuf == NULL) |
| { |
| newBuf = PacketBuffer::New(0); |
| if (newBuf != NULL) |
| buf->AddToEnd(newBuf); |
| } |
| |
| if (newBuf != NULL) |
| { |
| bufHandle = (uintptr_t) newBuf; |
| bufStart = newBuf->Start(); |
| bufLen = newBuf->MaxDataLength(); |
| } |
| else |
| { |
| bufStart = NULL; |
| bufLen = 0; |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * An implementation of a TLVWriter GetNewBuffer function for writing to a dynamic buffer. |
| * |
| * The GetNewBuffer_Malloced() function supplies new output space to a TLVWriter by doubling |
| * the size of the underlying dynamic buffer as needed to store the encoding. The function |
| * is designed to be assigned to the TLVWriter GetNewBuffer function pointer. |
| * |
| * See the GetNewBufferFunct type definition for additional information on the API of the |
| * GetNewBuffer_Malloced() function. |
| */ |
| WEAVE_ERROR TLVWriter::GetNewBuffer_Malloced(TLVWriter& writer, uintptr_t& bufHandle, uint8_t *& bufStart, uint32_t& remainingLen) |
| { |
| size_t prevSize = (writer.mWritePoint - *(uint8_t **)bufHandle) + writer.mRemainingLen; |
| |
| size_t newSize = prevSize * 2; |
| if (newSize > writer.mMaxLen) |
| { |
| newSize = writer.mMaxLen; |
| } |
| *(uint8_t **)bufHandle = (uint8_t *)realloc(*(uint8_t **)bufHandle, newSize); |
| bufStart = *(uint8_t **)bufHandle + prevSize; |
| remainingLen = newSize - prevSize; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * An implementation of a TLVWriter FinalizeBuffer function for writing to a chain of PacketBuffers. |
| * |
| * The FinalizePacketBuffer() function performs the necessary finalization required when using a |
| * TLVWriter to write to a chain of PacketBuffers. The function is designed to be used in conjunction |
| * with the GetNewPacketBuffer() function. |
| * |
| * See the FinalizeBufferFunct type definition for additional information on the API of the |
| * FinalizePacketBuffer() function. |
| */ |
| WEAVE_ERROR TLVWriter::FinalizePacketBuffer(TLVWriter& writer, uintptr_t bufHandle, uint8_t *bufStart, uint32_t dataLen) |
| { |
| PacketBuffer *buf = (PacketBuffer *) bufHandle; |
| uint8_t * endPtr = bufStart + dataLen; |
| |
| buf->SetDataLength(endPtr - buf->Start()); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| } // namespace TLV |
| } // namespace Weave |
| } // namespace nl |