/*

  silcstack_i.h

  Author: Pekka Riikonen <priikone@silcnet.org>

  Copyright (C) 2003 - 2005 Pekka Riikonen

  The contents of this file are subject to one of the Licenses specified 
  in the COPYING file;  You may not use this file except in compliance 
  with the License.

  The software distributed under the License is distributed on an "AS IS"
  basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
  KIND, either expressed or implied.  See the COPYING file for more
  information.

*/

#ifndef SILCSTACK_I_H
#define SILCSTACK_I_H

#ifndef SILCSTACK_H
#error "Do not include this header directly"
#endif

/* The default stack size when stack is created */
#define SILC_STACK_DEFAULT_SIZE       1024

/* Number of pre-allocated stack frames */
#define SILC_STACK_DEFAULT_NUM        8

/* Default alignment */
#define SILC_STACK_DEFAULT_ALIGN      sizeof(unsigned long)

/* Maximum allocation that can be made with SilcStack.  This is
   SILC_STACK_DEFAULT_SIZE * (2 ^ (SILC_STACK_BLOCK_NUM - 1)). */
#define SILC_STACK_MAX_ALLOC          0x02000000
#define SILC_STACK_BLOCK_NUM          16

/* Stack frame data area */
typedef struct SilcStackDataStruct {
  SilcUInt32 bytes_left;		      /* Free bytes in stack */
  /* Stack data area starts here */
} *SilcStackData;

/* Stack frame */
struct SilcStackFrameStruct {
  struct SilcStackFrameStruct *prev;          /* Pointer to previous frame */
  SilcUInt32 bytes_used;		      /* Bytes used when pushed */
  unsigned int sp : 27;			      /* Stack pointer */
  unsigned int si : 5;			      /* Stack index */
};

/* The SilcStack context */
struct SilcStackStruct {
  SilcStackData stack[SILC_STACK_BLOCK_NUM];  /* Allocated stack blocks */
  SilcStackFrame *frames;		      /* Allocated stack frames */
  SilcStackFrame *frame;		      /* Current stack frame */
  SilcUInt32 stack_size;		      /* Default stack size */
};

/* Align the requested amount bytes.  The `align' defines the requested
   alignment. */
#define SILC_STACK_ALIGN(bytes, align) (((bytes) + (align - 1)) & ~(align - 1))

/* Computes the size of stack block si. */
#define SILC_STACK_BLOCK_SIZE(stack, si)		\
  (((si) == 0) ? stack->stack_size :			\
   SILC_STACK_DEFAULT_SIZE * (1L << ((si) - 1)) << 1);

/* Returns a pointer to the data in the frame */
#define SILC_STACK_DATA(stack, si, bsize)				  \
  (((unsigned char *)(stack)->stack[si]) +				  \
   SILC_STACK_ALIGN(sizeof(**(stack)->stack), SILC_STACK_DEFAULT_ALIGN) + \
   ((bsize) - (stack)->stack[si]->bytes_left))

#define SILC_STACK_STAT(stack, stat, val)
#define SILC_ST_DEBUG(fmt)

/* Allocate memory.  If the `aligned' is FALSE this allocates unaligned
   memory, otherwise memory is aligned.  Returns pointer to the memory
   or NULL on error. */
void *silc_stack_malloc(SilcStack stack, SilcUInt32 size, SilcBool aligned);

/* Attempts to reallocate memory by changing the size of the `ptr' into
   `size'.  This routine works only if the previous allocation to `stack'
   was `ptr'.  If there is another memory allocation between allocating
   `ptr' and this call this routine will return NULL.  NULL is also returned
   if the `size' does not fit into the current block.  If NULL is returned
   the old memory remains intact. */
void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
			 void *ptr, SilcUInt32 size, SilcBool aligned);


#endif /* SILCSTACK_I_H */
