+ e->data[si]->bytes_left = bsize;
+ e->si = si;
+ e->bsize = bsize;
+
+ SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
+
+ return e;
+}
+
+/* Return the `data' back to the `stack'. */
+
+static void silc_stack_unref_stack(SilcStack stack, SilcStackDataEntry e)
+{
+ int i;
+
+ SILC_LOG_DEBUG(("Release stack blocks %p to stack %p, si %d",
+ e->data, stack, e->si));
+
+ /* Release all blocks from allocations */
+ for (i = e->si; i < SILC_STACK_BLOCK_NUM; i++) {
+ if (!e->data[i])
+ continue;
+ if (!i)
+ e->data[i]->bytes_left = e->bsize;
+ else
+ e->data[i]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, i);
+ }
+
+ silc_mutex_lock(stack->lock);
+ silc_list_add(stack->stacks, e);
+ silc_mutex_unlock(stack->lock);
+}
+
+/* Allocate memory from a specific stack block */
+
+static void *silc_stack_alloc_block(SilcStack stack, SilcStackDataEntry e,
+ SilcUInt32 items, SilcUInt32 size)
+{
+ SilcUInt32 asize;
+ void *ptr;
+
+ /* Get pointer and consume the stack block */
+ asize = SILC_STACK_ALIGN(items * size, stack->alignment);
+ ptr = SILC_STACK_DATA_EXT(e->data, e->si, e->bsize, stack->alignment);
+ e->data[e->si]->bytes_left -= asize;
+ memset(ptr, 0, items * size);
+
+ return ptr;
+}
+
+/***************************** SilcStack API ********************************/
+
+/* Allocate the stack */
+
+SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
+{
+ SilcStack stack;
+ SilcStackDataEntry e;
+ SilcUInt32 si = 0, bsize = 0;
+
+ stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
+ if (stack_size < SILC_STACK_DEFAULT_SIZE)
+ stack_size = SILC_STACK_DEFAULT_SIZE;
+
+ /* Align by 8 */
+ stack_size += ((-stack_size) % 8);
+
+ if (parent) {
+ /* Get stack from parent. The stack itself is allocated from the
+ parent (but does not consume parent's own stack). */
+ e = silc_stack_ref_stack(parent, stack_size, &si, &bsize);
+ if (!e)
+ return NULL;
+
+ /* Allocate stack from the returned stack. We allocate ourselves from
+ our own stack. */
+ stack = silc_stack_alloc_block(parent, e, 1, sizeof(*stack));
+ if (!stack) {
+ silc_stack_unref_stack(parent, e);
+ return NULL;
+ }
+
+ stack->parent = parent;
+ stack->stack_size = stack_size;
+ stack->alignment = SILC_STACK_DEFAULT_ALIGN;
+ stack->oom_handler = parent->oom_handler;
+ stack->oom_context = parent->oom_context;
+ stack->lock = parent->lock;
+ silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
+
+ /* Allocate stack frames from the stack itself */
+ stack->frames = silc_stack_alloc_block(stack, e, SILC_STACK_BLOCK_NUM,
+ sizeof(*stack->frames));
+ if (!stack->frames) {
+ silc_stack_unref_stack(parent, e);
+ return NULL;
+ }
+
+ /* Set the initial stack */
+ stack->stack = e;
+ } else {
+ /* Dynamically allocate new stack */
+ stack = silc_calloc(1, sizeof(*stack));
+ if (!stack)
+ return NULL;
+
+ stack->stack_size = stack_size;
+ stack->alignment = SILC_STACK_DEFAULT_ALIGN;
+ silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
+
+ /* Create initial stack */
+ stack->stack = silc_calloc(1, sizeof(*stack->stack));
+ if (!stack->stack) {
+ silc_free(stack);
+ return NULL;
+ }
+ stack->stack->data[0] =
+ silc_malloc(stack->stack_size +
+ SILC_STACK_ALIGN(sizeof(*stack->stack->data[0]),
+ stack->alignment));
+ if (!stack->stack->data[0]) {
+ silc_free(stack->stack);
+ silc_free(stack);
+ return NULL;
+ }
+ stack->stack->data[0]->bytes_left = stack->stack_size;
+ stack->stack->si = 0;
+ stack->stack->bsize = stack->stack_size;
+
+ /* Allocate stack frames from the stack itself */
+ stack->frames = silc_stack_alloc_block(stack, stack->stack,
+ SILC_STACK_DEFAULT_NUM,
+ sizeof(*stack->frames));
+ if (!stack->frames) {
+ silc_free(stack->stack->data[0]);
+ silc_free(stack->stack);
+ silc_free(stack);
+ return NULL;
+ }
+
+ /* Allocate lock */
+ silc_mutex_alloc(&stack->lock);
+ }