/* Copyright 1996 Acorn Computers Ltd
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
  Title:        alloc - Storage management (dynamic allocation/deallocation)
  Copyright:    (C) 1987, Acorn Computers Ltd., Cambridge, England.
  Revision: 4.2  LDS, LH 03-Nov-89
*/

#ifndef __alloc__h
#define __alloc__h

#ifndef __stddef__h
#include <stddef.h>
#endif

/* #define CAMEL */
#define BLOCKS_GUARDED */
#define MULTITHREADED */
/* #define GC */
/* #define STATS */
/* #define DEBUG */
/* #define STACKCHECK */
/* #define VERBOSE */
/* #define ANALYSIS prints +No on allocate and -No on deallocate */
/* #define MEMDUMP gives a memory dump to screen and $.memdump when corrupt */
/*
 * if the OSStorage can not be depended upon to allocate areas of the
 *   heap in consistently increasing address order then the following
 *   constant must be set to FALSE.
 */
#define HEAP_ALLOCATED_IN_ASCENDING_ADDRESS_ORDER 1

typedef struct BlockStruct {
#ifdef BLOCKS_GUARDED
  unsigned int guard; /* guard word should contain GuardConstant if all ok */
#endif
  size_t size; /* block size (not including header) in address units. */
               /* The top 5 bits of size hold the flags declared above */
  struct BlockStruct *next; /* next and previous are used internaly in */
  struct BlockStruct *previous; /* coalescing and managing free blocks. */
                                /* next is the first word of the user block */
} Block, *BlockP;

#define OK       0
#define FAILED  -1
#define CORRUPT -2

#define SIZEMASK      0x07ffffff /* all except top five bits, when applied */
                                 /* to block.size yields no of address units */
                                 /* of user space in this block. */
#define PUREDATABIT   (1<<31) /* indicates the block contains no pointers */
#define NOTGCABLEBIT  (1<<30) /* the block is not to be garbage collected */
#define USEDBIT       (1<<29) /* if set, indicates that the block is not free */
#define FREEBIT       (1<<28) /* if set, indicates that the block is free */
#define HEAPHOLEBIT   (1<<27) /* block used for marking start of heap hole */
#define GUARDCONSTANT 0x3E694C3C /* not a legal word pointer */
#define GCABLE        0
#define GCDATA        PUREDATABIT
#define DATA          (NOTGCABLEBIT|PUREDATABIT)
#define BYTESPERWORD  sizeof(int)
/* Block.next offset from Block (in words) */
#define FIRSTUSERWORD ((sizeof(Block)-sizeof(BlockP)*2) / BYTESPERWORD)

extern void _init_alloc(void);
/*
 * Initialise allocate (not to be called by anyone outside the M2Core!)
 */

typedef void FreeProc (BlockP block);
/* block = pointer to start of header of block to be freed */

/*
 * alloc maintains a bitmap to be used by the registered GCProc, this is
 * referred to a bitmapA. If the heap maintained by alloce has a hole in it
 * due to OSStorage not extending the heap contiguously then another bitmap
 * (bitmapB) is used to describe the second part of the heap. There may or may
 * not be a bitmapB, but if heapAEnd < topOfHeap there will be. This does not
 * mean that either of the bitmaps will never represent an area of the heap
 * which contains heap holes.
 */
typedef int (*GCProc) (
  BlockP base,
  BlockP limit, /* of the heap */
  char *mapA, /* one bit per word in [base..heapAEnd-1] */
  char *mapB, /* one bit per word in [heapBStart..limit-1] */
  BlockP heapAEnd, /* base == heapA < heapAEnd */
  BlockP heapBStart, /* if heapAEnd < limit --> heapBStart == heapB < limit */
  FreeProc *freeProc);
  /* returns values OK or Corrupt. */

#ifdef GC
extern void _gcallocate(void **a, size_t bitlen, int gcbits);
/*
 * Support for AEM-2 ALLOCATE.
 */

extern void _set_gcbits(void **a, int gcbits);
/*
 * Function to set the GCAble bit and PureData bit to the given values.
 */

extern int __register_gc_proc(GCProc proc);
/*
 * alloc will (at its discretion) call the registered GarbageCollect proc
 * to search for (and instruct alloc to free) any appropriate user block
 * (depends on the gcBits it was allocated with) which has no pointer it's
 * start. Failed or OK can be returned. Failed is returned if no procedure
 * was previously registered and the heap is almost full (alloc will not be
 * able to allocate an area of the heap to hold the bitmaps required for
 * garbage collection).
 */

extern void *_gc_malloc(int gcbits, size_t bytesize);
/*
 * A variant of malloc which allocates garbage-collectable store.
 * Note that the returned pointer must not be lost or chaos may ensue.
 * 'gcbits' allows blocks to be marked as pure data (which speeds up
 * garbage collection).
 */
#endif

extern size_t _byte_size(void *p);
/*
 * Return an approximation to the allocated size of the object pointed
 * to by p. The only guarantee is that:-
 *   requested_size(p) <= _byte_size(p) <= allocated_size(p).
 */

extern void *malloc(size_t size);
/*
 * Allocate an area of memory of size 'size' and return a pointer to it.
 */

extern void *realloc(void *p, size_t size);
/*
 * Reallocate the block pointed to by p with size 'size' (assumed >
 * _byte_size(p) or this is a no-op). In general, this leads to p
 * being reallocated at a different address.
 */

extern void *_sys_alloc(size_t n);
/*
 * A paranoid variant of malloc used by the C compiler.
 */

extern void *calloc(size_t count, size_t size);
/*
 * A variant of malloc which allocates 'count' zeroed objects of size 'size'.
 */

extern void free(void *p);
/*
 * Deallocate the block pointed to by 'p'.
 */

extern void _deallocate(void **p, size_t bitlen);
/*
 * Deallocate the block pointed to by 'p' raising the AllocateFailed
 * exception on error.  The M2 DEALLOCATE function.  The bitlen value
 * is ignored.
 */

extern int __coalesce(void);
/*
 * Perform a colesce step on the heap. Returns OK or Corrupt.
 */

extern int _alloc_reinit(void);
/*
 * re-initialise the heap returns 1 for success otherwise 0.
 */

/* ---------------------------- Debug aids --------------------------------- */

#ifdef BLOCKS_GUARDED
extern void __heap_checking_on_all_deallocates(int on);
/*
 * If on = TRUE, the structure of the heap is checked on every deallocate
 */

extern void __heap_checking_on_all_allocates(int on);
/*
 * If on = TRUE, the structure of the heap is checked on every allocate
 */
#endif

/* ------------------------- Statistics reporting --------------------------*/
typedef struct StorageInfoStruct {
  BlockP heapLow; /* heap base */
  BlockP heapHigh; /* heap top */
  unsigned int userHeap; /* user part of heap = heapHigh-heapLow-gcbitmaps */
  unsigned int maxHeapRequirement; /* max storage actually requested by user */
  unsigned int currentHeapRequirement;  /* current user requested storage */
  unsigned int coalesces; /* number of coalesces performed, includes number
                             of garbage collections cos a coalesce is done
                             after every garbage collection. */
  unsigned int heapExtensions;    /* number of times that heap is extended */
  unsigned int garbageCollects;   /* number of garbage collections */
  unsigned int blocksAllocated;   /* total number of allocates requested */
  unsigned int blocksDeallocated; /* total number of deallocates requested */
  unsigned int bytesAllocated;    /* total number of bytes allocated */
  unsigned int bytesDeallocated;  /* total number of bytes deallocated */
  unsigned int totalGCBlocks;     /* total number of blocks gc'd */
  unsigned int totalGCBytes;      /* total number of bytes garbage collected */
} StorageInfo, *StorageInfoP;

typedef struct EventInfoStruct {
  int event;
  size_t blockThatCausedEvent; /* size of allocate that caused event */
  unsigned int totalFree;  /* amount of heap that user can actually write to,
                              does not include bitmaps and block overheads */
  unsigned int userHeap;   /* user part of heap = heapHigh-heapLow-gcbitmaps */
  /* the following are changes from previous event */
  unsigned int allocates;        /* number of allocates requested */
  unsigned int bytesAllocated;   /* number of bytes allocated */
  unsigned int deallocates;      /* number of deallocates requested */
  unsigned int bytesDeallocated; /* number of bytes deallocated */
  unsigned int bytesGCd;         /* number of bytes garbage collected */
  unsigned int blocksGCd;        /* number of blocks garbage collected */
} EventInfo, *EventInfoP;

#ifdef STATS
extern void _GetStorageInfo(StorageInfoP info);
/*
 * Get statistics on the current state of the storage manager
 */

/* Events */
typedef int Events;

#define GARBAGE_COLLECT 0
#define COALESCE 1
#define EXTENSION 2
#define COALESCE_AND_EXTENSION 3
#define GC_AND_EXTENSION 4
#define COALESCE_AND_GC_AND_EXTENSION 5
#define COALESCE_AND_GC 6

extern int _GetLastEvent(void);
/*
 * returns the number of the last event to happen in the storage manager.
 */

extern int _GetEventData(int event, EventInfoP info);
/*
 * get the record which describes the state of the storage manager for the
 * given event. Returns FALSE if no more records available. All records can
 * be read from GetLastEvent() downwards until FALSE is returned.
 */

extern void _NextHeapElement(
  BlockP *nextBase,         /* updated to point to start of next block */
  unsigned int *guard,      /* value of guard word */
  size_t *size,             /* size of user block in bytes */
  int *free,                /* if true, block is free */
  int *heapHole,            /* if true, block is a heap hole */
  int *bitmap,              /* if true, block is a gc bitmap */
  unsigned int *firstWord); /* first word of the user block */
/*
 * Get data describing the heap block pointed at by nextBase (first block is
 * accessed by nextBase = NIL) nextBase is set to NIL when the last block has
 * been read (not on the attempt to read the last block)
 */
#endif /* stats */

#endif