5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2003 - 2007 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 */
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));
40 stack->alignment = SILC_STACK_DEFAULT_ALIGN;
42 /* Create initial stack */
43 stack->stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
44 stack->stack[0] = silc_malloc(stack->stack_size +
45 SILC_STACK_ALIGN(sizeof(*stack->stack[0]),
47 if (!stack->stack[0]) {
48 silc_free(stack->frames);
52 stack->stack[0]->bytes_left = stack->stack_size;
54 /* Use the allocated stack in first stack frame */
55 stack->frame = &stack->frames[0];
56 stack->frame->prev = NULL;
57 stack->frame->bytes_used = stack->stack_size;
64 /* Frees the stack and all allocated memory */
66 void silc_stack_free(SilcStack stack)
70 silc_free(stack->frames);
71 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
72 silc_free(stack->stack[i]);
76 /* Push to next stack frame */
78 SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
84 /* See if all frames are in use, and allocate SILC_STACK_DEFAULT_NUM
85 many new frames if needed. */
86 if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
87 SILC_STACK_DEFAULT_NUM)) {
88 int i = stack->frame->sp;
89 SILC_LOG_DEBUG(("Allocating more stack frames"));
90 frame = silc_realloc(stack->frames,
91 SILC_STACK_ALIGN(i + 1, SILC_STACK_DEFAULT_NUM) *
92 sizeof(*stack->frames));
95 stack->frames = frame;
96 stack->frame = &stack->frames[i - 1];
98 /* The prev pointers may become invalid in silc_realloc() */
99 for (i = 1; i < stack->frame->sp; i++)
100 stack->frames[i].prev = &stack->frames[i - 1];
103 frame = &stack->frames[stack->frame->sp];
107 frame->prev = stack->frame;
108 frame->sp = stack->frame->sp + 1;
109 frame->si = stack->frame->si;
110 frame->bytes_used = stack->stack[frame->si]->bytes_left;
111 stack->frame = frame;
113 SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
114 frame->sp, frame->si));
116 return stack->frame->sp;
119 /* Pop to previous stack frame */
121 SilcUInt32 silc_stack_pop(SilcStack stack)
129 assert(stack->frame->prev);
130 si = stack->frame->si;
131 while (si > stack->frame->prev->si) {
132 if (stack->stack[si])
133 stack->stack[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
136 stack->stack[si]->bytes_left = stack->frame->bytes_used;
137 stack->frame = stack->frame->prev;
139 SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
140 stack->frame->sp, stack->frame->si));
142 return stack->frame->sp + 1;
145 /* Allocate memory. Returns pointer to the memory or NULL on error. */
147 void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
150 SilcUInt32 bsize, bsize2;
151 SilcUInt32 si = stack->frame->si;
153 SILC_STACK_STAT(stack, num_malloc, 1);
154 SILC_ST_DEBUG(("Allocating %d bytes from %p", size, stack));
156 if (silc_unlikely(!size)) {
157 SILC_LOG_ERROR(("Allocation by zero (0)"));
158 SILC_STACK_STAT(stack, num_errors, 1);
162 if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
163 SILC_LOG_ERROR(("Allocating too much"));
164 SILC_STACK_STAT(stack, num_errors, 1);
169 size = SILC_STACK_ALIGN(size, stack->alignment);
171 /* Compute the size of current stack block */
172 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
174 /* See if there is space in the current stack block */
175 if (stack->stack[si]->bytes_left >= size) {
176 /* Get pointer to the memory */
177 ptr = SILC_STACK_DATA(stack, si, bsize);
178 stack->stack[si]->bytes_left -= size;
179 SILC_STACK_STAT(stack, bytes_malloc, size);
183 /* There is not enough space in this block. Find the spot to stack
184 block that can handle this size memory. */
185 if (bsize < SILC_STACK_DEFAULT_SIZE)
186 bsize = SILC_STACK_DEFAULT_SIZE;
188 bsize2 = SILC_STACK_DEFAULT_SIZE;
190 while (bsize2 < bsize) {
194 if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
195 SILC_LOG_ERROR(("Allocating too large block"));
196 SILC_STACK_STAT(stack, num_errors, 1);
200 /* Allocate the block if it doesn't exist yet */
201 if (!stack->stack[si]) {
202 SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
203 stack->stack[si] = silc_malloc(bsize2 +
204 SILC_STACK_ALIGN(sizeof(**stack->stack),
206 if (silc_unlikely(!stack->stack[si])) {
207 SILC_STACK_STAT(stack, num_errors, 1);
210 stack->stack[si]->bytes_left = bsize2;
213 /* Now return memory from this new block. It is guaranteed that in this
214 block there is enough space for this memory. */
215 assert(stack->stack[si]->bytes_left >= size);
216 ptr = SILC_STACK_DATA(stack, si, bsize2);
217 stack->stack[si]->bytes_left -= size;
218 stack->frame->si = si;
219 SILC_STACK_STAT(stack, bytes_malloc, size);
224 /* Attempts to reallocate memory by changing the size of the `ptr' into
225 `size'. This routine works only if the previous allocation to `stack'
226 was `ptr'. If there is another memory allocation between allocating
227 `ptr' and this call this routine will return NULL. NULL is also returned
228 if the `size' does not fit into the current block. If NULL is returned
229 the old memory remains intact. */
231 void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
232 void *ptr, SilcUInt32 size)
234 SilcUInt32 si = stack->frame->si;
239 return silc_stack_malloc(stack, size);
241 SILC_STACK_STAT(stack, num_malloc, 1);
242 SILC_ST_DEBUG(("Reallocating %d bytes (%d) from %p", size, old_size, stack));
244 if (silc_unlikely(!size || !old_size)) {
245 SILC_LOG_ERROR(("Allocation by zero (0)"));
246 SILC_STACK_STAT(stack, num_errors, 1);
250 if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
251 SILC_LOG_ERROR(("Allocating too much"));
252 SILC_STACK_STAT(stack, num_errors, 1);
257 old_size = SILC_STACK_ALIGN(old_size, stack->alignment);
259 /* Compute the size of current stack block */
260 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
262 /* Check that `ptr' is last allocation */
263 sptr = (unsigned char *)stack->stack[si] +
264 SILC_STACK_ALIGN(sizeof(**stack->stack), stack->alignment);
265 if (stack->stack[si]->bytes_left + old_size +
266 ((unsigned char *)ptr - (unsigned char *)sptr) != bsize) {
267 SILC_LOG_DEBUG(("Cannot reallocate"));
268 SILC_STACK_STAT(stack, num_errors, 1);
272 /* Now check that the new size fits to this block */
273 if (stack->stack[si]->bytes_left >= size) {
274 /* It fits, so simply return the old pointer */
275 size = SILC_STACK_ALIGN(size, stack->alignment);
276 stack->stack[si]->bytes_left -= (size - old_size);
277 SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
281 SILC_LOG_DEBUG(("Cannot reallocate in this block"));
282 SILC_STACK_STAT(stack, num_errors, 1);
286 /* Set default alignment */
288 void silc_stack_set_alignment(SilcStack stack, SilcUInt32 alignment)
290 SILC_LOG_DEBUG(("Set stack %p alignment to %d bytes", stack, alignment));
291 stack->alignment = alignment;
294 /* Get default alignment */
296 SilcUInt32 silc_stack_get_alignment(SilcStack stack)
298 return stack->alignment;
301 #ifdef SILC_DIST_INPLACE
302 /* Statistics dumping. */
304 void silc_stack_stats(SilcStack stack)
306 SilcUInt32 stack_size = 0;
309 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
310 if (!stack->stack[i])
312 stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
316 fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
317 fprintf(stdout, " Size of stack : %u\n",
318 (unsigned int)stack_size);
319 fprintf(stdout, " Stack alignment : %d\n",
320 (int)stack->alignment);
321 fprintf(stdout, " Number of allocs : %u\n",
322 (unsigned int)stack->snum_malloc);
323 fprintf(stdout, " Bytes allocated : %u\n",
324 (unsigned int)stack->sbytes_malloc);
325 fprintf(stdout, " Average alloc size : %.2f\n",
326 (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
327 fprintf(stdout, " Number of alloc errors : %u\n",
328 (unsigned int)stack->snum_errors);
329 fprintf(stdout, " Number of frames : %u\n",
330 (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
331 SILC_STACK_DEFAULT_NUM));
332 fprintf(stdout, " Number of blocks : %u\n", c);
334 #endif /* SILC_DIST_INPLACE */