#ifndef HEAP_H
#define HEAP_H

#include <stdint.h>
#include <sys/types.h>

/* The allocator divides the heap into blocks of fixed size; large
   requests receive one or more whole blocks, and small requests
   receive a fragment of a block.  Fragment sizes are powers of two,
   and all fragments of a block are the same size.  When all the
   fragments in a block have been freed, the block itself is freed.  */

#define BLOCKLOG        12
#define BLOCKSIZE       ((unsigned int) 1 << BLOCKLOG)
#define BLOCKIFY(SIZE)  (((SIZE) + BLOCKSIZE - 1) / BLOCKSIZE)

// The amount of memory spanned by the initial heap table (not an absolute
// limit).
#define HEAP 4194304

/* Number of contiguous free blocks allowed to build up at the end of
   memory before they will be returned to the system.  */
#define FINAL_FREE_BLOCKS       8

#define RESIDUAL(addr,bsize) ((unsigned int) ((uint32_t) (addr) % (bsize)))

// The RISC OS heap manager.  This incorporates some functionality
// from the GNU mmalloc package.
class Heap
{
private:
  /* Data structure giving per-block information.  */
  typedef union
  {
    /* Heap information for a busy block.  */
    struct
    {
      /* Zero for a large block, or positive giving the
	 logarithm to the base two of the fragment size.  */
      int type;
      union
      {
	struct
	{
	  size_t nfree;   /* Free fragments in a fragmented block.  */
	  size_t first;   /* First free fragment of the block.  */
	} frag;
	/* Size (in blocks) of a large cluster.  */
	size_t size;
      } info;
    } busy;
    /* Heap information for a free block (that may be the first of
       a free cluster).  */
    struct
    {
      size_t size;            /* Size (in blocks) of a free cluster.  */
      size_t next;            /* Index of next free cluster.  */
      size_t prev;            /* Index of previous free cluster.  */
    } free;
  } malloc_info;
  
  /* List of blocks allocated with `mmemalign' (or `mvalloc').  */
  struct alignlist
  {
    struct alignlist *next;
    void *aligned;                /* The address that mmemaligned returned.  */
    void *exact;                  /* The address that malloc returned.  */
  };

  /* Doubly linked lists of free fragments.  */
  struct list
  {
    struct list *next;
    struct list *prev;
  };

  /* Statistics available to the user.  */
  struct mstats
  {
    size_t bytes_total;         /* Total size of the heap. */
    size_t chunks_used;         /* Chunks allocated by the user. */
    size_t bytes_used;          /* Byte total of user-allocated chunks. */
    size_t chunks_free;         /* Chunks in the free list. */
    size_t bytes_free;          /* Byte total of chunks in the free list. */
  };

  size_t heapsize; // Number of info entries
  char *heapbase; // Pointer to first block of the heap
  size_t heapindex; // Current search index for the heap table
  size_t heaplimit; // Limit of valid info table indices

  /* Block information table.
     Allocated with malign/__mmalloc_free (not mmalloc/mfree).  */
  /* Table indexed by block number giving per-block information.  */

  malloc_info *heapinfo;

  /* Instrumentation.  */

  struct mstats heapstats;

  /* Free list headers for each fragment size.  */
  /* Free lists for each fragment size.  */

  struct list fraghead[12];

  /* List of blocks allocated by memalign.  */
  struct alignlist *aligned_blocks;

  /* The base address of the memory region for this malloc heap.  This
     is the location where the bookkeeping data for mmap and for malloc
     begins. */
  char *base;

  /* The current location in the memory region for this malloc heap which
     represents the end of memory in use. */
  char *breakval;

  /* The end of the current memory region for this malloc heap.  This is
     the first location past the end of mapped memory. */
  char *top;

  /* Open file descriptor for the file to which this malloc heap is mapped.
     This will always be a valid file descriptor, since /dev/zero is used
     by default if no open file is supplied by the client.  Also note that
     it may change each time the region is mapped and unmapped. */
  int fd;

  // Cached copy of the host system page size.
  size_t pagesize;

  // Address to block number and vice versa.
  inline unsigned int BLOCK (void *a)
  { return (unsigned int) ((char *) a - heapbase) / BLOCKSIZE + 1; }
  inline void *ADDRESS (unsigned int  b)
  { return (void *) ((b - 1) * BLOCKSIZE + heapbase); }
  inline caddr_t PAGE_ALIGN (caddr_t addr)
  { return (caddr_t) (((long) addr + pagesize - 1) & ~(pagesize - 1)); }

  void *align (size_t size);
  void *morecore (size_t size);
  void internal_free (void *ptr);
  void *mmap_morecore (int size);

  void *mmalloc (size_t size);
  void mfree (void *ptr);
  void *mrealloc (void *ptr, size_t size);

public:
  void *Alloc (uint32_t bytes);
  void Free (void *ptr);
  void Describe (uint32_t &maxsize, uint32_t &free);
  void *Extend (void *ptr, uint32_t bytes);
  void Initialise (void *addr, uint32_t bytes);
  uint32_t Size (void) { return top - base; }
  void *Base (void) { return heapbase; }
  uint32_t BlockSize (void *ptr);
  Heap ();
  ~Heap ();
};

#endif
