// Copyright 2020 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_bytes/endian.h" #include #include #include "gtest/gtest.h" namespace pw::bytes { namespace { constexpr std::endian kNonNative = (std::endian::native == std::endian::little) ? std::endian::big : std::endian::little; // ConvertOrderTo/From // // ConvertOrderTo and ConvertOrderFrom are implemented identically, but are // provided as separate functions to improve readability where they are used. // // clang-format off // Native endianess conversions (should do nothing) // Convert unsigned to native endianness static_assert(ConvertOrderTo(std::endian::native, uint8_t{0x12}) == uint8_t{0x12}); static_assert(ConvertOrderTo(std::endian::native, uint16_t{0x0011}) == uint16_t{0x0011}); static_assert(ConvertOrderTo(std::endian::native, uint32_t{0x33221100}) == uint32_t{0x33221100}); static_assert(ConvertOrderTo(std::endian::native, uint64_t{0x0011223344556677}) == uint64_t{0x0011223344556677}); // Convert signed to native endianness static_assert(ConvertOrderTo(std::endian::native, int8_t{0x12}) == int8_t{0x12}); static_assert(ConvertOrderTo(std::endian::native, int16_t{0x0011}) == int16_t{0x0011}); static_assert(ConvertOrderTo(std::endian::native, int32_t{0x33221100}) == int32_t{0x33221100}); static_assert(ConvertOrderTo(std::endian::native, int64_t{0x0011223344556677}) == int64_t{0x0011223344556677}); // Convert unsigned from native endianness static_assert(ConvertOrderFrom(std::endian::native, uint8_t{0x12}) == uint8_t{0x12}); static_assert(ConvertOrderFrom(std::endian::native, uint16_t{0x0011}) == uint16_t{0x0011}); static_assert(ConvertOrderFrom(std::endian::native, uint32_t{0x33221100}) == uint32_t{0x33221100}); static_assert(ConvertOrderFrom(std::endian::native, uint64_t{0x0011223344556677}) == uint64_t{0x0011223344556677}); // Convert signed from native endianness static_assert(ConvertOrderFrom(std::endian::native, int8_t{0x12}) == int8_t{0x12}); static_assert(ConvertOrderFrom(std::endian::native, int16_t{0x0011}) == int16_t{0x0011}); static_assert(ConvertOrderFrom(std::endian::native, int32_t{0x33221100}) == int32_t{0x33221100}); static_assert(ConvertOrderFrom(std::endian::native, int64_t{0x0011223344556677}) == int64_t{0x0011223344556677}); // Non-native endianess conversions (should reverse byte order) // Convert unsigned to non-native endianness static_assert(ConvertOrderTo(kNonNative, uint8_t{0x12}) == uint8_t{0x12}); static_assert(ConvertOrderTo(kNonNative, uint16_t{0x0011}) == uint16_t{0x1100}); static_assert(ConvertOrderTo(kNonNative, uint32_t{0x33221100}) == uint32_t{0x00112233}); static_assert(ConvertOrderTo(kNonNative, uint64_t{0x0011223344556677}) == uint64_t{0x7766554433221100}); // Convert signed to non-native endianness static_assert(ConvertOrderTo(kNonNative, int8_t{0x12}) == int8_t{0x12}); static_assert(ConvertOrderTo(kNonNative, int16_t{0x0011}) == int16_t{0x1100}); static_assert(ConvertOrderTo(kNonNative, int32_t{0x33221100}) == int32_t{0x00112233}); static_assert(ConvertOrderTo(kNonNative, int64_t{0x0011223344556677}) == int64_t{0x7766554433221100}); // Convert unsigned from non-native endianness static_assert(ConvertOrderFrom(kNonNative, uint8_t{0x12}) == uint8_t{0x12}); static_assert(ConvertOrderFrom(kNonNative, uint16_t{0x0011}) == uint16_t{0x1100}); static_assert(ConvertOrderFrom(kNonNative, uint32_t{0x33221100}) == uint32_t{0x00112233}); static_assert(ConvertOrderFrom(kNonNative, uint64_t{0x0011223344556677}) == uint64_t{0x7766554433221100}); // Convert signed from non-native endianness static_assert(ConvertOrderFrom(kNonNative, int8_t{0x12}) == int8_t{0x12}); static_assert(ConvertOrderFrom(kNonNative, int16_t{0x0011}) == int16_t{0x1100}); static_assert(ConvertOrderFrom(kNonNative, int32_t{0x33221100}) == int32_t{0x00112233}); static_assert(ConvertOrderFrom(kNonNative, int64_t{0x0011223344556677}) == int64_t{0x7766554433221100}); // clang-format on template constexpr bool Equal(const T& lhs, const U& rhs) { if (sizeof(lhs) != sizeof(rhs) || std::size(lhs) != std::size(rhs)) { return false; } for (size_t i = 0; i < std::size(lhs); ++i) { if (lhs[i] != rhs[i]) { return false; } } return true; } // CopyInOrder copies a value to a std::array with the specified endianness. // // clang-format off // 8-bit little static_assert(Equal(CopyInOrder(std::endian::little, '?'), Array<'?'>())); static_assert(Equal(CopyInOrder(std::endian::little, uint8_t{0x10}), Array<0x10>())); static_assert(Equal(CopyInOrder(std::endian::little, static_cast(0x10)), Array<0x10>())); // 8-bit big static_assert(Equal(CopyInOrder(std::endian::big, '?'), Array<'?'>())); static_assert(Equal(CopyInOrder(std::endian::big, static_cast(0x10)), Array<0x10>())); static_assert(Equal(CopyInOrder(std::endian::big, static_cast(0x10)), Array<0x10>())); // 16-bit little static_assert(Equal(CopyInOrder(std::endian::little, uint16_t{0xAB12}), Array<0x12, 0xAB>())); static_assert(Equal(CopyInOrder(std::endian::little, static_cast(0xAB12)), Array<0x12, 0xAB>())); // 16-bit big static_assert(Equal(CopyInOrder(std::endian::big, uint16_t{0xAB12}), Array<0xAB, 0x12>())); static_assert(Equal(CopyInOrder(std::endian::big, static_cast(0xAB12)), Array<0xAB, 0x12>())); // 32-bit little static_assert(Equal(CopyInOrder(std::endian::little, uint32_t{0xAABBCCDD}), Array<0xDD, 0xCC, 0xBB, 0xAA>())); static_assert(Equal(CopyInOrder(std::endian::little, static_cast(0xAABBCCDD)), Array<0xDD, 0xCC, 0xBB, 0xAA>())); // 32-bit big static_assert(Equal(CopyInOrder(std::endian::big, uint32_t{0xAABBCCDD}), Array<0xAA, 0xBB, 0xCC, 0xDD>())); static_assert(Equal(CopyInOrder(std::endian::big, static_cast(0xAABBCCDD)), Array<0xAA, 0xBB, 0xCC, 0xDD>())); // 64-bit little static_assert(Equal(CopyInOrder(std::endian::little, uint64_t{0xAABBCCDD11223344}), Array<0x44, 0x33, 0x22, 0x11, 0xDD, 0xCC, 0xBB, 0xAA>())); static_assert(Equal(CopyInOrder(std::endian::little, static_cast(0xAABBCCDD11223344ull)), Array<0x44, 0x33, 0x22, 0x11, 0xDD, 0xCC, 0xBB, 0xAA>())); // 64-bit big static_assert(Equal(CopyInOrder(std::endian::big, uint64_t{0xAABBCCDD11223344}), Array<0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44>())); static_assert(Equal(CopyInOrder(std::endian::big, static_cast(0xAABBCCDD11223344ull)), Array<0xAA, 0xBB, 0xCC, 0xDD, 0x11, 0x22, 0x33, 0x44>())); // clang-format on constexpr const char* kNumber = "\x11\x22\x33\x44\xaa\xbb\xcc\xdd"; TEST(ReadInOrder, 8Bit_Big) { EXPECT_EQ(ReadInOrder(std::endian::big, "\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80"), 0x80u); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11u); EXPECT_EQ(ReadInOrder(std::endian::big, "\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80"), -128); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11); } TEST(ReadInOrder, 8Bit_Little) { EXPECT_EQ(ReadInOrder(std::endian::little, "\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80"), 0x80u); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x11u); EXPECT_EQ(ReadInOrder(std::endian::little, "\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80"), -128); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x11); } TEST(ReadInOrder, 16Bit_Big) { EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0"), 0x8000u); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x1122u); EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0"), -32768); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x1122); } TEST(ReadInOrder, 16Bit_Little) { EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0"), 0x80u); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x2211u); EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0"), 0x80); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x2211); } TEST(ReadInOrder, 32Bit_Big) { EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0\0\0"), 0x80000000u); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11223344u); EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0\0\0"), -2147483648); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11223344); } TEST(ReadInOrder, 32Bit_Little) { EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0\0\0"), 0x80u); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x44332211u); EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0\0\0"), 0x80); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0x44332211); } TEST(ReadInOrder, 64Bit_Big) { EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0\0\0\0\0\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0\0\0\0\0\0\0"), 0x80000000'00000000llu); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11223344AABBCCDDu); EXPECT_EQ(ReadInOrder(std::endian::big, "\0\0\0\0\0\0\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::big, "\x80\0\0\0\0\0\0\0"), static_cast(1llu << 63)); EXPECT_EQ(ReadInOrder(std::endian::big, kNumber), 0x11223344AABBCCDD); } TEST(ReadInOrder, 64Bit_Little) { EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0\0\0\0\0\0\0"), 0u); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0\0\0\0\0\0\0"), 0x80u); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), 0xDDCCBBAA44332211u); EXPECT_EQ(ReadInOrder(std::endian::little, "\0\0\0\0\0\0\0\0"), 0); EXPECT_EQ(ReadInOrder(std::endian::little, "\x80\0\0\0\0\0\0\0"), 0x80); EXPECT_EQ(ReadInOrder(std::endian::little, kNumber), static_cast(0xDDCCBBAA44332211)); } TEST(ReadInOrder, StdArray) { std::array buffer = Array<1, 2, 3, 4>(); EXPECT_EQ(0x04030201, ReadInOrder(std::endian::little, buffer)); EXPECT_EQ(0x01020304, ReadInOrder(std::endian::big, buffer)); } TEST(ReadInOrder, CArray) { char buffer[5] = {1, 2, 3, 4, 99}; EXPECT_EQ(0x04030201, ReadInOrder(std::endian::little, buffer)); EXPECT_EQ(0x01020304, ReadInOrder(std::endian::big, buffer)); } TEST(ReadInOrder, BoundsChecking_Ok) { constexpr auto buffer = Array<1, 2, 3, 4>(); uint16_t value; EXPECT_TRUE(ReadInOrder(std::endian::little, buffer, value)); EXPECT_EQ(0x0201, value); } TEST(ReadInOrder, BoundsChecking_TooSmall) { constexpr auto buffer = Array<1, 2, 3>(); int32_t value = 0; EXPECT_FALSE(ReadInOrder(std::endian::little, buffer, value)); EXPECT_EQ(0, value); } } // namespace } // namespace pw::bytes