/* * * Copyright (c) 2020-2021 Project CHIP Authors * Copyright (c) 2013-2017 Nest Labs, Inc. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Doxygen is confused by the __attribute__ annotation #ifndef DOXYGEN #define NO_INLINE __attribute__((noinline)) #endif // DOXYGEN // You can enable this block manually to abort on usage of uninitialized writers in // your codebase. There are no such usages in the SDK (outside of tests). #if 0 #define ABORT_ON_UNINITIALIZED_IF_ENABLED() VerifyOrDie(IsInitialized() == true) #else #define ABORT_ON_UNINITIALIZED_IF_ENABLED() \ do \ { \ } while (0) #endif namespace chip { namespace TLV { using namespace chip::Encoding; TLVWriter::TLVWriter() : ImplicitProfileId(kProfileIdNotSpecified), AppData(nullptr), mBackingStore(nullptr), mBufStart(nullptr), mWritePoint(nullptr), mRemainingLen(0), mLenWritten(0), mMaxLen(0), mReservedSize(0), mContainerType(kTLVType_NotSpecified), mInitializationCookie(0), mContainerOpen(false), mCloseContainerReserved(true) {} NO_INLINE void TLVWriter::Init(uint8_t * buf, size_t maxLen) { // TODO: Maybe we can just make mMaxLen, mLenWritten, mRemainingLen size_t instead? uint32_t actualMaxLen = maxLen > UINT32_MAX ? UINT32_MAX : static_cast(maxLen); // TODO(#30825): Need to ensure a single init path for this complex data. mInitializationCookie = 0; mBackingStore = nullptr; mBufStart = buf; mWritePoint = buf; mRemainingLen = actualMaxLen; mLenWritten = 0; mMaxLen = actualMaxLen; mContainerType = kTLVType_NotSpecified; mReservedSize = 0; SetContainerOpen(false); SetCloseContainerReserved(true); ImplicitProfileId = kProfileIdNotSpecified; mInitializationCookie = kExpectedInitializationCookie; } CHIP_ERROR TLVWriter::Init(TLVBackingStore & backingStore, uint32_t maxLen /* = UINT32_MAX */) { // TODO(#30825): Need to ensure a single init path for this complex data. Init(nullptr, maxLen); mInitializationCookie = 0; mBackingStore = &backingStore; mBufStart = nullptr; mRemainingLen = 0; CHIP_ERROR err = mBackingStore->OnInit(*this, mBufStart, mRemainingLen); if (err != CHIP_NO_ERROR) return err; VerifyOrReturnError(mBufStart != nullptr, CHIP_ERROR_INTERNAL); mWritePoint = mBufStart; mInitializationCookie = kExpectedInitializationCookie; return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::Finalize() { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_ERROR err = CHIP_NO_ERROR; if (IsContainerOpen()) return CHIP_ERROR_TLV_CONTAINER_OPEN; if (mBackingStore != nullptr) err = mBackingStore->FinalizeBuffer(*this, mBufStart, static_cast(mWritePoint - mBufStart)); // TODO(#30825) The following should be safe, but in some cases (without mBackingStore), there are incremental writes that // start failing. #if 0 if (err == CHIP_NO_ERROR) mInitializationCookie = 0; #endif return err; } CHIP_ERROR TLVWriter::ReserveBuffer(uint32_t aBufferSize) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mRemainingLen >= aBufferSize, CHIP_ERROR_NO_MEMORY); if (mBackingStore) { VerifyOrReturnError(mBackingStore->GetNewBufferWillAlwaysFail(), CHIP_ERROR_INCORRECT_STATE); } mReservedSize += aBufferSize; mRemainingLen -= aBufferSize; return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::PutBoolean(Tag tag, bool v) { return WriteElementHead((v) ? TLVElementType::BooleanTrue : TLVElementType::BooleanFalse, tag, 0); } CHIP_ERROR TLVWriter::Put(Tag tag, uint8_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, uint8_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::UInt8, tag, v); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, uint16_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, uint16_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::UInt16, tag, v); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, uint32_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, uint32_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::UInt32, tag, v); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, uint64_t v) { TLVElementType elemType; if (v <= UINT8_MAX) elemType = TLVElementType::UInt8; else if (v <= UINT16_MAX) elemType = TLVElementType::UInt16; else if (v <= UINT32_MAX) elemType = TLVElementType::UInt32; else elemType = TLVElementType::UInt64; return WriteElementHead(elemType, tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, uint64_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::UInt64, tag, v); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, int8_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, int8_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::Int8, tag, static_cast(v)); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, int16_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, int16_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::Int16, tag, static_cast(v)); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, int32_t v) { return Put(tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, int32_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::Int32, tag, static_cast(v)); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, int64_t v) { TLVElementType elemType; if (v >= INT8_MIN && v <= INT8_MAX) elemType = TLVElementType::Int8; else if (v >= INT16_MIN && v <= INT16_MAX) elemType = TLVElementType::Int16; else if (v >= INT32_MIN && v <= INT32_MAX) elemType = TLVElementType::Int32; else elemType = TLVElementType::Int64; return WriteElementHead(elemType, tag, static_cast(v)); } CHIP_ERROR TLVWriter::Put(Tag tag, int64_t v, bool preserveSize) { if (preserveSize) return WriteElementHead(TLVElementType::Int64, tag, static_cast(v)); return Put(tag, v); } CHIP_ERROR TLVWriter::Put(Tag tag, const float v) { uint32_t u32; memcpy(&u32, &v, sizeof(u32)); return WriteElementHead(TLVElementType::FloatingPointNumber32, tag, u32); } CHIP_ERROR TLVWriter::Put(Tag tag, const double v) { uint64_t u64; memcpy(&u64, &v, sizeof(u64)); return WriteElementHead(TLVElementType::FloatingPointNumber64, tag, u64); } CHIP_ERROR TLVWriter::Put(Tag tag, ByteSpan data) { VerifyOrReturnError(CanCastTo(data.size()), CHIP_ERROR_MESSAGE_TOO_LONG); return PutBytes(tag, data.data(), static_cast(data.size())); } CHIP_ERROR TLVWriter::PutBytes(Tag tag, const uint8_t * buf, uint32_t len) { return WriteElementWithData(kTLVType_ByteString, tag, buf, len); } CHIP_ERROR TLVWriter::PutString(Tag tag, const char * buf) { size_t len = strlen(buf); if (!CanCastTo(len)) { return CHIP_ERROR_INVALID_ARGUMENT; } return PutString(tag, buf, static_cast(len)); } CHIP_ERROR TLVWriter::PutString(Tag tag, const char * buf, uint32_t len) { #if CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_WRITE // Spec requirement: A.11.2. UTF-8 and Octet Strings // // For UTF-8 strings, the value octets SHALL encode a valid // UTF-8 character (code points) sequence. // // Senders SHALL NOT include a terminating null character to // mark the end of a string. if (!Utf8::IsValid(CharSpan(buf, len))) { return CHIP_ERROR_INVALID_UTF8; } if ((len > 0) && (buf[len - 1] == 0)) { return CHIP_ERROR_INVALID_TLV_CHAR_STRING; } #endif // CHIP_CONFIG_TLV_VALIDATE_CHAR_STRING_ON_READ return WriteElementWithData(kTLVType_UTF8String, tag, reinterpret_cast(buf), len); } CHIP_ERROR TLVWriter::PutString(Tag tag, CharSpan str) { if (!CanCastTo(str.size())) { return CHIP_ERROR_INVALID_ARGUMENT; } return PutString(tag, str.data(), static_cast(str.size())); } CHIP_ERROR TLVWriter::PutStringF(Tag tag, const char * fmt, ...) { CHIP_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::TLVWriterPutcharCB(uint8_t c, void * appState) { TLVWriter * w = static_cast(appState); w->WriteData(&c, sizeof(c)); } #endif CHIP_ERROR TLVWriter::VPutStringF(Tag tag, const char * fmt, va_list ap) { va_list aq; size_t dataLen; CHIP_ERROR err = CHIP_NO_ERROR; TLVFieldSize lenFieldSize; #if !CONFIG_HAVE_VCBPRINTF char * tmpBuf; #endif va_copy(aq, ap); dataLen = static_cast(vsnprintf(nullptr, 0, fmt, aq)); va_end(aq); if (!CanCastTo(dataLen)) { return CHIP_ERROR_INVALID_ARGUMENT; } if (dataLen <= UINT8_MAX) lenFieldSize = kTLVFieldSize_1Byte; else if (dataLen <= UINT16_MAX) lenFieldSize = kTLVFieldSize_2Byte; else lenFieldSize = kTLVFieldSize_4Byte; // write length. err = WriteElementHead( static_cast(static_cast(kTLVType_UTF8String) | static_cast(lenFieldSize)), tag, dataLen); SuccessOrExit(err); VerifyOrExit((mLenWritten + dataLen) <= mMaxLen, err = CHIP_ERROR_BUFFER_TOO_SMALL); // write data #if CONFIG_HAVE_VCBPRINTF va_copy(aq, ap); vcbprintf(TLVWriterPutcharCB, this, dataLen, fmt, aq); va_end(aq); #else // CONFIG_HAVE_VCBPRINTF tmpBuf = static_cast(chip::Platform::MemoryAlloc(dataLen + 1)); VerifyOrExit(tmpBuf != nullptr, err = CHIP_ERROR_NO_MEMORY); va_copy(aq, ap); vsnprintf(tmpBuf, dataLen + 1, fmt, aq); va_end(aq); err = WriteData(reinterpret_cast(tmpBuf), static_cast(dataLen)); chip::Platform::MemoryFree(tmpBuf); #endif // CONFIG_HAVE_VCBPRINTF exit: return err; } CHIP_ERROR TLVWriter::PutNull(Tag tag) { return WriteElementHead(TLVElementType::Null, tag, 0); } CHIP_ERROR TLVWriter::CopyElement(TLVReader & reader) { return CopyElement(reader.GetTag(), reader); } const size_t kTLVCopyChunkSize = 16; CHIP_ERROR TLVWriter::CopyElement(Tag tag, TLVReader & reader) { 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[kTLVCopyChunkSize]; VerifyOrReturnError(elemType != TLVElementType::NotSpecified && elemType != TLVElementType::EndOfContainer, CHIP_ERROR_INCORRECT_STATE); // Initialize the helper readerHelper.Init(reader); // Skip to the end of the element. ReturnErrorOnFailure(reader.Skip()); // 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. ReturnErrorOnFailure(WriteElementHead(elemType, tag, elemLenOrVal)); while (copyDataLen > 0) { uint32_t chunkSize = copyDataLen > kTLVCopyChunkSize ? kTLVCopyChunkSize : copyDataLen; ReturnErrorOnFailure(readerHelper.ReadData(chunk, chunkSize)); ReturnErrorOnFailure(WriteData(chunk, chunkSize)); copyDataLen -= chunkSize; } return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & containerWriter) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE); if (IsCloseContainerReserved()) { VerifyOrReturnError(mMaxLen >= kEndOfContainerMarkerSize, CHIP_ERROR_BUFFER_TOO_SMALL); mMaxLen -= kEndOfContainerMarkerSize; } err = WriteElementHead(static_cast(containerType), tag, 0); if (err != CHIP_NO_ERROR) { // undo the space reservation, as the container is not actually open if (IsCloseContainerReserved()) mMaxLen += kEndOfContainerMarkerSize; return err; } // TODO(#30825): Clean-up this separate init path path. containerWriter.mBackingStore = mBackingStore; 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.mInitializationCookie = kExpectedInitializationCookie; SetContainerOpen(true); return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::CloseContainer(TLVWriter & containerWriter) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); if (!TLVTypeIsContainer(containerWriter.mContainerType)) return CHIP_ERROR_INCORRECT_STATE; if (containerWriter.IsContainerOpen()) return CHIP_ERROR_TLV_CONTAINER_OPEN; mBackingStore = containerWriter.mBackingStore; 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(static_cast(nullptr), 0); return WriteElementHead(TLVElementType::EndOfContainer, AnonymousTag(), 0); } CHIP_ERROR TLVWriter::StartContainer(Tag tag, TLVType containerType, TLVType & outerContainerType) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE); if (IsCloseContainerReserved()) { VerifyOrReturnError(mMaxLen >= kEndOfContainerMarkerSize, CHIP_ERROR_BUFFER_TOO_SMALL); mMaxLen -= kEndOfContainerMarkerSize; } err = WriteElementHead(static_cast(containerType), tag, 0); if (err != CHIP_NO_ERROR) { // undo the space reservation, as the container is not actually open if (IsCloseContainerReserved()) mMaxLen += kEndOfContainerMarkerSize; return err; } outerContainerType = mContainerType; mContainerType = containerType; SetContainerOpen(false); return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::EndContainer(TLVType outerContainerType) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); if (!TLVTypeIsContainer(mContainerType)) return CHIP_ERROR_INCORRECT_STATE; mContainerType = outerContainerType; if (IsCloseContainerReserved()) mMaxLen += kEndOfContainerMarkerSize; return WriteElementHead(TLVElementType::EndOfContainer, AnonymousTag(), 0); } CHIP_ERROR TLVWriter::PutPreEncodedContainer(Tag tag, TLVType containerType, const uint8_t * data, uint32_t dataLen) { if (!TLVTypeIsContainer(containerType)) return CHIP_ERROR_INVALID_ARGUMENT; CHIP_ERROR err = WriteElementHead(static_cast(containerType), tag, 0); if (err != CHIP_NO_ERROR) return err; return WriteData(data, dataLen); } CHIP_ERROR TLVWriter::CopyContainer(TLVReader & container) { return CopyContainer(container.GetTag(), container); } CHIP_ERROR TLVWriter::CopyContainer(Tag tag, TLVReader & container) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); // NOTE: This function MUST be used with a TVLReader that is reading from a contiguous buffer. if (container.mBackingStore != nullptr) return CHIP_ERROR_INVALID_ARGUMENT; CHIP_ERROR err; TLVType containerType, outerContainerType; const uint8_t * containerStart; containerType = container.GetType(); err = container.EnterContainer(outerContainerType); if (err != CHIP_NO_ERROR) return err; containerStart = container.GetReadPoint(); err = container.ExitContainer(outerContainerType); if (err != CHIP_NO_ERROR) return err; return PutPreEncodedContainer(tag, containerType, containerStart, static_cast(container.GetReadPoint() - containerStart)); } CHIP_ERROR TLVWriter::CopyContainer(Tag tag, const uint8_t * encodedContainer, uint16_t encodedContainerLen) { VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); TLVReader reader; reader.Init(encodedContainer, encodedContainerLen); ReturnErrorOnFailure(reader.Next()); ReturnErrorOnFailure(PutPreEncodedContainer(tag, reader.GetType(), reader.GetReadPoint(), reader.GetRemainingLength())); return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_t lenOrVal) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); if (IsContainerOpen()) return CHIP_ERROR_TLV_CONTAINER_OPEN; uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes uint8_t * p = stagingBuf; uint32_t tagNum = TagNumFromTag(tag); if (IsSpecialTag(tag)) { if (tagNum <= Tag::kContextTagMaxNum) { if (mContainerType != kTLVType_Structure && mContainerType != kTLVType_List) return CHIP_ERROR_INVALID_TLV_TAG; Write8(p, TLVTagControl::ContextSpecific | elemType); Write8(p, static_cast(tagNum)); } else { if (elemType != TLVElementType::EndOfContainer && mContainerType != kTLVType_NotSpecified && mContainerType != kTLVType_Array && mContainerType != kTLVType_List) return CHIP_ERROR_INVALID_TLV_TAG; Write8(p, TLVTagControl::Anonymous | elemType); } } else { uint32_t profileId = ProfileIdFromTag(tag); if (mContainerType != kTLVType_NotSpecified && mContainerType != kTLVType_Structure && mContainerType != kTLVType_List) return CHIP_ERROR_INVALID_TLV_TAG; if (profileId == kCommonProfileId) { if (tagNum < 65536) { Write8(p, TLVTagControl::CommonProfile_2Bytes | elemType); LittleEndian::Write16(p, static_cast(tagNum)); } else { Write8(p, TLVTagControl::CommonProfile_4Bytes | elemType); LittleEndian::Write32(p, tagNum); } } else if (profileId == ImplicitProfileId) { if (tagNum < 65536) { Write8(p, TLVTagControl::ImplicitProfile_2Bytes | elemType); LittleEndian::Write16(p, static_cast(tagNum)); } else { Write8(p, TLVTagControl::ImplicitProfile_4Bytes | elemType); LittleEndian::Write32(p, tagNum); } } else { uint16_t vendorId = static_cast(profileId >> 16); uint16_t profileNum = static_cast(profileId); if (tagNum < 65536) { Write8(p, TLVTagControl::FullyQualified_6Bytes | elemType); LittleEndian::Write16(p, vendorId); LittleEndian::Write16(p, profileNum); LittleEndian::Write16(p, static_cast(tagNum)); } else { Write8(p, TLVTagControl::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, static_cast(lenOrVal)); break; case kTLVFieldSize_2Byte: LittleEndian::Write16(p, static_cast(lenOrVal)); break; case kTLVFieldSize_4Byte: LittleEndian::Write32(p, static_cast(lenOrVal)); break; case kTLVFieldSize_8Byte: LittleEndian::Write64(p, lenOrVal); break; } uint32_t bytesStaged = static_cast(p - stagingBuf); VerifyOrDie(bytesStaged <= sizeof(stagingBuf)); return WriteData(stagingBuf, bytesStaged); } CHIP_ERROR TLVWriter::WriteElementWithData(TLVType type, Tag tag, const uint8_t * data, uint32_t dataLen) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); if (static_cast(type) & kTLVTypeSizeMask) { // We won't be able to recover this type properly! return CHIP_ERROR_INVALID_ARGUMENT; } TLVFieldSize lenFieldSize; if (dataLen <= UINT8_MAX) lenFieldSize = kTLVFieldSize_1Byte; else if (dataLen <= UINT16_MAX) lenFieldSize = kTLVFieldSize_2Byte; else lenFieldSize = kTLVFieldSize_4Byte; CHIP_ERROR err = WriteElementHead(static_cast(static_cast(type) | static_cast(lenFieldSize)), tag, dataLen); if (err != CHIP_NO_ERROR) return err; return WriteData(data, dataLen); } CHIP_ERROR TLVWriter::WriteData(const uint8_t * p, uint32_t len) { ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError((mLenWritten + len) <= mMaxLen, CHIP_ERROR_BUFFER_TOO_SMALL); while (len > 0) { if (mRemainingLen == 0) { VerifyOrReturnError(mBackingStore != nullptr, CHIP_ERROR_NO_MEMORY); VerifyOrReturnError(CanCastTo(mWritePoint - mBufStart), CHIP_ERROR_INCORRECT_STATE); ReturnErrorOnFailure(mBackingStore->FinalizeBuffer(*this, mBufStart, static_cast(mWritePoint - mBufStart))); ReturnErrorOnFailure(mBackingStore->GetNewBuffer(*this, mBufStart, mRemainingLen)); VerifyOrReturnError(mRemainingLen > 0, CHIP_ERROR_NO_MEMORY); 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; } return CHIP_NO_ERROR; } } // namespace TLV } // namespace chip