// Copyright 2025 The Pigweed Authors // // 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 // // https://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 "pw_span/cast.h" #include #include #include "pw_span/span.h" #include "pw_unit_test/framework.h" // TODO: https://pwbug.dev/395945006 - DRY these macros with // pw_numeric/checked_arithmetic_test.cc // Emits a TEST() for a given templated function with the given type. // // suite - The name of the test suite (the first argument to TEST()). // test_name - The full name of the test. // func - The templated function called by the test. // type - The type passed to the function template. #define TEST_FUNC_FOR_TYPE(suite, test_name, func, type) \ TEST(suite, test_name) { func(); } // Emits a TEST() for a templated function named by the concatention of the // test suite and test name, invoked with the given type. // // suite - The name of the test suite (the first argument to TEST()) // name - The base name of the test. // The full name of the test includes _suffix. // The called function is the concatenation of suite and name. // suffix - Appened to name (with an underscore) to generate the test name. // type - The template type of the called function. #define TEST_FOR_TYPE(suite, name, suffix, type) \ TEST_FUNC_FOR_TYPE(suite, name##_##suffix, suite##name, type) // Emits a TEST_FOR_TYPE() for all common types. #define TEST_FOR_STDINT_TYPES(suite, name) \ TEST_FOR_TYPE(suite, name, u8, uint8_t) \ TEST_FOR_TYPE(suite, name, i8, int8_t) \ TEST_FOR_TYPE(suite, name, u16, uint16_t) \ TEST_FOR_TYPE(suite, name, i16, int16_t) \ TEST_FOR_TYPE(suite, name, u32, uint32_t) \ TEST_FOR_TYPE(suite, name, i32, int32_t) \ TEST_FOR_TYPE(suite, name, u64, uint64_t) \ TEST_FOR_TYPE(suite, name, i64, int64_t) #define STATIC_ASSERT_EXTENT(spanvar, n) \ static_assert(decltype(spanvar)::extent == n) namespace { template void SpanCastRoundTrip() { constexpr size_t kNum = 4; std::array t_array; pw::span t_span = pw::span(t_array); pw::ByteSpan bytes = pw::as_writable_bytes(t_span); EXPECT_EQ(bytes.size(), t_span.size_bytes()); // The expectation above is actually outside the scope of this test, // but we do it to ensure proper setup for the test below. // Now verify the UUT span_cast. pw::span t_span2 = pw::span_cast(bytes); EXPECT_EQ(t_span.data(), t_span2.data()); EXPECT_EQ(t_span.size(), t_span2.size()); } template void SpanCastRoundTripConst() { constexpr size_t kNum = 4; std::array t_array; pw::span t_span = pw::span(t_array); pw::ConstByteSpan bytes = pw::as_bytes(t_span); EXPECT_EQ(bytes.size(), t_span.size_bytes()); // The expectation above is actually outside the scope of this test, // but we do it to ensure proper setup for the test below. // Now verify the UUT span_cast. // N.B. We use span_cast but the result is span due to the const // type of the span argument. pw::span t_span2 = pw::span_cast(bytes); // ...but it's okay if you want to include const in the template arg. [[maybe_unused]] pw::span t_span3 = pw::span_cast(bytes); EXPECT_EQ(t_span.data(), t_span2.data()); EXPECT_EQ(t_span.size(), t_span2.size()); } template void SpanCastRoundTripStaticExtent() { constexpr size_t kNumElem = 4; constexpr size_t kNumBytes = kNumElem * sizeof(T); std::array t_array; // N.B. We use `auto` variables throughout this test, rather than pw::span // or pw::ByteSpan, which are always of a dynamic extent! auto t_span = pw::span(t_array); STATIC_ASSERT_EXTENT(t_span, kNumElem); // Ensure static extent auto byte_span = pw::as_writable_bytes(t_span); STATIC_ASSERT_EXTENT(byte_span, kNumBytes); // Ensure static extent // Everything above is actually out of scope for this test... // Now verify the UUT span_cast... auto t_span2 = pw::span_cast(byte_span); STATIC_ASSERT_EXTENT(t_span2, kNumElem); // Ensure proper static extent } template void SpanCastRoundTripStaticExtentConst() { constexpr size_t kNumElem = 4; constexpr size_t kNumBytes = kNumElem * sizeof(T); std::array t_array; // N.B. We use `auto` variables throughout this test, rather than pw::span // or pw::ConstByteSpan, which are always of a dynamic extent! auto t_span = pw::span(t_array); STATIC_ASSERT_EXTENT(t_span, kNumElem); // Ensure static extent auto byte_span = pw::as_bytes(t_span); STATIC_ASSERT_EXTENT(byte_span, kNumBytes); // Ensure static extent // The static_asert_extent checks above are actually out of scope for this // test, but we check them to ensure the proper setup for the test below. // Now verify the UUT span_cast. auto t_span2 = pw::span_cast(byte_span); STATIC_ASSERT_EXTENT(t_span2, kNumElem); // Ensure proper static extent } struct MixedBag { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; float f; double d; }; #define TEST_FOR_ALL_TYPES(suite, name) \ TEST_FOR_TYPE(suite, name, u8, uint8_t) \ TEST_FOR_TYPE(suite, name, i8, int8_t) \ TEST_FOR_TYPE(suite, name, char, char) \ TEST_FOR_TYPE(suite, name, uchar, unsigned char) // TEST_FOR_STDINT_TYPES(suite, name) // TEST_FOR_TYPE(suite, name, MixedBag, MixedBag) TEST_FOR_ALL_TYPES(SpanCast, RoundTrip) TEST_FOR_ALL_TYPES(SpanCast, RoundTripConst) TEST_FOR_ALL_TYPES(SpanCast, RoundTripStaticExtent) TEST_FOR_ALL_TYPES(SpanCast, RoundTripStaticExtentConst) } // namespace // An example test for the docs, which includes the #includes. // DOCSTAG[start-pw_span-cast-example] #include "pw_bytes/span.h" #include "pw_span/cast.h" void SDK_ReadData(uint8_t* data, size_t size); void SDK_WriteData(const uint8_t* data, size_t size); void Write(pw::ConstByteSpan buffer) { auto data = pw::span_cast(buffer); SDK_WriteData(data.data(), data.size()); } void Read(pw::ByteSpan buffer) { auto data = pw::span_cast(buffer); SDK_ReadData(data.data(), data.size()); } // DOCSTAG[end-pw_span-cast-example] TEST(SpanCast, Examples) { std::array data{}; Read(data); Write(data); } void SDK_ReadData(uint8_t*, size_t) {} void SDK_WriteData(const uint8_t*, size_t) {}