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

#if !defined CONF_WPAD
#include "../../imglib_new/tiff/inc/sysdep.h"
#else
/* #include "../../../../ewb/imglib_new/tiff/inc/sysdep.h" */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <math.h>
#if defined VX
#include <vxWorks.h>
#endif
#endif


/* Classes */
class JSGCMemory {
    /* Constants */
    protected:
#define Roundup(a, u)      ((((a) + ((u) - 1)) / (u)) * (u))
        static  const size_t    HBLKSIZE = 1024;
        static  const size_t    ALIGN_BYTES = sizeof (double);
        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 void    *memalign(size_t alignment, size_t size) = 0;
                virtual void    *malloc(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 void    stopWorld() = 0;
                virtual void    markAll() = 0;
                virtual void    finalize(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);
        int     hash_code(uint32_t key);
        hblk_hdr_t *
                alloc_hdr();
        void    free_hdr(hblk_hdr_t *hhdr);
        BOOL    get_index(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(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(size_t size, int kind);
        void    free_hblk(hblk_t *hblk);
        void    new_hblk(size_t size, int kind);

        BOOL    expand(int blocks);
        BOOL    collect_or_expand(int needed_blocks);
        void    *alloc_obj(size_t size, int kind);
        void    gc_block(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();
        void    markObj(void *ptr);
        BOOL    isMarkObj(void *ptr);
        int     getObjKind(void *ptr);

        void    *malloc(size_t size, int kind);
        void    free(void *ptr);
        void    *realloc(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_ */

