// Copyright 2019 Google LLC // // 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 #include #if __cplusplus >= 201703L #include // std::byte #include #endif // __cplusplus >= 201703L #include #include "gtest/gtest.h" #include "runtime/cpp/emboss_memory_util.h" #include "runtime/cpp/emboss_prelude.h" namespace emboss { namespace support { namespace test { using ::emboss::prelude::IntView; using ::emboss::prelude::UIntView; template using BigEndianBitBlockN = BitBlock, kBits>; template using LittleEndianBitBlockN = BitBlock, kBits>; template std::array constexpr init_array(Args &&...args) { return {T(std::forward(args))...}; } template auto constexpr init_container(Args &&...args) -> Container { using CharType = typename ::std::remove_reference::type; return {CharType(std::forward(args))...}; } TEST(GreatestCommonDivisor, GreatestCommonDivisor) { EXPECT_EQ(4U, GreatestCommonDivisor(12, 20)); EXPECT_EQ(4U, GreatestCommonDivisor(20, 12)); EXPECT_EQ(4U, GreatestCommonDivisor(20, 4)); EXPECT_EQ(6U, GreatestCommonDivisor(12, 78)); EXPECT_EQ(6U, GreatestCommonDivisor(6, 0)); EXPECT_EQ(6U, GreatestCommonDivisor(0, 6)); EXPECT_EQ(3U, GreatestCommonDivisor(9, 6)); EXPECT_EQ(0U, GreatestCommonDivisor(0, 0)); } // Because MemoryAccessor's parameters are template parameters, it is not // possible to loop through them directly. Instead, TestMemoryAccessor tests // a particular MemoryAccessor's methods, then calls the next template to test // the next set of template parameters to MemoryAccessor. template void TestMemoryAccessor() { alignas(kAlignment) auto bytes = init_array(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); EXPECT_EQ( 0x0807060504030201UL & (~0x0UL >> (64 - kBits)), (MemoryAccessor::ReadLittleEndianUInt( bytes.data()))) << "kAlignment = " << kAlignment << "; kOffset = " << kOffset << "; kBits = " << kBits; EXPECT_EQ( 0x0102030405060708UL >> (64 - kBits), (MemoryAccessor::ReadBigEndianUInt( bytes.data()))) << "kAlignment = " << kAlignment << "; kOffset = " << kOffset << "; kBits = " << kBits; MemoryAccessor::WriteLittleEndianUInt( bytes.data(), 0x7172737475767778UL & (~0x0UL >> (64 - kBits))); auto expected_vector_after_write = init_container>( 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71); for (int i = kBits / 8; i < 8; ++i) { expected_vector_after_write[i] = CharT(i + 1); } EXPECT_EQ(expected_vector_after_write, ::std::vector(std::begin(bytes), std::end(bytes))) << "kAlignment = " << kAlignment << "; kOffset = " << kOffset << "; kBits = " << kBits; MemoryAccessor::WriteBigEndianUInt( bytes.data(), 0x7172737475767778UL >> (64 - kBits)); expected_vector_after_write = init_container>( 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78); for (int i = kBits / 8; i < 8; ++i) { expected_vector_after_write[i] = CharT(i + 1); } EXPECT_EQ(expected_vector_after_write, ::std::vector(std::begin(bytes), std::end(bytes))) << "kAlignment = " << kAlignment << "; kOffset = " << kOffset << "; kBits = " << kBits; // Recursively iterate the template: // // For every kAlignment/kOffset pair, check kBits from 64 to 8 in increments // of 8. // // If kBits is 8, reset kBits to 64 and go to the next kAlignment/kOffset // pair. // // For each kAlignment, try all kOffsets from 0 to kAlignment - 1. // // If kBits is 8 and kOffset is kAlignment - 1, reset kBits to 64, kOffset to // 0, and halve kAlignment. // // Base cases below handle kAlignment == 0, terminating the recursion. TestMemoryAccessor< CharT, kBits == 8 && kAlignment == kOffset + 1 ? kAlignment / 2 : kAlignment, kBits == 8 ? kAlignment == kOffset + 1 ? 0 : kOffset + 1 : kOffset, kBits == 8 ? 64 : kBits - 8>(); } template <> void TestMemoryAccessor() {} #if __cplusplus >= 201703L template <> void TestMemoryAccessor() {} #endif template <> void TestMemoryAccessor() {} TEST(MemoryAccessor, LittleEndianReads) { TestMemoryAccessor(); #if __cplusplus >= 201703L TestMemoryAccessor(); #endif TestMemoryAccessor(); } TEST(ContiguousBuffer, OffsetStorageType) { EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<2, 0>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<0, 0>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<4, 0>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<2, 0>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<2, 0>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<2, 1>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<4, 1>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<0, 2>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<4, 2>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<8, 6>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<12, 6>>::value)); EXPECT_TRUE((::std::is_same< ContiguousBuffer, ContiguousBuffer::OffsetStorageType<3, 1>>::value)); } // Minimal class that forwards to std::allocator. Used to test that // ReadOnlyContiguousBuffer can be constructed from std::vector<> and // std::basic_string<> with non-default trailing template parameters. template struct NonstandardAllocator { using value_type = typename ::std::allocator_traits<::std::allocator>::value_type; using pointer = typename ::std::allocator_traits<::std::allocator>::pointer; using const_pointer = typename ::std::allocator_traits<::std::allocator>::const_pointer; using reference = typename ::std::allocator::value_type &; using const_reference = const typename ::std::allocator_traits<::std::allocator>::value_type &; using size_type = typename ::std::allocator_traits<::std::allocator>::size_type; using difference_type = typename ::std::allocator_traits<::std::allocator>::difference_type; template struct rebind { using other = NonstandardAllocator; }; NonstandardAllocator() = default; // This constructor is *not* explicit in order to conform to the requirements // for an allocator. template NonstandardAllocator(const NonstandardAllocator &) {} // NOLINT T *allocate(::std::size_t n) { return ::std::allocator().allocate(n); } void deallocate(T *p, ::std::size_t n) { ::std::allocator().deallocate(p, n); } static size_type max_size() { return ::std::numeric_limits::max() / sizeof(value_type); } }; template bool operator==(const NonstandardAllocator &, const NonstandardAllocator &) { return true; } template bool operator!=(const NonstandardAllocator &, const NonstandardAllocator &) { return false; } // ContiguousBuffer tests for std::vector, std::array, and std::string types. template class ReadOnlyContiguousBufferTest : public ::testing::Test {}; typedef ::testing::Types< /**/ ::std::vector, ::std::array, ::std::vector, #if __cplusplus >= 201703L ::std::vector, #endif ::std::string, ::std::basic_string, ::std::vector>, ::std::basic_string, NonstandardAllocator>> ReadOnlyContiguousContainerTypes; TYPED_TEST_SUITE(ReadOnlyContiguousBufferTest, ReadOnlyContiguousContainerTypes); TYPED_TEST(ReadOnlyContiguousBufferTest, ConstructionFromContainers) { const TypeParam bytes = init_container(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); using CharType = typename ::std::remove_reference::type; const auto buffer = ContiguousBuffer{&bytes}; EXPECT_EQ(bytes.size(), buffer.SizeInBytes()); EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(0x0807060504030201UL, buffer.template ReadBigEndianUInt<64>()); const auto offset_buffer = buffer.template GetOffsetStorage<1, 0>(4, 4); EXPECT_EQ(4U, offset_buffer.SizeInBytes()); EXPECT_EQ(0x04030201U, offset_buffer.template ReadBigEndianUInt<32>()); // The size of the resulting buffer should be the minimum of the available // size and the requested size. EXPECT_EQ(bytes.size() - 4, (buffer.template GetOffsetStorage<1, 0>(2, bytes.size() - 4) .SizeInBytes())); EXPECT_EQ( 0U, (buffer.template GetOffsetStorage<1, 0>(bytes.size(), 4).SizeInBytes())); } // ContiguousBuffer tests for std::vector and std::array types. template class ReadWriteContiguousBufferTest : public ::testing::Test {}; typedef ::testing::Types, ::std::array, #if __cplusplus >= 201703L ::std::vector, #endif ::std::vector> ReadWriteContiguousContainerTypes; TYPED_TEST_SUITE(ReadWriteContiguousBufferTest, ReadWriteContiguousContainerTypes); TYPED_TEST(ReadWriteContiguousBufferTest, ConstructionFromContainers) { TypeParam bytes = init_container(0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01); using CharType = typename ::std::remove_reference::type; const auto buffer = ContiguousBuffer{&bytes}; // Read and Ok methods should work just as in ReadOnlyContiguousBuffer. EXPECT_EQ(bytes.size(), buffer.SizeInBytes()); EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(0x0807060504030201UL, buffer.template ReadBigEndianUInt<64>()); buffer.template WriteBigEndianUInt<64>(0x0102030405060708UL); EXPECT_EQ((init_container(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)), bytes); bytes[4] = static_cast(255); EXPECT_EQ(0x1020304ff060708UL, buffer.template ReadBigEndianUInt<64>()); } TEST(ContiguousBuffer, ReturnTypeOfReadUInt) { const auto buffer = ContiguousBuffer(); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint32_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint16_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint8_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint32_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint16_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint8_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint32_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint16_t>::value)); EXPECT_TRUE((::std::is_same()), ::std::uint8_t>::value)); EXPECT_TRUE( (::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE( (::std::is_same()), ::std::uint64_t>::value)); EXPECT_TRUE( (::std::is_same()), ::std::uint32_t>::value)); EXPECT_TRUE( (::std::is_same()), ::std::uint16_t>::value)); EXPECT_TRUE( (::std::is_same()), ::std::uint8_t>::value)); } TEST(ReadOnlyContiguousBuffer, Methods) { const ::std::vector bytes = { {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}}; const auto buffer = ReadOnlyContiguousBuffer{bytes.data(), bytes.size() - 4}; #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(buffer.ReadBigEndianUInt<64>(), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(bytes.size() - 4, buffer.SizeInBytes()); EXPECT_EQ(0x100f0e0d0c0b0a09UL, buffer.UncheckedReadBigEndianUInt<64>()); EXPECT_EQ(0x090a0b0c0d0e0f10UL, buffer.UncheckedReadLittleEndianUInt<64>()); const auto offset_buffer = buffer.GetOffsetStorage<1, 0>(4, 4); EXPECT_EQ(0x0c0b0a09U, offset_buffer.ReadBigEndianUInt<32>()); EXPECT_EQ(0x090a0b0cU, offset_buffer.ReadLittleEndianUInt<32>()); EXPECT_EQ(0x0c0b0a0908070605UL, offset_buffer.UncheckedReadBigEndianUInt<64>()); EXPECT_EQ(4U, offset_buffer.SizeInBytes()); EXPECT_TRUE(offset_buffer.Ok()); const auto small_offset_buffer = buffer.GetOffsetStorage<1, 0>(4, 1); EXPECT_EQ(0x0cU, small_offset_buffer.ReadBigEndianUInt<8>()); EXPECT_EQ(0x0cU, small_offset_buffer.ReadLittleEndianUInt<8>()); EXPECT_EQ(1U, small_offset_buffer.SizeInBytes()); EXPECT_TRUE(small_offset_buffer.Ok()); EXPECT_FALSE(ReadOnlyContiguousBuffer().Ok()); EXPECT_FALSE( (ReadOnlyContiguousBuffer{static_cast(nullptr), 12}.Ok())); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH((ReadOnlyContiguousBuffer{static_cast(nullptr), 4} .ReadBigEndianUInt<32>()), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_EQ(0U, ReadOnlyContiguousBuffer().SizeInBytes()); EXPECT_EQ(0U, (ReadOnlyContiguousBuffer{static_cast(nullptr), 12} .SizeInBytes())); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH( (ReadOnlyContiguousBuffer{bytes.data(), 0}.ReadBigEndianUInt<8>()), ""); #endif // EMBOSS_CHECK_ABORTS // The size of the resulting buffer should be the minimum of the available // size and the requested size. EXPECT_EQ(bytes.size() - 8, (buffer.GetOffsetStorage<1, 0>(4, bytes.size() - 4).SizeInBytes())); EXPECT_EQ(4U, (buffer.GetOffsetStorage<1, 0>(0, 4).SizeInBytes())); EXPECT_EQ(0U, (buffer.GetOffsetStorage<1, 0>(bytes.size(), 4).SizeInBytes())); EXPECT_FALSE((ReadOnlyContiguousBuffer().GetOffsetStorage<1, 0>(0, 0).Ok())); } TEST(ReadWriteContiguousBuffer, Methods) { ::std::vector bytes = { {0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}}; const auto buffer = ReadWriteContiguousBuffer{bytes.data(), bytes.size() - 4}; // Read and Ok methods should work just as in ReadOnlyContiguousBuffer. EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(bytes.size() - 4U, buffer.SizeInBytes()); EXPECT_EQ(0x0c0b0a0908070605UL, buffer.ReadBigEndianUInt<64>()); buffer.WriteBigEndianUInt<64>(0x05060708090a0b0c); EXPECT_EQ( (::std::vector{0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x04, 0x03, 0x02, 0x01}), bytes); buffer.WriteLittleEndianUInt<64>(0x05060708090a0b0c); EXPECT_EQ( (::std::vector{0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}), bytes); const auto offset_buffer = buffer.GetOffsetStorage<1, 0>(4, 4); offset_buffer.WriteBigEndianUInt<32>(0x05060708); EXPECT_EQ( (::std::vector{0x0c, 0x0b, 0x0a, 0x09, 0x05, 0x06, 0x07, 0x08, 0x04, 0x03, 0x02, 0x01}), bytes); offset_buffer.WriteLittleEndianUInt<32>(0x05060708); EXPECT_EQ( (::std::vector{0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}), bytes); const auto small_offset_buffer = buffer.GetOffsetStorage<1, 0>(4, 1); small_offset_buffer.WriteBigEndianUInt<8>(0x80); EXPECT_EQ( (::std::vector{0x0c, 0x0b, 0x0a, 0x09, 0x80, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}), bytes); small_offset_buffer.WriteLittleEndianUInt<8>(0x08); EXPECT_EQ( (::std::vector{0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}), bytes); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(ReadWriteContiguousBuffer().ReadLittleEndianUInt<8>(), ""); EXPECT_DEATH( (ReadWriteContiguousBuffer{static_cast(nullptr), 1} .ReadLittleEndianUInt<8>()), ""); EXPECT_DEATH( (ReadWriteContiguousBuffer{static_cast(nullptr), 1} .WriteLittleEndianUInt<8>(0xff)), ""); #endif // EMBOSS_CHECK_ABORTS } TEST(ContiguousBuffer, AssignmentFromCompatibleContiguousBuffers) { alignas(4) char data[8]; ContiguousBuffer buffer; buffer = ContiguousBuffer(data + 1, sizeof data - 1); EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(buffer.data(), reinterpret_cast(data + 1)); ContiguousBuffer aligned_buffer; aligned_buffer = ContiguousBuffer(data + 3, sizeof data - 3); EXPECT_TRUE(aligned_buffer.Ok()); EXPECT_EQ(aligned_buffer.data(), reinterpret_cast(data + 3)); } TEST(ContiguousBuffer, ConstructionFromCompatibleContiguousBuffers) { alignas(4) char data[8]; ContiguousBuffer buffer{ ContiguousBuffer(data + 1, sizeof data - 1)}; EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(buffer.data(), reinterpret_cast(data + 1)); ContiguousBuffer aligned_buffer{ ContiguousBuffer(data + 3, sizeof data - 3)}; EXPECT_TRUE(aligned_buffer.Ok()); EXPECT_EQ(aligned_buffer.data(), reinterpret_cast(data + 3)); } TEST(ContiguousBuffer, ToString) { const ::std::vector bytes = { {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}; const auto buffer = ReadOnlyContiguousBuffer{bytes.data(), bytes.size() - 4}; auto str = buffer.ToString(); EXPECT_TRUE((::std::is_same::value)); EXPECT_EQ(str, "abcd"); #if __cplusplus >= 201703L auto str_view = buffer.ToString(); EXPECT_TRUE( (::std::is_same::value)); EXPECT_EQ(str_view, "abcd"); #endif // __cplusplus >= 201703L } TEST(LittleEndianByteOrderer, Methods) { ::std::vector bytes = { {21, 22, 1, 2, 3, 4, 5, 6, 7, 8, 23, 24}}; const int buffer_start = 2; const auto buffer = LittleEndianByteOrderer{ ReadWriteContiguousBuffer{bytes.data() + buffer_start, 8}}; EXPECT_EQ(8U, buffer.SizeInBytes()); EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(0x0807060504030201UL, buffer.ReadUInt<64>()); EXPECT_EQ(0x0807060504030201UL, buffer.UncheckedReadUInt<64>()); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(buffer.ReadUInt<56>(), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_EQ(0x07060504030201UL, buffer.UncheckedReadUInt<56>()); buffer.WriteUInt<64>(0x0102030405060708); EXPECT_EQ((::std::vector{21, 22, 8, 7, 6, 5, 4, 3, 2, 1, 23, 24}), bytes); buffer.UncheckedWriteUInt<64>(0x0807060504030201); EXPECT_EQ((::std::vector{21, 22, 1, 2, 3, 4, 5, 6, 7, 8, 23, 24}), bytes); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(buffer.WriteUInt<56>(0x77777777777777), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_FALSE(LittleEndianByteOrderer().Ok()); EXPECT_EQ(0U, LittleEndianByteOrderer().SizeInBytes()); EXPECT_EQ(bytes[1], (LittleEndianByteOrderer{ ReadOnlyContiguousBuffer{bytes.data() + 1, 0}} .UncheckedReadUInt<8>())); EXPECT_TRUE((LittleEndianByteOrderer{ ReadOnlyContiguousBuffer{bytes.data(), 0}} .Ok())); } TEST(BigEndianByteOrderer, Methods) { ::std::vector bytes = { {21, 22, 1, 2, 3, 4, 5, 6, 7, 8, 23, 24}}; const int buffer_start = 2; const auto buffer = BigEndianByteOrderer{ ReadWriteContiguousBuffer{bytes.data() + buffer_start, 8}}; EXPECT_EQ(8U, buffer.SizeInBytes()); EXPECT_TRUE(buffer.Ok()); EXPECT_EQ(0x0102030405060708UL, buffer.ReadUInt<64>()); EXPECT_EQ(0x0102030405060708UL, buffer.UncheckedReadUInt<64>()); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(buffer.ReadUInt<56>(), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_EQ(0x01020304050607UL, buffer.UncheckedReadUInt<56>()); buffer.WriteUInt<64>(0x0807060504030201); EXPECT_EQ((::std::vector{21, 22, 8, 7, 6, 5, 4, 3, 2, 1, 23, 24}), bytes); buffer.UncheckedWriteUInt<64>(0x0102030405060708); EXPECT_EQ((::std::vector{21, 22, 1, 2, 3, 4, 5, 6, 7, 8, 23, 24}), bytes); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(buffer.WriteUInt<56>(0x77777777777777), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_FALSE(BigEndianByteOrderer().Ok()); EXPECT_EQ(0U, BigEndianByteOrderer().SizeInBytes()); EXPECT_EQ(bytes[1], (BigEndianByteOrderer{ ReadOnlyContiguousBuffer{bytes.data() + 1, 0}} .UncheckedReadUInt<8>())); EXPECT_TRUE((BigEndianByteOrderer{ ReadOnlyContiguousBuffer{bytes.data(), 0}} .Ok())); } TEST(NullByteOrderer, Methods) { ::std::uint8_t bytes[] = {0xdb, 0x0f, 0x0e, 0x0d}; const auto buffer = NullByteOrderer{ ReadWriteContiguousBuffer{bytes, 1}}; EXPECT_EQ(bytes[0], buffer.ReadUInt<8>()); EXPECT_EQ(bytes[0], buffer.UncheckedReadUInt<8>()); // NullByteOrderer::UncheckedRead ignores its argument. EXPECT_EQ(bytes[0], buffer.UncheckedReadUInt<8>()); buffer.WriteUInt<8>(0x24); EXPECT_EQ(0x24U, bytes[0]); buffer.UncheckedWriteUInt<8>(0x25); EXPECT_EQ(0x25U, bytes[0]); EXPECT_EQ(1U, buffer.SizeInBytes()); EXPECT_TRUE(buffer.Ok()); EXPECT_FALSE(NullByteOrderer().Ok()); EXPECT_EQ(0U, NullByteOrderer().SizeInBytes()); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH((NullByteOrderer{ ReadOnlyContiguousBuffer{bytes, 0}} .ReadUInt<8>()), ""); EXPECT_DEATH((NullByteOrderer{ ReadOnlyContiguousBuffer{bytes, 2}} .ReadUInt<8>()), ""); #endif // EMBOSS_CHECK_ABORTS EXPECT_EQ(bytes[0], (NullByteOrderer{ ReadOnlyContiguousBuffer{bytes, 0}} .UncheckedReadUInt<8>())); EXPECT_TRUE((NullByteOrderer{ ReadOnlyContiguousBuffer{bytes, 0}} .Ok())); } TEST(BitBlock, BigEndianMethods) { ::std::uint8_t bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; const auto big_endian = BigEndianBitBlockN<64>{ReadWriteContiguousBuffer{bytes + 4, 8}}; EXPECT_EQ(64U, big_endian.SizeInBits()); EXPECT_TRUE(big_endian.Ok()); EXPECT_EQ(0x05060708090a0b0cUL, big_endian.ReadUInt()); EXPECT_EQ(0x05060708090a0b0cUL, big_endian.UncheckedReadUInt()); EXPECT_FALSE(BigEndianBitBlockN<64>().Ok()); EXPECT_EQ(64U, BigEndianBitBlockN<64>().SizeInBits()); EXPECT_FALSE( (BigEndianBitBlockN<64>{ReadWriteContiguousBuffer{bytes, 0}}.Ok())); } TEST(BitBlock, LittleEndianMethods) { ::std::uint8_t bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; const auto little_endian = LittleEndianBitBlockN<64>{ReadWriteContiguousBuffer{bytes + 4, 8}}; EXPECT_EQ(64U, little_endian.SizeInBits()); EXPECT_TRUE(little_endian.Ok()); EXPECT_EQ(0x0c0b0a0908070605UL, little_endian.ReadUInt()); EXPECT_EQ(0x0c0b0a0908070605UL, little_endian.UncheckedReadUInt()); EXPECT_FALSE(LittleEndianBitBlockN<64>().Ok()); EXPECT_EQ(64U, LittleEndianBitBlockN<64>().SizeInBits()); EXPECT_FALSE( (LittleEndianBitBlockN<64>{ReadWriteContiguousBuffer{bytes, 0}}.Ok())); } TEST(BitBlock, GetOffsetStorage) { ::std::uint8_t bytes[] = {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}; const auto bit_block = LittleEndianBitBlockN<64>{ReadWriteContiguousBuffer{bytes, 8}}; const OffsetBitBlock> offset_block = bit_block.GetOffsetStorage<1, 0>(4, 8); EXPECT_EQ(8U, offset_block.SizeInBits()); EXPECT_EQ(0xf1U, offset_block.ReadUInt()); EXPECT_EQ(bit_block.SizeInBits(), (bit_block.GetOffsetStorage<1, 0>(8, bit_block.SizeInBits()) .SizeInBits())); EXPECT_FALSE( (bit_block.GetOffsetStorage<1, 0>(8, bit_block.SizeInBits()).Ok())); EXPECT_EQ(10U, (bit_block.GetOffsetStorage<1, 0>(bit_block.SizeInBits(), 10) .SizeInBits())); } TEST(OffsetBitBlock, Methods) { ::std::vector bytes = { {0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09}}; const auto bit_block = LittleEndianBitBlockN<64>{ReadWriteContiguousBuffer{&bytes}}; EXPECT_FALSE((bit_block.GetOffsetStorage<1, 0>(0, 96).Ok())); EXPECT_TRUE((bit_block.GetOffsetStorage<1, 0>(0, 64).Ok())); const auto offset_block = bit_block.GetOffsetStorage<1, 0>(8, 48); EXPECT_FALSE((offset_block.GetOffsetStorage<1, 0>(40, 16).Ok())); EXPECT_EQ(0x0a0b0c0d0e0fUL, offset_block.ReadUInt()); EXPECT_EQ(0x0a0b0c0d0e0fUL, offset_block.UncheckedReadUInt()); offset_block.WriteUInt(0x0f0e0d0c0b0a); EXPECT_EQ((::std::vector{0x10, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x09}), bytes); offset_block.UncheckedWriteUInt(0x0a0b0c0d0e0f); EXPECT_EQ((::std::vector{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09}), bytes); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(offset_block.WriteUInt(0x10f0e0d0c0b0a), ""); #endif // EMBOSS_CHECK_ABORTS offset_block.UncheckedWriteUInt(0x10f0e0d0c0b0a); EXPECT_EQ((::std::vector{0x10, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x09}), bytes); const auto offset_offset_block = offset_block.GetOffsetStorage<1, 0>(16, 16); EXPECT_FALSE((offset_offset_block.GetOffsetStorage<1, 0>(8, 16).Ok())); EXPECT_EQ(0x0d0cU, offset_offset_block.ReadUInt()); EXPECT_EQ(0x0d0cU, offset_offset_block.UncheckedReadUInt()); offset_offset_block.WriteUInt(0x0c0d); EXPECT_EQ((::std::vector{0x10, 0x0a, 0x0b, 0x0d, 0x0c, 0x0e, 0x0f, 0x09}), bytes); offset_offset_block.UncheckedWriteUInt(0x0d0c); EXPECT_EQ((::std::vector{0x10, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x09}), bytes); #if EMBOSS_CHECK_ABORTS EXPECT_DEATH(offset_offset_block.WriteUInt(0x10c0d), ""); #endif // EMBOSS_CHECK_ABORTS offset_offset_block.UncheckedWriteUInt(0x20c0d); EXPECT_EQ((::std::vector{0x10, 0x0a, 0x0b, 0x0d, 0x0c, 0x0e, 0x0f, 0x09}), bytes); const auto null_offset_block = OffsetBitBlock>(); EXPECT_FALSE(null_offset_block.Ok()); EXPECT_EQ(0U, null_offset_block.SizeInBits()); } } // namespace test } // namespace support } // namespace emboss