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

#ifndef _JS_GC_H_
#define _JS_GC_H_

/* Header files */
#include <stdlib.h>
#include <sys/types.h>
#include <assert.h>
#include "js-types.h"

/* Classes */
class JSContext;
class JSGCMemory {
    /* Constants */
    protected:
#define Roundup(a, u)	   ((((a) + ((u) - 1)) / (u)) * (u))
	static	const size_t	HBLKSIZE = 1024;
#if __x86_64
	static	const size_t	ALIGN_BYTES = sizeof (double) * 2;
#else
	static	const size_t	ALIGN_BYTES = sizeof (double);
#endif
	static	const int	BITS_PER_BYTE	= 8;
	static	const int	BITS_PER_UINT32	= BITS_PER_BYTE
							* sizeof (uint32_t);
	static	const size_t	MARK_BITS_SIZE
				   = Roundup((HBLKSIZE / ALIGN_BYTES),
					BITS_PER_UINT32) / BITS_PER_UINT32;
	static	const size_t	MAX_OBJ_SIZE = HBLKSIZE / 2;
	static	const size_t	MINHINCR = 8 * 8;  // '* 8' added temporaryly
	static	const int	True = 1;
	static	const int	False = 0;
#ifdef BEST_FIT
	static	const int	SORT_INDEX_SIZE = 4;
#endif
#undef Roundup

    public:
	static	const int	PTRFREE = 0;
	static	const int	NORMAL = 1;
	static	const int	MAX_KINDS = 2;

    protected:
	typedef struct _hblk {
	    uint32_t	body[HBLKSIZE / sizeof (uint32_t)];
	} hblk_t;
	typedef struct _hblk_hdr {
    	    size_t	hblk_size;	/* If in use, size in words, of
					   objects in the block.  If free, the
					   size in bytes of the whole block */
    	    hblk_t 	*hblk_next;	/* Link field for hblk free list */
	    uint32_t	kind;		/* object kind */
    	    uint32_t	marks[MARK_BITS_SIZE];
    	    uint32_t	finalizes[MARK_BITS_SIZE];
#ifdef BEST_FIT
	    hblk_t	*hblk;
	    struct _hblk_hdr *sort_next;
#endif
	} hblk_hdr_t;
	typedef struct _bottom_index {
    	    hblk_hdr_t	*index[1024];	/* must be power(2, n) */
    	    uint32_t	key;		/* high order address bits that 
					   is hash key */
    	    struct _bottom_index *next;
	} bottom_index_t;
 	typedef struct _objhdr {
	    struct _objhdr	*next;
	} objhdr_t;
	typedef struct _obj_kind {
   	    objhdr_t	*freelist[(MAX_OBJ_SIZE - 1) / ALIGN_BYTES + 1];
					/* Array of free list headers for this
					   type of object */
	} obj_kind_t;

    public:
	class MemoryAllocator {
	    public:
                virtual ~MemoryAllocator() {
                }

		virtual void	*memalign(JSContext* cx, size_t alignment,
                        size_t size) = 0;
		virtual void	*malloc(JSContext* cx, size_t size) = 0;
		virtual void    free(void *ptr) = 0;
	} ;
#ifdef THREADS
	class MutexListener {
	    public:
		virtual	void	lock() = 0;
		virtual	void	unlock() = 0;
	} ;
#endif
	class PhaseListener {
	    public:
                virtual ~PhaseListener() {
                }

		virtual	void	stopWorld() = 0;
		virtual	void	markAll(JSContext* cx) = 0;
		virtual	void	finalize(JSContext* cx, void *obj) = 0;
		virtual	void	resumeWorld() = 0;
	} ;
	typedef	uint8_t		BOOL;

    /* Variables */
    protected:
	static uint16_t		size_map[MAX_OBJ_SIZE + 1];

	MemoryAllocator		*allocator;
	PhaseListener		*phaseListener;
#ifdef THREADS 
	MutexListener		*mutexListener;
#endif
	size_t			minHIncr;
	hblk_t			*hblk_freelist;
	obj_kind_t		obj_kinds[MAX_KINDS];
	bottom_index_t		*top_index[32];	
#ifdef BEST_FIT
	static uint8_t		size_sort_map[(1 << (SORT_INDEX_SIZE - 1)) + 1];
        hblk_hdr_t		*sort_index[SORT_INDEX_SIZE];
#endif

	BOOL			triedGC;
	BOOL			disabled;
#ifdef STATS
	size_t			mem_found;
	size_t			allocated;
#endif
	int			maxUseSize;

    public:

    /* Procedures */
    protected:
	static	BOOL	small_obj(size_t size);
	static	int	size_to_blocks(size_t size);
	static	int	size_to_freelist_index(size_t size);
	static	void	clear_marks(hblk_hdr_t *hhdr);
	static	void	clear_finalizes(hblk_hdr_t *hhdr);
	static	void	setup_header(hblk_hdr_t *header, size_t size, int kind);
	static	int	mark_bit_from_hdr(hblk_hdr_t *hhdr, int n);
	static	void	set_mark_bit_from_hdr(hblk_hdr_t *hhdr, int n);
	static	int	finalize_bit_from_hdr(hblk_hdr_t *hhdr, int n);
	static	void	set_finalize_bit_from_hdr(hblk_hdr_t *hhdr, int n);
	static	BOOL	block_empty(hblk_hdr_t *hhdr);
	static	BOOL	block_full(hblk_hdr_t *hhdr);
	static	hblk_t *bottom_index_to_hblk(bottom_index_t *bottomIndex,
				int n);

	hblk_t *hblkptr(void *p);
#if __x86_64
	int	hash_code(uint64_t key);
#else
	int	hash_code(uint32_t key);
#endif
	hblk_hdr_t *
		alloc_hdr(JSContext* cx);
	void	free_hdr(hblk_hdr_t *hhdr);
	BOOL	get_index(JSContext* cx, hblk_t *hblk);
	hblk_hdr_t *
		get_hdr(hblk_t *hblk);
	hblk_hdr_t *
		set_hdr(hblk_t *hblk, hblk_hdr_t *hhdr);
	BOOL	install_header(JSContext* cx, hblk_t *hblk);
	void	remove_header(hblk_t *hblk);
#ifdef BEST_FIT
	int     size_to_sort_index(size_t size);
	void	sort_in(hblk_hdr_t *hhdr);
	void	sort_out(hblk_hdr_t *hhdr);
#endif

	hblk_t *alloc_hblk(JSContext* cx, size_t size, int kind);
	void	free_hblk(hblk_t *hblk);
	void	new_hblk(JSContext* cx, size_t size, int kind);

	BOOL	expand(JSContext* cx, int blocks);
	BOOL	collect_or_expand(JSContext* cx, int needed_blocks);
	void	*alloc_obj(JSContext* cx, size_t size, int kind);
	void	gc_block(JSContext* cx, bottom_index_t *bottom_index, int n);
	void	adjust();
	void	freeAll();

    public:
	static void	classInitialize();

 	JSGCMemory();
 	~JSGCMemory();
	void  operator delete(void *ptr) {
	    assert(0);
	}

	void	setMemoryAllocator(
				MemoryAllocator *allocator);
	void	setPhaseListener(PhaseListener *phaseListener);
#ifdef THREADS
	void	setMutexListener(MutexListener *mutexListener);
#endif
	void	setMinHIncr(size_t minHIncr); 
	void	gc(JSContext *cx);
	void	markObj(void *ptr);
	BOOL	isMarkObj(void *ptr);
	int	getObjKind(void *ptr);

	void	*malloc(JSContext *cx, size_t size, int kind);
	void	free(void *ptr);
	void	*realloc(JSContext *cx, void *ptr, size_t size);
	void	enable();
	void	disable();
	void	ShowGCFreeBlock(int mode);
	void	ShowGCFreeBlockDetail(int mode);
	int	GetGCFreeBlock();
	int	GetGCMaxUseBlock();
	void	SetGCMaxUseBlock();
	void	ResetGCMaxUseBlock();
} ;

/* Inlines */
inline void
JSGCMemory::setMemoryAllocator(JSGCMemory::MemoryAllocator *allocator)
{
    this->allocator = allocator;
}
inline void
JSGCMemory::setPhaseListener(PhaseListener *phaseListener)
{
    this->phaseListener = phaseListener;
}
#ifdef THREADS
inline void
JSGCMemory::setMutexListener(MutexListener *mutexListener)
{
    this->mutexListener = mutexListener;
}
#endif
inline void
JSGCMemory::setMinHIncr(size_t minHIncr)
{
    this->minHIncr = minHIncr;
}
inline void
JSGCMemory::enable()
{
    disabled = False;
}
inline void
JSGCMemory::disable()
{
    disabled = True;
}

#endif /* _JS_GC_H_ */

