diff --git a/src/lib/core/CHIPTLV.h b/src/lib/core/CHIPTLV.h index 71cdfbbc776fb4..9bb0487fbc717d 100644 --- a/src/lib/core/CHIPTLV.h +++ b/src/lib/core/CHIPTLV.h @@ -1113,7 +1113,7 @@ class DLL_EXPORT TLVWriter /** * static_cast to enumerations' underlying type when data is an enumeration. */ - template + template ::value>> CHIP_ERROR Put(uint64_t tag, T data) { return Put(tag, to_underlying(data)); diff --git a/src/lib/support/Span.h b/src/lib/support/Span.h index c4706a672f2930..12d5ad6622e031 100644 --- a/src/lib/support/Span.h +++ b/src/lib/support/Span.h @@ -26,6 +26,9 @@ namespace chip { +template +class FixedSpan; + /** * @brief A wrapper class for holding objects and its length, without the ownership of it. * We can use C++20 std::span once we support it, the data() and size() come from C++20 std::span. @@ -42,6 +45,17 @@ class Span constexpr explicit Span(T (&databuf)[N]) : Span(databuf, N) {} + // Allow implicit construction from a Span over a type that matches our + // type, up to const-ness. + template , std::remove_const_t>::value>> + constexpr Span(const Span & other) : Span(other.data(), other.size()) + {} + + // Allow implicit construction from a FixedSpan over a type that matches our + // type, up to const-ness. + template , std::remove_const_t>::value>> + constexpr inline Span(const FixedSpan & other); + constexpr pointer data() const { return mDataBuf; } constexpr size_t size() const { return mDataLen; } constexpr bool empty() const { return size() == 0; } @@ -52,6 +66,8 @@ class Span { return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0)); } + template , std::remove_const_t>::value>> + inline bool data_equal(const FixedSpan & other) const; Span SubSpan(size_t offset, size_t length) const { @@ -60,13 +76,6 @@ class Span return Span(mDataBuf + offset, length); } - // Allow converting a span with non-const T into a span with const T. - template - operator typename std::enable_if_t::value, Span>() const - { - return Span(data(), size()); - } - // Allow reducing the size of a span. void reduce_size(size_t new_size) { @@ -96,19 +105,30 @@ class FixedSpan // template). // // To do that we have a template constructor enabled only when the type - // passed to it is a pointer type, and rely on our assignment of that - // pointer type to mDataBuf to verify that the pointer is to a type - // compatible enough with T (T itself, a subclass, a non-const version if T - // is const, etc). - template ::value>> + // passed to it is a pointer type, and that pointer is to a type that + // matches T up to const-ness. Importantly, we do NOT want to allow + // subclasses of T here, because they would have a different size and our + // Span would not work right. + template < + class U, + typename = std::enable_if_t::value && + std::is_same, std::remove_const_t>>::value>> constexpr explicit FixedSpan(U databuf) : mDataBuf(databuf) {} - template + template , std::remove_const_t>::value>> constexpr explicit FixedSpan(U (&databuf)[M]) : mDataBuf(databuf) { static_assert(M >= N, "Passed-in buffer too small for FixedSpan"); } + // Allow implicit construction from a FixedSpan of sufficient size over a + // type that matches our type, up to const-ness. + template , std::remove_const_t>::value>> + constexpr FixedSpan(FixedSpan const & other) : mDataBuf(other.data()) + { + static_assert(M >= N, "Passed-in FixedSpan is smaller than we are"); + } + constexpr pointer data() const { return mDataBuf; } constexpr size_t size() const { return N; } constexpr bool empty() const { return data() == nullptr; } @@ -126,17 +146,28 @@ class FixedSpan (!empty() && !other.empty() && (memcmp(data(), other.data(), size() * sizeof(T)) == 0)); } - // Allow converting a span with non-const T into a span with const T. - template - operator typename std::enable_if_t::value, FixedSpan>() const + template , std::remove_const_t>::value>> + bool data_equal(const Span & other) const { - return FixedSpan(data()); + return (size() == other.size() && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0))); } private: pointer mDataBuf; }; +template +template +constexpr Span::Span(const FixedSpan & other) : Span(other.data(), other.size()) +{} + +template +template +inline bool Span::data_equal(const FixedSpan & other) const +{ + return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0)); +} + using ByteSpan = Span; using MutableByteSpan = Span; template diff --git a/src/lib/support/tests/TestSpan.cpp b/src/lib/support/tests/TestSpan.cpp index 27eb6cca4d9aa4..2589a70a777a87 100644 --- a/src/lib/support/tests/TestSpan.cpp +++ b/src/lib/support/tests/TestSpan.cpp @@ -138,6 +138,11 @@ static void TestMutableByteSpan(nlTestSuite * inSuite, void * inContext) ByteSpan s9 = s8; NL_TEST_ASSERT(inSuite, s9.data_equal(s8)); NL_TEST_ASSERT(inSuite, s8.data_equal(s9)); + + // Not mutable span on purpose. + ByteSpan s10(s8); + NL_TEST_ASSERT(inSuite, s10.data_equal(s8)); + NL_TEST_ASSERT(inSuite, s8.data_equal(s10)); } static void TestFixedByteSpan(nlTestSuite * inSuite, void * inContext) @@ -171,7 +176,7 @@ static void TestFixedByteSpan(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, s3.data_equal(s2)); uint8_t arr2[] = { 3, 2, 1 }; - FixedByteSpan<3> s4(arr2); + FixedSpan s4(arr2); NL_TEST_ASSERT(inSuite, !s4.data_equal(s2)); size_t idx = 0; @@ -180,6 +185,58 @@ static void TestFixedByteSpan(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, entry == arr2[idx++]); } NL_TEST_ASSERT(inSuite, idx == 3); + + FixedByteSpan<3> s5(arr2); + NL_TEST_ASSERT(inSuite, s5.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s5)); + + FixedByteSpan<2> s6(s4); + idx = 0; + for (auto & entry : s6) + { + NL_TEST_ASSERT(inSuite, entry == arr2[idx++]); + } + NL_TEST_ASSERT(inSuite, idx == 2); + + // Not fixed, to test conversion. + ByteSpan s7(s4); + NL_TEST_ASSERT(inSuite, s7.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s7)); + + MutableByteSpan s8(s4); + NL_TEST_ASSERT(inSuite, s8.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s8)); +} + +static void TestSpanOfPointers(nlTestSuite * inSuite, void * inContext) +{ + uint8_t x = 5; + uint8_t * ptrs[] = { &x, &x }; + Span s1(ptrs); + Span s2(s1); + NL_TEST_ASSERT(inSuite, s1.data_equal(s2)); + NL_TEST_ASSERT(inSuite, s2.data_equal(s1)); + + FixedSpan s3(ptrs); + FixedSpan s4(s3); + NL_TEST_ASSERT(inSuite, s1.data_equal(s3)); + NL_TEST_ASSERT(inSuite, s3.data_equal(s1)); + + NL_TEST_ASSERT(inSuite, s2.data_equal(s3)); + NL_TEST_ASSERT(inSuite, s3.data_equal(s2)); + + NL_TEST_ASSERT(inSuite, s1.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s1)); + + NL_TEST_ASSERT(inSuite, s2.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s2)); + + NL_TEST_ASSERT(inSuite, s3.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s4.data_equal(s3)); + + Span s5(s3); + NL_TEST_ASSERT(inSuite, s5.data_equal(s3)); + NL_TEST_ASSERT(inSuite, s3.data_equal(s5)); } #define NL_TEST_DEF_FN(fn) NL_TEST_DEF("Test " #fn, fn) @@ -187,7 +244,7 @@ static void TestFixedByteSpan(nlTestSuite * inSuite, void * inContext) * Test Suite. It lists all the test functions. */ static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan), - NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_SENTINEL() }; + NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_DEF_FN(TestSpanOfPointers), NL_TEST_SENTINEL() }; int TestSpan(void) {