5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2003 - 2005 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* #define SILC_STACK_DEBUG 1 */
22 #include "silcincludes.h"
24 /* Allocate the stack */
26 SilcStack silc_stack_alloc(SilcUInt32 stack_size)
30 stack = silc_calloc(1, sizeof(*stack));
34 stack->frames = silc_calloc(SILC_STACK_DEFAULT_NUM,
35 sizeof(*stack->frames));
41 /* Create initial stack */
42 stack->stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
43 stack->stack[0] = silc_malloc(stack->stack_size +
44 SILC_STACK_ALIGN(sizeof(*stack->stack[0]),
45 SILC_STACK_DEFAULT_ALIGN));
46 if (!stack->stack[0]) {
47 silc_free(stack->frames);
51 stack->stack[0]->bytes_left = stack->stack_size;
53 /* Use the allocated stack in first stack frame */
54 stack->frame = &stack->frames[0];
55 stack->frame->prev = NULL;
56 stack->frame->bytes_used = stack->stack_size;
63 /* Frees the stack and all allocated memory */
65 void silc_stack_free(SilcStack stack)
69 silc_free(stack->frames);
70 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
71 silc_free(stack->stack[i]);
75 /* Push to next stack frame */
77 SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
83 /* See if all frames are in use, and allocate SILC_STACK_DEFAULT_NUM
84 many new frames if needed. */
85 if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
86 SILC_STACK_DEFAULT_NUM)) {
87 int i = stack->frame->sp;
88 SILC_LOG_DEBUG(("Allocating more stack frames"));
89 frame = silc_realloc(stack->frames,
90 SILC_STACK_ALIGN(i + 1, SILC_STACK_DEFAULT_NUM) *
91 sizeof(*stack->frames));
94 stack->frames = frame;
95 stack->frame = &stack->frames[i - 1];
97 /* The prev pointers may become invalid in silc_realloc() */
98 for (i = 1; i < stack->frame->sp; i++)
99 stack->frames[i].prev = &stack->frames[i - 1];
102 frame = &stack->frames[stack->frame->sp];
106 frame->prev = stack->frame;
107 frame->sp = stack->frame->sp + 1;
108 frame->si = stack->frame->si;
109 frame->bytes_used = stack->stack[frame->si]->bytes_left;
110 stack->frame = frame;
112 SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
113 frame->sp, frame->si));
115 return stack->frame->sp;
118 /* Pop to previous stack frame */
120 SilcUInt32 silc_stack_pop(SilcStack stack)
128 assert(stack->frame->prev);
129 si = stack->frame->si;
130 while (si > stack->frame->prev->si) {
131 if (stack->stack[si])
132 stack->stack[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
135 stack->stack[si]->bytes_left = stack->frame->bytes_used;
136 stack->frame = stack->frame->prev;
138 SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
139 stack->frame->sp, stack->frame->si));
141 return stack->frame->sp + 1;
144 /* Allocate memory. If the `aligned' is FALSE this allocates unaligned
145 memory, otherwise memory is aligned. Returns pointer to the memory
148 void *silc_stack_malloc(SilcStack stack, SilcUInt32 size, bool aligned)
151 SilcUInt32 bsize, bsize2;
152 SilcUInt32 si = stack->frame->si;
154 SILC_STACK_STAT(stack, num_malloc, 1);
155 SILC_ST_DEBUG(("Allocating %d bytes (%s) from %p",
156 size, aligned ? "align" : "not align", stack));
159 SILC_LOG_ERROR(("Allocation by zero (0)"));
160 SILC_STACK_STAT(stack, num_errors, 1);
164 if (size > SILC_STACK_MAX_ALLOC) {
165 SILC_LOG_ERROR(("Allocating too much"));
166 SILC_STACK_STAT(stack, num_errors, 1);
170 /* Align properly if wanted */
171 size = (aligned ? SILC_STACK_ALIGN(size, SILC_STACK_DEFAULT_ALIGN) : size);
173 /* Compute the size of current stack block */
174 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
176 /* See if there is space in the current stack block */
177 if (stack->stack[si]->bytes_left >= size) {
178 /* Get pointer to the memory */
179 ptr = SILC_STACK_DATA(stack, si, bsize);
180 stack->stack[si]->bytes_left -= size;
181 SILC_STACK_STAT(stack, bytes_malloc, size);
185 /* There is not enough space in this block. Find the spot to stack
186 block that can handle this size memory. */
187 if (bsize < SILC_STACK_DEFAULT_SIZE)
188 bsize = SILC_STACK_DEFAULT_SIZE;
190 bsize2 = SILC_STACK_DEFAULT_SIZE;
192 while (bsize2 < bsize) {
196 if (si >= SILC_STACK_BLOCK_NUM) {
197 SILC_LOG_ERROR(("Allocating too large block"));
198 SILC_STACK_STAT(stack, num_errors, 1);
202 /* Allocate the block if it doesn't exist yet */
203 if (!stack->stack[si]) {
204 SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
205 stack->stack[si] = silc_malloc(bsize2 +
206 SILC_STACK_ALIGN(sizeof(**stack->stack),
207 SILC_STACK_DEFAULT_ALIGN));
208 if (!stack->stack[si]) {
209 SILC_STACK_STAT(stack, num_errors, 1);
212 stack->stack[si]->bytes_left = bsize2;
215 /* Now return memory from this new block. It is guaranteed that in this
216 block there is enough space for this memory. */
217 assert(stack->stack[si]->bytes_left >= size);
218 ptr = SILC_STACK_DATA(stack, si, bsize2);
219 stack->stack[si]->bytes_left -= size;
220 stack->frame->si = si;
221 SILC_STACK_STAT(stack, bytes_malloc, size);
226 /* Attempts to reallocate memory by changing the size of the `ptr' into
227 `size'. This routine works only if the previous allocation to `stack'
228 was `ptr'. If there is another memory allocation between allocating
229 `ptr' and this call this routine will return NULL. NULL is also returned
230 if the `size' does not fit into the current block. If NULL is returned
231 the old memory remains intact. */
233 void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
234 void *ptr, SilcUInt32 size, bool aligned)
236 SilcUInt32 si = stack->frame->si;
241 return silc_stack_malloc(stack, size, aligned);
243 SILC_STACK_STAT(stack, num_malloc, 1);
244 SILC_ST_DEBUG(("Reallocating %d bytes (%d) (%s) from %p", size, old_size,
245 aligned ? "align" : "not align", stack));
247 if (!size || !old_size) {
248 SILC_LOG_ERROR(("Allocation by zero (0)"));
249 SILC_STACK_STAT(stack, num_errors, 1);
253 if (size > SILC_STACK_MAX_ALLOC) {
254 SILC_LOG_ERROR(("Allocating too much"));
255 SILC_STACK_STAT(stack, num_errors, 1);
259 /* Align the old size if needed */
260 old_size = (aligned ?
261 SILC_STACK_ALIGN(old_size, SILC_STACK_DEFAULT_ALIGN) : old_size);
263 /* Compute the size of current stack block */
264 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
266 /* Check that `ptr' is last allocation */
267 sptr = (unsigned char *)stack->stack[si] +
268 SILC_STACK_ALIGN(sizeof(**stack->stack), SILC_STACK_DEFAULT_ALIGN);
269 if (stack->stack[si]->bytes_left + old_size + (ptr - sptr) != bsize) {
270 SILC_LOG_DEBUG(("Cannot reallocate"));
271 SILC_STACK_STAT(stack, num_errors, 1);
275 /* Now check that the new size fits to this block */
276 if (stack->stack[si]->bytes_left >= size) {
277 /* It fits, so simply return the old pointer */
278 size = (aligned ? SILC_STACK_ALIGN(size, SILC_STACK_DEFAULT_ALIGN) : size);
279 stack->stack[si]->bytes_left -= (size - old_size);
280 SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
284 SILC_LOG_DEBUG(("Cannot reallocate in this block"));
285 SILC_STACK_STAT(stack, num_errors, 1);
289 #ifdef SILC_DIST_INPLACE
290 /* Statistics dumping. */
292 void silc_stack_stats(SilcStack stack)
294 SilcUInt32 stack_size = 0;
297 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
298 if (!stack->stack[i])
300 stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
304 fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
305 fprintf(stdout, " Size of stack : %u\n",
306 (unsigned int)stack_size);
307 fprintf(stdout, " Number of allocs : %u\n",
308 (unsigned int)stack->snum_malloc);
309 fprintf(stdout, " Bytes allocated : %u\n",
310 (unsigned int)stack->sbytes_malloc);
311 fprintf(stdout, " Average alloc size : %.2f\n",
312 (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
313 fprintf(stdout, " Number of alloc errors : %u\n",
314 (unsigned int)stack->snum_errors);
315 fprintf(stdout, " Number of frames : %u\n",
316 (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
317 SILC_STACK_DEFAULT_NUM));
318 fprintf(stdout, " Number of blocks : %u\n", c);
320 #endif /* SILC_DIST_INPLACE */