/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * 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.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header files */
#include <stdio.h>
#include <string.h>
#include "js-gcp.h"
#if 0
#include "vxmalloc.h"
#include "ewb-dbg.h"
#else
#include "js-types.h"
#include "js-dbg.h"
extern "C" {
	extern int js_get_now_used();
}
#endif
/* Data */ uint16_t JSGCMemory::size_map[MAX_OBJ_SIZE + 1];
#ifdef BEST_FIT
uint8_t JSGCMemory::size_sort_map[(1 << (SORT_INDEX_SIZE - 1)) + 1];
#endif

/* Procedures */
JSGCMemory::BOOL JSGCMemory::small_obj(size_t size)
{
	return size <= MAX_OBJ_SIZE;
}

int JSGCMemory::size_to_blocks(size_t size)
{
	return (Roundup(size, HBLKSIZE) / HBLKSIZE);
}

int JSGCMemory::size_to_freelist_index(size_t size)
{
	return (size - 1) / ALIGN_BYTES;
}

void JSGCMemory::clear_marks(hblk_hdr_t * hhdr)
{
	/* Clear mark bits */
	if (small_obj(hhdr->hblk_size)) {
		/* clear needed part */
		int n = Roundup(HBLKSIZE / hhdr->hblk_size, BITS_PER_UINT32)
				/ BITS_PER_UINT32;

		memset(hhdr->marks, 0, sizeof(uint32_t) * n);
	}
	else {
		hhdr->marks[0] = 0;
	}
}
void JSGCMemory::clear_finalizes(hblk_hdr_t * hhdr)
{
	/* Clear finalize bits */
	if (small_obj(hhdr->hblk_size)) {
		/* clear needed part */
		int n = Roundup(HBLKSIZE / hhdr->hblk_size, BITS_PER_UINT32)
				/ BITS_PER_UINT32;

		memset(hhdr->finalizes, 0, sizeof(uint32_t) * n);
	}
	else {
		hhdr->finalizes[0] = 0;
	}
}
void JSGCMemory::setup_header(hblk_hdr_t * hhdr, size_t size, int kind)
{
	/* Set size, kind and mark proc fields */
	hhdr->hblk_size = size;
	hhdr->kind = kind;

	clear_marks(hhdr);
	clear_finalizes(hhdr);
}
int JSGCMemory::mark_bit_from_hdr(hblk_hdr_t * hhdr, int n)
{
	return ((hhdr->marks[n / BITS_PER_UINT32] >> (n % BITS_PER_UINT32)) & 0x1);
}
void JSGCMemory::set_mark_bit_from_hdr(hblk_hdr_t * hhdr, int n)
{
	hhdr->marks[n / BITS_PER_UINT32] |= 1 << (n % BITS_PER_UINT32);
}
int JSGCMemory::finalize_bit_from_hdr(hblk_hdr_t * hhdr, int n)
{
	return ((hhdr->finalizes[n / BITS_PER_UINT32] >> (n % BITS_PER_UINT32)) & 0x1);
}
void JSGCMemory::set_finalize_bit_from_hdr(hblk_hdr_t * hhdr, int n)
{
	hhdr->finalizes[n / BITS_PER_UINT32] |= 1 << (n % BITS_PER_UINT32);
}

JSGCMemory::BOOL JSGCMemory::block_empty(hblk_hdr_t * hhdr)
{
	int n = Roundup(HBLKSIZE / hhdr->hblk_size, BITS_PER_UINT32)
			/ BITS_PER_UINT32;

	for (int i = 0; i < n; ++i) {
		if (hhdr->marks[i] != 0)
			return False;
	}
	return True;
}

JSGCMemory::BOOL JSGCMemory::block_full(hblk_hdr_t * hhdr)
{
	int n = Roundup(HBLKSIZE / hhdr->hblk_size, BITS_PER_UINT32)
			/ BITS_PER_UINT32;
	int i;

	for (i = 0; i < n - 1; ++i) {
		if (~hhdr->marks[i] != 0)
			return False;
	}
	int rem = (HBLKSIZE / hhdr->hblk_size) % BITS_PER_UINT32;

	if (rem != 0) {
		uint32_t bit = 1;

		for (i = 0; i < rem; ++i) {
			if ((hhdr->marks[n - 1] & bit) == 0) {
				return False;
			}
			bit <<= 1;
		}
	}
	else {
		if (~hhdr->marks[n - 1] != 0)
			return False;
	}
	return True;
}

JSGCMemory::hblk_t * JSGCMemory::bottom_index_to_hblk(bottom_index_t * bottom_index, int n)
{
	return ((hblk_t *) (bottom_index->key * TableSize(bottom_index->index)
					* HBLKSIZE + n * HBLKSIZE)
			);
}

JSGCMemory::hblk_t * JSGCMemory::hblkptr(void *p)
{
	return (hblk_t *) Rounddown((uint32_t) p, HBLKSIZE);
}

int JSGCMemory::hash_code(uint32_t key)
{
	return key % TableSize(top_index);
}

JSGCMemory::hblk_hdr_t * JSGCMemory::alloc_hdr()
{
	return (hblk_hdr_t *) allocator->malloc(sizeof(hblk_hdr_t));
}

void JSGCMemory::free_hdr(hblk_hdr_t * hhdr)
{
	allocator->free(hhdr);
}

JSGCMemory::BOOL JSGCMemory::get_index(hblk_t * hblk)
{
	uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
	uint32_t key = nth_block / TableSize(top_index[0]->index);
	bottom_index_t *bottom_index;
	int i = hash_code(key);

	bottom_index = top_index[i];
	while (bottom_index != NULL) {
		if (bottom_index->key == key)
			return True;
		bottom_index = bottom_index->next;
	}
	bottom_index = (bottom_index_t *) allocator->malloc(sizeof(*bottom_index));
	if (bottom_index != NULL) {
		bottom_index->key = key;
		memset(bottom_index->index, 0, sizeof(bottom_index->index));
		bottom_index->next = top_index[i];
		top_index[i] = bottom_index;
		return True;
	}
	return False;
}

JSGCMemory::hblk_hdr_t * JSGCMemory::get_hdr(hblk_t * hblk)
{
	uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
	uint32_t key = nth_block / TableSize(top_index[0]->index);
	bottom_index_t *bottom_index = top_index[hash_code(key)];

	while (bottom_index != NULL) {
		if (bottom_index->key == key) {
			int n = nth_block % TableSize(top_index[0]->index);

			return bottom_index->index[n];
		}
		bottom_index = bottom_index->next;
	}
	return NULL;
}

JSGCMemory::hblk_hdr_t * JSGCMemory::set_hdr(hblk_t * hblk, hblk_hdr_t * hhdr)
{
	uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
	uint32_t key = nth_block / TableSize(top_index[0]->index);
	bottom_index_t *bottom_index = top_index[hash_code(key)];
	int n = nth_block % TableSize(top_index[0]->index);

	while (bottom_index != NULL) {
		if (bottom_index->key == key) {
			hblk_hdr_t *result = bottom_index->index[n];

			bottom_index->index[n] = hhdr;
			return result;
		}
		bottom_index = bottom_index->next;
	}
	return NULL;
}

JSGCMemory::BOOL JSGCMemory::install_header(hblk_t * hblk)
{
	if (get_index(hblk)) {
		hblk_hdr_t *hhdr = alloc_hdr();

		if (hhdr != NULL) {
			(void)set_hdr(hblk, hhdr);
			return True;
		}
	}
	return False;
}

void JSGCMemory::remove_header(hblk_t * hblk)
{
	hblk_hdr_t *hhdr = set_hdr(hblk, NULL);

	if (hhdr != NULL) {
		free_hdr(hhdr);
	}
}

#ifdef BEST_FIT
int JSGCMemory::size_to_sort_index(size_t size)
{
	const int max_blocks = 1 << (TableSize(sort_index) - 2);
	int blocks = size_to_blocks(size);

	if (blocks < max_blocks)
		return size_sort_map[blocks];
	return TableSize(sort_index) - 1;
}

void JSGCMemory::sort_in(hblk_hdr_t * hhdr)
{
	int index = size_to_sort_index(hhdr->hblk_size);
	hblk_hdr_t **phhdr = &sort_index[index];
	hblk_hdr_t *chhdr;

	for (; (chhdr = *phhdr) != NULL; phhdr = &chhdr->sort_next) {
		if (hhdr->hblk_size > chhdr->hblk_size)
			break;
		if (hhdr->hblk_size == chhdr->hblk_size) {
			if (hhdr->hblk < chhdr->hblk)
				break;
		}
	}
	hhdr->sort_next = *phhdr;
	*phhdr = hhdr;
}

void JSGCMemory::sort_out(hblk_hdr_t * hhdr)
{
	int index = size_to_sort_index(hhdr->hblk_size);
	hblk_hdr_t **phhdr = &sort_index[index];
	hblk_hdr_t *chhdr;

	for (; (chhdr = *phhdr) != NULL; phhdr = &chhdr->sort_next) {
		if (hhdr == chhdr) {
			*phhdr = hhdr->sort_next;
			break;
		}
	}
}
JSGCMemory::hblk_t * JSGCMemory::alloc_hblk(size_t size, int kind)
{
	if (hblk_freelist == NULL)
		return NULL;

	size_t size_needed = HBLKSIZE * size_to_blocks(size);
	int index = size_to_sort_index(size);

	for (; index < (int)TableSize(sort_index); ++index) {
		hblk_hdr_t **phhdr = &sort_index[index];
		hblk_hdr_t *hhdr;

		for (; (hhdr = *phhdr) != NULL; phhdr = &hhdr->sort_next) {
			if (hhdr->hblk_size == size_needed) {
				*phhdr = hhdr->sort_next;   /* faster than sort_out */
				if (hhdr->hblk == hblk_freelist) {
					if (hhdr->hblk != hhdr->hblk_next) {
						hblk_freelist = hhdr->hblk_next;
					}
					else {
						hblk_freelist = NULL;
					}
				}
				setup_header(hhdr, size, kind);
				return hhdr->hblk;
			}
			if (hhdr->hblk_size >= size_needed) {
				*phhdr = hhdr->sort_next;   /* faster than sort_out */
				hblk_t *new_hblk = hhdr->hblk + (hhdr->hblk_size - size_needed) / HBLKSIZE;

				if (install_header(new_hblk)) {
					hblk_hdr_t *new_hhdr = get_hdr(new_hblk);

					setup_header(new_hhdr, size, kind);
					hhdr->hblk_size -= size_needed;
					new_hhdr->hblk = new_hblk;
					sort_in(hhdr);
					return new_hblk;
				}
			}
		}
	}
	return NULL;
}
#else /* BEST_FIT */
JSGCMemory::hblk_t * JSGCMemory::alloc_hblk(size_t size, int kind)
{
	if (hblk_freelist == NULL)
		return NULL;

	size_t size_needed = HBLKSIZE * size_to_blocks(size);
	hblk_t *hblk = hblk_freelist;
	hblk_hdr_t *hhdr = get_hdr(hblk);

	for (;;) {
		hblk_t *prev_hblk = hblk;
		hblk_hdr_t *prev_hhdr = hhdr;

		hblk = prev_hhdr->hblk_next;
		hhdr = get_hdr(hblk);

		if (size_needed == hhdr->hblk_size) {
			if (hblk != prev_hblk) {
				prev_hhdr->hblk_next = hhdr->hblk_next;
				hblk_freelist = prev_hblk;
			}
			else {
				hblk_freelist = NULL;
			}
			setup_header(hhdr, size, kind);
			return hblk;
		}
		else if (size_needed < hhdr->hblk_size) {
			hblk_t *new_hblk = hblk + (hhdr->hblk_size - size_needed) / HBLKSIZE;

			if (install_header(new_hblk)) {
				hblk_hdr_t *new_hhdr = get_hdr(new_hblk);

				setup_header(new_hhdr, size, kind);
				hhdr->hblk_size -= size_needed;
				hblk_freelist = prev_hblk;
				return new_hblk;
			}
			return NULL;
		}
		if (hblk == hblk_freelist)
			break;
	}
	return NULL;
}
#endif /* BEST_FIT */
void JSGCMemory::free_hblk(hblk_t * hblk)
{
	hblk_hdr_t *hhdr = get_hdr(hblk);
	hblk_t *last_hblk = hblk + size_to_blocks(hhdr->hblk_size);

	if (hblk_freelist == NULL) {
		hhdr->hblk_size = size_to_blocks(hhdr->hblk_size) * HBLKSIZE;
		hhdr->hblk_next = hblk;
		set_finalize_bit_from_hdr(hhdr, 0);
		hblk_freelist = hblk;
#ifdef BEST_FIT
		sort_in(hhdr);
#endif
		return;
	}
	hblk_t *chblk = hblk_freelist;
	hblk_hdr_t *chhdr = get_hdr(chblk);

	for (;;) {
		hblk_t *prev_hblk = chblk;
		hblk_hdr_t *prev_hhdr = chhdr;
		hblk_t *prev_last_hblk = prev_hblk + size_to_blocks(prev_hhdr->hblk_size);

		chblk = prev_hhdr->hblk_next;
		chhdr = get_hdr(chblk);
		if (hblk == prev_last_hblk) {
#ifdef BEST_FIT
			sort_out(prev_hhdr);
#endif
			prev_hhdr->hblk_size += size_to_blocks(hhdr->hblk_size) * HBLKSIZE;
			remove_header(hblk);
			if (last_hblk == chblk) {
#ifdef BEST_FIT
				sort_out(chhdr);
#endif
				prev_hhdr->hblk_size += size_to_blocks(chhdr->hblk_size) * HBLKSIZE;
				prev_hhdr->hblk_next = chhdr->hblk_next;
				remove_header(chblk);
			}
			set_finalize_bit_from_hdr(prev_hhdr, 0);
			hblk_freelist = prev_hblk;
#ifdef BEST_FIT
			sort_in(prev_hhdr);
#endif
			break;
		}
		else if (last_hblk == chblk) {
#ifdef BEST_FIT
			sort_out(chhdr);
#endif
			hhdr->hblk_size = (size_to_blocks(hhdr->hblk_size)
					+ size_to_blocks(chhdr->hblk_size))
					* HBLKSIZE;
			prev_hhdr->hblk_next = hblk;
			hhdr->hblk_next = chhdr->hblk_next;
			remove_header(chblk);
			set_finalize_bit_from_hdr(hhdr, 0);
			hblk_freelist = hblk;
#ifdef BEST_FIT
			sort_in(hhdr);
#endif
			break;
		}
		else if ((hblk > prev_last_hblk && last_hblk < chblk)   /* middle */
				||((chblk < prev_last_hblk)
						&& (last_hblk < chblk || hblk > prev_last_hblk)
				)
				) {
			hhdr->hblk_size = size_to_blocks(hhdr->hblk_size) * HBLKSIZE;
			hhdr->hblk_next = prev_hhdr->hblk_next;
			prev_hhdr->hblk_next = hblk;
			set_finalize_bit_from_hdr(hhdr, 0);
			hblk_freelist = hblk;
#ifdef BEST_FIT
			sort_in(hhdr);
#endif
			break;
		}
	}
}
void JSGCMemory::new_hblk(size_t size, int kind)
{
	hblk_t *hblk = alloc_hblk(size, kind);

	if (hblk != NULL) {
		obj_kind_t *obj_kind = &obj_kinds[kind];
		uint32_t *p = hblk->body;
		int index = size_to_freelist_index(size);
		int n = HBLKSIZE / size;

		for (int i = 0; i < n; ++i) {
			objhdr_t *obj = (objhdr_t *) p;

			obj->next = obj_kind->freelist[index];
			obj_kind->freelist[index] = obj;
			p += size / sizeof(*p);
		}
	}
}
JSGCMemory::BOOL JSGCMemory::expand(int blocks)
{
	if (blocks < (int)minHIncr)
		blocks = minHIncr;

	hblk_t *hblk = (hblk_t *) allocator->memalign(HBLKSIZE, HBLKSIZE * blocks);

	if (hblk != NULL) {
		if (get_index(hblk)) {
			hblk_hdr_t *hhdr = alloc_hdr();
			hhdr->kind = NORMAL;    // avoid purify UMR error
			if (hhdr != NULL) {
				set_hdr(hblk, hhdr);
				hhdr->hblk_size = HBLKSIZE * blocks;
				clear_marks(hhdr);
#ifdef BEST_FIT
				hhdr->hblk = hblk;
#endif
				free_hblk(hblk);
				return True;
			}
		}
		allocator->free((void *)hblk);
	}
	return False;
}

JSGCMemory::BOOL JSGCMemory::collect_or_expand(int needed_blocks)
{
	if (expand(needed_blocks))
		return True;

	if (!triedGC) {
		if (!disabled) {
			char jsLogbuf[64];
			memset(jsLogbuf, 0, sizeof(jsLogbuf));
			sprintf(jsLogbuf, "Call GC from ObjPart. NeededBlock:%d\n", needed_blocks);
			EwbSetLog(jsLogbuf);
			EwbSetLog("1st Log is Befor GC. 2nd is after.\n");
			ShowGCFreeBlock(1);
			gc();
			ShowGCFreeBlock(1);
		}
		else {
			adjust();
		}
		triedGC = True;
		return True;
	}
	return False;
}
void *JSGCMemory::alloc_obj(size_t size, int kind)
{
	int index = size_to_freelist_index(size);
	objhdr_t **objp = &obj_kinds[kind].freelist[index];
	triedGC = False;
	while (*objp == NULL) {
		new_hblk(size, kind);
		if (*objp == NULL) {
			if (!collect_or_expand(1)) {
				return NULL;
			}
		}
	}
	objhdr_t *obj = *objp;

	*objp = obj->next;
	return obj;
}
void JSGCMemory::gc_block(bottom_index_t * bottom_index, int n)
{
	hblk_hdr_t *hhdr = bottom_index->index[n];
	hblk_t *hblk = bottom_index_to_hblk(bottom_index, n);



	if (small_obj(hhdr->hblk_size)) {
		obj_kind_t *obj_kind = &obj_kinds[hhdr->kind];
		int index = size_to_freelist_index(hhdr->hblk_size);

		if (block_empty(hhdr)) {
			switch (hhdr->kind) {
			case NORMAL:
				uint32_t * p = hblk->body;
				int n = HBLKSIZE / hhdr->hblk_size;

				for (int i = 0; i < n; ++i) {
					objhdr_t *obj = (objhdr_t *) p;

					if (finalize_bit_from_hdr(hhdr, i) == 0) {
						phaseListener->finalize(obj);
					}
					p += hhdr->hblk_size / sizeof(*p);
				}
				break;
			}
#ifdef STATS
			mem_found += HBLKSIZE;
#endif
			free_hblk(hblk);
		}
		else {
			uint32_t *p = hblk->body;
			int n = HBLKSIZE / hhdr->hblk_size;

			for (int i = 0; i < n; ++i) {
				if (mark_bit_from_hdr(hhdr, i) == 0) {
					objhdr_t *obj = (objhdr_t *) p;

					switch (hhdr->kind) {
					case NORMAL:
						if (finalize_bit_from_hdr(hhdr, i) == 0) {
							phaseListener->finalize(obj);
						}
						break;
					}
					obj->next = obj_kind->freelist[index];
					obj_kind->freelist[index] = obj;
#ifdef STATS
					mem_found += hhdr->hblk_size;
#endif
				}
				p += hhdr->hblk_size / sizeof(*p);
			}
			clear_marks(hhdr);
			clear_finalizes(hhdr);
		}
	}
	else {
		if (mark_bit_from_hdr(hhdr, 0) == 0) {
			switch (hhdr->kind) {
			case NORMAL:
				if (finalize_bit_from_hdr(hhdr, 0) == 0) {
					phaseListener->finalize(hblk);
				}
				break;
			}
#ifdef STATS
			mem_found += size_to_blocks(hhdr->hblk_size) * HBLKSIZE;
#endif
			free_hblk(hblk);
		}
		else {
			clear_marks(hhdr);
			clear_finalizes(hhdr);  // not needed
		}
	}
}
void JSGCMemory::adjust()
{
	int i;

	for (i = 0; i < (int)TableSize(obj_kinds); ++i) {
		obj_kind_t *obj_kind = &obj_kinds[i];

		for (int j = 0; j < (int)TableSize(obj_kind->freelist); ++j) {
			for (objhdr_t * obj = obj_kind->freelist[j]; obj != NULL; obj = obj->next) {
				markObj(obj);
			}
		}
		memset(obj_kind->freelist, 0, sizeof(obj_kind->freelist));
	}
	hblk_freelist = NULL;
#ifdef BEST_FIT
	memset(sort_index, 0, sizeof(sort_index));
#endif
	for (i = 0; i < (int)TableSize(top_index); ++i) {
		bottom_index_t *bottom_index = top_index[i];

		while (bottom_index != NULL) {
			for (int j = 0; j < (int)TableSize(bottom_index->index); ++j) {
				if (bottom_index->index[j] != NULL) {
					hblk_t *hblk = bottom_index_to_hblk(bottom_index, j);
					hblk_hdr_t *hhdr = bottom_index->index[j];

					if (small_obj(hhdr->hblk_size)) {
						if (block_full(hhdr)) {
							free_hblk(hblk);
							clear_marks(hhdr);
						}
						else {
							obj_kind_t *obj_kind = &obj_kinds[hhdr->kind];
							uint32_t *p = hblk->body;
							int index = size_to_freelist_index(hhdr->hblk_size);
							int n = HBLKSIZE / hhdr->hblk_size;

							for (int i = 0; i < n; ++i) {
								if (mark_bit_from_hdr(hhdr, i) != 0) {
									objhdr_t *obj = (objhdr_t *) p;

									obj->next = obj_kind->freelist[index];
									obj_kind->freelist[index] = obj;
								}
								p += hhdr->hblk_size / sizeof(*p);
							}
							clear_marks(hhdr);
						}
					}
					else {
						free_hblk(hblk);
					}
				}
			}
			bottom_index = bottom_index->next;
		}
	}
}
void JSGCMemory::freeAll()
{
	int i;

	hblk_freelist = NULL;
#ifdef BEST_FIT
	memset(sort_index, 0, sizeof(sort_index));
#endif

	/* gather blocks */
	for (i = 0; i < (int)TableSize(top_index); ++i) {
		bottom_index_t *bottom_index = top_index[i];

		while (bottom_index != NULL) {
			for (int j = 0; j < (int)TableSize(bottom_index->index); ++j) {
				hblk_hdr_t *hhdr = bottom_index->index[j];

				if (hhdr != NULL) {
					hblk_t *hblk = bottom_index_to_hblk(bottom_index, j);

					free_hblk(hblk);
				}
			}
			bottom_index = bottom_index->next;
		}
	}
	for (hblk_t * hblk = hblk_freelist; hblk != NULL;) {
		hblk_hdr_t *hhdr = get_hdr(hblk);

		allocator->free(hblk);
		hblk = hhdr->hblk_next;
		if (hblk == hblk_freelist)
			break;
	}
	for (i = 0; i < (int)TableSize(top_index); ++i) {
		bottom_index_t *bottom_index = top_index[i];

		while (bottom_index != NULL) {
			for (int j = 0; j < (int)TableSize(bottom_index->index); ++j) {
				hblk_hdr_t *hhdr = bottom_index->index[j];

				if (hhdr != NULL) {
					free_hdr(hhdr);
				}
			}
			bottom_index_t *old_bottom_index = bottom_index;

			bottom_index = bottom_index->next;
			allocator->free(old_bottom_index);
		}
	}
}

#ifndef SHELL
#ifdef JSTIME_TRACE
/* #include "ewb-trace.h" */
#include "js-trace.h"
#endif
#endif
void JSGCMemory::gc()
{
	int i;
	extern int ewbJsObjMaxMemShow;
#ifdef STATS
# ifdef FASTER
	if (allocated == 0)
		return;
# endif
#endif
#ifndef SHELL
#ifdef JSTIME_TRACE
	EWBTIME("SetGCMaxUseBlock", NULL);
#endif
#endif
	if (ewbJsObjMaxMemShow)
		SetGCMaxUseBlock(); // 20060822 ichino
#ifndef SHELL
#ifdef JSTIME_TRACE
	EWBTIME("SetGCMaxUseBlock", NULL);
#endif
#endif

#ifndef SHELL
#ifdef JSTIME_TRACE
	EWBTIME("StartGC", NULL);
#endif
#endif

	phaseListener->stopWorld();
	phaseListener->markAll();

	hblk_freelist = NULL;
#ifdef BEST_FIT
	memset(sort_index, 0, sizeof(sort_index));
#endif

#ifdef VX   // avoid gcc-2.96 bug of ppc
	obj_kind_t *obj_kind;

	obj_kind = &obj_kinds[PTRFREE];
	memset(obj_kind->freelist, 0, sizeof(obj_kind->freelist));

	obj_kind = &obj_kinds[NORMAL];
	for (int j = 0; j < (int)TableSize(obj_kind->freelist); ++j) {
		for (objhdr_t * obj = obj_kind->freelist[j]; obj != NULL; obj = obj->next) {
			hblk_t *hblk = hblkptr(obj);
			hblk_hdr_t *hhdr = get_hdr(hblk);
			int n = ((uint32_t) obj - (uint32_t) hblk) / hhdr->hblk_size;

			set_finalize_bit_from_hdr(hhdr, n);
		}
	}
	memset(obj_kind->freelist, 0, sizeof(obj_kind->freelist));
#else /* !VX */
	for (i = 0; i < (int)TableSize(obj_kinds); ++i) {
		obj_kind_t *obj_kind = &obj_kinds[i];

		switch (i) {
		case NORMAL:
			for (int j = 0; j < (int)TableSize(obj_kind->freelist); ++j) {
				for (objhdr_t * obj = obj_kind->freelist[j]; obj != NULL; obj = obj->next) {
					hblk_t *hblk = hblkptr(obj);
					hblk_hdr_t *hhdr = get_hdr(hblk);
					int n = ((uint32_t) obj - (uint32_t) hblk)
							/ hhdr->hblk_size;

					set_finalize_bit_from_hdr(hhdr, n);
				}
			}
			break;
		}
		memset(obj_kind->freelist, 0, sizeof(obj_kind->freelist));
	}
#endif /* VX */

#ifdef STATS
	mem_found = 0;
#endif
	for (i = 0; i < (int)TableSize(top_index); ++i) {
		bottom_index_t *bottom_index = top_index[i];

		while (bottom_index != NULL) {
			for (int j = 0; j < (int)TableSize(bottom_index->index); ++j) {
				if (bottom_index->index[j] != NULL) {
					gc_block(bottom_index, j);
				}
			}
			bottom_index = bottom_index->next;
		}
	}
#ifndef SHELL
#ifdef STATS
	extern int ewbPrintOn;
	if (ewbPrintOn) {
		printf("JSGCMemory::gc 0: %d bytes allocated during gc," " %d bytes free after gc\n", allocated, mem_found);
		//20050922 ShowGCFreeBlock();//ichino 20040702
	}
	allocated = 0;
#endif
#endif
	phaseListener->resumeWorld();

#ifndef SHELL
#ifdef JSTIME_TRACE
	EWBTIME("EndGC", NULL);
#endif
#endif

}
void JSGCMemory::markObj(void *ptr)
{
	hblk_t *hblk = hblkptr(ptr);
	hblk_hdr_t *hhdr = get_hdr(hblk);
	int n = ((uint32_t) ptr - (uint32_t) hblk) / hhdr->hblk_size;

	set_mark_bit_from_hdr(hhdr, n);
}
JSGCMemory::BOOL JSGCMemory::isMarkObj(void *ptr)
{
	hblk_t *hblk = hblkptr(ptr);
	hblk_hdr_t *hhdr = get_hdr(hblk);
	int n = ((uint32_t) ptr - (uint32_t) hblk) / hhdr->hblk_size;

	return mark_bit_from_hdr(hhdr, n) != 0;
}
int JSGCMemory::getObjKind(void *ptr)
{
	hblk_t *hblk = hblkptr(ptr);
	hblk_hdr_t *hhdr = get_hdr(hblk);

	return hhdr->kind;
}
void *JSGCMemory::malloc(size_t size, int kind)
{
	int index;
#ifdef THREADS
	mutexListener->lock();
#endif
	if (small_obj(size)) {
		obj_kind_t *obj_kind = &obj_kinds[kind];

		size = size_map[size];

		index = size_to_freelist_index(size);
		objhdr_t *obj = obj_kind->freelist[index];

		if (obj != NULL) {
			obj_kind->freelist[index] = obj->next;
		}
		else {
			obj = (objhdr_t *) alloc_obj(size, kind);
		}
		if (obj != NULL) {
#ifdef THREADS
			mutexListener->unlock();
#endif
#ifdef STATS
			allocated += size;
#endif
			return (void *)obj;
		}
	}
	else {
		int blocks = size_to_blocks(size);
		hblk_t *hblk;

		triedGC = False;
		while ((hblk = alloc_hblk(size, kind)) == NULL) {
			if (!collect_or_expand(blocks))
				break;
		}
		if (hblk != NULL) {
#ifdef THREADS
			mutexListener->unlock();
#endif
#ifdef STATS
			allocated += blocks * HBLKSIZE;
#endif
			return (void *)hblk->body;
		}
	}
#ifdef THREADS
	mutexListener->unlock();
#endif
	return NULL;
}
void JSGCMemory::free(void *ptr)
{
	int index;

	if (ptr == NULL)
		return;

#ifdef THREADS
	mutexListener->lock();
#endif
	hblk_t *hblk = hblkptr(ptr);
	hblk_hdr_t *hhdr = get_hdr(hblk);
	int kind = hhdr->kind;
	size_t size = hhdr->hblk_size;
	obj_kind_t *obj_kind = &obj_kinds[kind];

	if (small_obj(size)) {
		objhdr_t *obj = (objhdr_t *) ptr;
		index = size_to_freelist_index(size);

		obj->next = obj_kind->freelist[index];
		obj_kind->freelist[index] = obj;
#ifdef STATS
		allocated -= size;
#endif
	}
	else {
#ifdef STATS
		allocated -= size_to_blocks(size) * HBLKSIZE;
#endif
		free_hblk(hblk);
	}
#ifdef THREADS
	mutexListener->unlock();
#endif
}
void *JSGCMemory::realloc(void *ptr, size_t size)
{
	if (ptr == NULL)
		return malloc(size, NORMAL);

#ifdef THREADS
	mutexListener->lock();
#endif
	hblk_t *hblk = hblkptr(ptr);
	hblk_hdr_t *hhdr = get_hdr(hblk);
	int kind = hhdr->kind;

	if (size <= hhdr->hblk_size) {
#ifdef THREADS
		mutexListener->unlock();
#endif
		return ptr;
	}
	else {
		void *nptr = malloc(size, kind);

		if (nptr != NULL) {
			memcpy(nptr, ptr, hhdr->hblk_size);
			free(ptr);
#ifdef THREADS
			mutexListener->unlock();
#endif
			return nptr;
		}
	}
#ifdef THREADS
	mutexListener->unlock();
#endif
    return NULL;
}

/*
 * Call From ewbhelp.
 * mode 0: print out to sebug serial
 * mode 0: print out to SysLog
 */
void JSGCMemory::ShowGCFreeBlock(int mode)
{
	if (hblk_freelist == NULL)
		return;

	hblk_t *hblk = hblk_freelist;
	hblk_hdr_t *hhdr = get_hdr(hblk);
	size_t maxBlockSize = 0;
	int blockCount = 0;
	int avrBlockSize = 0;
	int totalFreeBlock = 0;
	int i = 0;
	int j = 0;

	for (;;) {
		hblk_hdr_t *prev_hhdr = hhdr;

		hblk = prev_hhdr->hblk_next;
		hhdr = get_hdr(hblk);
		if (maxBlockSize < hhdr->hblk_size) {
			maxBlockSize = hhdr->hblk_size;
		}
		totalFreeBlock += hhdr->hblk_size;
		blockCount++;
		if (hblk == hblk_freelist)
			break;
	}
	for (j = 0; j <= 1; j++) {
		for (i = 0; i < 64; i++) {
			objhdr_t *objp_first = obj_kinds[0].freelist[i];
			objhdr_t *objp = objp_first;
			objhdr_t *obj;
			if (objp_first != NULL) {
				for (;;) {
					totalFreeBlock += (i + 1) * 8;
					blockCount++;
					obj = objp;
					objp = obj->next;
					if (objp_first == objp || objp == NULL)
						break;
				}
			}
		}
	}
	if (blockCount > 0)
		avrBlockSize = totalFreeBlock / blockCount;
	if (mode == 0) {
		printf("GC MaxFreeBlockSize:%d BlockCount:%d AverageBlockSize:%d TotalFreeBlock:%d\n", maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
	}
	else if (mode == 1) {
		char sysLogBuf[128];
		memset(sysLogBuf, 0, sizeof(sysLogBuf));
		sprintf(sysLogBuf, "MaxFB:%d BC:%d Avr:%d Total:%d\n", maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
		EwbSetLog(sysLogBuf);
	}

	return;
}


int JSGCMemory::GetGCFreeBlock()
{
	if (hblk_freelist == NULL)
		return 0;

	hblk_t *hblk = hblk_freelist;
	hblk_hdr_t *hhdr = get_hdr(hblk);
	size_t maxBlockSize = 0;
	int blockCount = 0;
	int totalFreeBlock = 0;
	int i = 0;
	int j = 0;

	for (;;) {
		hblk_hdr_t *prev_hhdr = hhdr;

		hblk = prev_hhdr->hblk_next;
		hhdr = get_hdr(hblk);
		if (maxBlockSize < hhdr->hblk_size) {
			maxBlockSize = hhdr->hblk_size;
		}
		totalFreeBlock += hhdr->hblk_size;
		blockCount++;
		if (hblk == hblk_freelist)
			break;
	}
	for (j = 0; j <= 1; j++) {
		for (i = 0; i < 64; i++) {
			objhdr_t *objp_first = obj_kinds[0].freelist[i];
			objhdr_t *objp = objp_first;
			objhdr_t *obj;
			if (objp_first != NULL) {
				for (;;) {
					totalFreeBlock += (i + 1) * 8;
					blockCount++;
					obj = objp;
					objp = obj->next;
					if (objp_first == objp || objp == NULL)
						break;
				}
			}
		}
	}
	return totalFreeBlock;
}

/* 20060822 ichino */
void JSGCMemory::SetGCMaxUseBlock()
{
#ifdef VX
	int maxUseSize_tmp = js_get_now_used() - GetGCFreeBlock();
	if (maxUseSize_tmp > maxUseSize)
		maxUseSize = maxUseSize_tmp;
#endif
}

int JSGCMemory::GetGCMaxUseBlock()
{
	return maxUseSize;
}

void JSGCMemory::ResetGCMaxUseBlock()
{
	maxUseSize = 0;
}
void JSGCMemory::ShowGCFreeBlockDetail(int mode)
{
	if (hblk_freelist == NULL)
		return;

	hblk_t *hblk = hblk_freelist;
	hblk_hdr_t *hhdr = get_hdr(hblk);
	size_t maxBlockSize = 0;
	int blockCount = 0;
	int avrBlockSize = 0;
	int totalFreeBlock = 0;
	int i = 0;
	int j = 0;

	int memLargeBlock[65];
	for (i = 0; i < 65; i++)
		memLargeBlock[i] = 0;
	int memSmallBlock[65];
	for (i = 0; i < 65; i++)
		memSmallBlock[i] = 0;

	for (;;) {
		hblk_hdr_t *prev_hhdr = hhdr;
		hblk = prev_hhdr->hblk_next;
		hhdr = get_hdr(hblk);
		if (maxBlockSize < hhdr->hblk_size) {
			maxBlockSize = hhdr->hblk_size;
		}
		totalFreeBlock += hhdr->hblk_size;
		blockCount++;
		//printf("hhdr->hblk_size:%d\n",hhdr->hblk_size);
		memLargeBlock[hhdr->hblk_size / 1024]++;
		if (hblk == hblk_freelist)
			break;
	}
	for (j = 0; j <= 1; j++) {
		for (i = 0; i < 64; i++) {
			//printf("i:%d\n",i);
			objhdr_t *objp_first = obj_kinds[j].freelist[i];
			objhdr_t *objp = objp_first;
			objhdr_t *obj;
			//printf("objp_first:0x%x objp:0x%x obj:0x%x\n",objp_first,objp,obj);
			if (objp_first != NULL) {
				for (;;) {
					totalFreeBlock += (i + 1) * 8;
					blockCount++;
					memSmallBlock[i + 1]++;
					//printf("TFB%d",totalFreeBlock);
					obj = objp;
					objp = obj->next;
					//printf("objp:0x%x obj:0x%x\n",objp,obj);
					if (objp_first == objp || objp == NULL)
						break;
				}
			}
		}
	}

	if (mode == 1) {
		for (i = 1; i < 65; i++)
			printf("%dkB %d\n", i, memLargeBlock[i]);
	}
	if (mode == 2) {
		for (i = 1; i < 65; i++)
			printf("%dByte %d\n", i * 8, memSmallBlock[i]);
		for (i = 1; i < 65; i++)
			printf("%dkB %d\n", i, memLargeBlock[i]);
	}

	avrBlockSize = totalFreeBlock / blockCount;
	printf("GC MaxFreeBlockSize:%d BlockCount:%d AverageBlockSize:%d TotalFreeBlockSize:%d\n", maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
	//printf("mem_found:%d\n",mem_found);
	return;
}

JSGCMemory::JSGCMemory()
{
	hblk_freelist = NULL;
	minHIncr = MINHINCR;
	memset(obj_kinds, 0, sizeof(obj_kinds));
	memset(top_index, 0, sizeof(top_index));
#ifdef BEST_FIT
	memset(sort_index, 0, sizeof(sort_index));
#endif
	disabled = False;
#ifdef STATS
	allocated = 0;
#endif
	maxUseSize = 0;
}

JSGCMemory::~JSGCMemory()
{
	freeAll();
}

void JSGCMemory::classInitialize()
{
	size_map[0] = Roundup(1, ALIGN_BYTES);
	for (int i = 1; i <= (int)MAX_OBJ_SIZE; ++i) {
		size_map[i] = Roundup(i, ALIGN_BYTES);
	}
#ifdef BEST_FIT
	size_t hblk_size = 1;
	int index = 0;

	for (int i = 0; i < (int)TableSize(size_sort_map); ++i) {
		if (i > (int)hblk_size) {
			hblk_size <<= 1;
			index += 1;
		}
		size_sort_map[i] = index;
	}
#endif
}

#include "js-gcnew.h"
void JSGCNew::_ninline()
{
}
