// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/containers/span_reader.h"

#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::Optional;

namespace base {
namespace {

TEST(SpanReaderTest, Construct) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  EXPECT_EQ(r.remaining(), 5u);
  EXPECT_EQ(r.remaining_span().data(), &kArray[0u]);
  EXPECT_EQ(r.remaining_span().size(), 5u);
}

TEST(SpanReaderTest, Skip) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  EXPECT_EQ(r.num_read(), 0u);
  EXPECT_FALSE(r.Skip(6u));
  EXPECT_THAT(r.Skip(2u), Optional(span(kArray).first(2u)));
  EXPECT_EQ(r.num_read(), 2u);
}

TEST(SpanReaderTest, Read) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  EXPECT_EQ(r.num_read(), 0u);
  {
    auto o = r.Read(2u);
    static_assert(std::same_as<decltype(*o), span<const int>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(0u, 2u));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(r.num_read(), 2u);
  }
  {
    auto o = r.Read(5u);
    static_assert(std::same_as<decltype(*o), span<const int>&>);
    EXPECT_FALSE(o.has_value());
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(r.num_read(), 2u);
  }
  {
    auto o = r.Read(1u);
    static_assert(std::same_as<decltype(*o), span<const int>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(2u, 1u));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(r.num_read(), 3u);
  }
  {
    auto o = r.Read(2u);
    static_assert(std::same_as<decltype(*o), span<const int>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(3u, 2u));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(r.num_read(), 5u);
  }
}

TEST(SpanReaderTest, ReadFixed) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  {
    auto o = r.Read<2u>();
    static_assert(std::same_as<decltype(*o), span<const int, 2u>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(0u, 2u));
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    auto o = r.Read<5u>();
    static_assert(std::same_as<decltype(*o), span<const int, 5u>&>);
    EXPECT_FALSE(o.has_value());
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    auto o = r.Read<1u>();
    static_assert(std::same_as<decltype(*o), span<const int, 1u>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(2u, 1u));
    EXPECT_EQ(r.remaining(), 2u);
  }
  {
    auto o = r.Read<2u>();
    static_assert(std::same_as<decltype(*o), span<const int, 2u>&>);
    ASSERT_TRUE(o.has_value());
    EXPECT_TRUE(*o == span(kArray).subspan(3u, 2u));
    EXPECT_EQ(r.remaining(), 0u);
  }
}

TEST(SpanReaderTest, ReadInto) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  {
    span<const int> s;
    EXPECT_TRUE(r.ReadInto(2u, s));
    EXPECT_TRUE(s == span(kArray).subspan(0u, 2u));
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    span<const int> s;
    EXPECT_FALSE(r.ReadInto(5u, s));
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    span<const int> s;
    EXPECT_TRUE(r.ReadInto(1u, s));
    EXPECT_TRUE(s == span(kArray).subspan(2u, 1u));
    EXPECT_EQ(r.remaining(), 2u);
  }
  {
    span<const int> s;
    EXPECT_TRUE(r.ReadInto(2u, s));
    EXPECT_TRUE(s == span(kArray).subspan(3u, 2u));
    EXPECT_EQ(r.remaining(), 0u);
  }
}

TEST(SpanReaderTest, ReadCopy) {
  std::array<const int, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  {
    std::array<int, 2u> s;
    EXPECT_TRUE(r.ReadCopy(s));
    EXPECT_TRUE(s == span(kArray).subspan(0u, 2u));
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    std::array<int, 5u> s;
    EXPECT_FALSE(r.ReadCopy(s));
    EXPECT_EQ(r.remaining(), 3u);
  }
  {
    std::array<int, 1u> s;
    EXPECT_TRUE(r.ReadCopy(s));
    EXPECT_TRUE(s == span(kArray).subspan(2u, 1u));
    EXPECT_EQ(r.remaining(), 2u);
  }
  {
    std::array<int, 2u> s;
    EXPECT_TRUE(r.ReadCopy(s));
    EXPECT_TRUE(s == span(kArray).subspan(3u, 2u));
    EXPECT_EQ(r.remaining(), 0u);
  }
}

TEST(SpanReaderTest, ReadBigEndian_Unsigned) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    uint8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU8BigEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02u);
  }
  {
    uint16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU16BigEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0203u);
  }
  {
    uint32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU32BigEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x02030405u);
  }
  {
    uint64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU64BigEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0203040506070809llu);
  }
}

TEST(SpanReaderTest, ReadBigEndian_Signed) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    int8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI8BigEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02);
  }
  {
    int16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI16BigEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0203);
  }
  {
    int32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI32BigEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x02030405);
  }
  {
    int64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI64BigEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0203040506070809ll);
  }
}

TEST(SpanReaderTest, ReadLittleEndian_Unsigned) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    uint8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU8LittleEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02u);
  }
  {
    uint16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU16LittleEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0302u);
  }
  {
    uint32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU32LittleEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x05040302u);
  }
  {
    uint64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU64LittleEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0908070605040302llu);
  }
}

TEST(SpanReaderTest, ReadLittleEndian_Signed) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    int8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI8LittleEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02);
  }
  {
    int16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI16LittleEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0302);
  }
  {
    int32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI32LittleEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x05040302);
  }
  {
    int64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI64LittleEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0908070605040302ll);
  }
}

TEST(SpanReaderTest, ReadNativeEndian_Unsigned) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    uint8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU8NativeEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02u);
  }
  {
    uint16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU16NativeEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0302u);
  }
  {
    uint32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU32NativeEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x05040302u);
  }
  {
    uint64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadU64NativeEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0908070605040302llu);
  }
}

TEST(SpanReaderTest, ReadNativeEndian_Signed) {
  const std::array<uint8_t, 5u> kArray = {1, 2, 3, 4, 5};
  const std::array<uint8_t, 9u> kBigArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};

  {
    int8_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI8NativeEndian(val));
    EXPECT_EQ(r.remaining(), 3u);
    EXPECT_EQ(val, 0x02);
  }
  {
    int16_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI16NativeEndian(val));
    EXPECT_EQ(r.remaining(), 2u);
    EXPECT_EQ(val, 0x0302);
  }
  {
    int32_t val;

    auto r = SpanReader(span(kArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI32NativeEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x05040302);
  }
  {
    int64_t val;

    auto r = SpanReader(span(kBigArray));
    EXPECT_TRUE(r.Skip(1u));
    EXPECT_TRUE(r.ReadI64NativeEndian(val));
    EXPECT_EQ(r.remaining(), 0u);
    EXPECT_EQ(val, 0x0908070605040302ll);
  }
}

TEST(SpanReaderTest, ReadChar) {
  std::array<const uint8_t, 5u> kArray = {1, 2, 3, 4, 5};

  auto r = SpanReader(span(kArray));
  EXPECT_EQ(r.num_read(), 0u);

  char c;
  EXPECT_TRUE(r.ReadChar(c));
  EXPECT_EQ(c, char{1});
  EXPECT_TRUE(r.Skip(3u));
  EXPECT_TRUE(r.ReadChar(c));
  EXPECT_EQ(c, char{5});
  EXPECT_FALSE(r.ReadChar(c));
  EXPECT_EQ(c, char{5});
}

}  // namespace
}  // namespace base
