// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE

// included by json_value.cpp

namespace Json {

// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueInternalMap
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////

/** \internal MUST be safely initialized using memset( this, 0,
 * sizeof(ValueInternalLink) );
   * This optimization is used by the fast allocator.
   */
ValueInternalLink::ValueInternalLink() : previous_(0), next_(0) {}

ValueInternalLink::~ValueInternalLink() {
  for (int index = 0; index < itemPerLink; ++index) {
    if (!items_[index].isItemAvailable()) {
      if (!items_[index].isMemberNameStatic())
        free(keys_[index]);
    } else
      break;
  }
}

ValueMapAllocator::~ValueMapAllocator() {}

#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
class DefaultValueMapAllocator : public ValueMapAllocator {
public: // overridden from ValueMapAllocator
  virtual ValueInternalMap* newMap() { return new ValueInternalMap(); }

  virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) {
    return new ValueInternalMap(other);
  }

  virtual void destructMap(ValueInternalMap* map) { delete map; }

  virtual ValueInternalLink* allocateMapBuckets(unsigned int size) {
    return new ValueInternalLink[size];
  }

  virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; }

  virtual ValueInternalLink* allocateMapLink() {
    return new ValueInternalLink();
  }

  virtual void releaseMapLink(ValueInternalLink* link) { delete link; }
};
#else
/// @todo make this thread-safe (lock when accessign batch allocator)
class DefaultValueMapAllocator : public ValueMapAllocator {
public: // overridden from ValueMapAllocator
  virtual ValueInternalMap* newMap() {
    ValueInternalMap* map = mapsAllocator_.allocate();
    new (map) ValueInternalMap(); // placement new
    return map;
  }

  virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) {
    ValueInternalMap* map = mapsAllocator_.allocate();
    new (map) ValueInternalMap(other); // placement new
    return map;
  }

  virtual void destructMap(ValueInternalMap* map) {
    if (map) {
      map->~ValueInternalMap();
      mapsAllocator_.release(map);
    }
  }

  virtual ValueInternalLink* allocateMapBuckets(unsigned int size) {
    return new ValueInternalLink[size];
  }

  virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; }

  virtual ValueInternalLink* allocateMapLink() {
    ValueInternalLink* link = linksAllocator_.allocate();
    memset(link, 0, sizeof(ValueInternalLink));
    return link;
  }

  virtual void releaseMapLink(ValueInternalLink* link) {
    link->~ValueInternalLink();
    linksAllocator_.release(link);
  }

private:
  BatchAllocator<ValueInternalMap, 1> mapsAllocator_;
  BatchAllocator<ValueInternalLink, 1> linksAllocator_;
};
#endif

static ValueMapAllocator*& mapAllocator() {
  static DefaultValueMapAllocator defaultAllocator;
  static ValueMapAllocator* mapAllocator = &defaultAllocator;
  return mapAllocator;
}

static struct DummyMapAllocatorInitializer {
  DummyMapAllocatorInitializer() {
    mapAllocator(); // ensure mapAllocator() statics are initialized before
                    // main().
  }
} dummyMapAllocatorInitializer;

// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.

/*
use linked list hash map.
buckets array is a container.
linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
value have extra state: valid, available, deleted
*/

ValueInternalMap::ValueInternalMap()
    : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {}

ValueInternalMap::ValueInternalMap(const ValueInternalMap& other)
    : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {
  reserve(other.itemCount_);
  IteratorState it;
  IteratorState itEnd;
  other.makeBeginIterator(it);
  other.makeEndIterator(itEnd);
  for (; !equals(it, itEnd); increment(it)) {
    bool isStatic;
    const char* memberName = key(it, isStatic);
    const Value& aValue = value(it);
    resolveReference(memberName, isStatic) = aValue;
  }
}

ValueInternalMap& ValueInternalMap::operator=(ValueInternalMap other) {
  swap(other);
  return *this;
}

ValueInternalMap::~ValueInternalMap() {
  if (buckets_) {
    for (BucketIndex bucketIndex = 0; bucketIndex < bucketsSize_;
         ++bucketIndex) {
      ValueInternalLink* link = buckets_[bucketIndex].next_;
      while (link) {
        ValueInternalLink* linkToRelease = link;
        link = link->next_;
        mapAllocator()->releaseMapLink(linkToRelease);
      }
    }
    mapAllocator()->releaseMapBuckets(buckets_);
  }
}

void ValueInternalMap::swap(ValueInternalMap& other) {
  ValueInternalLink* tempBuckets = buckets_;
  buckets_ = other.buckets_;
  other.buckets_ = tempBuckets;
  ValueInternalLink* tempTailLink = tailLink_;
  tailLink_ = other.tailLink_;
  other.tailLink_ = tempTailLink;
  BucketIndex tempBucketsSize = bucketsSize_;
  bucketsSize_ = other.bucketsSize_;
  other.bucketsSize_ = tempBucketsSize;
  BucketIndex tempItemCount = itemCount_;
  itemCount_ = other.itemCount_;
  other.itemCount_ = tempItemCount;
}

void ValueInternalMap::clear() {
  ValueInternalMap dummy;
  swap(dummy);
}

ValueInternalMap::BucketIndex ValueInternalMap::size() const {
  return itemCount_;
}

bool ValueInternalMap::reserveDelta(BucketIndex growth) {
  return reserve(itemCount_ + growth);
}

bool ValueInternalMap::reserve(BucketIndex newItemCount) {
  if (!buckets_ && newItemCount > 0) {
    buckets_ = mapAllocator()->allocateMapBuckets(1);
    bucketsSize_ = 1;
    tailLink_ = &buckets_[0];
  }
  //   BucketIndex idealBucketCount = (newItemCount +
  // ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
  return true;
}

const Value* ValueInternalMap::find(const char* key) const {
  if (!bucketsSize_)
    return 0;
  HashKey hashedKey = hash(key);
  BucketIndex bucketIndex = hashedKey % bucketsSize_;
  for (const ValueInternalLink* current = &buckets_[bucketIndex]; current != 0;
       current = current->next_) {
    for (BucketIndex index = 0; index < ValueInternalLink::itemPerLink;
         ++index) {
      if (current->items_[index].isItemAvailable())
        return 0;
      if (strcmp(key, current->keys_[index]) == 0)
        return &current->items_[index];
    }
  }
  return 0;
}

Value* ValueInternalMap::find(const char* key) {
  const ValueInternalMap* constThis = this;
  return const_cast<Value*>(constThis->find(key));
}

Value& ValueInternalMap::resolveReference(const char* key, bool isStatic) {
  HashKey hashedKey = hash(key);
  if (bucketsSize_) {
    BucketIndex bucketIndex = hashedKey % bucketsSize_;
    ValueInternalLink** previous = 0;
    BucketIndex index;
    for (ValueInternalLink* current = &buckets_[bucketIndex]; current != 0;
         previous = &current->next_, current = current->next_) {
      for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
        if (current->items_[index].isItemAvailable())
          return setNewItem(key, isStatic, current, index);
        if (strcmp(key, current->keys_[index]) == 0)
          return current->items_[index];
      }
    }
  }

  reserveDelta(1);
  return unsafeAdd(key, isStatic, hashedKey);
}

void ValueInternalMap::remove(const char* key) {
  HashKey hashedKey = hash(key);
  if (!bucketsSize_)
    return;
  BucketIndex bucketIndex = hashedKey % bucketsSize_;
  for (ValueInternalLink* link = &buckets_[bucketIndex]; link != 0;
       link = link->next_) {
    BucketIndex index;
    for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
      if (link->items_[index].isItemAvailable())
        return;
      if (strcmp(key, link->keys_[index]) == 0) {
        doActualRemove(link, index, bucketIndex);
        return;
      }
    }
  }
}

void ValueInternalMap::doActualRemove(ValueInternalLink* link,
                                      BucketIndex index,
                                      BucketIndex bucketIndex) {
  // find last item of the bucket and swap it with the 'removed' one.
  // set removed items flags to 'available'.
  // if last page only contains 'available' items, then desallocate it (it's
  // empty)
  ValueInternalLink*& lastLink = getLastLinkInBucket(index);
  BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
  for (; lastItemIndex < ValueInternalLink::itemPerLink;
       ++lastItemIndex) // may be optimized with dicotomic search
  {
    if (lastLink->items_[lastItemIndex].isItemAvailable())
      break;
  }

  BucketIndex lastUsedIndex = lastItemIndex - 1;
  Value* valueToDelete = &link->items_[index];
  Value* valueToPreserve = &lastLink->items_[lastUsedIndex];
  if (valueToDelete != valueToPreserve)
    valueToDelete->swap(*valueToPreserve);
  if (lastUsedIndex == 0) // page is now empty
  {                       // remove it from bucket linked list and delete it.
    ValueInternalLink* linkPreviousToLast = lastLink->previous_;
    if (linkPreviousToLast != 0) // can not deleted bucket link.
    {
      mapAllocator()->releaseMapLink(lastLink);
      linkPreviousToLast->next_ = 0;
      lastLink = linkPreviousToLast;
    }
  } else {
    Value dummy;
    valueToPreserve->swap(dummy); // restore deleted to default Value.
    valueToPreserve->setItemUsed(false);
  }
  --itemCount_;
}

ValueInternalLink*&
ValueInternalMap::getLastLinkInBucket(BucketIndex bucketIndex) {
  if (bucketIndex == bucketsSize_ - 1)
    return tailLink_;
  ValueInternalLink*& previous = buckets_[bucketIndex + 1].previous_;
  if (!previous)
    previous = &buckets_[bucketIndex];
  return previous;
}

Value& ValueInternalMap::setNewItem(const char* key,
                                    bool isStatic,
                                    ValueInternalLink* link,
                                    BucketIndex index) {
  char* duplicatedKey = makeMemberName(key);
  ++itemCount_;
  link->keys_[index] = duplicatedKey;
  link->items_[index].setItemUsed();
  link->items_[index].setMemberNameIsStatic(isStatic);
  return link->items_[index]; // items already default constructed.
}

Value&
ValueInternalMap::unsafeAdd(const char* key, bool isStatic, HashKey hashedKey) {
  JSON_ASSERT_MESSAGE(bucketsSize_ > 0,
                      "ValueInternalMap::unsafeAdd(): internal logic error.");
  BucketIndex bucketIndex = hashedKey % bucketsSize_;
  ValueInternalLink*& previousLink = getLastLinkInBucket(bucketIndex);
  ValueInternalLink* link = previousLink;
  BucketIndex index;
  for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
    if (link->items_[index].isItemAvailable())
      break;
  }
  if (index == ValueInternalLink::itemPerLink) // need to add a new page
  {
    ValueInternalLink* newLink = mapAllocator()->allocateMapLink();
    index = 0;
    link->next_ = newLink;
    previousLink = newLink;
    link = newLink;
  }
  return setNewItem(key, isStatic, link, index);
}

ValueInternalMap::HashKey ValueInternalMap::hash(const char* key) const {
  HashKey hash = 0;
  while (*key)
    hash += *key++ * 37;
  return hash;
}

int ValueInternalMap::compare(const ValueInternalMap& other) const {
  int sizeDiff(itemCount_ - other.itemCount_);
  if (sizeDiff != 0)
    return sizeDiff;
  // Strict order guaranty is required. Compare all keys FIRST, then compare
  // values.
  IteratorState it;
  IteratorState itEnd;
  makeBeginIterator(it);
  makeEndIterator(itEnd);
  for (; !equals(it, itEnd); increment(it)) {
    if (!other.find(key(it)))
      return 1;
  }

  // All keys are equals, let's compare values
  makeBeginIterator(it);
  for (; !equals(it, itEnd); increment(it)) {
    const Value* otherValue = other.find(key(it));
    int valueDiff = value(it).compare(*otherValue);
    if (valueDiff != 0)
      return valueDiff;
  }
  return 0;
}

void ValueInternalMap::makeBeginIterator(IteratorState& it) const {
  it.map_ = const_cast<ValueInternalMap*>(this);
  it.bucketIndex_ = 0;
  it.itemIndex_ = 0;
  it.link_ = buckets_;
}

void ValueInternalMap::makeEndIterator(IteratorState& it) const {
  it.map_ = const_cast<ValueInternalMap*>(this);
  it.bucketIndex_ = bucketsSize_;
  it.itemIndex_ = 0;
  it.link_ = 0;
}

bool ValueInternalMap::equals(const IteratorState& x,
                              const IteratorState& other) {
  return x.map_ == other.map_ && x.bucketIndex_ == other.bucketIndex_ &&
         x.link_ == other.link_ && x.itemIndex_ == other.itemIndex_;
}

void ValueInternalMap::incrementBucket(IteratorState& iterator) {
  ++iterator.bucketIndex_;
  JSON_ASSERT_MESSAGE(
      iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
      "ValueInternalMap::increment(): attempting to iterate beyond end.");
  if (iterator.bucketIndex_ == iterator.map_->bucketsSize_)
    iterator.link_ = 0;
  else
    iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
  iterator.itemIndex_ = 0;
}

void ValueInternalMap::increment(IteratorState& iterator) {
  JSON_ASSERT_MESSAGE(iterator.map_,
                      "Attempting to iterator using invalid iterator.");
  ++iterator.itemIndex_;
  if (iterator.itemIndex_ == ValueInternalLink::itemPerLink) {
    JSON_ASSERT_MESSAGE(
        iterator.link_ != 0,
        "ValueInternalMap::increment(): attempting to iterate beyond end.");
    iterator.link_ = iterator.link_->next_;
    if (iterator.link_ == 0)
      incrementBucket(iterator);
  } else if (iterator.link_->items_[iterator.itemIndex_].isItemAvailable()) {
    incrementBucket(iterator);
  }
}

void ValueInternalMap::decrement(IteratorState& iterator) {
  if (iterator.itemIndex_ == 0) {
    JSON_ASSERT_MESSAGE(iterator.map_,
                        "Attempting to iterate using invalid iterator.");
    if (iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_]) {
      JSON_ASSERT_MESSAGE(iterator.bucketIndex_ > 0,
                          "Attempting to iterate beyond beginning.");
      --(iterator.bucketIndex_);
    }
    iterator.link_ = iterator.link_->previous_;
    iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
  }
}

const char* ValueInternalMap::key(const IteratorState& iterator) {
  JSON_ASSERT_MESSAGE(iterator.link_,
                      "Attempting to iterate using invalid iterator.");
  return iterator.link_->keys_[iterator.itemIndex_];
}

const char* ValueInternalMap::key(const IteratorState& iterator,
                                  bool& isStatic) {
  JSON_ASSERT_MESSAGE(iterator.link_,
                      "Attempting to iterate using invalid iterator.");
  isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
  return iterator.link_->keys_[iterator.itemIndex_];
}

Value& ValueInternalMap::value(const IteratorState& iterator) {
  JSON_ASSERT_MESSAGE(iterator.link_,
                      "Attempting to iterate using invalid iterator.");
  return iterator.link_->items_[iterator.itemIndex_];
}

int ValueInternalMap::distance(const IteratorState& x, const IteratorState& y) {
  int offset = 0;
  IteratorState it = x;
  while (!equals(it, y))
    increment(it);
  return offset;
}

} // namespace Json
