//===- swift/unittests/runtime/Enum.cpp - Enum tests --------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/Runtime/Metadata.h"
#include "swift/Runtime/Enum.h"
#include "gtest/gtest.h"

using namespace swift;

// Mock up a value witness table for Builtin.Int8 will 254 and 255 as extra
// inhabitants.
ExtraInhabitantsValueWitnessTable Int8WithExtraInhabitantValueWitness
= {
  // ValueWitnessTable
  ValueWitnessTable{
#define STEAL_INT8_WITNESS(witness) _TWVBi8_.witness,
    FOR_ALL_FUNCTION_VALUE_WITNESSES(STEAL_INT8_WITNESS)
#undef STEAL_INT8_WITNESS
    _TWVBi8_.size,
    _TWVBi8_.flags.withExtraInhabitants(true),
    _TWVBi8_.stride
  },
  // extraInhabitantFlags
  ExtraInhabitantFlags().withNumExtraInhabitants(2),
  // storeExtraInhabitant
  [](OpaqueValue *dest, int index, const Metadata *self) {
    *reinterpret_cast<uint8_t*>(dest) = 254 + index;
  },
  // getExtraInhabitantIndex
  [](const OpaqueValue *src, const Metadata *self) -> int {
    uint8_t byte = *reinterpret_cast<const uint8_t*>(src);
    if (byte >= 254)
      return byte - 254;
    return -1;
  }
};

FullMetadata<OpaqueMetadata> XI_TMBi8_ = {
  {&Int8WithExtraInhabitantValueWitness},
  {{MetadataKind::Opaque}}
};

const OpaqueValue *asOpaque(const void *v) {
  return reinterpret_cast<const OpaqueValue*>(v);
}
OpaqueValue *asOpaque(void *v) {
  return reinterpret_cast<OpaqueValue*>(v);
}

int test_getEnumCaseSinglePayload(std::initializer_list<uint8_t> repr,
                                   const FullOpaqueMetadata &metadata,
                                   unsigned numEmptyCases) {
  return swift_getEnumCaseSinglePayload(asOpaque(repr.begin()),
                                         &metadata.base, numEmptyCases);
}

TEST(EnumTest, getEnumCaseSinglePayload) {
  // Test with no XI.
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({0, 0}, _TMBi8_, 512));
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({255, 0}, _TMBi8_, 512));

  ASSERT_EQ(0, test_getEnumCaseSinglePayload({0, 1}, _TMBi8_, 512));
  ASSERT_EQ(255, test_getEnumCaseSinglePayload({255, 1}, _TMBi8_, 512));
  ASSERT_EQ(511, test_getEnumCaseSinglePayload({255, 2}, _TMBi8_, 512));

  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({0, 0, 0}, _TMBi8_,
                                               128*1024));
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({255, 0, 0}, _TMBi8_,
                                               128*1024));
  ASSERT_EQ(65535 - 255,
            test_getEnumCaseSinglePayload({0, 0, 1}, _TMBi8_,
                                           128*1024));

  // Test with XI.
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({0}, XI_TMBi8_, 2));
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({253}, XI_TMBi8_, 2));
  ASSERT_EQ(0, test_getEnumCaseSinglePayload({254}, XI_TMBi8_, 2));
  ASSERT_EQ(1, test_getEnumCaseSinglePayload({255}, XI_TMBi8_, 2));

  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({0, 0}, XI_TMBi8_, 4));
  ASSERT_EQ(-1, test_getEnumCaseSinglePayload({253, 0}, XI_TMBi8_, 4));
  ASSERT_EQ(0, test_getEnumCaseSinglePayload({254, 0}, XI_TMBi8_, 4));
  ASSERT_EQ(1, test_getEnumCaseSinglePayload({255, 0}, XI_TMBi8_, 4));
  ASSERT_EQ(2, test_getEnumCaseSinglePayload({0, 1}, XI_TMBi8_, 4));
  ASSERT_EQ(3, test_getEnumCaseSinglePayload({1, 1}, XI_TMBi8_, 4));
}

bool test_storeEnumTagSinglePayload(std::initializer_list<uint8_t> after,
                                     std::initializer_list<uint8_t> before,
                                     const FullOpaqueMetadata &metadata,
                                     unsigned whichCase,
                                     unsigned numEmptyCases) {
  assert(after.size() == before.size());

  std::vector<uint8_t> buf;
  buf.resize(before.size());
  memcpy(buf.data(), before.begin(), before.size());

  swift_storeEnumTagSinglePayload(asOpaque(buf.data()),
                                   &metadata.base,
                                   whichCase,
                                   numEmptyCases);

  return memcmp(buf.data(), after.begin(), after.size()) == 0;
}

TEST(EnumTest, storeEnumTagSinglePayload) {
  // Test with no XI.
  ASSERT_TRUE(test_storeEnumTagSinglePayload({219, 0}, {219, 123},
                                              _TMBi8_, -1, 512));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({0, 1}, {219, 123},
                                              _TMBi8_, 0, 512));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255, 1}, {219, 123},
                                              _TMBi8_, 255, 512));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255, 2}, {219, 123},
                                              _TMBi8_, 511, 512));

  ASSERT_TRUE(test_storeEnumTagSinglePayload({219, 0, 0}, {219, 123, 77},
                                              _TMBi8_, -1, 128*1024));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({0, 1, 0}, {219, 123, 77},
                                              _TMBi8_, 0, 128*1024));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255, 1, 0}, {219, 123, 77},
                                              _TMBi8_, 255, 128*1024));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({0, 2, 0}, {219, 123, 77},
                                              _TMBi8_, 256, 128*1024));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255, 0, 2}, {219, 123, 77},
                                              _TMBi8_, 128*1024 - 1, 128*1024));

  // Test with XI.
  ASSERT_TRUE(test_storeEnumTagSinglePayload({219}, {219},
                                              XI_TMBi8_, -1, 2));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({254}, {219},
                                              XI_TMBi8_, 0, 2));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255}, {219},
                                              XI_TMBi8_, 1, 2));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({219, 0}, {219, 123},
                                              XI_TMBi8_, -1, 4));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({254, 0}, {219, 123},
                                              XI_TMBi8_, 0, 4));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({255, 0}, {219, 123},
                                              XI_TMBi8_, 1, 4));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({0, 1}, {219, 123},
                                              XI_TMBi8_, 2, 4));
  ASSERT_TRUE(test_storeEnumTagSinglePayload({1, 1}, {219, 123},
                                              XI_TMBi8_, 3, 4));
}
