/*
 * This software and its associated documentation are protected by
 *   Copyright 1995 Geodesic Systems Inc. All Rights Reserved.
 * Portions of the software include modification to code which was
 * released publicly by Xerox Corporation, subject to the requirement that
 * the following notice be retained and included with the modified code:
 *  "Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers.
 *   Copyright (c) 1991-1995 by Xerox Corporation.
 *   All rights reserved. THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY
 *   NO WARRANTY EXPRESS OR IMPLIED. ANY USE IS AT YOUR OWN RISK."
 */
/* Fiterman, May 8, 1997 14:20 am CST */

#ifndef _GCT_H
# define _GCT_H

# ifdef __cplusplus
    extern "C" {
# endif

# include <stddef.h>
# include <stdlib.h>


#define NO_PARAMS void

#ifdef __cplusplus
# undef NO_PARAMS
#endif


#ifndef NO_PARAMS
# define NO_PARAMS
#endif

#ifdef GC_BUILD_DLL
#  define GC_SYS_IMPORT __declspec(dllimport)
#  define GC_IMPORTX  __declspec(dllexport)
#  define GC_EXPORT   __declspec(dllexport)
#  ifdef GC_CRTDLL
#	define GC_EXPORTY(t)	__declspec(dllexport) t
#  else
#   define GC_EXPORTY(t) t
#  endif

#  define GC_EXPORTX(t) GC_EXPORTY(t) __cdecl
#else /* GC_BUILD_DLL */
#  define GC_IMPORTX    __declspec(dllimport)
#  define GC_EXPORTX(t) __declspec(dllimport) t
#  ifdef __cplusplus
#	define gcInitFunction extern "C" __declspec(dllexport) void __cdecl
#  else
#	define gcInitFunction __declspec(dllexport) void
#  endif
#  define GC_EXPORTY(x) x
#endif /* GC_BUILD_DLL */
#define GC_IMPORT extern GC_IMPORTX

/*
 * Define word and signed_word to be unsigned and signed types of the
 * size as char * or void *.  There seems to be no way to do this
 * even semi-portably.  The following is probably no better/worse
 * than almost anything else
 * The ANSI standard suggests that size_t and ptr_diff_t might be
 * better choices.  But those appear to have incorrect definitions
 * on may systems.  Notably "typedef int size_t" seems to be both
 * frequent and WRONG
 */
typedef unsigned long gcWord;
typedef long gcSignedWord;

/* Public read-only variables */

GC_IMPORT gcWord gcCollections;   /* Counter incremented per collection. */

/* Public R/W variables */

/*
 * Non zero Enables logging output. This involves a performance
 * cost and is thus not the default.
 */
GC_IMPORT long gcPrintStats;

/*
 * Print memory usage statistics
 */
GC_IMPORT int gcPrintMemUsage;

/* 
 * Consider as roots all non heap mappings where pointers can be found 
 * Sunos only
 */
GC_IMPORT int gcScanAllPotentialRoots;

/* Disable signals in critical sections of the collector */
GC_IMPORT int gcDisableSignalsSwitch;

/* Memory explicitly freed before next footprint-reduce */
GC_IMPORT unsigned long gcMaxMemFreedBeforeNextFootPrintReduce;

/* 
 * Some applications like netscape with java change the location of
 * the execution stack (Argh!). This causes the collector to smash memory when
 * cleaning the stack. When this variable is set to 0, the stack is cleaned
 * just a little above the current sp. This prevents netscape with java from
 * crashing.
 */
GC_IMPORT int gcAllowUserStacks;

/*
 * Memory usage statistics are obtained at the end of a garbage collection
 * This variable forces a collection every so many bytes allocated.
 * The initialy value is 1 Mb and it can be changed at run time with
 * an environment var.
 */
GC_IMPORT unsigned long gcBytesBeforeNextStatistics;


/*
 * Non zero enables collect at end.	Defaults to 1 for report
 * libs and zero otherwise.
 */
GC_IMPORT int gcCollectAtEnd;

/*
 * Defaults to zero which implies not flushing the log file.
 * If gcFlushLog > 0 flush the log file every gcFlushLog lines.
 */
GC_IMPORT int gcFlushLog;

/*
 * Normally non leaf objects are zeroed to avoid spotting pointers
 * in them when they are reallocated. Setting this to zero prevents
 * that and may speed up some programs.
 */
GC_IMPORT int gcZeroAllocatedObject;

/*
 * Non zero enables free(). zero causes free() to be ignored.
 * defaults to one. Not used in the report libraries.
 */
GC_IMPORT char gcEnableFree;

/*
 * gcFixPrematureFrees() sets gcEnableFree to 0; and other 
 * tuning stuff. Fixing premature frees may increase memory 
 * usage. gcStopFixingPrematureFrees(NO_PARAMS) has the opposite
 * effect. Both functions return nonzero if premature frees
 * were previously being fixed.
 */
GC_EXPORTX(int) gcFixPrematureFrees(NO_PARAMS);
GC_EXPORTX(int) gcStopFixingPrematureFrees(NO_PARAMS);

/*
 * If gcEnableFree is zero and gcFreeProcessOldObject is non
 * zero it will be called to process the free()ed object.
 * gcFreeProcessOldObject defaults to zero.	gcDefaultFreeProcessOldObject
 * will zero old objects which seems a reasonable.
 */
typedef void (* gcObjectFunction)(void *obj, size_t size);
GC_EXPORTX(void) gcDefaultFreeProcessOldObject(void *obj, size_t size);

GC_IMPORT gcObjectFunction gcFreeProcessOldObject;


/*
 * This points to the name of the log file. It is initially aimed at
 * "gc.log", it is used to create the log file the first time output
 * is done, after that it is never used. This constant is contained
 * in a separate module so it can be replaced before startup.
 */
typedef const char * gcConstCharStar;
GC_IMPORT gcConstCharStar gcLogFile;

/*
 * Activates stack tracing on the position where items are allocated.
 * Only used when linked to a debug library.
 */
GC_IMPORT int gcShowStackTrace;

/*
 * When an attempted allocation fails, Great Circle must decide whether to
 * collect or expand the heap.	gcNotTransparent != 0 says always expand
 * this until stopped by gcSetMaxHeapSize or a failure to expand.
 * gcDontExpand != 0 says always collect but then expand if that isn't enough.
 *
 * Otherwise collect if (M > N/(gcPriority + 1)) where N is the heap size plus
 * a rough estimate of the root set size, and M is the amount allocated since
 * the last complete collection.
 *
 * Initially, gcPriority = 3, increasing its value will use less space but
 * more collection time. Decreasing it will appreciably decrease collection
 * time at the expense of space. gcPriority = 0 will cause the equation to
 * always choose expand. Setting incremental mode or pseudo incremental mode
 * effectivly doubles gcPriority. Since a single paging operation is likely
 * to eat more time than the collector ever could, increasing the number of
 * collections has the paradoxical effect of speeding up some programs.
 *
 * If you call gcAttemptCollection() the equation (M > N/(gcPriority + 1))
 * will determine if a collection actually takes place.
 */
GC_IMPORT unsigned gcPriority;
GC_IMPORT int gcNotTransparent;	
GC_IMPORT int gcDontExpand;

/*
 * Number of partial collections between full collections.
 * Matters only if gcIncremental is set.
 */
GC_IMPORT int gcFullFrequency;
			
#ifdef GC_DEBUG
#define GC_DEBUG_BOUNDS_CHECK 1
#define GC_DEBUG_LINE_NUMBERS 1
#define GC_WRAPPED_NEW new(__FILE__, __LINE__)
#else
#define GC_WRAPPED_NEW new
#endif

/* Public procedures */

/*
 * General purpose allocation routines, with malloc calling conventions.
 * The leaf versions promise that no relevant pointers are contained
 * in the object.  The nonleaf versions guarantee that the new object
 * is cleared. gcMallocManual allocates an object that is scanned
 * for pointers to collectible objects, but is not itself collectible.
 */
GC_EXPORTX(void *) gcMalloc(size_t size_in_bytes);
GC_EXPORTX(void *) gcMallocLeaf(size_t size_in_bytes);
GC_EXPORTX(void *) gcMallocManual(size_t size_in_bytes);

/*
 * Explicitly deallocate an object.  Dangerous if used incorrectly.
 * Requires a pointer to the base of an object.
 * An object should not be enabled for finalization when it is
 * explicitly deallocated.
 * gcFree(0) is a no-op, as required by ANSI C for free.
 */
  GC_EXPORTX(void) gcFree(void * object_addr);

/*
 * gcMallocIgnoreOffPage reduces the chance of accidentally retaining
 * a large, > 4096 byte, object as a result of scanning an integer
 * that happens to be an address inside the array. Large arrays usually
 * are only used where there is a pointer to the beginning of the array.
 *
 * This was built around the needs of very large Xwindows programs, we
 * have discovered that it works well with almost all programs, improving
 * space and speed efficiency.
 *
 * gcMallocIgnoreOffPage(lb) acts like malloc(lb) except that only pointers
 * to the first 4096 bytes will be used by the collector to keep the
 * object alive. If lb is smaller than 4K it acts exactly like malloc.
 *
 * gcMallocIgnoreOffPage is the connection for malloc(lb) where
 * lb > gcVeryLargeAllocationSize && gcIgnoreOffPage
 *
 * gcVeryLargeAllocationSize is initialized to 100000.
 *
 * gcIgnoreOffPage is in a separate module in the libraries to
 * allow you to replace it in programs where you have no source code, or
 * where you need it replaced before startup code runs. This also effects
 * calloc and realloc. It is initialized to 1 except in the gcsome and
 * gcsomedb libraries where it is initialized to 0.
 *
 * If we need a block N bytes long and have a block > N + gcBlackSizeLimit
 * and N > gcBlackSizeLimit but all possible positions in it are
 * the targets of apparent pointers, we use it anyway and print a warning.
 * This risks leaking the block due to a false reference. But not using
 * it risks unreasonable immeadiate heap growth. gcBlackSizeLimit defaults
 * to 100K.
 */ 
GC_EXPORTX(void *) gcMallocIgnoreOffPage(size_t lb);
GC_EXPORTX(void *) gcMallocLeafIgnoreOffPage(size_t lb);

GC_IMPORT unsigned long gcVeryLargeAllocationSize;
GC_IMPORT int gcIgnoreOffPage;
GC_IMPORT long gcBlackSizeLimit;

/* Kinds of objects */
# define gcLeafObject							0
# define gcCollectibleObject					1
# define gcManualObject							2

/*
 * Return the kind of an object, gcLeafObject etc.
 */
GC_EXPORTX(int) gcWhatKind(void *p);

/*
 * Return a pointer to the base (lowest address) of an object given
 * a pointer to a location within the object
 * Return 0 if displaced_pointer doesn't point to within a valid object.
 * gcIsValidPointer returns the start of the users area.
 * gcBase may point to debug information if present.
 */
GC_EXPORTX(void *) gcIsValidPointer(const void * displaced_pointer);
GC_EXPORTX(void *) gcBase(const void * displaced_pointer);

/*
 * Check object with debugging info. Return kinds of
 * damage found as bit flags.
 *  1 User size smashed
 *  2 Start Flag smashed
 *  4 Near End flag smashed
 *  8 Far End flag smashed
 * 16 Previously freed.
 * 32 not a collectible object
 * 64 too small for a debug object od debug lib not used
 */
GC_EXPORTX(int) gcObjectCheck(const void *ohdr);

/*
 * Given a pointer into an object, return its size in bytes. gcFullSize
 * includes the debug header.
 */
GC_EXPORTX(size_t) gcSize(const void * object_addr);
GC_EXPORTX(size_t) gcFullSize(const void * object_addr);

/*
 * A realloc()'ed object has the same collection kind as the original
 */
GC_EXPORTX(void *) gcRealloc(void * old_object, size_t new_size_in_bytes);

/* 
 * Logging and diagnostic output.
 *
 * gcPrintf, gcErrorPrintf, gcWarningPrintf, gcAbort, gcReportLeak,
 * gcAbortPrintf and gcLogFileAbort all point at functions used for 
 * various kinds of logging. The are initialized to point to functions 
 * who`s names are gcDefaultPrintf, gcDefaultErrorPrintf etc.
 *
 * gcDefaultPrintf and gcDefaultErrorPrintf both print to the log file.
 *
 * gcDefaultWarningPrintf() only prints to the logfile if gcPrintStats
 * is nonzero. Otherwise it does nothing.
 * 
 * gcDefaultAbortPrintf() formats a line and calls gcAbort.
 *
 * gcDefaultAbort() prints and exits.
 *
 * gcReportLeak() is used by the gcreport and gcreptdb libs to report
 * leaks. It's work is primarily done by gcPrintObj().
 *
 * If gcLogAllLeaks is nonzero, all leaks are reported to the log file.
 * If nonzero, only the first gcMaximumLeaksToLogFile are printed.
 *
 * void gcPuts() invokes via the function pointer gcPutsFunction which
 * defaults to gcDefaultPuts. In the threads libraries it locks and unlocks
 * the log file to prevent chaos. gcDefaultPuts discards calls made before
 * the collector is initialized.
 *
 * void gcDefaultPuts(const char *msg); writes to gcLogFile and insures it
 * is flushed every gcFlushLog lines. It is used by the above logging
 * functions. It may call gcLogFileAbort() which reports failures in using 
 * gcLogFile and then aborts, hopefully a rare event. In GUI environment and 
 * other situations, the user may wish to replace this. The function of 
 * gcDefaultLogFileAbort is 
 *   sprintf(buf, "%s of %s failed.\n", msg, gcLogFile);
 *   display buf somehow with the log file dead.
 *   abort();
 */
typedef void (* gcPrintFunction)(const char *, ...);
typedef void (* gcPutFunction)(const char *msg);

GC_EXPORTX(void) gcDefaultPrintf(const char *, ...);
GC_EXPORTX(void) gcDefaultAbortPrintf(const char *, ...);
GC_EXPORTX(void) gcDefaultWarningPrintf(const char *, ...);
GC_EXPORTX(void) gcDefaultErrorPrintf(const char *, ...);
GC_EXPORTX(void) gcDefaultAbort(const char *msg);
GC_EXPORTX(void) gcPuts(const char *msg);
GC_EXPORTX(void) gcDefaultPuts(const char *msg);
GC_EXPORTX(void) gcDefaultLogFileAbort(const char *msg);
GC_EXPORTX(void) gcDefaultReportLeak(const char *msg);

GC_IMPORT gcPrintFunction gcPrintf,	gcAbortPrintf, gcWarningPrintf, 
		  gcErrorPrintf;
GC_IMPORT gcPutFunction gcAbort, gcLogFileAbort, gcReportLeak, gcPutsFunction;
GC_IMPORT int gcLogAllLeaks;
GC_IMPORT unsigned gcMaximumLeaksToLogFile;
GC_IMPORT int gcGUIEnabled;

/*
 * p points to somewhere inside an object.
 * Print a human readable description of the object using gcPrintf. If the
 * object has debugging information, this will all be printed as well.
 */
GC_EXPORTX(void) gcPrintObject(const void *p);

/*
 * Returns the debugging information
 */
GC_EXPORTX(char *) gcDebugString(const void *p);

GC_EXPORTX(int) gcDebugInt(const void *p);

/*
 * Explicitly increase the heap size. Returns 0 on failure, 1 on success.
 * If you can use this to set your heap size to near its final value
 * your program will run more efficiently due to fewer collection cycles
 * and more efficient data structures. gcLogFile will show log when collector
 * expands the heap and how much. You may want to use gcNotTransparent 
 * and gcSetMaxHeapSize() instead.
 */
GC_EXPORTX(int) gcExpandHeap(size_t number_of_bytes);

/*
 * Limit the heap size to n bytes.  Useful when you're debugging
 * especially on systems that don't handle running out of memory well
 * n == 0 ==> unbounded.  This is the default
 */
GC_EXPORTX(void) gcSetMaxHeapSize(long n);

/*
 * gcClearRoots clears the set of root segments.
 * gcAddRoots Adds a root segment. 
 * gcDeclareLeafRoot declares all or part of a root segment as leaf. 
 * All for wizards only. 
 */
GC_EXPORTX(void) gcClearRoots(NO_PARAMS);
GC_EXPORTX(void) gcAddRoots(const char *low_address, 
							const char *high_address_plus_1);
GC_EXPORTX(void) gcDeclareLeafRoot(const char *low_address, 
								   const char *high_address_plus_1);

/* Explicitly trigger a full, world-stop collection. 	*/
GC_EXPORTX(void) __cdecl gcCollect(NO_PARAMS);

/*
 * Trigger a full world-stopped collection.  Abort the collection if
 * and when stop_func returns a nonzero value.  gcIdleTest will be
 * called frequently, and should be reasonably fast.  This works even
 * if virtual dirty bits, and hence incremental collection is not
 * available for this architecture.  Collections can be aborted faster
 * than normal pause times for incremental collection.  However,
 * aborted collections do no useful work; the next collection needs
 * to start from the beginning.
 */
typedef int (* gcIdleTestFunction)(NO_PARAMS);
GC_EXPORTX(int) gcAttemptCollection(NO_PARAMS);

/*
 * Explicitly trigger a full world-stop collection followed by
 * an explicit foot print reduce, or attempt to do so. Footprint
 * reduce wont free memory used since the last footprint reduce.
 * Normally this is correct but to do an extreme job call twice.
 */
GC_EXPORTX(void) gcFootPrintReduce(NO_PARAMS);
GC_EXPORTX(int) gcAttemptFootPrintReduce(NO_PARAMS);

/*
 * In windows mode this defaults to gcMSWinIdleTest otherwise it
 * starts null and must be aimed by the user. Only used by gcAttemptCollection
 * and gcEnablePseudoIncremental.
 */
GC_IMPORT gcIdleTestFunction gcIdleTest;

/*
 * In Pseudo incremental mode Great Circle will periodically call
 * gcAttemptCollection();
 * in an attempt to free storage before increasing allocated heap.
 * There is actually a complex heuristic involved using the amount
 * allocated since the last collection and a few other things.
 */
GC_EXPORTX(int) gcEnablePseudoIncremental(NO_PARAMS);
GC_EXPORTX(int) gcDisablePseudoIncremental(NO_PARAMS);

/*
 * gcNeverStopFunc can be used as a gcIdleTestFunction, gcAttemptCollection 
 * recongizes it and is extra efficient.
 */
GC_EXPORTX(int) gcNeverStopFunc(NO_PARAMS);

/*
 * gcMSWinIdleTest is an idle test designed for the Windows environment.
 * It returns a 1 if there are windows events to process.
 */
GC_EXPORTX(int) gcMSWinIdleTest(NO_PARAMS);


/*
 * Return the number of bytes in the heap.  Excludes collector private
 * data structures.  Includes empty blocks and fragmentation loss.
 */
GC_EXPORTX(size_t) gcGetHeapSize(NO_PARAMS);

/* Return the number of bytes allocated since the last collection.	*/
GC_EXPORTX(size_t) gcGetBytesSinceGc(NO_PARAMS);

/*
 * Enable incremental/generational collection.
 * Don't use in leak finding mode.
 */
GC_EXPORTX(void) gcEnableIncremental(NO_PARAMS);

/*
 * Perform some garbage collection work, if appropriate.
 * Return 0 if there is no more work to be done.
 * Typically performs an amount of work corresponding roughly
 * to marking from one page.  Does nothing if incremental collection is
 * disabled.  It is reasonable to call this in a wait loop
 * until it returns 0. Returns 0 if not in incremental mode.
 */
GC_EXPORTX(int) gcMinWork(NO_PARAMS);

/*
 * This sets the scan alignment. gcSetScanAlignment(4) says all pointers
 * will be found on a 4 boundary. gcSetScanAlignment(1) says pointers may
 * be on any byte boundary. This returns True on success False on failure.
 * It hopefully defaults to the values used by the compiler to align pointers
 * and should not be reset unless you force pointers to odd boundaries.
 * This is checked to be 8, 4, 2 or 1 and minamum value.
 */
GC_EXPORTX(int) gcSetScanAlignment(int align);

/* Get current scan Alignment */
GC_EXPORTX(int) gcGetScanAlignment(NO_PARAMS);

/*
 * Debugging (annotated) allocation.  gcCollect will check
 * objects allocated in this way for overwrites, etc. See
 * #ifdef GC_DEBUG at the end of this file.
 */
GC_EXPORTX(void *) __cdecl gcMallocDebug(size_t size_in_bytes,
				const char * descr_string, int descr_int);
GC_EXPORTX(void *) __cdecl gcMallocLeafDebug(size_t size_in_bytes,
  				       const char * descr_string, int descr_int);
GC_EXPORTX(void *) __cdecl gcMallocIgnoreOffPageDebug(size_t size_in_bytes,
  				const char * descr_string, int descr_int);
GC_EXPORTX(void *) __cdecl gcMallocLeafIgnoreOffPageDebug(size_t size_in_bytes,
  				       const char * descr_string, int descr_int);
GC_EXPORTX(void *) __cdecl gcMallocManualDebug(size_t size_in_bytes,
  				           const char * descr_string, int descr_int);
GC_EXPORTX(void) __cdecl gcFreeDebug(void * object_addr);
GC_EXPORTX(void *) __cdecl gcReallocDebug(void * old_object,
  			 	 size_t new_size_in_bytes,
  			 	 const char * descr_string, int descr_int);
GC_EXPORTX(void *) __cdecl gcGlobalMallocDebug(size_t size,
									const char * descr_string,
									int lineNo);

#define gcCalloc(size, num) gcMalloc((size) * (num))

/*
 * Finalization.  gcDeclareFinalizer[Offset] uses an ignore 
 * selfpointers form required by C++. The Offset form is required by C++.
 * See the C++ section of this file for an example.
 * gcDeclareFinalizerNoPointers declares the finalized object has no pointers
 * to other objects that it requires at finalization time.
 */
typedef void (* gcFinalizationProc)(void * obj);
GC_EXPORTX(void) gcRegisterFinalizer(void * obj,
		gcFinalizationProc fn, void * cd);
GC_EXPORTX(void) gcDeclareFinalizer(void * obj, gcFinalizationProc fn);
GC_EXPORTX(void) gcDeclareFinalizerNoPointers(void * obj, 
	  gcFinalizationProc fn, void * cd);
GC_EXPORTX(void) gcDeclareFinalizerOffset(void * obj,
		gcFinalizationProc fn, void * cd);

/*
 * The following routine may be used to break cycles between
 * finalizable objects, thus causing cyclic finalizable
 * objects to be finalized in the correct order.  Standard
 * use involves calling gcPtrNotUsedByFinalizer(&p)
 * where p is a pointer that is not used by finalization
 * code, and should not be considered in determining
 * finalization order.
 */
GC_EXPORTX(int) gcPtrNotUsedByFinalizer(void **link);

/*
 * If you have called gcDisappearingPtr(link, obj), then *link
 * will be automatically zeroed when the data pointed to by
 * obj becomes inaccessible. This will happen before any finalization
 * occurs.
 *
 * Returns 1 if link was already registered, 0 otherwise.
 *
 * gcDisappearingPtr is often used when implementing weak pointers
 * (pointers that are not traced during collection). By ensuring that
 * the weak pointer is zeroed if the data it is pointing to goes away,
 * the danger of following a loose weak pointer is eliminated.
 *
 * In this case, have link point to a location holding
 * a disguised pointer to obj.  (A pointer inside a "leaf"
 * object is efficiently disguised.) The pointer is zeroed
 * when obj becomes inaccessible. Each link may be registered only
 * once. However, it should be unregistered and reregistered if
 * the pointer is modified to point at a differenct object.
 *
 * Note that obj may be resurrected by another finalizer.
 */
GC_EXPORTX(int) gcDisappearingPtr(void ** link, void * obj);
GC_EXPORTX(int) gcUnregisterDisappearingPtr(void ** link);

/*
 * Converting a hidden pointer to a real pointer requires verifying
 * that the object still exists.  This involves acquiring the
 * allocator lock to avoid a race with the collector.
 */
typedef void * (*gcFnType)(void *);
GC_EXPORTX(void *) gcCallWithAllocLock(gcFnType fn, void * client_data);

/*
 * If p and q point to the same object returns p else calls gcAbort()
 * Succeeds if neither p nor q points to the heap.
 */
GC_EXPORTX(void *) gcSameObj(void *p, void *q);
GC_EXPORTX(void) gcSetDirty(void *);
GC_EXPORTX(int) gcInSameObj(void *, void *);

/*
 * Safer, but slow, pointer addition.  Probably useful mainly with
 * a preprocessor.  Useful only for heap pointers.
 */
#ifdef GC_DEBUG_BOUNDS_CHECK
#   define GC_PTR_ADD(x, n)  ((gcSameObj((void *)((x)+(n)), (void *)(x))), ((x)+(n)))
#else	/* !GC_DEBUG_BOUNDS_CHECK */
#   define GC_PTR_ADD(x, n) ((x)+(n))
#endif

/* Safer assignment of a pointer to a nonstack location.	*/
#ifdef GC_DEBUG_BOUNDS_CHECK
#   define GC_PTR_STORE(p, q) \
	(*(void **)gcIsVisible(p) = gcIsValidDisplacement(q))
#else /* !GC_DEBUG_BOUNDS_CHECK */
#   define GC_PTR_STORE(p, q) *((p) = (q))
#endif

/*
 * gcHidePointer takes a pointer and flips its bits so Great Circle
 * wont recognise it as a pointer. gcRevealPointer flips them back.
 */
# define gcHidePointer(p) (~(gcWord)(p))
# define gcRevealPointer(p) ((void *)(gcHidePointer(p)))

/*
 * Under Windows there are operating system calls to get memory
 * (Global|Local)|(Alloc|ReAlloc). By default, we pass these calls
 * on to Windows, although we still scan such memory for pointers.
 * You can redefine this behavior in your gcInitialize() function
 * by settin gcAllocBehavior to GC_INTERCEPT. In this case, Great
 * Circle will garbage collect memory allocated by (Global|Local)|(Alloc|ReAlloc)
 * except when allocated with the (GMEM|LMEM)_(MOVEABLE|DISCARDABLE) flags.
 */
#define GC_INTERCEPT		 0	/* intercept calls */
#define GC_PASS_THROUGH		 1	/* DEFAULT: pass through calls trace */
                                /* their results. */
#define GC_TRACE_ALL		 2  /* pass through calls trace their results */
                                /* on a per-object basis. */

GC_IMPORT int gcAllocBehavior;
GC_IMPORT int gcAllocWarn;	/* warnings for (Global|Local)|(Alloc|ReAlloc) */

GC_EXPORTX(const char *) gcGetDllName();
GC_IMPORT int gcDontInterceptCRunTimeDLL;

/*
 * Allocates a page, generally 4K, of objects and returns them as a list
 * linked through their first word.  Its use can greatly reduce lock 
 * contention problems in threaded systems, since the allocation lock 
 * can be acquired and released many fewer times. In unthreaded systems
 * this is pointless. These are always non debug objects.
 */
GC_EXPORTX(void *) gcMallocMany(size_t lb);

/* Retrive the next item in list reutrned by gcMallocMany */
#define GC_NEXT(p) (*(void **)(p)) 
 
#   define GC_INIT()


/*
 * Call to register root segments.
 */
GC_EXPORTX(void) gcRegisterDLL(char * static_root);

/* these are used to identify the library used */
GC_EXPORTX(const char *) gcLibrary(NO_PARAMS);    /* gcall etc */
GC_EXPORTX(const char *) gcCompiler(NO_PARAMS);   /* compiler used */
GC_EXPORTX(int) gcVersion(NO_PARAMS); /* version number * 100, 1.1 -> 110 */
GC_EXPORTX(int) gcBuildNo(NO_PARAMS); /* Build number */
GC_EXPORTX(int) gcThreads(NO_PARAMS); /* 1 if thread safe 0 otherwise */
GC_EXPORTX(long) gcEvaluationCopy();  /* Returns non zero for eval copies */

/*
 * gcFreeX is exactly like free but has a consistent interface.
 * some systems have a free that cleverly returns an int.
 */
GC_EXPORTX(void) gcFreeX(void *);

enum gcNewType {
	gcMemDefault = 0, /* don't change threads code uses values */
	gcMemAuto    = 1,
	gcMemLeaf    = 2,
	gcMemManual  = 3,
	gcMemAutoIgn = 4,
	gcMemLeafIgn = 5
};

#ifdef __cplusplus
    }  /* end of extern "C" */
/* C++ Interface to GCTransparent */







# include <new.h>
#define gcSetDirty(x)


#define GC_CLASS_HAS_POINTERS \
  void * operator new(size_t s) { return gcNewDefaultAuto(s); } \
  void * operator new(size_t s, char *f, int l) \
    { return gcNewDefaultAuto(s, f, l); } \
  void operator delete( void* obj ) { gcFreeX(obj); }
#define GC_CLASS_HAS_NO_POINTERS \
  void * operator new(size_t s) { return gcNewDefaultLeaf(s); } \
  void * operator new(size_t s, char *f, int l) \
    { return gcNewDefaultLeaf(s, f, l); } \
  void operator delete( void* obj ) { gcFreeX(obj); }
#define GC_CLASS_IS_MANUAL \
  void * operator new(size_t s) { return gcNewDefaultManual(s); } \
  void * operator new(size_t s, char *f, int l) \
    { return gcNewDefaultManual(s, f, l); } \
  void operator delete( void* obj ) { gcFreeX(obj); }


GC_EXPORTX(void *) gcNewDefaultAuto(size_t s);
GC_EXPORTX(void *) gcNewDefaultAuto(size_t s, const char *file, int line);
GC_EXPORTX(void *) gcNewDefaultLeaf(size_t s);
GC_EXPORTX(void *) gcNewDefaultLeaf(size_t s, const char *file, int line);
GC_EXPORTX(void *) gcNewDefaultManual(size_t s);
GC_EXPORTX(void *) gcNewDefaultManual(size_t s, const char *file, int line);

GC_EXPORTY(void *) __cdecl operator new(size_t size);
GC_EXPORTY(void *) __cdecl operator new(size_t size, const char *file, int line);
GC_EXPORTY(void) __cdecl operator delete(void *);



/*
 * Instances of classes derived from "gc" will be allocated in the 
 * collected heap by default, unless an explicit placement is
 * specified.
 */
GC_EXPORTY(class) gc {
public:
  GC_CLASS_HAS_POINTERS
  void* operator new(size_t, void *p) { return p; }
};

extern "C" {
  GC_EXPORTX(void) gcSetAllocator(gcNewType);
}

#define GC_NEW(x)					   (gcSetAllocator(gcMemAuto), new x)
#define GC_NEW_LEAF(x)				   (gcSetAllocator(gcMemLeaf), new x)
#define GC_NEW_MANUAL(x)			   (gcSetAllocator(gcMemManual), new x)
#define GC_NEW_IGNORE_OFF_PAGE(x)	   (gcSetAllocator(gcMemAutoIgn), new x)
#define GC_NEW_LEAF_IGNORE_OFF_PAGE(x) (gcSetAllocator(gcMemLeafIgn), new x)

#define GC_NEW_ARRAY(s, t) \
 (new gcArrayBase(GC_NEW(t[s]), s, sizeof(t)))
#define GC_NEW_LEAF_ARRAY(s, t) \
 (new gcArrayBase(GC_NEW_LEAF(t[s]), s, sizeof(t)))
#define GC_NEW_MANUAL_ARRAY(s, t) \
 (new gcArrayBase(GC_NEW_MANUAL(t[s]), s, sizeof(t)))
#define GC_NEW_LEAF_IGNORE_OFF_PAGE_ARRAY(s, t) \
 (new gcArrayBase(GC_NEW_LEAF_IGNORE_OFF_PAGE(t[s]), s, sizeof(t)))
#define GC_NEW_IGNORE_OFF_PAGE_ARRAY(s, t) \
 (new gcArrayBase(GC_NEW_IGNORE_OFF_PAGE(t[s]), s, sizeof(t)))
/*
 * Class gcLWCleanup behaves like gcCleanup except that it does not inherit
 * from class gc. Therefore, you must be sure that your class is being
 * allocated as garbage collected (e.g. if you are using the gcall library).
 * gcLWCleanup is useful when private inheritance is making class gc's
 * operator new inaccessible.
 */
GC_EXPORTY(class) gcLWCleanup {
public:
	inline gcLWCleanup();
inline virtual ~gcLWCleanup();
private:
	inline static void cleanup( void* obj );
};

class gcCleanup: virtual public gc, virtual public gcLWCleanup {};

/*
 * Instances of classes derived from "gcCleanup" will be allocated
 * in the collected heap by default.  When the collector discovers an
 * inaccessible object derived from "gcCleanup" or containing a
 * member derived from "gcCleanup", its destructors will be
 * invoked.
 */

inline gcLWCleanup::~gcLWCleanup() {
	 gcDeclareFinalizer(this, 0);
}

inline void gcLWCleanup::cleanup( void* obj ) {
	((gcLWCleanup*)obj)->~gcLWCleanup();
}

inline gcLWCleanup::gcLWCleanup() {
	 void* base;

	 if (0 != (base = gcBase((void *)this)))
		  gcDeclareFinalizerOffset(base,
								 (gcFinalizationProc)cleanup,
								 (void*)this);
}

#include <assert.h>

#define GC_TEMPLATE template<class T>


#ifndef GC_DEBUG
#define GC_ASSERT(cond, message)
#else
#define GC_ASSERT(cond, message) if (!(cond)) \
  gcErrorPrintf("%s\n", message);
#endif

/* Parent for wrapped data pointers and references. */
template<class T>
class gcWrap {
public:
  int gcPtrNotUsedByFinalizer() {
	return ::gcPtrNotUsedByFinalizer((void **)&data);
  }
protected:
  gcWrap() { 
	setPointer(); 
  }
  gcWrap(T *p) { 
	setPointer(p);
  }
  gcWrap(const gcWrap< T > &p) {
	setPointer(p);
  }
  /* This exists because stack frames may not be zeroed. */
  ~gcWrap() {
	setPointer();
  }
  void dirtyPointer() { 
	gcSetDirty(&data);
  }
  void setPointer() { 
	data = 0; 
  }
  void setPointer(T* p) { 
	dirtyPointer(); 
	data = p; 
  }
  void setPointer(const gcWrap< T > &p) { 
	dirtyPointer();	
	data = p.data; 
  }

  T* data;			/* The pointer to the actual object */
};


template<class T>
class gcPtr : public gcWrap<T> {
public:
	gcPtr()                           {}
	gcPtr(T *p) : gcWrap<T>(p)       {}
	gcPtr(const gcPtr< T > &p) : gcWrap<T>(p) {}
	T* operator=(T *x)                 { 
		setPointer(x); 
		return data; 
	}
	T* operator=(const gcPtr< T > &x) { 
		setPointer(x); 
		return data; 
	}
	operator T*()	const              { 
		return data; 
	}
	operator void*()	const              { 
		return (void *)data; 
	}
	T* operator()() const              { 
		return data; 
	}
	T* operator->() const              { 
		return data; 
	}
	int operator!() const              { 
		return data == 0; 
	}
	int operator==(const gcPtr< T > &x) const { 
		return data == x.data; 
	}
	int operator==(T *x) const         { 
		return data == x; 
	}
	int operator!=(const gcPtr< T > &x) const { 
		return data != x.data;	
	}
	int operator!=(T *x) const         { 
		return data != x; 
	}
	T& operator[](int n) const;
	T* operator+(int n) const;
	T* operator-(int n) const;
	T* operator+=(int n);
	T* operator-=(int n);
#ifdef GC_DEBUG_BOUNDS_CHECK
	T* operator=(int n) {
		GC_ASSERT(!n, "Invalid integer to pointer assignment");
		setPointer();
		return 0;
	}
	T& operator*() const {
		GC_ASSERT(data, "Dereference of null pointer");
		return *data;
	}
#else
	T* operator=(int)                  { 
		setPointer(); 
		return 0; 
	}
	T& operator*() const               { return *data; }
#endif
};

template<class T>
inline T& gcPtr<T>::operator[]( int n ) const {
	return *(data + n); 
}

template<class T>
inline 	T* gcPtr<T>::operator+(int n) const          { 
	return data + n; 
}

template<class T>
inline 	T* gcPtr<T>::operator-(int n) const          { 
	return data - n; 
}

template<class T>
inline 	T* gcPtr<T>::operator+=(int n)               { 
	return data = (data + n); 
}

template<class T>
inline	T* gcPtr<T>::operator-=(int n)               { 
	return data = (data - n); 
}

template<class T>
class gcRef : public gcWrap<T> {
	gcRef()                           {}
	gcRef(const T &x) : gcWrap<T>((T *)&x) {}
	gcRef(const gcRef< T > &x) : gcWrap<T>(x) {}
/* The next two operators may not inline right */
	void operator=(T &x)               { 
		*data = x; 
	}
	void operator=(const gcRef< T > &x) { 
		*data = x(); 
		x.dirtyPointer(); 
	}

	T& operator()() const              { 
		return *data; 
	}
	operator T&() const                { 
		return *data; 
	}
};

/* Return values for gcInBounds tests */
enum gcArrayTest {
	gcNotAnArray,
	gcPointerOk,
	gcPointerTooLow,
	gcPointerTooHigh,
	gcPointerAtEnd
};

/* Parent of all array class templates. */
class gcArrayBase : public gc {
public:	
	gcArrayBase(void *array, size_t count, size_t size) :
		gcData(array), gcCount(count), gcItemLen(size) {}

	operator void *() const {
		return gcData;
	}
	size_t size() const {
		return gcCount * gcItemLen;
	}
	void * gcArrayTop() const {		
		return (char *)gcData + size();
	}
	size_t len() const {
		return gcCount;
	}
	void setLen(size_t newLen) {
		GC_ASSERT((gcCount >= newLen), "Array length set too long");
		gcCount = newLen;
	}
	int gcValidReference(const void *p) {
		return p < gcArrayTop() && p >= (void *)*this;
	}
	void * operator +(size_t x) {
		return (char *)(void *)*this + (x * gcItemLen);
	} 
	gcArrayTest gcInBounds(const void *newP) const {
		gcArrayTest ii = ( gcArrayTest ) 0;
		if ((unsigned long)newP < (unsigned long)(void *)*this)
			ii = gcPointerTooLow;

		if ((unsigned long)newP > (unsigned long)gcArrayTop())
			ii = gcPointerTooHigh;
		if (!ii)
			ii = (newP == gcArrayTop()) ? gcPointerAtEnd : gcPointerOk;
		return ii;
	}

private:
	void *gcData;
	size_t gcCount, gcItemLen;
};

template<class T>
class gcArrayPtr {
protected:
	T *data;					/* Pointer to array item */
	gcArrayBase * array;		/* The pointer to the actual array base or zero */

	void dirtyPointer() { 
        gcSetDirty(&data);
	}
	void setPointer() { 
		data  = 0;
		array = 0;
	}
	void setPointer(T* p) { 
		dirtyPointer(); 
		data  = p; 
		array = 0;
	}
	void setPointer(const gcArrayPtr< T > &p) { 
		dirtyPointer();	
		data  = p.data; 
	    array = p.array;
	}
	void setPointer(gcArrayBase *p) { 
		dirtyPointer();	
		data  = (T*)(void *)*p; 
	    array = p;
	}
public:
	gcArrayPtr() { 
		setPointer(); 
	}
	gcArrayPtr(T *p) {
		setPointer(p);
	}
	gcArrayPtr(const gcArrayPtr< T > &p) {
		setPointer(p);
	}
	gcArrayPtr(gcArrayBase *p) {
		setPointer(p);
	}
	int gcPtrNotUsedByFinalizer() {
		::gcPtrNotUsedByFinalizer((void **)&array);
		return ::gcPtrNotUsedByFinalizer((void **)&data);
	}
	T* operator=(gcArrayBase *x) {
		setPointer(x);
		return data;
	}
	T* operator=(T *x) {
		array = 0;
		setPointer(x);
		return data;
	}
	T* operator=(const gcArrayPtr< T > &x) {
		setPointer(x);
		return data;
	}
	operator void*()	const              { 
		return (void *)data; 
	}
	T* operator()() const {
		return data;
	}
	int operator!() const {
		return data == 0;
	}
	int operator==(const gcArrayPtr< T > &x) const {
		return data == x.data;
	}
	int operator==(T *x) const {
		return data == x;
	}
	int operator!=(const gcArrayPtr< T > &x) const {
		return data != x.data;
	}
	int operator!=(T *x) const {
		return data != x;
	}
	T* operator+(int n) const {
                return data + n;
        }
	T* operator-(int n) const {
		return (*this) + -n;
	}
	T* operator+=(int n) {
		return data = (*this + n);
	}
	T* operator-=(int n) {
		return data = (*this + -n);
	}
	size_t len() const {
		return array ? array->len() : 0;
	}
	size_t size() const {
		return array ? array->size() : 0;
	}
	void setLen(size_t l) {
		if (array)
			array->setLen(l);
	}
#ifdef GC_DEBUG_BOUNDS_CHECK
	T* operator=(int n) {
		GC_ASSERT(!n, "Invalid integer to pointer assignment");
		setPointer();
		return 0;
	}																
	T& operator[](int n) {
		GC_ASSERT(array->gcValidReference((void *)(data + n)),
			"Invalid array reference");
		return *(*this + n);
	}
	T& operator	 *() const {
		GC_ASSERT(array->gcValidReference((void *)data),
			"Invalid array pointer dereferenced");
		return *data;
	}
#else
	T* operator=(int) {
		setPointer();
		return 0;
	}
	T& operator*() const {
		return *data;
	}
	T& operator[](int n) {
		return *(*this + n);
	}
#endif

	gcArrayTest gcInObject(void *loc) const {
		gcArrayTest ii = ( gcArrayTest ) 0;
		if (!data)
			ii = gcNotAnArray;
		else if (array) {
			if (loc < (void *)*array)
				ii = gcPointerTooLow;
			else if (loc > array->gcArrayTop())
				ii = gcPointerTooHigh;
			else if (loc == array->gcArrayTop())
				ii = gcPointerAtEnd;
			else
				ii = gcPointerOk;
		}
		else if (gcInSameObj((void *)data, (void *)loc))
			ii = gcPointerOk;
		else
			ii = ((void *)loc > (void *)data) ? gcPointerTooHigh : gcPointerTooLow;
		return (ii);
	}
	gcArrayTest gcInBounds(int n) const {
		return gcInObject(data + n);
	}
};

#endif /* __cplusplus */

# ifdef GC_DEBUG_LINE_NUMBERS
#   define malloc(sz) gcGlobalMallocDebug((sz), __FILE__, __LINE__)
#   define calloc(sz, cnt) gcGlobalMallocDebug((sz)*(cnt), __FILE__, __LINE__)
#   define gcMalloc(sz) gcMallocDebug((sz), __FILE__, __LINE__)
#   define gcMallocLeaf(sz) gcMallocLeafDebug((sz), __FILE__, __LINE__)
#   define gcMallocIgnoreOffPage(sz) gcMallocIgnoreOffPageDebug((sz), __FILE__, __LINE__)
#   define gcMallocLeafIgnoreOffPage(sz) gcMallocLeafIgnoreOffPageDebug((sz), __FILE__, __LINE__)
#   define gcMallocManual(sz) gcMallocManualDebug((sz), \
							__FILE__, __LINE__)
#   define gcRealloc(old, sz) gcReallocDebug((old), (sz), __FILE__, \
							       __LINE__)
#   define realloc(old, sz) gcReallocDebug((old), (sz), __FILE__, \
							       __LINE__)
#   define gcFree(p) gcFreeDebug(p)
# endif
#endif /* _GCT_H */