diff --git a/src/lib/support/Span.h b/src/lib/support/Span.h index bd1e7cdd72219d..b6c5213bf5fe06 100644 --- a/src/lib/support/Span.h +++ b/src/lib/support/Span.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -44,7 +45,10 @@ class Span constexpr pointer data() const { return mDataBuf; } size_t size() const { return mDataLen; } bool empty() const { return size() == 0; } - bool data_equal(const Span & other) const + + // Allow data_equal for spans that are over the same type up to const-ness. + template , std::remove_const_t>::value>> + bool data_equal(const Span & other) const { return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0)); } @@ -56,6 +60,20 @@ 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) + { + VerifyOrDie(new_size <= size()); + mDataLen = new_size; + } + private: pointer mDataBuf; size_t mDataLen; @@ -73,12 +91,22 @@ class FixedSpan constexpr pointer data() const { return mDataBuf; } size_t size() const { return N; } bool empty() const { return data() == nullptr; } - bool data_equal(const FixedSpan & other) const + + // Allow data_equal for spans that are over the same type up to const-ness. + template , std::remove_const_t>::value>> + bool data_equal(const FixedSpan & other) const { return (empty() && other.empty()) || (!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 + { + return FixedSpan(data()); + } + private: pointer mDataBuf; }; diff --git a/src/lib/support/tests/TestSpan.cpp b/src/lib/support/tests/TestSpan.cpp index 5546df79b45719..83566db22bdaac 100644 --- a/src/lib/support/tests/TestSpan.cpp +++ b/src/lib/support/tests/TestSpan.cpp @@ -70,6 +70,74 @@ static void TestByteSpan(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, !s5.data_equal(s4)); NL_TEST_ASSERT(inSuite, s5.data_equal(s0)); NL_TEST_ASSERT(inSuite, s0.data_equal(s5)); + + ByteSpan s6(arr2); + s6.reduce_size(2); + NL_TEST_ASSERT(inSuite, s6.size() == 2); + ByteSpan s7(arr2, 2); + NL_TEST_ASSERT(inSuite, s6.data_equal(s7)); + NL_TEST_ASSERT(inSuite, s7.data_equal(s6)); +} + +static void TestMutableByteSpan(nlTestSuite * inSuite, void * inContext) +{ + uint8_t arr[] = { 1, 2, 3 }; + + MutableByteSpan s0 = MutableByteSpan(); + NL_TEST_ASSERT(inSuite, s0.data() == nullptr); + NL_TEST_ASSERT(inSuite, s0.size() == 0); + NL_TEST_ASSERT(inSuite, s0.empty()); + NL_TEST_ASSERT(inSuite, s0.data_equal(s0)); + + MutableByteSpan s1(arr, 2); + NL_TEST_ASSERT(inSuite, s1.data() == arr); + NL_TEST_ASSERT(inSuite, s1.size() == 2); + NL_TEST_ASSERT(inSuite, !s1.empty()); + NL_TEST_ASSERT(inSuite, s1.data_equal(s1)); + NL_TEST_ASSERT(inSuite, !s1.data_equal(s0)); + + MutableByteSpan s2(arr); + NL_TEST_ASSERT(inSuite, s2.data() == arr); + NL_TEST_ASSERT(inSuite, s2.size() == 3); + NL_TEST_ASSERT(inSuite, s2.data()[2] == 3); + NL_TEST_ASSERT(inSuite, !s2.empty()); + NL_TEST_ASSERT(inSuite, s2.data_equal(s2)); + NL_TEST_ASSERT(inSuite, !s2.data_equal(s1)); + + MutableByteSpan s3 = s2; + NL_TEST_ASSERT(inSuite, s3.data() == arr); + NL_TEST_ASSERT(inSuite, s3.size() == 3); + NL_TEST_ASSERT(inSuite, s3.data()[2] == 3); + NL_TEST_ASSERT(inSuite, !s3.empty()); + NL_TEST_ASSERT(inSuite, s3.data_equal(s2)); + + uint8_t arr2[] = { 3, 2, 1 }; + MutableByteSpan s4(arr2); + NL_TEST_ASSERT(inSuite, !s4.data_equal(s2)); + + MutableByteSpan s5(arr2, 0); + NL_TEST_ASSERT(inSuite, s5.data() != nullptr); + NL_TEST_ASSERT(inSuite, !s5.data_equal(s4)); + NL_TEST_ASSERT(inSuite, s5.data_equal(s0)); + NL_TEST_ASSERT(inSuite, s0.data_equal(s5)); + + MutableByteSpan s6(arr2); + s6.reduce_size(2); + NL_TEST_ASSERT(inSuite, s6.size() == 2); + MutableByteSpan s7(arr2, 2); + NL_TEST_ASSERT(inSuite, s6.data_equal(s7)); + NL_TEST_ASSERT(inSuite, s7.data_equal(s6)); + + uint8_t arr3[] = { 1, 2, 3 }; + MutableByteSpan s8(arr3); + NL_TEST_ASSERT(inSuite, arr3[1] == 2); + s8.data()[1] = 3; + NL_TEST_ASSERT(inSuite, arr3[1] == 3); + + // Not mutable span on purpose, to test conversion. + ByteSpan s9 = s8; + NL_TEST_ASSERT(inSuite, s9.data_equal(s8)); + NL_TEST_ASSERT(inSuite, s8.data_equal(s9)); } static void TestFixedByteSpan(nlTestSuite * inSuite, void * inContext) @@ -111,7 +179,8 @@ 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(TestFixedByteSpan), NL_TEST_SENTINEL() }; +static const nlTest sTests[] = { NL_TEST_DEF_FN(TestByteSpan), NL_TEST_DEF_FN(TestMutableByteSpan), + NL_TEST_DEF_FN(TestFixedByteSpan), NL_TEST_SENTINEL() }; int TestSpan(void) {