diff --git a/src/lib/core/TLVReader.cpp b/src/lib/core/TLVReader.cpp index 8368b9fb0fac04..ca3da3b555ba12 100644 --- a/src/lib/core/TLVReader.cpp +++ b/src/lib/core/TLVReader.cpp @@ -647,31 +647,18 @@ CHIP_ERROR TLVReader::Next(TLVType expectedType, Tag expectedTag) CHIP_ERROR TLVReader::Skip() { - CHIP_ERROR err; - TLVElementType elemType = ElementType(); - - if (elemType == TLVElementType::EndOfContainer) - return CHIP_END_OF_TLV; + const TLVElementType elemType = ElementType(); + VerifyOrReturnError(elemType != TLVElementType::EndOfContainer, CHIP_END_OF_TLV); if (TLVTypeIsContainer(elemType)) { TLVType outerContainerType; - err = EnterContainer(outerContainerType); - if (err != CHIP_NO_ERROR) - return err; - err = ExitContainer(outerContainerType); - if (err != CHIP_NO_ERROR) - return err; + ReturnErrorOnFailure(EnterContainer(outerContainerType)); + return ExitContainer(outerContainerType); } - else - { - err = SkipData(); - if (err != CHIP_NO_ERROR) - return err; - - ClearElementState(); - } + ReturnErrorOnFailure(SkipData()); + ClearElementState(); return CHIP_NO_ERROR; } @@ -705,8 +692,6 @@ CHIP_ERROR TLVReader::SkipData() if (TLVTypeHasLength(elemType)) { err = ReadData(nullptr, static_cast(mElemLenOrVal)); - if (err != CHIP_NO_ERROR) - return err; } return err; @@ -755,27 +740,16 @@ CHIP_ERROR TLVReader::SkipToEndOfContainer() CHIP_ERROR TLVReader::ReadElement() { - CHIP_ERROR err; - uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes - const uint8_t * p; - TLVElementType elemType; - // Make sure we have input data. Return CHIP_END_OF_TLV if no more data is available. - err = EnsureData(CHIP_END_OF_TLV); - if (err != CHIP_NO_ERROR) - return err; + ReturnErrorOnFailure(EnsureData(CHIP_END_OF_TLV)); + VerifyOrReturnError(mReadPoint != nullptr, CHIP_ERROR_INVALID_TLV_ELEMENT); - if (mReadPoint == nullptr) - { - return CHIP_ERROR_INVALID_TLV_ELEMENT; - } // Get the element's control byte. mControlByte = *mReadPoint; // Extract the element type from the control byte. Fail if it's invalid. - elemType = ElementType(); - if (!IsValidTLVType(elemType)) - return CHIP_ERROR_INVALID_TLV_ELEMENT; + TLVElementType elemType = ElementType(); + VerifyOrReturnError(IsValidTLVType(elemType), CHIP_ERROR_INVALID_TLV_ELEMENT); // Extract the tag control from the control byte. TLVTagControl tagControl = static_cast(mControlByte & kTLVTagControlMask); @@ -787,54 +761,40 @@ CHIP_ERROR TLVReader::ReadElement() TLVFieldSize lenOrValFieldSize = GetTLVFieldSize(elemType); // Determine the number of bytes in the length/value field. - uint8_t valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); + const uint8_t valOrLenBytes = TLVFieldSizeToBytes(lenOrValFieldSize); // Determine the number of bytes in the element's 'head'. This includes: the control byte, the tag bytes (if present), the // length bytes (if present), and for elements that don't have a length (e.g. integers), the value bytes. - uint8_t elemHeadBytes = static_cast(1 + tagBytes + valOrLenBytes); + const uint8_t elemHeadBytes = static_cast(1 + tagBytes + valOrLenBytes); - // If the head of the element overlaps the end of the input buffer, read the bytes into the staging buffer - // and arrange to parse them from there. Otherwise read them directly from the input buffer. - if (elemHeadBytes > (mBufEnd - mReadPoint)) - { - err = ReadData(stagingBuf, elemHeadBytes); - if (err != CHIP_NO_ERROR) - return err; - p = stagingBuf; - } - else - { - p = mReadPoint; - mReadPoint += elemHeadBytes; - mLenRead += elemHeadBytes; - } + // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes + uint8_t stagingBuf[17]; + + // Odd workaround: clang-tidy claims garbage value otherwise as it does not + // understand that ReadData initializes stagingBuf + stagingBuf[1] = 0; + + // If the head of the element goes past the end of the current input buffer, + // we need to read it into the staging buffer to parse it. Just do that unconditionally, + // even if the head does not go past end of current buffer, to save codesize. + ReturnErrorOnFailure(ReadData(stagingBuf, elemHeadBytes)); - // Skip over the control byte. - p++; + // +1 to skip over the control byte + const uint8_t * p = stagingBuf + 1; // Read the tag field, if present. - mElemTag = ReadTag(tagControl, p); + mElemTag = ReadTag(tagControl, p); + mElemLenOrVal = 0; // Read the length/value field, if present. - switch (lenOrValFieldSize) - { - case kTLVFieldSize_0Byte: - mElemLenOrVal = 0; - break; - case kTLVFieldSize_1Byte: - mElemLenOrVal = Read8(p); - break; - case kTLVFieldSize_2Byte: - mElemLenOrVal = LittleEndian::Read16(p); - break; - case kTLVFieldSize_4Byte: - mElemLenOrVal = LittleEndian::Read32(p); - break; - case kTLVFieldSize_8Byte: - mElemLenOrVal = LittleEndian::Read64(p); - VerifyOrReturnError(!TLVTypeHasLength(elemType) || (mElemLenOrVal <= UINT32_MAX), CHIP_ERROR_NOT_IMPLEMENTED); - break; - } + // NOTE: this is works because even though we only memcpy a subset of values and leave + // the rest 0. Value looks like " ... 0 0 ... 0" + // which is the TLV format. HostSwap ensures this becomes a real host value + // (should be a NOOP on LE machines, will full-swap on big-endian machines) + memcpy(&mElemLenOrVal, p, valOrLenBytes); + LittleEndian::HostSwap(mElemLenOrVal); + + VerifyOrReturnError(!TLVTypeHasLength(elemType) || (mElemLenOrVal <= UINT32_MAX), CHIP_ERROR_NOT_IMPLEMENTED); return VerifyElement(); } @@ -929,13 +889,9 @@ Tag TLVReader::ReadTag(TLVTagControl tagControl, const uint8_t *& p) const CHIP_ERROR TLVReader::ReadData(uint8_t * buf, uint32_t len) { - CHIP_ERROR err; - while (len > 0) { - err = EnsureData(CHIP_ERROR_TLV_UNDERRUN); - if (err != CHIP_NO_ERROR) - return err; + ReturnErrorOnFailure(EnsureData(CHIP_ERROR_TLV_UNDERRUN)); uint32_t remainingLen = static_cast(mBufEnd - mReadPoint); @@ -958,29 +914,17 @@ CHIP_ERROR TLVReader::ReadData(uint8_t * buf, uint32_t len) CHIP_ERROR TLVReader::EnsureData(CHIP_ERROR noDataErr) { - CHIP_ERROR err; - if (mReadPoint == mBufEnd) { - if (mLenRead == mMaxLen) - return noDataErr; - - if (mBackingStore == nullptr) - return noDataErr; + VerifyOrReturnError((mLenRead != mMaxLen) && (mBackingStore != nullptr), noDataErr); uint32_t bufLen; - err = mBackingStore->GetNextBuffer(*this, mReadPoint, bufLen); - if (err != CHIP_NO_ERROR) - return err; - if (bufLen == 0) - return noDataErr; + ReturnErrorOnFailure(mBackingStore->GetNextBuffer(*this, mReadPoint, bufLen)); + VerifyOrReturnError(bufLen > 0, noDataErr); // Cap mBufEnd so that we don't read beyond the user's specified maximum length, even // if the underlying buffer is larger. - uint32_t overallLenRemaining = mMaxLen - mLenRead; - if (overallLenRemaining < bufLen) - bufLen = overallLenRemaining; - + bufLen = std::min(bufLen, mMaxLen - mLenRead); mBufEnd = mReadPoint + bufLen; } diff --git a/src/lib/core/TLVTypes.h b/src/lib/core/TLVTypes.h index e6fb238660ac31..06f58d3d8b5c11 100644 --- a/src/lib/core/TLVTypes.h +++ b/src/lib/core/TLVTypes.h @@ -120,7 +120,7 @@ enum * * @return @p true if the specified TLV type is valid; otherwise @p false. */ -inline bool IsValidTLVType(TLVElementType type) +constexpr bool IsValidTLVType(TLVElementType type) { return type <= TLVElementType::EndOfContainer; } @@ -130,7 +130,7 @@ inline bool IsValidTLVType(TLVElementType type) * * @return @p true if the specified TLV type implies the presence of an associated value field; otherwise @p false. */ -inline bool TLVTypeHasValue(TLVElementType type) +constexpr bool TLVTypeHasValue(TLVElementType type) { return (type <= TLVElementType::UInt64 || (type >= TLVElementType::FloatingPointNumber32 && type <= TLVElementType::ByteString_8ByteLength)); @@ -141,7 +141,7 @@ inline bool TLVTypeHasValue(TLVElementType type) * * @return @p true if the specified TLV type implies the presence of an associated length field; otherwise @p false. */ -inline bool TLVTypeHasLength(TLVElementType type) +constexpr bool TLVTypeHasLength(TLVElementType type) { return type >= TLVElementType::UTF8String_1ByteLength && type <= TLVElementType::ByteString_8ByteLength; } @@ -186,13 +186,13 @@ inline bool TLVTypeIsUTF8String(TLVElementType type) * * @return @p true if the specified TLV type is a byte string; otherwise @p false. */ -inline bool TLVTypeIsByteString(TLVElementType type) +constexpr bool TLVTypeIsByteString(TLVElementType type) { return type >= TLVElementType::ByteString_1ByteLength && type <= TLVElementType::ByteString_8ByteLength; } // TODO: move to private namespace -inline TLVFieldSize GetTLVFieldSize(TLVElementType type) +constexpr TLVFieldSize GetTLVFieldSize(TLVElementType type) { if (TLVTypeHasValue(type)) return static_cast(static_cast(type) & kTLVTypeSizeMask); @@ -200,12 +200,18 @@ inline TLVFieldSize GetTLVFieldSize(TLVElementType type) } // TODO: move to private namespace -inline uint8_t TLVFieldSizeToBytes(TLVFieldSize fieldSize) +constexpr uint8_t TLVFieldSizeToBytes(TLVFieldSize fieldSize) { // We would like to assert fieldSize < 7, but that gives us fatal // -Wtautological-constant-out-of-range-compare warnings... return static_cast((fieldSize != kTLVFieldSize_0Byte) ? (1 << fieldSize) : 0); } +static_assert(TLVFieldSizeToBytes(kTLVFieldSize_0Byte) == 0); +static_assert(TLVFieldSizeToBytes(kTLVFieldSize_1Byte) == 1); +static_assert(TLVFieldSizeToBytes(kTLVFieldSize_2Byte) == 2); +static_assert(TLVFieldSizeToBytes(kTLVFieldSize_4Byte) == 4); +static_assert(TLVFieldSizeToBytes(kTLVFieldSize_8Byte) == 8); + } // namespace TLV } // namespace chip diff --git a/src/lib/core/TLVWriter.cpp b/src/lib/core/TLVWriter.cpp index db019fb5488622..aee91011bc1373 100644 --- a/src/lib/core/TLVWriter.cpp +++ b/src/lib/core/TLVWriter.cpp @@ -659,13 +659,13 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ ABORT_ON_UNINITIALIZED_IF_ENABLED(); VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); - if (IsContainerOpen()) - return CHIP_ERROR_TLV_CONTAINER_OPEN; + VerifyOrReturnError(!IsContainerOpen(), 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); + Encoding::LittleEndian::BufferWriter writer(stagingBuf, sizeof(stagingBuf)); + if (IsSpecialTag(tag)) { if (tagNum <= Tag::kContextTagMaxNum) @@ -673,8 +673,8 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ if (mContainerType != kTLVType_Structure && mContainerType != kTLVType_List) return CHIP_ERROR_INVALID_TLV_TAG; - Write8(p, TLVTagControl::ContextSpecific | elemType); - Write8(p, static_cast(tagNum)); + writer.Put8(TLVTagControl::ContextSpecific | elemType); + writer.Put8(static_cast(tagNum)); } else { @@ -682,7 +682,7 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ mContainerType != kTLVType_Array && mContainerType != kTLVType_List) return CHIP_ERROR_INVALID_TLV_TAG; - Write8(p, TLVTagControl::Anonymous | elemType); + writer.Put8(TLVTagControl::Anonymous | elemType); } } else @@ -694,28 +694,28 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ if (profileId == kCommonProfileId) { - if (tagNum < 65536) + if (tagNum <= std::numeric_limits::max()) { - Write8(p, TLVTagControl::CommonProfile_2Bytes | elemType); - LittleEndian::Write16(p, static_cast(tagNum)); + writer.Put8(TLVTagControl::CommonProfile_2Bytes | elemType); + writer.Put16(static_cast(tagNum)); } else { - Write8(p, TLVTagControl::CommonProfile_4Bytes | elemType); - LittleEndian::Write32(p, tagNum); + writer.Put8(TLVTagControl::CommonProfile_4Bytes | elemType); + writer.Put32(tagNum); } } else if (profileId == ImplicitProfileId) { - if (tagNum < 65536) + if (tagNum <= std::numeric_limits::max()) { - Write8(p, TLVTagControl::ImplicitProfile_2Bytes | elemType); - LittleEndian::Write16(p, static_cast(tagNum)); + writer.Put8(TLVTagControl::ImplicitProfile_2Bytes | elemType); + writer.Put16(static_cast(tagNum)); } else { - Write8(p, TLVTagControl::ImplicitProfile_4Bytes | elemType); - LittleEndian::Write32(p, tagNum); + writer.Put8(TLVTagControl::ImplicitProfile_4Bytes | elemType); + writer.Put32(tagNum); } } else @@ -723,44 +723,33 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ uint16_t vendorId = static_cast(profileId >> 16); uint16_t profileNum = static_cast(profileId); - if (tagNum < 65536) + if (tagNum <= std::numeric_limits::max()) { - Write8(p, TLVTagControl::FullyQualified_6Bytes | elemType); - LittleEndian::Write16(p, vendorId); - LittleEndian::Write16(p, profileNum); - LittleEndian::Write16(p, static_cast(tagNum)); + + writer.Put8(TLVTagControl::FullyQualified_6Bytes | elemType); + writer.Put16(vendorId); + writer.Put16(profileNum); + writer.Put16(static_cast(tagNum)); } else { - Write8(p, TLVTagControl::FullyQualified_8Bytes | elemType); - LittleEndian::Write16(p, vendorId); - LittleEndian::Write16(p, profileNum); - LittleEndian::Write32(p, tagNum); + writer.Put8(TLVTagControl::FullyQualified_8Bytes | elemType); + writer.Put16(vendorId); + writer.Put16(profileNum); + writer.Put32(tagNum); } } } - switch (GetTLVFieldSize(elemType)) + uint8_t lengthSize = TLVFieldSizeToBytes(GetTLVFieldSize(elemType)); + if (lengthSize > 0) { - 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; + writer.EndianPut(lenOrVal, lengthSize); } - uint32_t bytesStaged = static_cast(p - stagingBuf); - VerifyOrDie(bytesStaged <= sizeof(stagingBuf)); - return WriteData(stagingBuf, bytesStaged); + size_t written = 0; + VerifyOrDie(writer.Fit(written)); + return WriteData(stagingBuf, static_cast(written)); } CHIP_ERROR TLVWriter::WriteElementWithData(TLVType type, Tag tag, const uint8_t * data, uint32_t dataLen)