diff --git a/src/lib/core/WeaveTLVReader.cpp b/src/lib/core/WeaveTLVReader.cpp index ae48bf93f..e6e198bad 100644 --- a/src/lib/core/WeaveTLVReader.cpp +++ b/src/lib/core/WeaveTLVReader.cpp @@ -541,6 +541,45 @@ WEAVE_ERROR TLVReader::Get(uint64_t& v) return WEAVE_NO_ERROR; } +namespace { +float BitCastToFloat(const uint64_t elemLenOrVal) +{ + float f; + auto u32 = static_cast(elemLenOrVal); + memcpy(&f, &u32, sizeof(f)); + return f; +} +} // namespace + +// Note: Unlike the integer Get functions, this code avoids doing conversions +// between float and double wherever possible, because these conversions are +// relatively expensive on platforms that use soft-float instruction sets. + +/** + * Get the value of the current element as a single-precision floating point number. + * + * @param[out] v Receives the value associated with current TLV element. + * + * @retval #WEAVE_NO_ERROR If the method succeeded. + * @retval #WEAVE_ERROR_WRONG_TLV_TYPE If the current element is not a TLV floating point type, or + * the reader is not positioned on an element. + * + */ +WEAVE_ERROR TLVReader::Get(float & v) +{ + switch (ElementType()) + { + case kTLVElementType_FloatingPointNumber32: + { + v = BitCastToFloat(mElemLenOrVal); + break; + } + default: + return WEAVE_ERROR_WRONG_TLV_TYPE; + } + return WEAVE_NO_ERROR; +} + /** * Get the value of the current element as a double-precision floating point number. * @@ -557,24 +596,14 @@ WEAVE_ERROR TLVReader::Get(double& v) { case kTLVElementType_FloatingPointNumber32: { - union - { - uint32_t u32; - float f; - } cvt; - cvt.u32 = (uint32_t)mElemLenOrVal; - v = cvt.f; + v = BitCastToFloat(mElemLenOrVal); break; } case kTLVElementType_FloatingPointNumber64: { - union - { - uint64_t u64; - double d; - } cvt; - cvt.u64 = mElemLenOrVal; - v = cvt.d; + double d; + memcpy(&d, &mElemLenOrVal, sizeof(d)); + v = d; break; } default: diff --git a/src/test-apps/TestTLV.cpp b/src/test-apps/TestTLV.cpp index ff280adff..9a4431aeb 100644 --- a/src/test-apps/TestTLV.cpp +++ b/src/test-apps/TestTLV.cpp @@ -580,11 +580,12 @@ void ReadEncoding1(nlTestSuite *inSuite, TLVReader& reader) TestNext(inSuite, reader2); - TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), (float)17.9); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), 17.9f); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), 17.9f); TestNext(inSuite, reader2); - TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65536), (double)17.9); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65536), 17.9); TestEndAndCloseContainer(inSuite, reader, reader2); } @@ -2946,11 +2947,12 @@ void TestWeaveTLVReaderDup(nlTestSuite *inSuite) TestNext(inSuite, reader2); - TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), (float)17.9); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), 17.9f); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65535), 17.9f); TestNext(inSuite, reader2); - TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65536), (double)17.9); + TestGet(inSuite, reader2, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_2, 65536), 17.9); TestEndAndCloseContainer(inSuite, reader, reader2); } @@ -2974,6 +2976,11 @@ void TestWeaveTLVReaderErrorHandling(nlTestSuite *inSuite) err = reader.Get(val); NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_WRONG_TLV_TYPE); + // Get(float&) + float numF; + err = reader.Get(numF); + NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_WRONG_TLV_TYPE); + // Get(double&) double numD; err = reader.Get(numD); @@ -3020,6 +3027,38 @@ void TestWeaveTLVReaderErrorHandling(nlTestSuite *inSuite) NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_WRONG_TLV_TYPE); free((void *)data); } + +/** + * Test that Weave TLV reader returns an error when a read is requested that + * would truncate the output. + */ +void TestWeaveTLVReaderTruncatedReads(nlTestSuite * inSuite) +{ + uint8_t buf[2048]; + TLVWriter writer; + TLVReader reader; + + WEAVE_ERROR err; + float outF; + + // Setup: Write some values into the buffer + writer.Init(buf, sizeof(buf)); + writer.ImplicitProfileId = TestProfile_2; + + err = writer.Put(AnonymousTag, double(12.5)); + NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); + + // Test reading values from the buffer + reader.Init(buf, sizeof(buf)); + + TestNext(inSuite, reader); + + TestGet(inSuite, reader, kTLVType_FloatingPointNumber, AnonymousTag, 12.5); + + err = reader.Get(outF); + NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_WRONG_TLV_TYPE); +} + /** * Test Weave TLV Reader in a use case */ @@ -3046,7 +3085,7 @@ void TestWeaveTLVReaderInPractice(nlTestSuite *inSuite) TestNext(inSuite, reader); - TestGet(inSuite, reader, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_1, 4000000000ULL), (float) 1.0); + TestGet(inSuite, reader, kTLVType_FloatingPointNumber, ProfileTag(TestProfile_1, 4000000000ULL), (float) 1.0); } void TestWeaveTLVReader_NextOverContainer_ProcessElement(nlTestSuite *inSuite, TLVReader& reader, void *context) @@ -3147,6 +3186,8 @@ void CheckWeaveTLVReader(nlTestSuite *inSuite, void *inContext) TestWeaveTLVReaderErrorHandling(inSuite); + TestWeaveTLVReaderTruncatedReads(inSuite); + TestWeaveTLVReaderInPractice(inSuite); TestWeaveTLVReader_NextOverContainer(inSuite);