//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef ATOMIC_HELPERS_H
#define ATOMIC_HELPERS_H

#include <cassert>
#include <cstdint>

#include "test_macros.h"

struct UserAtomicType {
  int i;

  explicit UserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

  friend bool operator==(const UserAtomicType& x, const UserAtomicType& y) { return x.i == y.i; }
};

/*

Enable these once we have P0528

struct WeirdUserAtomicType
{
    char i, j, k; // the 3 chars of doom

    explicit WeirdUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

    friend bool operator==(const WeirdUserAtomicType& x, const WeirdUserAtomicType& y)
    { return x.i == y.i; }
};

struct PaddedUserAtomicType
{
    char i; int j; // probably lock-free?

    explicit PaddedUserAtomicType(int d = 0) TEST_NOEXCEPT : i(d) {}

    friend bool operator==(const PaddedUserAtomicType& x, const PaddedUserAtomicType& y)
    { return x.i == y.i; }
};

*/

struct LargeUserAtomicType {
  int a[128]; /* decidedly not lock-free */

  LargeUserAtomicType(int d = 0) TEST_NOEXCEPT {
    for (auto&& e : a)
      e = d++;
  }

  friend bool operator==(LargeUserAtomicType const& x, LargeUserAtomicType const& y) TEST_NOEXCEPT {
    for (int i = 0; i < 128; ++i)
      if (x.a[i] != y.a[i])
        return false;
    return true;
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachIntegralType {
  void operator()() const {
    TestFunctor<char>()();
    TestFunctor<signed char>()();
    TestFunctor<unsigned char>()();
    TestFunctor<short>()();
    TestFunctor<unsigned short>()();
    TestFunctor<int>()();
    TestFunctor<unsigned int>()();
    TestFunctor<long>()();
    TestFunctor<unsigned long>()();
    TestFunctor<long long>()();
    TestFunctor<unsigned long long>()();
    TestFunctor<wchar_t>()();
#if TEST_STD_VER > 17 && defined(__cpp_char8_t)
    TestFunctor<char8_t>()();
#endif
    TestFunctor<char16_t>()();
    TestFunctor<char32_t>()();
    TestFunctor<std::int8_t>()();
    TestFunctor<std::uint8_t>()();
    TestFunctor<std::int16_t>()();
    TestFunctor<std::uint16_t>()();
    TestFunctor<std::int32_t>()();
    TestFunctor<std::uint32_t>()();
    TestFunctor<std::int64_t>()();
    TestFunctor<std::uint64_t>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachFloatingPointType {
  void operator()() const {
    TestFunctor<float>()();
    TestFunctor<double>()();
    TestFunctor<long double>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachPointerType {
  void operator()() const {
    TestFunctor<int*>()();
    TestFunctor<const int*>()();
  }
};

template <template <class TestArg> class TestFunctor>
struct TestEachAtomicType {
  void operator()() const {
    TestEachIntegralType<TestFunctor>()();
    TestFunctor<UserAtomicType>()();
    /*
            Note: These aren't going to be lock-free,
            so some libatomic.a is necessary.
        */
    TestFunctor<LargeUserAtomicType>()();
    /*
    Enable these once we have P0528

        TestFunctor<PaddedUserAtomicType>()();
        TestFunctor<WeirdUserAtomicType>()();
*/
    TestFunctor<int*>()();
    TestFunctor<const int*>()();
    TestFunctor<float>()();
    TestFunctor<double>()();
  }
};

#endif // ATOMIC_HELPERS_H
