5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 2003 - 2006 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));
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, SilcBool 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));
158 if (silc_unlikely(!size)) {
159 SILC_LOG_ERROR(("Allocation by zero (0)"));
160 SILC_STACK_STAT(stack, num_errors, 1);
164 if (silc_unlikely(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 (silc_unlikely(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 (silc_unlikely(!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, SilcBool 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 (silc_unlikely(!size || !old_size)) {
248 SILC_LOG_ERROR(("Allocation by zero (0)"));
249 SILC_STACK_STAT(stack, num_errors, 1);
253 if (silc_unlikely(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,
262 SILC_STACK_DEFAULT_ALIGN) : old_size);
264 /* Compute the size of current stack block */
265 bsize = SILC_STACK_BLOCK_SIZE(stack, si);
267 /* Check that `ptr' is last allocation */
268 sptr = (unsigned char *)stack->stack[si] +
269 SILC_STACK_ALIGN(sizeof(**stack->stack), SILC_STACK_DEFAULT_ALIGN);
270 if (stack->stack[si]->bytes_left + old_size + (ptr - sptr) != bsize) {
271 SILC_LOG_DEBUG(("Cannot reallocate"));
272 SILC_STACK_STAT(stack, num_errors, 1);
276 /* Now check that the new size fits to this block */
277 if (stack->stack[si]->bytes_left >= size) {
278 /* It fits, so simply return the old pointer */
279 size = (aligned ? SILC_STACK_ALIGN(size,
280 SILC_STACK_DEFAULT_ALIGN) : size);
281 stack->stack[si]->bytes_left -= (size - old_size);
282 SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
286 SILC_LOG_DEBUG(("Cannot reallocate in this block"));
287 SILC_STACK_STAT(stack, num_errors, 1);
291 #ifdef SILC_DIST_INPLACE
292 /* Statistics dumping. */
294 void silc_stack_stats(SilcStack stack)
296 SilcUInt32 stack_size = 0;
299 for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
300 if (!stack->stack[i])
302 stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
306 fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
307 fprintf(stdout, " Size of stack : %u\n",
308 (unsigned int)stack_size);
309 fprintf(stdout, " Number of allocs : %u\n",
310 (unsigned int)stack->snum_malloc);
311 fprintf(stdout, " Bytes allocated : %u\n",
312 (unsigned int)stack->sbytes_malloc);
313 fprintf(stdout, " Average alloc size : %.2f\n",
314 (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
315 fprintf(stdout, " Number of alloc errors : %u\n",
316 (unsigned int)stack->snum_errors);
317 fprintf(stdout, " Number of frames : %u\n",
318 (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
319 SILC_STACK_DEFAULT_NUM));
320 fprintf(stdout, " Number of blocks : %u\n", c);
322 #endif /* SILC_DIST_INPLACE */