/*
 * 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>
#if __x86_64
# include "js-types.h"
#endif
#include "js-gcp.h"
#include "ewb-mem.h"
#include "ewb-dbg.h"

/* 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)
{
#if __x86_64
    return (hblk_t *) Rounddown((uint64_t) p, HBLKSIZE);
#else
    return (hblk_t *) Rounddown((uint32_t) p, HBLKSIZE);
#endif
}
#if __x86_64
int
JSGCMemory::hash_code(uint64_t key)
#else
int
JSGCMemory::hash_code(uint32_t key)
#endif
{
    return key % TableSize(top_index);
}
JSGCMemory::hblk_hdr_t *
JSGCMemory::alloc_hdr(JSContext* cx) {
    return (hblk_hdr_t *) allocator->malloc(cx, sizeof (hblk_hdr_t));
}
void
JSGCMemory::free_hdr(hblk_hdr_t *hhdr)
{
    allocator->free(hhdr);
}
JSGCMemory::BOOL  
JSGCMemory::get_index(JSContext* cx, hblk_t *hblk)
{
#if __x86_64
    uint64_t nth_block = (uint64_t) hblk / HBLKSIZE;
    uint64_t key = nth_block / TableSize(top_index[0]->index);
#else
    uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
    uint32_t key = nth_block / TableSize(top_index[0]->index);
#endif
    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(cx, 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)
{
#if __x86_64
    uint64_t nth_block = (uint64_t) hblk / HBLKSIZE;
    uint64_t key = nth_block / TableSize(top_index[0]->index);
#else
    uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
    uint32_t key = nth_block / TableSize(top_index[0]->index);
#endif
    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)
{
#if __x86_64
    uint64_t nth_block = (uint64_t) hblk / HBLKSIZE;
    uint64_t key = nth_block / TableSize(top_index[0]->index);
#else
    uint32_t nth_block = (uint32_t) hblk / HBLKSIZE;
    uint32_t key = nth_block / TableSize(top_index[0]->index);
#endif
    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(JSContext* cx, hblk_t *hblk)
{
    if (get_index(cx, hblk)) {
	hblk_hdr_t *hhdr = alloc_hdr(cx);

	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(JSContext* cx, 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(JSContext* cx, 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(cx, 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(JSContext* cx, size_t size, int kind)
{
    hblk_t * hblk = alloc_hblk(cx, 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(JSContext* cx, int blocks)
{
    if (blocks < (int) minHIncr) blocks = minHIncr;

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

    if (hblk != NULL) {
	if (get_index(cx, hblk)) {
	    hblk_hdr_t *hhdr = alloc_hdr(cx);
	    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(JSContext* cx, int needed_blocks)
{
    if (expand(cx, 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(cx);
	    ShowGCFreeBlock(1);
	} else {
	    adjust();
	}
	triedGC = True;
	return True;
    }
    return False;
}
void *
JSGCMemory::alloc_obj(JSContext* cx, 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(cx, size, kind);
	if (*objp == NULL) {
	    if (!collect_or_expand(cx, 1)) {
		return NULL;
	    }
	}
    }
    objhdr_t *obj = *objp;

    *objp = obj->next;
    return obj;
}
void
JSGCMemory::gc_block(JSContext* cx, 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(cx, obj);
		    }
		    p += hhdr->hblk_size / sizeof (*p);
		}
		break;
	    }
	    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(cx, 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(cx, 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"
#endif
#endif
void 
JSGCMemory::gc(JSContext* cx)
{
    int i;
#ifndef SHELL
    extern int ewbJsObjMaxMemShow;
#endif

#ifdef STATS
# ifdef FASTER
    if ( allocated == 0 ) return;
# endif
#endif
#ifndef SHELL
#ifdef JSTIME_TRACE
    EWBTIME("SetGCMaxUseBlock",NULL);
#endif
#endif
#ifndef SHELL
    if(ewbJsObjMaxMemShow) SetGCMaxUseBlock(); // 20060822 ichino
# ifdef JSTIME_TRACE
    EWBTIME("SetGCMaxUseBlock",NULL);
# endif
#endif

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

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

    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);
#if __x86_64
    		    int n = ((uint64_t) obj -  (uint64_t) hblk)
				/ hhdr->hblk_size;
#else
    		    int n = ((uint32_t) obj -  (uint32_t) hblk)
				/ hhdr->hblk_size;
#endif
 
    		    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(cx, 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);
#if __x86_64
    int n = ((uint64_t) ptr -  (uint64_t) hblk) / hhdr->hblk_size;
#else
    int n = ((uint32_t) ptr -  (uint32_t) hblk) / hhdr->hblk_size;
#endif
 
    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);
#if __x86_64
    int n = ((uint64_t) ptr -  (uint64_t) hblk) / hhdr->hblk_size;
#else
    int n = ((uint32_t) ptr -  (uint32_t) hblk) / hhdr->hblk_size;
#endif

    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(JSContext* cx, 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(cx, 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(cx, size, kind)) == NULL) {
	    if (!collect_or_expand(cx, 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(JSContext* cx, void *ptr, size_t size)
{
    if (ptr == NULL) return malloc(cx, 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(cx, 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){
#if __x86_64
	printf("GC MaxFreeBlockSize:%ld BlockCount:%d AverageBlockSize:%d TotalFreeBlock:%d\n",
	       maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#else
	printf("GC MaxFreeBlockSize:%d BlockCount:%d AverageBlockSize:%d TotalFreeBlock:%d\n",
	       maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#endif
    }else if(mode == 1){
        char sysLogBuf[128];
	memset(sysLogBuf, 0, sizeof(sysLogBuf));
#if __x86_64
	sprintf(sysLogBuf, "MaxFB:%ld BC:%d Avr:%d Total:%d\n",
		maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#else
	sprintf(sysLogBuf, "MaxFB:%d BC:%d Avr:%d Total:%d\n",
		maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#endif
	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();
    int maxUseSize_tmp = EwbJsGetNowUsed() - 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;
#if __x86_64
    printf("GC MaxFreeBlockSize:%ld BlockCount:%d AverageBlockSize:%d TotalFreeBlockSize:%d\n",
    maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#else
    printf("GC MaxFreeBlockSize:%d BlockCount:%d AverageBlockSize:%d TotalFreeBlockSize:%d\n",
    maxBlockSize, blockCount, avrBlockSize, totalFreeBlock);
#endif
    //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()
{
}
