/* poolsP.h: pools library private declarations - include this one
 * before the public header file pools.h !!! */

#ifndef _POOLSP_H_
#define _POOLSP_H_

/* Memory cells allocated with New() will be aligned to a POOL_ALIGN
 * byte boundary. POOL_ALIGN shall be a power of 2, greater of equal to
 * the machine word size. Failing to compile with a proper value for
 * POOL_ALIGN yields segmentation faults. */
#ifndef POOL_ALIGN
#define POOL_ALIGN (sizeof(double))
#endif

/* Define the following symbol if you want new cells in pools to be cleared
 * to zero. This takes some extra computation work in NewPoolCell(), but
 * avoids problems in software that assumes that allocated memory is cleared
 * (e.g. software that assumes that uninitialised variables or fields in
 * structs contain zeroes) */
#define POOL_CLEAR_CELLS

/* Define the following symbol for lazy garbage collection: empty pool pages
 * will then only be made available to other pools when the whole pool is 
 * empty.
 * The default behaviour is to release a page as soon as it is empty.
 * The advantage of lazy garbage collection are:
 * - minimum cell size is lower: size of just 1 pointer instead of 2 pointers
 * (= size of FREE_CHAIN struct below).
 * - faster Dispose().
 * Drawback:
 * - total memory consumption of your program may be higher because empty
 * pages are not immediately re-used. */
#undef POOL_LAZY_GARBAGE_COLLECTION

/* Define the following symbol if you want the pools library to check the
 * arguments to NewPoolCell(), Dispose() and DisposeAll(). The diadvantage
 * is that it takes some additional computations to do so. */
#undef SANITY_CHECKS

/* The following symbol determines the size of pool pages. The default is to
 * use the system memory page size, as returned with the getpagesize()
 * routine (see man getpagesize). */
/* #define POOL_PAGESIZE (16*1024) */

/* Pool pages are allocated in chuncks of this many pages. This grouping
 * has two advantages:
 * - works around a malloc() limitation of Linux: such large chunks
 * of memory are allocated in a different way by the kernel, not suffering 
 * a 0.5GB storage allocation capacity limit (suggested by Jan Prikryl, 
 * T.U.Wien)
 * - economical alignement of pages. */
#define PAGES_IN_MASTERBLOCK 1024

/* free cells chain link. Each unused cell in a pool starts with
 * a free cell chain link struct like this. The free list thus takes
 * no additional storage. The only drawback is that the minimum cell
 * size shall be large enough to contain such a free chain link. */
typedef struct FREE_CHAIN {
  struct FREE_CHAIN *next;
#ifndef POOL_LAZY_GARBAGE_COLLECTION  
  struct FREE_CHAIN *prev;     /* doubly linked list */
#endif
} FREE_CHAIN;

/* global parameters for a pool, shared by all the pages in the pool */
typedef struct POOL_STATE {
  unsigned magic;              /* magic number */
  unsigned nr_pages;           /* nr of pages in this pool */
  unsigned cells_per_page;     /* nr of cells per page */
  unsigned nr_used_cells;      /* nr of cells used in total */
  unsigned req_cell_size;      /* requested cell size */
  unsigned cell_size;          /* effective cell size, corrected for alignement */
  unsigned cell_align;         /* cell alignement */
  unsigned cell_offset;        /* nr of bytes in page before first cell */
  FREE_CHAIN *next_free_cell;  /* pointer to next free cell in pool */
  struct POOL *pages;          /* pointer to last page in pool */
  struct POOL_STATE *prev, *next; /* doubly linked list */
  char *name;                  /* name for the pool */
} POOL_STATE;

/* overhead per pool page in bytes */
#define PAGE_OVERHEAD(state) (global.pagesize - state->cells_per_page * state->req_cell_size)

/* pool page data structure - this is only the header. The pages themselves
 * are of equal size and alignement as the system memory pages as determined
 * with the getpagesize() C run time library routine. Their layout is
 * as follows:
 * - first this header
 * - next potentially some ununsed bytes up to the right byte boundary
 * - finally state->cells_per_page pages of state->cell_size bytes. 
 * cell_size is automatically computed to ensure proper alignement. */
typedef struct POOL {
  struct POOL_STATE *state;    /* global data for the pool */
  struct POOL *prev, *next;    /* pages form a doubly linked list */
  unsigned cells_used_this_page; /* cells used in this page */
} POOL;

/* global variables for the pools library */
typedef struct POOL_GLOBALS {
  int  inited;               /* TRUE if library has been initialised */
  POOL_STATE *pools;         /* pointer to registered pools */
  unsigned magic1, magic2;   /* magic numbers */
  unsigned long bytesAllocated, /* total bytes allocated */
    bytesOverhead;           /* total bytes overhead */
  unsigned pagesize;         /* system memory page size */
  unsigned align;            /* default cell alignement in bytes */
  unsigned char *masterblock, /* current group of PAGES_IN_MASTERBLOCK 
			      * pages to allocate new pages from */
    *nextpagetotake,         /* next page to take in the current master
			      * block */
    *nextfreepage;           /* next page to take from the free list
			      * (if there are pages to be recycled) */
  unsigned nrmasterblocks;   /* nr of allocated master blocks */
} POOL_GLOBALS;

/* Each masterblock contains this many additional bytes besides the pages 
 * in order to do alignement: alignement is obtained by rounding up the
 * masterblock adres returned by Alloc() to the nearest global.pagesize
 * byte boundary. */
#define MASTERBLOCKOVERHEAD (global.pagesize-1)

/* Total size of each masterblock, includes the pages + the overhead */
#define MASTERBLOCKSIZE (PAGES_IN_MASTERBLOCK*global.pagesize + MASTERBLOCKOVERHEAD)

/* Rounds up n to the next integer multiple of size */
#define ROUND_UP(n, size)  (((n + ((size)-1))/size)*size)

/* Rounds up ptr to the next integer multiple of 'align'. 'align' needs 
 * to be a power of 2. */
#define ALIGN_UP(ptr, align)  (((unsigned long)ptr + ((align)-1)) & ~((align)-1))
/* same, but rounds down */
#define ALIGN_DOWN(ptr, align) (((unsigned long)ptr) & ~((align)-1))

/* macros to compute the adress of the first, last, next and previous 
 * cell in a page. These macros do no boundary checking! */
#define FIRST_CELL_IN_PAGE(page)  ((unsigned char *)(page) + (page)->state->cell_offset)
#define LAST_CELL_IN_PAGE(page)   ((unsigned char *)FIRST_CELL_IN_PAGE(page) + ((page)->state->cells_in_page-1)*(page)->state->cell_size)
#define PREVIOUS_CELL_IN_PAGE(p, page)  ((unsigned char *)p - (page)->state->cell_size)
#define NEXT_CELL_IN_PAGE(p, page)  ((unsigned char *)p + (page)->state->cell_size)

/* iterates over all cells in a page */
#define ForAllCellsInPage(cell, page) { \
  int _i_; \
  unsigned char *_p_; \
  for (_i_=0, _p_ = FIRST_CELL_IN_PAGE(page); _i_<(page)->state->cells_per_page; _i_++, _p_ += (page)->state->cell_size) { \
    void *cell = _p_; {

#ifndef EndForAll
#define EndForAll }}}
#endif

/* checks whether page points to a valid POOL page by inspecting the magic
 * number of the pool state. 'page' shall not be a null pointer! */
#define VALID_POOL(page)   ((page)->state && (page)->state->magic == global.magic1)

#endif /*_POOLSP_H_*/
