/*
 * %CopyrightBegin%
 * 
 * Copyright Ericsson AB 1996-2012. All Rights Reserved.
 * 
 * The contents of this file are subject to the Erlang Public License,
 * Version 1.1, (the "License"); you may not use this file except in
 * compliance with the License. You should have received a copy of the
 * Erlang Public License along with this software. If not, it can be
 * retrieved online at http://www.erlang.org/.
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * %CopyrightEnd%
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "sys.h"
#include "erl_vm.h"
#include "global.h"
#include "index.h"

void index_info(int to, void *arg, IndexTable *t)
{
    hash_info(to, arg, &t->htable);
    erts_print(to, arg, "=index_table:%s\n", t->htable.name);
    erts_print(to, arg, "size: %d\n",	t->size);
    erts_print(to, arg, "limit: %d\n",	t->limit);
    erts_print(to, arg, "entries: %d\n",t->entries);
}


/*
 * Returns size of table in bytes. Stored objects not included.
 */
int 
index_table_sz(IndexTable *t)
{
  return (sizeof(IndexTable)
          - sizeof(Hash)
          + t->size*sizeof(IndexSlot*)
          +  hash_table_sz(&(t->htable)));
}


/*
** init a pre allocated or static hash structure
** and allocate buckets.
*/
IndexTable*
erts_index_init(ErtsAlcType_t type, IndexTable* t, char* name,
		int size, int limit, HashFunctions fun)
{
    Uint base_size = ((limit+INDEX_PAGE_SIZE-1)/INDEX_PAGE_SIZE)*sizeof(IndexSlot*);
    hash_init(type, &t->htable, name, 3*size/4, fun);

    t->size = 0;
    t->limit = limit;
    t->entries = 0;
    t->type = type;
    t->seg_table = (IndexSlot***) erts_alloc(type, base_size);
    return t;
}

IndexSlot*
index_put_entry(IndexTable* t, void* tmpl)
{
    int ix;
    IndexSlot* p = (IndexSlot*) hash_put(&t->htable, tmpl);

    if (p->index >= 0) {
	return p;
    }

    ix = t->entries;
    if (ix >= t->size) {
	Uint sz;
	if (ix >= t->limit) {
	    /* A core dump is unnecessary */
	    erl_exit(ERTS_DUMP_EXIT, "no more index entries in %s (max=%d)\n",
		     t->htable.name, t->limit);
	}
	sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
	t->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(t->type, sz);
	t->size += INDEX_PAGE_SIZE;
    }
    t->entries++;
    p->index = ix;
    t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
    return p;
}

int index_get(IndexTable* t, void* tmpl)
{
    IndexSlot* p = (IndexSlot*) hash_get(&t->htable, tmpl);

    if (p != NULL) {
	return p->index;
    }
    return -1;
}

void erts_index_merge(Hash* src, IndexTable* dst)
{
    int limit = src->size;
    HashBucket** bucket = src->bucket;
    int i;

    for (i = 0; i < limit; i++) {
	HashBucket* b = bucket[i];
	IndexSlot* p;
	int ix;

	while (b) {
	    Uint sz;
	    ix = dst->entries++;
	    if (ix >= dst->size) {
		if (ix >= dst->limit) {
		    erl_exit(1, "no more index entries in %s (max=%d)\n",
			     dst->htable.name, dst->limit);
		}
		sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
		dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
		dst->size += INDEX_PAGE_SIZE;
	    }
	    p = (IndexSlot*) b;
	    p->index = ix;
	    dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
	    b = b->next;
	}
    }
}

void index_erase_latest_from(IndexTable* t, Uint from_ix)
{
    if(from_ix < (Uint)t->entries) {
	int ix;
	for (ix = from_ix; ix < t->entries; ix++)  {
	    IndexSlot* obj = t->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK];
	    hash_erase(&t->htable, obj);
	}
	t->entries = from_ix;
    }
    else {
	ASSERT(from_ix == t->entries);
    }
}