/* Copyright (C) 2021-2026 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

/*
 *	String Map implementation.
 */

#ifndef _DBE_STRINGMAP_H
#define _DBE_STRINGMAP_H

#include <assert.h>
#include <vec.h>
#include <Map.h>
#include <util.h>

template <typename Value_t>
class StringMap : public Map<const char*, Value_t>
{
public:

  StringMap (int htable_size = 1024, int chunk_size = 16384);
  ~StringMap ();
  void clear ();
  void put (const char *key, Value_t val);
  Value_t get (const char *key);
  Value_t get (const char *key, typename Map<const char*, Value_t>::Relation rel);
  Value_t remove (const char*);
  Vector<const char*> *keySet ();
  Vector<Value_t> *values ();

private:

  static unsigned
  hash (const char *key)
  {
    return (unsigned) crc64 (key, strlen (key));
  }

  struct Entry
  {
    char *key;
    Value_t val;
  };

  int CHUNK_SIZE, HTABLE_SIZE;
  int entries;
  int nchunks;
  Entry **chunks;
  Vector<Entry*> *index;
  Entry **hashTable;
};

template <typename Value_t>
StringMap<Value_t>::StringMap (int htable_size, int chunk_size)
{
  HTABLE_SIZE = htable_size;
  CHUNK_SIZE = chunk_size;
  entries = 0;
  nchunks = 0;
  chunks = NULL;
  index = new Vector<Entry*>;
  hashTable = new Entry*[HTABLE_SIZE];
  for (int i = 0; i < HTABLE_SIZE; i++)
    hashTable[i] = NULL;
}

template <typename Value_t>
StringMap<Value_t>::~StringMap ()
{
  for (int i = 0; i < entries; ++i)
    {
      Entry *entry = index->fetch (i);
      free (entry->key);
    }
  for (int i = 0; i < nchunks; i++)
    delete[] chunks[i];
  delete[] chunks;
  delete index;
  delete[] hashTable;
}

template <typename Value_t>
void
StringMap<Value_t>::clear ()
{
  for (int i = 0; i < entries; ++i)
    {
      Entry *entry = index->fetch (i);
      free (entry->key);
    }
  entries = 0;
  index->reset ();
  for (int i = 0; i < HTABLE_SIZE; i++)
    hashTable[i] = NULL;
}

template <typename Value_t>
void
StringMap<Value_t>::put (const char *key, Value_t val)
{
  unsigned idx = hash (key) % HTABLE_SIZE;
  Entry *entry = hashTable[idx];
  if (entry && strcmp (entry->key, key) == 0)
    {
      entry->val = val;
      return;
    }
  int lo = 0;
  int hi = entries - 1;
  while (lo <= hi)
    {
      int md = (lo + hi) / 2;
      entry = index->fetch (md);
      int cmp = strcmp (entry->key, key);
      if (cmp < 0)
	lo = md + 1;
      else if (cmp > 0)
	hi = md - 1;
      else
	{
	  entry->val = val;
	  return;
	}
    }
  if (entries >= nchunks * CHUNK_SIZE)
    {
      nchunks++;

      // Reallocate Entry chunk array
      Entry **new_chunks = new Entry*[nchunks];
      for (int i = 0; i < nchunks - 1; i++)
	new_chunks[i] = chunks[i];
      delete[] chunks;
      chunks = new_chunks;

      // Allocate new chunk for entries.
      chunks[nchunks - 1] = new Entry[CHUNK_SIZE];
    }
  entry = &chunks[entries / CHUNK_SIZE][entries % CHUNK_SIZE];
  entry->key = xstrdup (key);
  entry->val = val;
  index->insert (lo, entry);
  hashTable[idx] = entry;
  entries++;
}

template <typename Value_t>
Value_t
StringMap<Value_t>::get (const char *key)
{
  unsigned idx = hash (key) % HTABLE_SIZE;
  Entry *entry = hashTable[idx];
  if (entry && strcmp (entry->key, key) == 0)
    return entry->val;
  int lo = 0;
  int hi = entries - 1;
  while (lo <= hi)
    {
      int md = (lo + hi) / 2;
      entry = index->fetch (md);
      int cmp = strcmp (entry->key, key);
      if (cmp < 0)
	lo = md + 1;
      else if (cmp > 0)
	hi = md - 1;
      else
	{
	  hashTable[idx] = entry;
	  return entry->val;
	}
    }
  return (Value_t) 0;
}

template <typename Value_t>
Value_t
StringMap<Value_t>::get (const char *key, typename Map<const char*,
			 Value_t>::Relation rel)
{
  if (rel != Map<const char*, Value_t>::REL_EQ)
    return (Value_t) 0;
  return get (key);
}

template <typename Value_t>
Value_t
StringMap<Value_t>::remove (const char*)
{
  // Not implemented
  if (1)
    assert (0);
  return (Value_t) 0;
}

template <typename Value_t>
Vector<Value_t> *
StringMap<Value_t>::values ()
{
  Vector<Value_t> *vals = new Vector<Value_t>(entries);
  for (int i = 0; i < entries; ++i)
    {
      Entry *entry = index->fetch (i);
      vals->append (entry->val);
    }
  return vals;
}

template <typename Value_t>
Vector<const char*> *
StringMap<Value_t>::keySet ()
{
  Vector<const char*> *keys = new Vector<const char*>(entries);
  for (int i = 0; i < entries; ++i)
    {
      Entry *entry = index->fetch (i);
      keys->append (entry->key);
    }
  return keys;
}

#endif
