blob: e488f99a4c79f1f1614304451c325634117ae271 [file]
// 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 "runtime/cpp/emboss_prelude.h"
#include <type_traits>
#include "gtest/gtest.h"
#include "runtime/cpp/emboss_cpp_util.h"
#include "runtime/cpp/emboss_text_util.h"
namespace emboss {
namespace prelude {
namespace test {
using ::emboss::support::OffsetBitBlock;
using ::emboss::support::ReadWriteContiguousBuffer;
template </**/ ::std::size_t kBits>
using BitBlockN = ::emboss::support::BitBlock<
::emboss::support::LittleEndianByteOrderer<ReadWriteContiguousBuffer>,
kBits>;
template </**/ ::std::size_t kBits>
using ViewParameters = ::emboss::support::FixedSizeViewParameters<
kBits, ::emboss::support::AllValuesAreOk>;
TEST(FlagView, Methods) {
::std::uint8_t byte = 0;
auto flag_view = FlagView<ViewParameters<1>, OffsetBitBlock<BitBlockN<8>>>{
BitBlockN<8>{ReadWriteContiguousBuffer{&byte, 1}}.GetOffsetStorage<1, 0>(
0, 1)};
EXPECT_FALSE(flag_view.Read());
byte = 0xfe;
EXPECT_FALSE(flag_view.Read());
byte = 0x01;
EXPECT_TRUE(flag_view.Read());
byte = 0xff;
EXPECT_TRUE(flag_view.Read());
EXPECT_TRUE(flag_view.CouldWriteValue(false));
EXPECT_TRUE(flag_view.CouldWriteValue(true));
flag_view.Write(false);
EXPECT_EQ(0xfe, byte);
byte = 0xaa;
flag_view.Write(true);
EXPECT_EQ(0xab, byte);
}
TEST(FlagView, TextDecode) {
::std::uint8_t byte = 0;
const auto flag_view =
FlagView<ViewParameters<1>, OffsetBitBlock<BitBlockN<8>>>{
BitBlockN<8>{ReadWriteContiguousBuffer{&byte, 1}}
.GetOffsetStorage<1, 0>(0, 1)};
EXPECT_FALSE(UpdateFromText(flag_view, ""));
EXPECT_FALSE(UpdateFromText(flag_view, "FALSE"));
EXPECT_FALSE(UpdateFromText(flag_view, "TRUE"));
EXPECT_FALSE(UpdateFromText(flag_view, "+true"));
EXPECT_TRUE(UpdateFromText(flag_view, "true"));
EXPECT_EQ(0x01, byte);
EXPECT_TRUE(UpdateFromText(flag_view, "false"));
EXPECT_EQ(0x00, byte);
EXPECT_TRUE(UpdateFromText(flag_view, " true"));
EXPECT_EQ(0x01, byte);
{
auto stream = support::TextStream{" false xxx"};
EXPECT_TRUE(flag_view.UpdateFromTextStream(&stream));
EXPECT_EQ(0x00, byte);
::std::string token;
EXPECT_TRUE(::emboss::support::ReadToken(&stream, &token));
EXPECT_EQ("xxx", token);
}
}
TEST(FlagView, TextEncode) {
::std::uint8_t byte = 0;
const auto flag_view =
FlagView<ViewParameters<1>, OffsetBitBlock<BitBlockN<8>>>{
BitBlockN<8>{ReadWriteContiguousBuffer{&byte, 1}}
.GetOffsetStorage<1, 0>(0, 1)};
EXPECT_EQ("false", WriteToString(flag_view));
byte = 1;
EXPECT_EQ("true", WriteToString(flag_view));
}
// Helper to select appropriate BitBlock size for a given view size
template <int kBits>
struct BitBlockSelector {
#if EMBOSS_HAS_INT128
// Use 128-bit BitBlock for sizes > 64
using Type = typename ::std::conditional<(kBits > 64), BitBlockN<128>,
BitBlockN<64>>::type;
#else
using Type = BitBlockN<64>;
#endif
};
template <template <typename, typename> class ViewType, int kMaxBits>
void CheckViewSizeInBits() {
using BlockType = typename BitBlockSelector<kMaxBits>::Type;
const int size_in_bits =
ViewType<ViewParameters<kMaxBits>, BlockType>::SizeInBits();
EXPECT_EQ(size_in_bits, kMaxBits);
return CheckViewSizeInBits<ViewType, kMaxBits - 1>();
}
template <>
void CheckViewSizeInBits<UIntView, 0>() {
return;
}
template <>
void CheckViewSizeInBits<IntView, 0>() {
return;
}
template <>
void CheckViewSizeInBits<BcdView, 0>() {
return;
}
#if EMBOSS_HAS_INT128
TEST(UIntView, SizeInBits) { CheckViewSizeInBits<UIntView, 128>(); }
TEST(IntView, SizeInBits) { CheckViewSizeInBits<IntView, 128>(); }
#else
TEST(UIntView, SizeInBits) { CheckViewSizeInBits<UIntView, 64>(); }
TEST(IntView, SizeInBits) { CheckViewSizeInBits<IntView, 64>(); }
#endif // EMBOSS_HAS_INT128
TEST(BcdView, SizeInBits) { CheckViewSizeInBits<BcdView, 64>(); }
template </**/ ::std::size_t kBits>
using UIntViewN = UIntView<ViewParameters<kBits>, BitBlockN<kBits>>;
TEST(UIntView, ValueType) {
using BitBlockType = BitBlockN<64>;
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint8_t,
UIntView<ViewParameters<8>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint8_t,
UIntView<ViewParameters<6>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint16_t,
UIntView<ViewParameters<9>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint16_t,
UIntView<ViewParameters<16>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint32_t,
UIntView<ViewParameters<17>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint32_t,
UIntView<ViewParameters<32>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint64_t,
UIntView<ViewParameters<33>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint64_t,
UIntView<ViewParameters<64>, BitBlockType>::ValueType>::value));
#if EMBOSS_HAS_INT128
using BitBlockType128 = BitBlockN<128>;
EXPECT_TRUE(
(::std::is_same<
__uint128_t,
UIntView<ViewParameters<65>, BitBlockType128>::ValueType>::value));
EXPECT_TRUE(
(::std::is_same<
__uint128_t,
UIntView<ViewParameters<96>, BitBlockType128>::ValueType>::value));
EXPECT_TRUE(
(::std::is_same<
__uint128_t,
UIntView<ViewParameters<128>, BitBlockType128>::ValueType>::value));
#endif // EMBOSS_HAS_INT128
}
TEST(UIntView, CouldWriteValue) {
EXPECT_TRUE(UIntViewN<8>::CouldWriteValue(0xff));
EXPECT_TRUE(UIntViewN<8>::CouldWriteValue(0));
EXPECT_FALSE(UIntViewN<8>::CouldWriteValue(0x100));
EXPECT_FALSE(UIntViewN<8>::CouldWriteValue(-1));
EXPECT_TRUE(UIntViewN<16>::CouldWriteValue(0xffff));
EXPECT_TRUE(UIntViewN<16>::CouldWriteValue(0));
EXPECT_FALSE(UIntViewN<16>::CouldWriteValue(0x10000));
EXPECT_FALSE(UIntViewN<16>::CouldWriteValue(-1));
EXPECT_TRUE(UIntViewN<32>::CouldWriteValue(0xffffffffU));
EXPECT_TRUE(UIntViewN<32>::CouldWriteValue(0xffffffffL));
EXPECT_TRUE(UIntViewN<32>::CouldWriteValue(0));
EXPECT_FALSE(UIntViewN<32>::CouldWriteValue(0x100000000L));
EXPECT_FALSE(UIntViewN<32>::CouldWriteValue(-1));
EXPECT_TRUE(UIntViewN<48>::CouldWriteValue(0x0000ffffffffffffUL));
EXPECT_TRUE(UIntViewN<48>::CouldWriteValue(0x0000ffffffffffffL));
EXPECT_TRUE(UIntViewN<48>::CouldWriteValue(0));
EXPECT_FALSE(UIntViewN<48>::CouldWriteValue(0x1000000000000UL));
EXPECT_FALSE(UIntViewN<48>::CouldWriteValue(0x1000000000000L));
EXPECT_FALSE(UIntViewN<48>::CouldWriteValue(-1));
EXPECT_TRUE(UIntViewN<64>::CouldWriteValue(0xffffffffffffffffUL));
EXPECT_TRUE(UIntViewN<64>::CouldWriteValue(0));
EXPECT_FALSE(UIntViewN<64>::CouldWriteValue(-1));
}
#if EMBOSS_HAS_INT128
TEST(UIntView, Int128ReadAndWriteWithSufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0xff}};
auto uint128_view = UIntView<ViewParameters<128>, BitBlockN<128>>{
BitBlockN<128>{ReadWriteContiguousBuffer{bytes.data(), 16}}};
__uint128_t expected =
(static_cast<__uint128_t>(0x100f0e0d0c0b0a09UL) << 64) |
static_cast<__uint128_t>(0x0807060504030201UL);
EXPECT_EQ(expected, uint128_view.Read());
EXPECT_EQ(expected, uint128_view.UncheckedRead());
__uint128_t write_value =
(static_cast<__uint128_t>(0xfedcba9876543210UL) << 64) |
static_cast<__uint128_t>(0x0123456789abcdefUL);
uint128_view.Write(write_value);
EXPECT_EQ(write_value, uint128_view.Read());
// Verify the buffer was written in little-endian order
EXPECT_EQ(0xef, bytes[0]);
EXPECT_EQ(0xcd, bytes[1]);
EXPECT_EQ(0x10, bytes[8]);
EXPECT_EQ(0xfe, bytes[15]);
// Last byte should be untouched
EXPECT_EQ(0xff, bytes[16]);
EXPECT_TRUE(uint128_view.Ok());
EXPECT_TRUE(uint128_view.IsComplete());
}
TEST(UIntView, Int128NonFullWidth) {
// Test 72-bit (9 byte) unsigned integer - BitBlock size must match view size
::std::vector</**/ ::std::uint8_t> bytes = {
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff}};
auto uint72_view = UIntView<ViewParameters<72>, BitBlockN<72>>{
BitBlockN<72>{ReadWriteContiguousBuffer{bytes.data(), 9}}};
__uint128_t expected_72 =
(static_cast<__uint128_t>(0x09UL) << 64) |
static_cast<__uint128_t>(0x0807060504030201UL);
EXPECT_EQ(expected_72, uint72_view.Read());
EXPECT_TRUE(uint72_view.Ok());
// Write a value that fits in 72 bits
__uint128_t write_72 =
(static_cast<__uint128_t>(0xffUL) << 64) |
static_cast<__uint128_t>(0xffffffffffffffffUL);
uint72_view.Write(write_72);
EXPECT_EQ(write_72, uint72_view.Read());
// Last byte should be untouched
EXPECT_EQ(0xff, bytes[9]);
}
TEST(UIntView, Int128CouldWriteValue) {
using UIntView128 = UIntView<ViewParameters<128>, BitBlockN<128>>;
// 128-bit view should accept full 128-bit values
__uint128_t max_128 =
(static_cast<__uint128_t>(0xffffffffffffffffUL) << 64) |
static_cast<__uint128_t>(0xffffffffffffffffUL);
EXPECT_TRUE(UIntView128::CouldWriteValue(max_128));
EXPECT_TRUE(UIntView128::CouldWriteValue(static_cast<__uint128_t>(0)));
// Test that values in the valid range work
__uint128_t mid_value =
(static_cast<__uint128_t>(0x8000000000000000UL) << 64) |
static_cast<__uint128_t>(0x0000000000000000UL);
EXPECT_TRUE(UIntView128::CouldWriteValue(mid_value));
// Test that negative values (which convert to large unsigned) fail
EXPECT_FALSE(UIntView128::CouldWriteValue(static_cast<__int128_t>(-1)));
}
// Regression test: for a field wider than 64 bits but narrower than 128, the
// upper bound must be enforced without truncating the (128-bit) value to 64
// bits. A value that is out of range but congruent to an in-range value mod
// 2**64 must still be rejected.
TEST(UIntView, Int128CouldWriteValueNonFullWidth) {
using UIntView72 = UIntView<ViewParameters<72>, BitBlockN<72>>;
const __uint128_t max_72 = (static_cast<__uint128_t>(1) << 72) - 1;
EXPECT_TRUE(UIntView72::CouldWriteValue(max_72));
EXPECT_TRUE(UIntView72::CouldWriteValue(static_cast<__uint128_t>(0)));
// 2**72 is one past the maximum; its low 64 bits are zero, so a 64-bit
// truncation would incorrectly accept it.
EXPECT_FALSE(UIntView72::CouldWriteValue(static_cast<__uint128_t>(1) << 72));
// 2**80 likewise truncates to zero in 64 bits.
EXPECT_FALSE(UIntView72::CouldWriteValue(static_cast<__uint128_t>(1) << 80));
// TryToWrite must fail gracefully (return false) rather than abort or
// silently truncate when the value is out of range.
::std::vector</**/ ::std::uint8_t> bytes(9, 0);
auto view = UIntView72{BitBlockN<72>{ReadWriteContiguousBuffer{bytes.data(),
9}}};
EXPECT_FALSE(view.TryToWrite(static_cast<__uint128_t>(1) << 80));
EXPECT_TRUE(view.TryToWrite(max_72));
EXPECT_EQ(max_72, view.Read());
}
#endif // EMBOSS_HAS_INT128
TEST(UIntView, CouldWriteValueNarrowing) {
auto narrowing_could_write = [](int value) {
return UIntViewN<8>::CouldWriteValue(value);
};
EXPECT_TRUE(narrowing_could_write(0));
EXPECT_TRUE(narrowing_could_write(255));
EXPECT_FALSE(narrowing_could_write(-1));
EXPECT_FALSE(narrowing_could_write(256));
}
TEST(UIntView, ReadAndWriteWithSufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
auto uint64_view =
UIntViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}}};
EXPECT_EQ(0x090a0b0c0d0e0f10UL, uint64_view.Read());
EXPECT_EQ(0x090a0b0c0d0e0f10UL, uint64_view.UncheckedRead());
uint64_view.Write(0x100f0e0d0c0b0a09UL);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
uint64_view.UncheckedWrite(0x090a0b0c0d0e0f10UL);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}}),
bytes);
EXPECT_TRUE(uint64_view.TryToWrite(0x100f0e0d0c0b0a09UL));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
EXPECT_TRUE(uint64_view.TryToWrite(0x090a0b0c0d0e0f10UL));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}}),
bytes);
EXPECT_TRUE(uint64_view.Ok());
EXPECT_TRUE(uint64_view.IsComplete());
}
TEST(UIntView, ReadAndWriteWithInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
auto uint64_view =
UIntViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 4}}};
EXPECT_EQ(0x090a0b0c0d0e0f10UL, uint64_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(uint64_view.Read(), "");
EXPECT_DEATH(uint64_view.Write(0x100f0e0d0c0b0a09UL), "");
#endif // EMBOSS_CHECK_ABORTS
EXPECT_FALSE(uint64_view.TryToWrite(0x100f0e0d0c0b0a09UL));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}}),
bytes);
uint64_view.UncheckedWrite(0x100f0e0d0c0b0a09UL);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
EXPECT_FALSE(uint64_view.Ok());
EXPECT_FALSE(uint64_view.IsComplete());
uint64_view.UncheckedWrite(0x090a0b0c0d0e0f10UL);
}
TEST(UIntView, NonPowerOfTwoSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
auto uint24_view =
UIntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}};
EXPECT_EQ(0x0e0f10U, uint24_view.Read());
EXPECT_EQ(0x0e0f10U, uint24_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(uint24_view.Write(0x1000000), "");
#endif // EMBOSS_CHECK_ABORTS
uint24_view.Write(0x100f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0x10, 0x0d}}),
bytes);
uint24_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x0d}}),
bytes);
EXPECT_TRUE(uint24_view.Ok());
EXPECT_TRUE(uint24_view.IsComplete());
}
TEST(UIntView, NonPowerOfTwoSizeInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
auto uint24_view =
UIntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 2}}};
EXPECT_EQ(0x0e0f10U, uint24_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(uint24_view.Read(), "");
EXPECT_DEATH(uint24_view.Write(0x100f0e), "");
#endif // EMBOSS_CHECK_ABORTS
uint24_view.UncheckedWrite(0x100f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0x10, 0x0d}}),
bytes);
uint24_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x0d}}),
bytes);
EXPECT_FALSE(uint24_view.Ok());
EXPECT_FALSE(uint24_view.IsComplete());
}
TEST(UIntView, NonByteSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x00, 0x00, 0x80, 0x80}};
auto uint23_view =
UIntView<ViewParameters<23>, OffsetBitBlock<BitBlockN<24>>>{
BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}
.GetOffsetStorage<1, 0>(0, 23)};
EXPECT_EQ(0x0U, uint23_view.Read());
EXPECT_FALSE(uint23_view.CouldWriteValue(0x800f0e));
EXPECT_FALSE(uint23_view.CouldWriteValue(0x800000));
EXPECT_TRUE(uint23_view.CouldWriteValue(0x7fffff));
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(uint23_view.Write(0x800f0e), "");
#endif // EMBOSS_CHECK_ABORTS
uint23_view.Write(0x400f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0xc0, 0x80}}),
bytes);
uint23_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x80, 0x80}}),
bytes);
EXPECT_TRUE(uint23_view.Ok());
EXPECT_TRUE(uint23_view.IsComplete());
}
TEST(UIntView, TextDecode) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x00, 0x00, 0x00, 0xff}};
const auto uint24_view =
UIntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}};
EXPECT_TRUE(UpdateFromText(uint24_view, "23"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_EQ(23U, uint24_view.Read());
EXPECT_FALSE(UpdateFromText(uint24_view, "16777216"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(uint24_view, "16777215"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xff, 0xff, 0xff, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(uint24_view, "0x01_0203"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x03, 0x02, 0x01, 0xff}}),
bytes);
}
template </**/ ::std::size_t kBits>
using IntViewN = IntView<ViewParameters<kBits>, BitBlockN<kBits>>;
TEST(IntView, ValueType) {
using BitBlockType = BitBlockN<64>;
EXPECT_TRUE((::std::is_same<
/**/ ::std::int8_t,
IntView<ViewParameters<8>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int8_t,
IntView<ViewParameters<6>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int16_t,
IntView<ViewParameters<9>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int16_t,
IntView<ViewParameters<16>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int32_t,
IntView<ViewParameters<17>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int32_t,
IntView<ViewParameters<32>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int64_t,
IntView<ViewParameters<33>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::int64_t,
IntView<ViewParameters<64>, BitBlockType>::ValueType>::value));
#if EMBOSS_HAS_INT128
using BitBlockType128 = BitBlockN<128>;
EXPECT_TRUE(
(::std::is_same<__int128_t, IntView<ViewParameters<65>,
BitBlockType128>::ValueType>::value));
EXPECT_TRUE(
(::std::is_same<__int128_t, IntView<ViewParameters<96>,
BitBlockType128>::ValueType>::value));
EXPECT_TRUE(
(::std::is_same<__int128_t, IntView<ViewParameters<128>,
BitBlockType128>::ValueType>::value));
#endif // EMBOSS_HAS_INT128
}
TEST(IntView, CouldWriteValue) {
// Note that many values are in decimal in order to avoid C++'s implicit
// conversions to unsigned for hex constants.
EXPECT_TRUE(IntViewN<8>::CouldWriteValue(0x7f));
EXPECT_TRUE(IntViewN<8>::CouldWriteValue(-0x80));
EXPECT_FALSE(IntViewN<8>::CouldWriteValue(0x80));
EXPECT_FALSE(IntViewN<8>::CouldWriteValue(0x8000000000000000UL));
EXPECT_FALSE(IntViewN<8>::CouldWriteValue(-0x81));
EXPECT_TRUE(IntViewN<16>::CouldWriteValue(32767));
EXPECT_TRUE(IntViewN<16>::CouldWriteValue(0));
EXPECT_FALSE(IntViewN<16>::CouldWriteValue(0x8000));
EXPECT_FALSE(IntViewN<16>::CouldWriteValue(-0x8001));
EXPECT_TRUE(IntViewN<32>::CouldWriteValue(0x7fffffffU));
EXPECT_TRUE(IntViewN<32>::CouldWriteValue(0x7fffffffL));
EXPECT_FALSE(IntViewN<32>::CouldWriteValue(0x80000000U));
EXPECT_FALSE(IntViewN<32>::CouldWriteValue(-2147483649L));
EXPECT_TRUE(IntViewN<48>::CouldWriteValue(0x00007fffffffffffUL));
EXPECT_FALSE(IntViewN<48>::CouldWriteValue(140737488355328L));
EXPECT_FALSE(IntViewN<48>::CouldWriteValue(-140737488355329L));
EXPECT_TRUE(IntViewN<64>::CouldWriteValue(0x7fffffffffffffffUL));
EXPECT_TRUE(IntViewN<64>::CouldWriteValue(9223372036854775807L));
EXPECT_TRUE(IntViewN<64>::CouldWriteValue(-9223372036854775807L - 1));
EXPECT_FALSE(IntViewN<64>::CouldWriteValue(0x8000000000000000UL));
}
#if EMBOSS_HAS_INT128
TEST(IntView, Int128ReadAndWriteWithSufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0xff}};
auto int128_view = IntView<ViewParameters<128>, BitBlockN<128>>{
BitBlockN<128>{ReadWriteContiguousBuffer{bytes.data(), 16}}};
__int128_t expected =
static_cast<__int128_t>(
(static_cast<__uint128_t>(0x100f0e0d0c0b0a09UL) << 64) |
static_cast<__uint128_t>(0x0807060504030201UL));
EXPECT_EQ(expected, int128_view.Read());
EXPECT_EQ(expected, int128_view.UncheckedRead());
// Write a negative value
__int128_t write_value = -1;
int128_view.Write(write_value);
EXPECT_EQ(write_value, int128_view.Read());
// All bytes should be 0xff
for (int i = 0; i < 16; ++i) {
EXPECT_EQ(0xff, bytes[i]);
}
// Last byte should be untouched
EXPECT_EQ(0xff, bytes[16]);
EXPECT_TRUE(int128_view.Ok());
EXPECT_TRUE(int128_view.IsComplete());
}
TEST(IntView, Int128SignExtension) {
// Test that signed 128-bit values with high bit set are properly sign-extended
// Min int128 in little-endian: 0x00...00 with MSB set = 0x80 in the last byte
::std::vector</**/ ::std::uint8_t> bytes = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}}; // -2^127 in LE
auto int128_view = IntView<ViewParameters<128>, BitBlockN<128>>{
BitBlockN<128>{ReadWriteContiguousBuffer{bytes.data(), 16}}};
// The MSB is set, so this should be the minimum 128-bit signed integer
__int128_t min_int128 = static_cast<__int128_t>(1) << 127;
// This is negative due to two's complement
EXPECT_TRUE(int128_view.Read() < 0);
EXPECT_EQ(min_int128, int128_view.Read());
// Write max positive value
__int128_t max_positive = ~min_int128; // 2^127 - 1
int128_view.Write(max_positive);
EXPECT_EQ(max_positive, int128_view.Read());
EXPECT_TRUE(int128_view.Read() > 0);
// Verify the bytes are correct for max positive
// Max positive = 0x7fffffffffffffff_ffffffffffffffff in LE
EXPECT_EQ(0xff, bytes[0]);
EXPECT_EQ(0xff, bytes[7]);
EXPECT_EQ(0xff, bytes[14]);
EXPECT_EQ(0x7f, bytes[15]); // The MSB should be 0
}
TEST(IntView, Int128NonFullWidth) {
// Test 72-bit (9 byte) signed integer with negative value
::std::vector</**/ ::std::uint8_t> bytes = {
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}};
auto int72_view = IntView<ViewParameters<72>, BitBlockN<72>>{
BitBlockN<72>{ReadWriteContiguousBuffer{bytes.data(), 9}}};
// 72 bits all set to 1 should be -1 in two's complement
EXPECT_EQ(static_cast<__int128_t>(-1), int72_view.Read());
EXPECT_TRUE(int72_view.Ok());
// Write a small negative value
int72_view.Write(static_cast<__int128_t>(-100));
EXPECT_EQ(static_cast<__int128_t>(-100), int72_view.Read());
// Last byte should be untouched (but may have been modified by writes above)
}
TEST(IntView, Int128CouldWriteValue) {
using IntView128 = IntView<ViewParameters<128>, BitBlockN<128>>;
// Max and min 128-bit signed values
__int128_t max_128 =
static_cast<__int128_t>(
(static_cast<__uint128_t>(0x7fffffffffffffffUL) << 64) |
static_cast<__uint128_t>(0xffffffffffffffffUL));
__int128_t min_128 = static_cast<__int128_t>(1) << 127;
EXPECT_TRUE(IntView128::CouldWriteValue(max_128));
EXPECT_TRUE(IntView128::CouldWriteValue(min_128));
EXPECT_TRUE(IntView128::CouldWriteValue(static_cast<__int128_t>(0)));
EXPECT_TRUE(IntView128::CouldWriteValue(static_cast<__int128_t>(-1)));
EXPECT_TRUE(IntView128::CouldWriteValue(static_cast<__int128_t>(1)));
// Test various values in the middle of the range
EXPECT_TRUE(IntView128::CouldWriteValue(
static_cast<__int128_t>(0x7fffffffffffffffLL)));
EXPECT_TRUE(IntView128::CouldWriteValue(
static_cast<__int128_t>(-0x7fffffffffffffffLL)));
}
// Regression test: for a signed field wider than 64 bits but narrower than
// 128, both bounds must be enforced on the full 128-bit value. In particular
// the lower (negative) bound must not be skipped -- ::std::is_signed is false
// for __int128_t under a strict -std, so the signedness check must be made via
// ::std::numeric_limits -- and it must not truncate the value to 64 bits.
TEST(IntView, Int128CouldWriteValueNonFullWidth) {
using IntView72 = IntView<ViewParameters<72>, BitBlockN<72>>;
const __int128_t max_72 = (static_cast<__int128_t>(1) << 71) - 1;
const __int128_t min_72 = -(static_cast<__int128_t>(1) << 71);
EXPECT_TRUE(IntView72::CouldWriteValue(max_72));
EXPECT_TRUE(IntView72::CouldWriteValue(min_72));
EXPECT_TRUE(IntView72::CouldWriteValue(static_cast<__int128_t>(0)));
// One past each bound must be rejected.
EXPECT_FALSE(IntView72::CouldWriteValue(max_72 + 1));
EXPECT_FALSE(IntView72::CouldWriteValue(min_72 - 1));
// A value far below the range, whose low 64 bits are zero (so a 64-bit
// truncation would misjudge it), must also be rejected.
EXPECT_FALSE(IntView72::CouldWriteValue(-(static_cast<__int128_t>(1) << 100)));
// TryToWrite must fail gracefully rather than abort or silently truncate.
::std::vector</**/ ::std::uint8_t> bytes(9, 0);
auto view = IntView72{BitBlockN<72>{ReadWriteContiguousBuffer{bytes.data(),
9}}};
EXPECT_FALSE(view.TryToWrite(-(static_cast<__int128_t>(1) << 100)));
EXPECT_TRUE(view.TryToWrite(min_72));
EXPECT_EQ(min_72, view.Read());
}
#endif // EMBOSS_HAS_INT128
TEST(IntView, CouldWriteValueNarrowing) {
auto narrowing_could_write = [](int value) {
return IntViewN<8>::CouldWriteValue(value);
};
EXPECT_TRUE(narrowing_could_write(-128));
EXPECT_TRUE(narrowing_could_write(127));
EXPECT_FALSE(narrowing_could_write(-129));
EXPECT_FALSE(narrowing_could_write(128));
}
TEST(IntView, ReadAndWriteWithSufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
auto int64_view =
IntViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}}};
EXPECT_EQ(0x090a0b0c0d0e0f10L, int64_view.Read());
EXPECT_EQ(0x090a0b0c0d0e0f10L, int64_view.UncheckedRead());
int64_view.Write(0x100f0e0d0c0b0a09L);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
int64_view.UncheckedWrite(0x090a0b0c0d0e0f10L);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}}),
bytes);
EXPECT_TRUE(int64_view.TryToWrite(0x100f0e0d0c0b0a09L));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
int64_view.Write(-0x100f0e0d0c0b0a09L);
EXPECT_EQ(-0x100f0e0d0c0b0a09L, int64_view.Read());
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0xf7, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0x08}}),
bytes);
EXPECT_TRUE(int64_view.Ok());
EXPECT_TRUE(int64_view.IsComplete());
}
TEST(IntView, ReadAndWriteWithInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}};
auto int64_view =
IntViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 4}}};
EXPECT_EQ(0x090a0b0c0d0e0f10L, int64_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int64_view.Read(), "");
EXPECT_DEATH(int64_view.Write(0x100f0e0d0c0b0a09L), "");
#endif // EMBOSS_CHECK_ABORTS
EXPECT_FALSE(int64_view.TryToWrite(0x100f0e0d0c0b0a09L));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08}}),
bytes);
int64_view.UncheckedWrite(0x100f0e0d0c0b0a09L);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x08}}),
bytes);
EXPECT_FALSE(int64_view.Ok());
EXPECT_FALSE(int64_view.IsComplete());
}
TEST(IntView, NonPowerOfTwoSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
auto int24_view =
IntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}};
EXPECT_EQ(0x0e0f10, int24_view.Read());
EXPECT_EQ(0x0e0f10, int24_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int24_view.Write(0x1000000), "");
#endif // EMBOSS_CHECK_ABORTS
int24_view.Write(0x100f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0x10, 0x0d}}),
bytes);
int24_view.Write(-0x100f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xf2, 0xf0, 0xef, 0x0d}}),
bytes);
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int24_view.Write(0x1000000), "");
#endif // EMBOSS_CHECK_ABORTS
int24_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x0d}}),
bytes);
EXPECT_TRUE(int24_view.Ok());
EXPECT_TRUE(int24_view.IsComplete());
}
TEST(IntView, NonPowerOfTwoSizeInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x10, 0x0f, 0x0e, 0x0d}};
auto int24_view =
IntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 2}}};
EXPECT_EQ(0x0e0f10, int24_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int24_view.Read(), "");
EXPECT_DEATH(int24_view.Write(0x100f0e), "");
#endif // EMBOSS_CHECK_ABORTS
int24_view.UncheckedWrite(0x100f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0x10, 0x0d}}),
bytes);
int24_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x0d}}),
bytes);
EXPECT_FALSE(int24_view.Ok());
EXPECT_FALSE(int24_view.IsComplete());
}
TEST(IntView, NonByteSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x00, 0x00, 0x80, 0x80}};
auto int23_view = IntView<ViewParameters<23>, OffsetBitBlock<BitBlockN<24>>>{
BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}
.GetOffsetStorage<1, 0>(0, 23)};
EXPECT_EQ(0x0, int23_view.Read());
EXPECT_FALSE(int23_view.CouldWriteValue(0x400f0e));
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int23_view.Write(0x400f0e), "");
#endif // EMBOSS_CHECK_ABORTS
int23_view.Write(0x200f0e);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x0e, 0x0f, 0xa0, 0x80}}),
bytes);
int23_view.Write(-0x400000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0xc0, 0x80}}),
bytes);
int23_view.UncheckedWrite(0x1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x80, 0x80}}),
bytes);
EXPECT_TRUE(int23_view.Ok());
EXPECT_TRUE(int23_view.IsComplete());
}
TEST(IntView, OneBit) {
::std::uint8_t bytes[] = {0xfe};
auto int1_view = IntView<ViewParameters<1>, OffsetBitBlock<BitBlockN<8>>>{
BitBlockN<8>{ReadWriteContiguousBuffer{bytes, 1}}.GetOffsetStorage<1, 0>(
0, 1)};
EXPECT_TRUE(int1_view.Ok());
EXPECT_TRUE(int1_view.IsComplete());
EXPECT_EQ(0, int1_view.Read());
EXPECT_FALSE(int1_view.CouldWriteValue(1));
EXPECT_TRUE(int1_view.CouldWriteValue(0));
EXPECT_TRUE(int1_view.CouldWriteValue(-1));
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(int1_view.Write(1), "");
#endif // EMBOSS_CHECK_ABORTS
int1_view.Write(-1);
EXPECT_EQ(0xff, bytes[0]);
EXPECT_EQ(-1, int1_view.Read());
int1_view.Write(0);
EXPECT_EQ(0xfe, bytes[0]);
bytes[0] = 0;
int1_view.Write(-1);
EXPECT_EQ(0x01, bytes[0]);
}
TEST(IntView, TextDecode) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x00, 0x00, 0x00, 0xff}};
const auto int24_view =
IntViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}};
EXPECT_TRUE(UpdateFromText(int24_view, "23"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_EQ(23, int24_view.Read());
EXPECT_FALSE(UpdateFromText(int24_view, "16777216"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_FALSE(UpdateFromText(int24_view, "16777215"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_FALSE(UpdateFromText(int24_view, "8388608"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{23, 0x00, 0x00, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(int24_view, "8388607"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xff, 0xff, 0x7f, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(int24_view, "-8388608"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x80, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(int24_view, "-1"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xff, 0xff, 0xff, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(int24_view, "0x01_0203"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x03, 0x02, 0x01, 0xff}}),
bytes);
EXPECT_TRUE(UpdateFromText(int24_view, "-0x01_0203"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xfd, 0xfd, 0xfe, 0xff}}),
bytes);
EXPECT_FALSE(UpdateFromText(int24_view, "- 0x01_0203"));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0xfd, 0xfd, 0xfe, 0xff}}),
bytes);
}
TEST(MaxBcd, Values) {
EXPECT_EQ(0U, MaxBcd</**/ ::std::uint64_t>(0));
EXPECT_EQ(1U, MaxBcd</**/ ::std::uint64_t>(1));
EXPECT_EQ(3U, MaxBcd</**/ ::std::uint64_t>(2));
EXPECT_EQ(7U, MaxBcd</**/ ::std::uint64_t>(3));
EXPECT_EQ(9U, MaxBcd</**/ ::std::uint64_t>(4));
EXPECT_EQ(19U, MaxBcd</**/ ::std::uint64_t>(5));
EXPECT_EQ(39U, MaxBcd</**/ ::std::uint64_t>(6));
EXPECT_EQ(79U, MaxBcd</**/ ::std::uint64_t>(7));
EXPECT_EQ(99U, MaxBcd</**/ ::std::uint64_t>(8));
EXPECT_EQ(199U, MaxBcd</**/ ::std::uint64_t>(9));
EXPECT_EQ(999U, MaxBcd</**/ ::std::uint64_t>(12));
EXPECT_EQ(9999U, MaxBcd</**/ ::std::uint64_t>(16));
EXPECT_EQ(999999U, MaxBcd</**/ ::std::uint64_t>(24));
EXPECT_EQ(3999999999999999UL, MaxBcd</**/ ::std::uint64_t>(62));
EXPECT_EQ(7999999999999999UL, MaxBcd</**/ ::std::uint64_t>(63));
EXPECT_EQ(9999999999999999UL, MaxBcd</**/ ::std::uint64_t>(64));
// Max uint64_t is 18446744073709551616, which is big enough to hold a 76-bit
// BCD value.
EXPECT_EQ(19999999999999999UL, MaxBcd</**/ ::std::uint64_t>(65));
EXPECT_EQ(39999999999999999UL, MaxBcd</**/ ::std::uint64_t>(66));
EXPECT_EQ(99999999999999999UL, MaxBcd</**/ ::std::uint64_t>(68));
EXPECT_EQ(999999999999999999UL, MaxBcd</**/ ::std::uint64_t>(72));
EXPECT_EQ(9999999999999999999UL, MaxBcd</**/ ::std::uint64_t>(76));
}
TEST(IsBcd, Values) {
EXPECT_TRUE(IsBcd(0x00U));
EXPECT_TRUE(IsBcd(0x12U));
EXPECT_TRUE(IsBcd(0x91U));
EXPECT_TRUE(IsBcd(0x99U));
EXPECT_TRUE(IsBcd(::std::uint8_t{0x00}));
EXPECT_TRUE(IsBcd(::std::uint8_t{0x99}));
EXPECT_TRUE(IsBcd(::std::uint16_t{0x0000}));
EXPECT_TRUE(IsBcd(::std::uint16_t{0x9999}));
EXPECT_TRUE(IsBcd(0x9999999999999999UL));
EXPECT_FALSE(IsBcd(::std::uint8_t{0x0a}));
EXPECT_FALSE(IsBcd(::std::uint8_t{0xa0}));
EXPECT_FALSE(IsBcd(::std::uint8_t{0xff}));
EXPECT_FALSE(IsBcd(::std::uint16_t{0x0a00}));
EXPECT_FALSE(IsBcd(::std::uint16_t{0x000a}));
EXPECT_FALSE(IsBcd(0x999999999999999aUL));
EXPECT_FALSE(IsBcd(0xaUL));
EXPECT_FALSE(IsBcd(0xa000000000000000UL));
EXPECT_FALSE(IsBcd(0xf000000000000000UL));
EXPECT_FALSE(IsBcd(0xffffffffffffffffUL));
}
TEST(BcdView, ValueType) {
using BitBlockType = BitBlockN<64>;
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint8_t,
BcdView<ViewParameters<8>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint8_t,
BcdView<ViewParameters<6>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint16_t,
BcdView<ViewParameters<9>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint16_t,
BcdView<ViewParameters<16>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint32_t,
BcdView<ViewParameters<17>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint32_t,
BcdView<ViewParameters<32>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint64_t,
BcdView<ViewParameters<33>, BitBlockType>::ValueType>::value));
EXPECT_TRUE((::std::is_same<
/**/ ::std::uint64_t,
BcdView<ViewParameters<64>, BitBlockType>::ValueType>::value));
}
TEST(BcdView, CouldWriteValue) {
EXPECT_TRUE((BcdView<ViewParameters<64>, int>::CouldWriteValue(0)));
EXPECT_TRUE(
(BcdView<ViewParameters<64>, int>::CouldWriteValue(9999999999999999)));
EXPECT_FALSE(
(BcdView<ViewParameters<64>, int>::CouldWriteValue(10000000000000000)));
EXPECT_FALSE((
BcdView<ViewParameters<64>, int>::CouldWriteValue(0xffffffffffffffffUL)));
EXPECT_FALSE(
(BcdView<ViewParameters<48>, int>::CouldWriteValue(9999999999999999)));
EXPECT_TRUE(
(BcdView<ViewParameters<48>, int>::CouldWriteValue(999999999999)));
EXPECT_TRUE((BcdView<ViewParameters<48>, int>::CouldWriteValue(0)));
EXPECT_FALSE((BcdView<ViewParameters<48>, int>::CouldWriteValue(
(0xffUL << 48) + 999999999999)));
EXPECT_FALSE(
(BcdView<ViewParameters<48>, int>::CouldWriteValue(10000000000000000)));
EXPECT_FALSE((
BcdView<ViewParameters<48>, int>::CouldWriteValue(0xffffffffffffffffUL)));
}
template </**/ ::std::size_t kBits>
using BcdViewN = BcdView<ViewParameters<kBits>, BitBlockN<kBits>>;
TEST(BcdView, ReadAndWriteWithSufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08}};
auto bcd64_view =
BcdViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 8}}};
EXPECT_EQ(910111213141516UL, bcd64_view.Read());
EXPECT_EQ(910111213141516UL, bcd64_view.UncheckedRead());
bcd64_view.Write(1615141312111009);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08}}),
bytes);
bcd64_view.UncheckedWrite(910111213141516UL);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08}}),
bytes);
EXPECT_TRUE(bcd64_view.TryToWrite(1615141312111009));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08}}),
bytes);
EXPECT_TRUE(bcd64_view.Ok());
EXPECT_TRUE(bcd64_view.IsComplete());
}
TEST(BcdView, ReadAndWriteWithInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {
{0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08}};
auto bcd64_view =
BcdViewN<64>{BitBlockN<64>{ReadWriteContiguousBuffer{bytes.data(), 4}}};
EXPECT_EQ(910111213141516UL, bcd64_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(bcd64_view.Read(), "");
EXPECT_DEATH(bcd64_view.Write(1615141312111009), "");
#endif // EMBOSS_CHECK_ABORTS
EXPECT_FALSE(bcd64_view.TryToWrite(1615141312111009));
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08}}),
bytes);
bcd64_view.UncheckedWrite(1615141312111009);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{
{0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08}}),
bytes);
EXPECT_FALSE(bcd64_view.Ok());
EXPECT_FALSE(bcd64_view.IsComplete());
}
TEST(BcdView, NonPowerOfTwoSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x16, 0x15, 0x14, 0x13}};
auto bcd24_view =
BcdViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}};
EXPECT_EQ(141516U, bcd24_view.Read());
EXPECT_EQ(141516U, bcd24_view.UncheckedRead());
bcd24_view.Write(161514);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x14, 0x15, 0x16, 0x13}}),
bytes);
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(bcd24_view.Write(1000000), "");
#endif // EMBOSS_CHECK_ABORTS
bcd24_view.UncheckedWrite(1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x13}}),
bytes);
bcd24_view.UncheckedWrite(141516);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x16, 0x15, 0x14, 0x13}}),
bytes);
EXPECT_TRUE(bcd24_view.Ok());
EXPECT_TRUE(bcd24_view.IsComplete());
}
TEST(BcdView, NonPowerOfTwoSizeInsufficientBuffer) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x16, 0x15, 0x14, 0x13}};
auto bcd24_view =
BcdViewN<24>{BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 2}}};
EXPECT_EQ(141516U, bcd24_view.UncheckedRead());
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(bcd24_view.Read(), "");
EXPECT_DEATH(bcd24_view.Write(161514), "");
#endif // EMBOSS_CHECK_ABORTS
bcd24_view.UncheckedWrite(161514);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x14, 0x15, 0x16, 0x13}}),
bytes);
bcd24_view.UncheckedWrite(1000000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x00, 0x13}}),
bytes);
EXPECT_FALSE(bcd24_view.Ok());
EXPECT_FALSE(bcd24_view.IsComplete());
}
TEST(BcdView, NonByteSize) {
::std::vector</**/ ::std::uint8_t> bytes = {{0x00, 0x00, 0x80, 0x80}};
auto bcd23_view = BcdView<ViewParameters<23>, OffsetBitBlock<BitBlockN<24>>>{
BitBlockN<24>{ReadWriteContiguousBuffer{bytes.data(), 3}}
.GetOffsetStorage<1, 0>(0, 23)};
EXPECT_EQ(0x0U, bcd23_view.Read());
EXPECT_FALSE(bcd23_view.CouldWriteValue(800000));
EXPECT_TRUE(bcd23_view.CouldWriteValue(799999));
#if EMBOSS_CHECK_ABORTS
EXPECT_DEATH(bcd23_view.Write(800000), "");
#endif // EMBOSS_CHECK_ABORTS
bcd23_view.Write(432198);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x98, 0x21, 0xc3, 0x80}}),
bytes);
bcd23_view.UncheckedWrite(800000);
EXPECT_EQ((::std::vector</**/ ::std::uint8_t>{{0x00, 0x00, 0x80, 0x80}}),
bytes);
EXPECT_TRUE(bcd23_view.Ok());
EXPECT_TRUE(bcd23_view.IsComplete());
}
TEST(BcdLittleEndianView, AllByteValues) {
::std::uint8_t byte = 0;
auto bcd8_view =
BcdViewN<8>{BitBlockN<8>{ReadWriteContiguousBuffer{&byte, 1}}};
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 15; ++j) {
byte = i * 16 + j;
if (i > 9 || j > 9) {
EXPECT_FALSE(bcd8_view.Ok()) << i << ", " << j;
} else {
EXPECT_TRUE(bcd8_view.Ok()) << i << ", " << j;
}
}
}
}
} // namespace test
} // namespace prelude
} // namespace emboss