/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * 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
 *
 *      http://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 "bit_struct.h"

#include "gtest/gtest.h"

namespace art {

// A copy of detail::ValidateBitStructSize that uses EXPECT for a more
// human-readable message.
template <typename T>
static constexpr bool ValidateBitStructSize(const char* name) {
  const size_t kBitStructSizeOf = BitStructSizeOf<T>();
  const size_t kExpectedSize = (BitStructSizeOf<T>() < kBitsPerByte)
                                   ? kBitsPerByte
                                   : RoundUpToPowerOfTwo(kBitStructSizeOf);

  // Ensure no extra fields were added in between START/END.
  const size_t kActualSize = sizeof(T) * kBitsPerByte;
  EXPECT_EQ(kExpectedSize, kActualSize) << name;
  return true;
}

#define VALIDATE_BITSTRUCT_SIZE(type) ValidateBitStructSize<type>(#type)

TEST(BitStructs, MinimumType) {
  EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<1>::type));
  EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<2>::type));
  EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<3>::type));
  EXPECT_EQ(1u, sizeof(typename detail::MinimumTypeUnsignedHelper<8>::type));
  EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<9>::type));
  EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<10>::type));
  EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<15>::type));
  EXPECT_EQ(2u, sizeof(typename detail::MinimumTypeUnsignedHelper<16>::type));
  EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<17>::type));
  EXPECT_EQ(4u, sizeof(typename detail::MinimumTypeUnsignedHelper<32>::type));
  EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<33>::type));
  EXPECT_EQ(8u, sizeof(typename detail::MinimumTypeUnsignedHelper<64>::type));
}

template <typename T>
size_t AsUint(const T& value) {
  size_t uint_value = 0;
  memcpy(&uint_value, &value, sizeof(value));
  return uint_value;
}

struct CustomBitStruct {
  CustomBitStruct() = default;
  explicit CustomBitStruct(uint8_t data) : data(data) {}

  static constexpr size_t BitStructSizeOf() {
    return 4;
  }

  uint8_t data;
};

TEST(BitStructs, Custom) {
  CustomBitStruct expected(0b1111u);

  BitStructField<CustomBitStruct, /*lsb=*/4, /*width=*/4, uint8_t> f{};

  EXPECT_EQ(1u, sizeof(f));

  f = CustomBitStruct(0b1111u);

  CustomBitStruct read_out = f;
  EXPECT_EQ(read_out.data, 0b1111u);

  EXPECT_EQ(AsUint(f), 0b11110000u);
}

BITSTRUCT_DEFINE_START(TestTwoCustom, /* size= */ 8)
  BITSTRUCT_FIELD(CustomBitStruct, /*lsb=*/0, /*width=*/4) f4_a;
  BITSTRUCT_FIELD(CustomBitStruct, /*lsb=*/4, /*width=*/4) f4_b;
BITSTRUCT_DEFINE_END(TestTwoCustom);

TEST(BitStructs, TwoCustom) {
  EXPECT_EQ(sizeof(TestTwoCustom), 1u);

  VALIDATE_BITSTRUCT_SIZE(TestTwoCustom);

  TestTwoCustom cst{};

  // Test the write to most-significant field doesn't clobber least-significant.
  cst.f4_a = CustomBitStruct(0b0110);
  cst.f4_b = CustomBitStruct(0b0101);

  int8_t read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
  int8_t read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;

  EXPECT_EQ(0b0110, static_cast<int>(read_out));
  EXPECT_EQ(0b0101, static_cast<int>(read_out_b));

  EXPECT_EQ(AsUint(cst), 0b01010110u);

  // Test write to least-significant field doesn't clobber most-significant.
  cst.f4_a = CustomBitStruct(0);

  read_out = static_cast<CustomBitStruct>(cst.f4_a).data;
  read_out_b = static_cast<CustomBitStruct>(cst.f4_b).data;

  EXPECT_EQ(0b0, static_cast<int>(read_out));
  EXPECT_EQ(0b0101, static_cast<int>(read_out_b));

  EXPECT_EQ(AsUint(cst), 0b01010000u);
}

TEST(BitStructs, Number) {
  BitStructNumber<uint16_t, /*lsb=*/4, /*width=*/4, uint16_t> bsn{};
  EXPECT_EQ(2u, sizeof(bsn));

  bsn = 0b1111;

  uint32_t read_out = static_cast<uint32_t>(bsn);
  uint32_t read_out_impl = bsn;

  EXPECT_EQ(read_out, read_out_impl);
  EXPECT_EQ(read_out, 0b1111u);
  EXPECT_EQ(AsUint(bsn), 0b11110000u);
}

TEST(BitStructs, NumberNarrowStorage) {
  BitStructNumber<uint16_t, /*lsb=*/4, /*width=*/4, uint8_t> bsn{};
  EXPECT_EQ(1u, sizeof(bsn));

  bsn = 0b1111;

  uint32_t read_out = static_cast<uint32_t>(bsn);
  uint32_t read_out_impl = bsn;

  EXPECT_EQ(read_out, read_out_impl);
  EXPECT_EQ(read_out, 0b1111u);
  EXPECT_EQ(AsUint(bsn), 0b11110000u);
}

BITSTRUCT_DEFINE_START(TestBitStruct, /* size= */ 8)
  BITSTRUCT_INT(/*lsb=*/0, /*width=*/3) i3;
  BITSTRUCT_UINT(/*lsb=*/3, /*width=*/4) u4;

  BITSTRUCT_UINT(/*lsb=*/0, /*width=*/7) alias_all;
BITSTRUCT_DEFINE_END(TestBitStruct);

TEST(BitStructs, Test1) {
  TestBitStruct tst{};

  // Check minimal size selection is correct.
  EXPECT_EQ(1u, sizeof(TestBitStruct));
  EXPECT_EQ(1u, sizeof(tst._));
  EXPECT_EQ(1u, sizeof(tst.i3));
  EXPECT_EQ(1u, sizeof(tst.u4));
  EXPECT_EQ(1u, sizeof(tst.alias_all));

  // Check operator assignment.
  tst.i3 = -1;
  tst.u4 = 0b1010;

  // Check implicit operator conversion.
  int8_t read_i3 = tst.i3;
  uint8_t read_u4 = tst.u4;

  // Ensure read-out values were correct.
  EXPECT_EQ(static_cast<int8_t>(-1), read_i3);
  EXPECT_EQ(0b1010, read_u4);

  // Ensure aliasing is working.
  EXPECT_EQ(0b1010111, static_cast<uint8_t>(tst.alias_all));

  // Ensure the bit pattern is correct.
  EXPECT_EQ(0b1010111u, AsUint(tst));

  // Math operator checks
  {
    // In-place
    ++tst.u4;
    EXPECT_EQ(static_cast<uint8_t>(0b1011), static_cast<uint8_t>(tst.u4));
    --tst.u4;
    EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));

    // Copy
    uint8_t read_and_convert = tst.u4++;
    EXPECT_EQ(static_cast<uint8_t>(0b1011), read_and_convert);
    EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));
    read_and_convert = tst.u4--;
    EXPECT_EQ(static_cast<uint8_t>(0b1001), read_and_convert);
    EXPECT_EQ(static_cast<uint8_t>(0b1010), static_cast<uint8_t>(tst.u4));

    // Check boolean operator conversion.
    tst.u4 = 0b1010;
    EXPECT_TRUE(static_cast<bool>(tst.u4));
    bool succ = tst.u4 ? true : false;
    EXPECT_TRUE(succ);

    tst.u4 = 0;
    EXPECT_FALSE(static_cast<bool>(tst.u4));

/*
    // Disabled: Overflow is caught by the BitFieldInsert DCHECKs.
    // Check overflow for uint.
    tst.u4 = 0b1111;
    ++tst.u4;
    EXPECT_EQ(static_cast<uint8_t>(0), static_cast<uint8_t>(tst.u4));
*/
  }
}

BITSTRUCT_DEFINE_START(MixedSizeBitStruct, /* size= */ 32)
  BITSTRUCT_UINT(/*lsb=*/0, /*width=*/3) u3;
  BITSTRUCT_UINT(/*lsb=*/3, /*width=*/10) u10;
  BITSTRUCT_UINT(/*lsb=*/13, /*width=*/19) u19;

  BITSTRUCT_UINT(/*lsb=*/0, /*width=*/32) alias_all;
BITSTRUCT_DEFINE_END(MixedSizeBitStruct);

// static_assert(sizeof(MixedSizeBitStruct) == sizeof(uint32_t), "TestBitStructs#MixedSize");

TEST(BitStructs, Mixed) {
  EXPECT_EQ(4u, sizeof(MixedSizeBitStruct));

  MixedSizeBitStruct tst{};

  // Check operator assignment.
  tst.u3 = 0b111u;
  tst.u10 = 0b1111010100u;
  tst.u19 = 0b1010101010101010101u;

  // Check implicit operator conversion.
  uint8_t read_u3 = tst.u3;
  uint16_t read_u10 = tst.u10;
  uint32_t read_u19 = tst.u19;

  // Ensure read-out values were correct.
  EXPECT_EQ(0b111u, read_u3);
  EXPECT_EQ(0b1111010100u, read_u10);
  EXPECT_EQ(0b1010101010101010101u, read_u19);

  uint32_t read_all = tst.alias_all;

  // Ensure aliasing is working.
  EXPECT_EQ(0b10101010101010101011111010100111u, read_all);

  // Ensure the bit pattern is correct.
  EXPECT_EQ(0b10101010101010101011111010100111u, AsUint(tst));
}

BITSTRUCT_DEFINE_START(TestBitStruct_u8, /* size= */ 8)
  BITSTRUCT_INT(/*lsb=*/0, /*width=*/3) i3;
  BITSTRUCT_UINT(/*lsb=*/3, /*width=*/4) u4;

  BITSTRUCT_UINT(/*lsb=*/0, /*width=*/8) alias_all;
BITSTRUCT_DEFINE_END(TestBitStruct_u8);

TEST(BitStructs, FieldAssignment) {
  TestBitStruct_u8 all_1s{};
  all_1s.alias_all = 0xffu;

  {
    TestBitStruct_u8 tst{};
    tst.i3 = all_1s.i3;

    // Copying a single bitfield does not copy all bitfields.
    EXPECT_EQ(0b111, tst.alias_all);
  }

  {
    TestBitStruct_u8 tst{};
    tst.u4 = all_1s.u4;

    // Copying a single bitfield does not copy all bitfields.
    EXPECT_EQ(0b1111000, tst.alias_all);
  }
}

BITSTRUCT_DEFINE_START(NestedStruct, /* size= */ 2 * MixedSizeBitStruct::BitStructSizeOf())
  BITSTRUCT_FIELD(MixedSizeBitStruct,
                  /*lsb=*/0,
                  /*width=*/MixedSizeBitStruct::BitStructSizeOf()) mixed_lower;
  BITSTRUCT_FIELD(MixedSizeBitStruct,
                  /*lsb=*/MixedSizeBitStruct::BitStructSizeOf(),
                  /*width=*/MixedSizeBitStruct::BitStructSizeOf()) mixed_upper;

  BITSTRUCT_UINT(/*lsb=*/0, /*width=*/ 2 * MixedSizeBitStruct::BitStructSizeOf()) alias_all;
BITSTRUCT_DEFINE_END(NestedStruct);

TEST(BitStructs, NestedFieldAssignment) {
  MixedSizeBitStruct mixed_all_1s{};
  mixed_all_1s.alias_all = 0xFFFFFFFFu;

  {
    NestedStruct xyz{};

    NestedStruct other{};
    other.mixed_upper = mixed_all_1s;
    other.mixed_lower = mixed_all_1s;

    // Copying a single bitfield does not copy all bitfields.
    xyz.mixed_lower = other.mixed_lower;
    EXPECT_EQ(0xFFFFFFFFu, xyz.alias_all);
  }

  {
    NestedStruct xyz{};

    NestedStruct other{};
    other.mixed_upper = mixed_all_1s;
    other.mixed_lower = mixed_all_1s;

    // Copying a single bitfield does not copy all bitfields.
    xyz.mixed_upper = other.mixed_upper;
    EXPECT_EQ(0xFFFFFFFF00000000u, xyz.alias_all);
  }
}

}  // namespace art
