//===- StringUnorderedMap.h -----------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef MCLD_SEARCH_TABLE_H
#define MCLD_SEARCH_TABLE_H
#include <vector>
// For std::allocate.
#include <memory>
// For uint32_t.
#include <stdint.h>
#include <cassert>
// For memset.
#include <cstring>
#ifdef ENABLE_UNITTEST
#include <gtest.h>
#endif
/* FIXME: Move StringUnorderedMap under ADT. */

namespace mcld
{

struct StringUnorderedMapDefaultHash
{
  size_t operator()(const char *pStr) {
    size_t hashVal = 31;
    while (*pStr)
      hashVal = hashVal * 131 + (*pStr++);
    return hashVal;
  }
};

template<typename ValueType,
         typename KeyType>
struct StringUnorderedMapEntryInit
{
  template <typename InitType>
  void operator()(KeyType &pKey, ValueType &pValue,
                  const KeyType &pStr, InitType pInitVal) {
    ::new ((void*)&pKey) KeyStorageType(pStr);
    ::new ((void*)&pValue) ValueType(pInitVal);
  }
};

uint32_t findNextPrime(uint32_t x);

/** \class StringUnorderedMap
 *  \brief The most simple hash of linked list version.
 *
 *  \see
 */
template<typename KeyType,
         typename ValueType,
         typename KeyCompareFunctor,
         typename HashFunction = StringUnorderedMapDefaultHash,
         typename Allocator = std::allocator<std::pair<KeyType, ValueType> > >
class StringUnorderedMap
{
public:
  explicit StringUnorderedMap(size_t pCapacity = 17)
  : m_Impl(pCapacity)
  {}

  ~StringUnorderedMap();

  void reserve(size_t pCapacity);

  ValueType &getOrCreate(const KeyType &pStr, const ValueType &pInitVal);

  ValueType &getOrCreate(const KeyType &pStr);

  bool empty()
  { return m_Size == 0; }

  size_t capacity() const
  { return m_Capacity; }

  void clear();

private:
  struct HashEntry {
    size_t hashVal;
    std::pair<KeyType, ValueType>
    HashEntry *next;
  };
  typedef Allocator::template rebind<HashEntry>::other HashEntryAllocator;
  void rehash(size_t pCapacity);

private:
  size_t m_Capacity;
  size_t m_Size;
  // array of pointers to hash entries
  HashEntry **m_HashTable;
  HashEntryAllocator m_HashEntryAllocator;
};


// =================================implementation============================
// StringUnorderedMap::StringUnorderedMapImpl
template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::StringUnorderedMapImpl(size_t pCapacity)
  : m_Capacity(0), m_Size(0), m_HashTable(0)
{
  this->reserve(pCapacity);
}

template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
void
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::reserve(size_t pCapacity)
{
  if (pCapacity < this->m_Capacity)
    return;
  size_t nextSize = findNextPrime(static_cast<uint32_t>(pCapacity));
  // FIXME: Error handling.
  assert(nextSize > this->m_Capacity && "findNextPrime error.");
  if (this->m_Capacity != nextSize)
    this->rehash(nextSize);
}

template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
void
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::rehash(size_t pCapacity)
{
  HashEntry **tmpTable = new HashEntry*[pCapacity];
  std::memset(tmpTable, 0, pCapacity * sizeof(HashEntry*));
  if (this->m_HashTable) {
    for (size_t i = 0; i < this->m_Capacity; ++i)
      for (HashEntry *j = this->m_HashTable[i]; j != 0; ) {
        HashEntry *nextJ = j->next;
        j->next = tmpTable[j->hashVal % pCapacity];
        tmpTable[j->hashVal % pCapacity] = j;
        j = nextJ;
      }
    delete[] m_HashTable;
  }
  this->m_Capacity = pCapacity;
  this->m_HashTable = tmpTable;
}

template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
template<typename InitType>
ValueType &
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::getOrCreate(const KeyType &pStr, ValueType &pInitVal)
{
  HashFunction hash;
  size_t hashVal = hash(pStr);
  HashEntry *&head =  this->m_HashTable[hashVal % this->m_Capacity];

  HashEntry *ans = 0;
  for(HashEntry *ptr = head; ptr != 0; ptr = ptr->next)
    if(hashVal == ptr->hashVal && pStr.equals(ptr->str)) {
      ans = ptr;
      break;
    }
  if (ans == 0) {
    ans = this->allocate(1);
    ans->hashVal = hashVal;
    StringUnorderedMapEntryInit<ValueType, KeyStorageType> init;
    init(ans->str, ans->value, pStr, pInitVal);
    ans->next = head;
    head = ans;
    ++m_Size;
    if(this->m_Size * 4LL >= this->m_Capacity * 3LL) // load factor = 0.75
      this->reserve(this->m_Capacity+1);
  }

  return ans->value;
}

template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
void
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::clear()
{
  if (this->m_HashTable) {
    for (size_t i = 0; i < this->m_Capacity; ++i)
      for (HashEntry *j = this->m_HashTable[i]; j != 0; ) {
        HashEntry *nextJ = j->next;
        this->destroy(j);
        this->deallocate(j, 1);
        j = nextJ;
      }
    delete[] m_HashTable;
  }
}


template<typename ValueType,
         typename KeyStorageType,
         typename HashFunction,
         typename Allocator>
StringUnorderedMap<ValueType, KeyStorageType, HashFunction, Allocator>::
StringUnorderedMapImpl::~StringUnorderedMapImpl()
{
  this->clear();
}


} // namespace of mcld

#endif