Added SILC Thread Queue API
[silc.git] / lib / silcutil / silcstack.c
1 /*
2
3   silcstack.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2003 - 2008 Pekka Riikonen
8
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.
12
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.
17
18 */
19
20 #include "silc.h"
21
22 /************************** Types and definitions ***************************/
23
24 /* The SilcStack context */
25 struct SilcStackStruct {
26   SilcStack parent;                           /* Parent stack */
27   SilcMutex lock;                             /* Stack lock */
28   SilcList stacks;                            /* List of stacks for childs */
29   SilcStackDataEntry stack;                   /* The allocated stack */
30   SilcStackFrame *frames;                     /* Allocated stack frames */
31   SilcStackFrame *frame;                      /* Current stack frame */
32   SilcStackOomHandler oom_handler;            /* OOM handler */
33   void *oom_context;                          /* OOM handler context */
34   SilcUInt32 stack_size;                      /* Default stack size */
35   SilcUInt32 alignment;                       /* Memory alignment */
36 #ifdef SILC_DIST_INPLACE
37   /* Statistics */
38   SilcUInt32 snum_malloc;
39   SilcUInt32 sbytes_malloc;
40   SilcUInt32 snum_errors;
41 #endif /* SILC_DIST_INPLACE */
42 };
43
44 /************************ Static utility functions **************************/
45
46 /* Compute stack block index for the `size'. */
47
48 static SilcUInt32 silc_stack_get_index(SilcUInt32 size, SilcUInt32 *ret_bsize)
49 {
50   SilcUInt32 bsize, si;
51
52   if (size < SILC_STACK_DEFAULT_SIZE)
53     size = SILC_STACK_DEFAULT_SIZE;
54   si = 0;
55   bsize = SILC_STACK_DEFAULT_SIZE;
56   while (bsize < size) {
57     bsize <<= 1;
58     si++;
59   }
60
61   *ret_bsize = bsize;
62
63   return si;
64 }
65
66 /* Get stack from `stack' or allocate new one. */
67
68 static SilcStackDataEntry silc_stack_ref_stack(SilcStack stack,
69                                                SilcUInt32 size,
70                                                SilcUInt32 *ret_si,
71                                                SilcUInt32 *ret_bsize)
72 {
73   SilcStackDataEntry e;
74   SilcUInt32 si, bsize;
75
76   /* Get stack block index and block size for requested size */
77   si = silc_stack_get_index(size, &bsize);
78   *ret_si = si;
79   *ret_bsize = bsize;
80
81   SILC_ST_DEBUG(("Get stack block, si %d, size %lu, stack %p",
82                  si, bsize, stack));
83
84   silc_mutex_lock(stack->lock);
85
86   /* Get stack that has block that can house our size requirement. */
87   silc_list_start(stack->stacks);
88   while ((e = silc_list_get(stack->stacks))) {
89     if (!e->data[si])
90       continue;
91
92     silc_list_del(stack->stacks, e);
93     SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
94     silc_mutex_unlock(stack->lock);
95     return e;
96   }
97
98   silc_mutex_unlock(stack->lock);
99
100   /* If we are child, get block from parent */
101   if (stack->parent)
102     return silc_stack_ref_stack(stack->parent, size, ret_si, ret_bsize);
103
104   SILC_ST_DEBUG(("Allocate new stack blocks"));
105
106   /* Allocate new stack blocks */
107   e = silc_calloc(1, sizeof(*e));
108   if (!e)
109     return NULL;
110   e->data[si] = silc_malloc(bsize + SILC_STACK_ALIGN(sizeof(*e->data[0]),
111                                                      stack->alignment));
112   if (!e->data[si]) {
113     silc_free(e);
114     return NULL;
115   }
116   e->data[si]->bytes_left = bsize;
117   e->si = si;
118   e->bsize = bsize;
119
120   SILC_ST_DEBUG(("Got stack blocks %p from stack %p", e->data, stack));
121
122   return e;
123 }
124
125 /* Return the `data' back to the `stack'. */
126
127 static void silc_stack_unref_stack(SilcStack stack, SilcStackDataEntry e)
128 {
129   int i;
130
131   SILC_LOG_DEBUG(("Release stack blocks %p to stack %p, si %d",
132                   e->data, stack, e->si));
133
134   /* Release all blocks from allocations */
135   for (i = e->si; i < SILC_STACK_BLOCK_NUM; i++) {
136     if (!e->data[i])
137       continue;
138     if (!i)
139       e->data[i]->bytes_left = e->bsize;
140     else
141       e->data[i]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, i);
142   }
143
144   silc_mutex_lock(stack->lock);
145   silc_list_add(stack->stacks, e);
146   silc_mutex_unlock(stack->lock);
147 }
148
149 /* Allocate memory from a specific stack block */
150
151 static void *silc_stack_alloc_block(SilcStack stack, SilcStackDataEntry e,
152                                     SilcUInt32 items, SilcUInt32 size)
153 {
154   SilcUInt32 asize;
155   void *ptr;
156
157   /* Get pointer and consume the stack block */
158   asize = SILC_STACK_ALIGN(items * size, stack->alignment);
159   ptr = SILC_STACK_DATA_EXT(e->data, e->si, e->bsize, stack->alignment);
160   e->data[e->si]->bytes_left -= asize;
161   memset(ptr, 0, items * size);
162
163   return ptr;
164 }
165
166 /***************************** SilcStack API ********************************/
167
168 /* Allocate the stack */
169
170 SilcStack silc_stack_alloc(SilcUInt32 stack_size, SilcStack parent)
171 {
172   SilcStack stack;
173   SilcStackDataEntry e;
174   SilcUInt32 si = 0, bsize = 0;
175
176   stack_size = stack_size ? stack_size : SILC_STACK_DEFAULT_SIZE;
177   if (stack_size < SILC_STACK_DEFAULT_SIZE)
178     stack_size = SILC_STACK_DEFAULT_SIZE;
179
180   /* Align by 8 */
181   stack_size += ((-stack_size) % 8);
182
183   if (parent) {
184     /* Get stack from parent.  The stack itself is allocated from the
185        parent (but does not consume parent's own stack). */
186     e = silc_stack_ref_stack(parent, stack_size, &si, &bsize);
187     if (!e)
188       return NULL;
189
190     /* Allocate stack from the returned stack.  We allocate ourselves from
191        our own stack. */
192     stack = silc_stack_alloc_block(parent, e, 1, sizeof(*stack));
193     if (!stack) {
194       silc_stack_unref_stack(parent, e);
195       return NULL;
196     }
197
198     stack->parent = parent;
199     stack->stack_size = stack_size;
200     stack->alignment = SILC_STACK_DEFAULT_ALIGN;
201     stack->oom_handler = parent->oom_handler;
202     stack->oom_context = parent->oom_context;
203     stack->lock = parent->lock;
204     silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
205
206     /* Allocate stack frames from the stack itself */
207     stack->frames = silc_stack_alloc_block(stack, e, SILC_STACK_BLOCK_NUM,
208                                            sizeof(*stack->frames));
209     if (!stack->frames) {
210       silc_stack_unref_stack(parent, e);
211       return NULL;
212     }
213
214     /* Set the initial stack */
215     stack->stack = e;
216   } else {
217     /* Dynamically allocate new stack */
218     stack = silc_calloc(1, sizeof(*stack));
219     if (!stack)
220       return NULL;
221
222     stack->stack_size = stack_size;
223     stack->alignment = SILC_STACK_DEFAULT_ALIGN;
224     silc_list_init(stack->stacks, struct SilcStackDataEntryStruct, next);
225
226     /* Create initial stack */
227     stack->stack = silc_calloc(1, sizeof(*stack->stack));
228     if (!stack->stack) {
229       silc_free(stack);
230       return NULL;
231     }
232     stack->stack->data[0] =
233       silc_malloc(stack->stack_size +
234                   SILC_STACK_ALIGN(sizeof(*stack->stack->data[0]),
235                                    stack->alignment));
236     if (!stack->stack->data[0]) {
237       silc_free(stack->stack);
238       silc_free(stack);
239       return NULL;
240     }
241     stack->stack->data[0]->bytes_left = stack->stack_size;
242     stack->stack->si = 0;
243     stack->stack->bsize = stack->stack_size;
244
245     /* Allocate stack frames from the stack itself */
246     stack->frames = silc_stack_alloc_block(stack, stack->stack,
247                                            SILC_STACK_DEFAULT_NUM,
248                                            sizeof(*stack->frames));
249     if (!stack->frames) {
250       silc_free(stack->stack->data[0]);
251       silc_free(stack->stack);
252       silc_free(stack);
253       return NULL;
254     }
255
256     /* Allocate lock */
257     silc_mutex_alloc(&stack->lock);
258   }
259
260   /* Use the allocated stack in first stack frame */
261   stack->frame = &stack->frames[0];
262   stack->frame->prev = NULL;
263   stack->frame->bytes_used = stack->stack_size;
264   stack->frame->sp = 1;
265   stack->frame->si = si;
266
267   SILC_LOG_DEBUG(("New stack %p, size %d bytes", stack, stack->stack_size));
268
269   return stack;
270 }
271
272 /* Frees the stack and all allocated memory */
273
274 void silc_stack_free(SilcStack stack)
275 {
276   SilcStackDataEntry e;
277   int i;
278
279   if (!stack)
280     return;
281
282   SILC_LOG_DEBUG(("Free stack %p", stack));
283
284   if (!stack->parent) {
285     silc_list_start(stack->stacks);
286     while ((e = silc_list_get(stack->stacks))) {
287       for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
288         silc_free(e->data[i]);
289       silc_free(e);
290     }
291
292     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
293       silc_free(stack->stack->data[i]);
294     silc_free(stack->stack);
295
296     if (stack->lock)
297       silc_mutex_free(stack->lock);
298
299     silc_free(stack);
300   } else {
301     /* Return all stack blocks to the parent */
302     silc_list_start(stack->stacks);
303     while ((e = silc_list_get(stack->stacks)))
304       silc_stack_unref_stack(stack->parent, e);
305
306     silc_stack_unref_stack(stack->parent, stack->stack);
307   }
308 }
309
310 /* Push to next stack frame */
311
312 SilcUInt32 silc_stack_push(SilcStack stack, SilcStackFrame *frame)
313 {
314   if (!stack)
315     return 0;
316
317   if (!frame) {
318     if (stack->frame->sp >= SILC_STACK_ALIGN(stack->frame->sp,
319                                              SILC_STACK_DEFAULT_NUM)) {
320       SILC_LOG_DEBUG(("SilcStack %p running out of frames, cannot push",
321                       stack));
322       return stack->frame->sp;
323     }
324
325     frame = &stack->frames[stack->frame->sp];
326   }
327
328   /* Push */
329   frame->prev = stack->frame;
330   frame->sp = stack->frame->sp + 1;
331   frame->si = stack->frame->si;
332   frame->bytes_used = stack->stack->data[frame->si]->bytes_left;
333   stack->frame = frame;
334
335   SILC_ST_DEBUG(("Push %p: sp %d -> %d, si %d", stack, frame->prev->sp,
336                  frame->sp, frame->si));
337
338   return stack->frame->sp;
339 }
340
341 /* Pop to previous stack frame */
342
343 SilcUInt32 silc_stack_pop(SilcStack stack)
344 {
345   SilcUInt32 si;
346
347   if (!stack || !stack->frame->prev)
348     return 0;
349
350   /* Pop */
351   si = stack->frame->si;
352   while (si > stack->frame->prev->si) {
353     if (stack->stack->data[si])
354       stack->stack->data[si]->bytes_left = SILC_STACK_BLOCK_SIZE(stack, si);
355     si--;
356   }
357   stack->stack->data[si]->bytes_left = stack->frame->bytes_used;
358   stack->frame = stack->frame->prev;
359
360   SILC_ST_DEBUG(("Pop %p: sp %d -> %d, si %d", stack, stack->frame->sp + 1,
361                  stack->frame->sp, stack->frame->si));
362
363   return stack->frame->sp + 1;
364 }
365
366 /* Allocate memory.  Returns pointer to the memory or NULL on error. */
367
368 void *silc_stack_malloc(SilcStack stack, SilcUInt32 size)
369 {
370   void *ptr;
371   SilcUInt32 bsize, bsize2;
372   SilcUInt32 si = stack->frame->si;
373
374   SILC_STACK_STAT(stack, num_malloc, 1);
375   SILC_ST_DEBUG(("Allocating %d bytes from %p", size, stack));
376
377   if (silc_unlikely(!size)) {
378     SILC_LOG_DEBUG(("Allocation by zero (0)"));
379     silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
380     SILC_STACK_STAT(stack, num_errors, 1);
381     return NULL;
382   }
383
384   if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
385     SILC_LOG_DEBUG(("Allocating too much"));
386     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
387     SILC_STACK_STAT(stack, num_errors, 1);
388     if (stack->oom_handler)
389       stack->oom_handler(stack, stack->oom_context);
390     return NULL;
391   }
392
393   /* Align properly  */
394   size = SILC_STACK_ALIGN(size, stack->alignment);
395
396   /* Compute the size of current stack block */
397   bsize = SILC_STACK_BLOCK_SIZE(stack, si);
398
399   /* See if there is space in the current stack block */
400   if (stack->stack->data[si]->bytes_left >= size) {
401     /* Get pointer to the memory */
402     ptr = SILC_STACK_DATA(stack, si, bsize);
403     stack->stack->data[si]->bytes_left -= size;
404     SILC_STACK_STAT(stack, bytes_malloc, size);
405     return ptr;
406   }
407
408   /* There is not enough space in this block.  Find the spot to stack
409      block that can handle this size memory. */
410   if (bsize < SILC_STACK_DEFAULT_SIZE)
411     bsize = SILC_STACK_DEFAULT_SIZE;
412   bsize += size;
413   bsize2 = SILC_STACK_DEFAULT_SIZE;
414   si = 0;
415   while (bsize2 < bsize) {
416     bsize2 <<= 1;
417     si++;
418   }
419   if (silc_unlikely(si >= SILC_STACK_BLOCK_NUM)) {
420     SILC_LOG_DEBUG(("Allocating too large block"));
421     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
422     SILC_STACK_STAT(stack, num_errors, 1);
423     if (stack->oom_handler)
424       stack->oom_handler(stack, stack->oom_context);
425     return NULL;
426   }
427
428   /* Allocate the block if it doesn't exist yet */
429   if (!stack->stack->data[si]) {
430     SILC_ST_DEBUG(("Allocating new stack block, %d bytes", bsize2));
431     stack->stack->data[si] =
432       silc_malloc(bsize2 +
433                   SILC_STACK_ALIGN(sizeof(**stack->stack->data),
434                                    stack->alignment));
435     if (silc_unlikely(!stack->stack->data[si])) {
436       SILC_STACK_STAT(stack, num_errors, 1);
437       if (stack->oom_handler)
438         stack->oom_handler(stack, stack->oom_context);
439       return NULL;
440     }
441     stack->stack->data[si]->bytes_left = bsize2;
442   }
443
444   /* Now return memory from this new block.  It is guaranteed that in this
445      block there is enough space for this memory. */
446   assert(stack->stack->data[si]->bytes_left >= size);
447   ptr = SILC_STACK_DATA(stack, si, bsize2);
448   stack->stack->data[si]->bytes_left -= size;
449   stack->frame->si = si;
450   SILC_STACK_STAT(stack, bytes_malloc, size);
451
452   return ptr;
453 }
454
455 /* Attempts to reallocate memory by changing the size of the `ptr' into
456    `size'.  This routine works only if the previous allocation to `stack'
457    was `ptr'.  If there is another memory allocation between allocating
458    `ptr' and this call this routine will return NULL.  NULL is also returned
459    if the `size' does not fit into the current block.  If NULL is returned
460    the old memory remains intact. */
461
462 void *silc_stack_realloc(SilcStack stack, SilcUInt32 old_size,
463                          void *ptr, SilcUInt32 size)
464 {
465   SilcUInt32 si = stack->frame->si;
466   SilcUInt32 bsize;
467   void *sptr;
468
469   if (!ptr)
470     return silc_stack_malloc(stack, size);
471
472   SILC_STACK_STAT(stack, num_malloc, 1);
473   SILC_ST_DEBUG(("Reallocating %d bytes (%d) from %p", size, old_size, stack));
474
475   if (silc_unlikely(!size || !old_size)) {
476     SILC_LOG_DEBUG(("Allocation by zero (0)"));
477     silc_set_errno_nofail(SILC_ERR_ZERO_ALLOCATION);
478     SILC_STACK_STAT(stack, num_errors, 1);
479     return NULL;
480   }
481
482   if (silc_unlikely(size > SILC_STACK_MAX_ALLOC)) {
483     SILC_LOG_DEBUG(("Allocating too much"));
484     silc_set_errno_nofail(SILC_ERR_TOO_LARGE_ALLOCATION);
485     SILC_STACK_STAT(stack, num_errors, 1);
486     if (stack->oom_handler)
487       stack->oom_handler(stack, stack->oom_context);
488     return NULL;
489   }
490
491   /* Align properly */
492   old_size = SILC_STACK_ALIGN(old_size, stack->alignment);
493
494   /* Compute the size of current stack block */
495   bsize = SILC_STACK_BLOCK_SIZE(stack, si);
496
497   /* Check that `ptr' is last allocation */
498   sptr = (unsigned char *)stack->stack->data[si] +
499     SILC_STACK_ALIGN(sizeof(**stack->stack->data), stack->alignment);
500   if (stack->stack->data[si]->bytes_left + old_size +
501       ((unsigned char *)ptr - (unsigned char *)sptr) != bsize) {
502     SILC_LOG_DEBUG(("Cannot reallocate"));
503     silc_set_errno_nofail(SILC_ERR_INVALID_ARGUMENT);
504     SILC_STACK_STAT(stack, num_errors, 1);
505     return NULL;
506   }
507
508   /* Now check that the new size fits to this block */
509   if (stack->stack->data[si]->bytes_left >= size) {
510     /* It fits, so simply return the old pointer */
511     size = SILC_STACK_ALIGN(size, stack->alignment);
512     stack->stack->data[si]->bytes_left -= (size - old_size);
513     SILC_STACK_STAT(stack, bytes_malloc, (size - old_size));
514     return ptr;
515   }
516
517   SILC_LOG_DEBUG(("Cannot reallocate in this block"));
518   silc_set_errno_reason_nofail(SILC_ERR_TOO_LARGE_ALLOCATION,
519                                "Cannot reallocate in this memory block");
520   SILC_STACK_STAT(stack, num_errors, 1);
521   return NULL;
522 }
523
524 /* Set OOM handler */
525
526 void silc_stack_set_oom_handler(SilcStack stack,
527                                 SilcStackOomHandler oom_handler,
528                                 void *context)
529 {
530   stack->oom_handler = oom_handler;
531   stack->oom_context = context;
532 }
533
534 /* Set default alignment */
535
536 void silc_stack_set_alignment(SilcStack stack, SilcUInt32 alignment)
537 {
538   SILC_LOG_DEBUG(("Set stack %p alignment to %d bytes", stack, alignment));
539   stack->alignment = alignment;
540 }
541
542 /* Get default alignment */
543
544 SilcUInt32 silc_stack_get_alignment(SilcStack stack)
545 {
546   return stack->alignment;
547 }
548
549 /* Purge stack */
550
551 SilcBool silc_stack_purge(SilcStack stack)
552 {
553   SilcStackDataEntry e;
554   SilcBool ret = FALSE;
555   int i;
556
557   SILC_LOG_DEBUG(("Purge stack %p", stack));
558
559   /* Go through the default stack */
560   for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
561     if (stack->stack->data[i] &&
562         stack->stack->data[i]->bytes_left == SILC_STACK_BLOCK_SIZE(stack, i)) {
563       SILC_LOG_DEBUG(("Purge %d bytes",
564                       SILC_STACK_BLOCK_SIZE(stack, i)));
565       silc_free(stack->stack->data[i]);
566       stack->stack->data[i] = NULL;
567       ret = TRUE;
568     }
569   }
570
571   silc_mutex_lock(stack->lock);
572
573   /* Remove one child stack */
574   if (silc_list_count(stack->stacks) > 2) {
575     silc_list_start(stack->stacks);
576     e = silc_list_get(stack->stacks);
577
578     SILC_LOG_DEBUG(("Remove stack blocks %p", e->data));
579     silc_list_del(stack->stacks, e);
580     ret = TRUE;
581
582     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++)
583       silc_free(e->data[i]);
584     silc_free(e);
585   }
586
587   /* Go through the child stacks */
588   silc_list_start(stack->stacks);
589   while ((e = silc_list_get(stack->stacks))) {
590     for (i = SILC_STACK_BLOCK_NUM - 1; i > 3; i--) {
591       if (e->data[i]) {
592         SILC_LOG_DEBUG(("Purge %d bytes",
593                         SILC_STACK_BLOCK_SIZE(stack, i)));
594         silc_free(e->data[i]);
595         e->data[i] = NULL;
596         ret = TRUE;
597       }
598     }
599   }
600
601   silc_mutex_unlock(stack->lock);
602
603   return ret;
604 }
605
606 /* Set global stack */
607
608 void silc_stack_set_global(SilcStack stack)
609 {
610   SilcTls tls = silc_thread_get_tls();
611
612   if (!tls) {
613     /* Try to initialize Tls */
614     tls = silc_thread_tls_init();
615     SILC_VERIFY(tls);
616     if (!tls)
617       return;
618   }
619
620   tls->stack = stack;
621 }
622
623 /* Return global stack */
624
625 SilcStack silc_stack_get_global(void)
626 {
627   SilcTls tls = silc_thread_get_tls();
628
629   if (!tls)
630     return NULL;
631
632   return tls->stack;
633 }
634
635 #ifdef SILC_DIST_INPLACE
636 /* Statistics dumping. */
637
638 void silc_stack_stats(SilcStack stack)
639 {
640   SilcStackDataEntry e;
641   SilcUInt32 stack_size = 0;
642   int i, c = 0;
643
644   for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
645     if (!stack->stack->data[i])
646       continue;
647     stack_size += SILC_STACK_BLOCK_SIZE(stack, i);
648     c++;
649   }
650
651   fprintf(stdout, "\nSilcStack %p statistics :\n\n", stack);
652   fprintf(stdout, "  Size of stack           : %u\n",
653           (unsigned int)stack_size);
654   fprintf(stdout, "  Stack alignment         : %d\n",
655           (int)stack->alignment);
656   fprintf(stdout, "  Number of allocs        : %u\n",
657           (unsigned int)stack->snum_malloc);
658   fprintf(stdout, "  Bytes allocated         : %u\n",
659           (unsigned int)stack->sbytes_malloc);
660   fprintf(stdout, "  Average alloc size      : %.2f\n",
661           (double)((double)stack->sbytes_malloc / (double)stack->snum_malloc));
662   fprintf(stdout, "  Number of alloc errors  : %u\n",
663           (unsigned int)stack->snum_errors);
664   fprintf(stdout, "  Number of frames        : %u\n",
665           (unsigned int)SILC_STACK_ALIGN(stack->frame->sp,
666                                          SILC_STACK_DEFAULT_NUM));
667   fprintf(stdout, "  Number of blocks        : %u\n", c);
668   fprintf(stdout, "  Number of stacks        : %d\n",
669           silc_list_count(stack->stacks));
670
671   silc_list_start(stack->stacks);
672   while ((e = silc_list_get(stack->stacks))) {
673     stack_size = 0;
674     c = 0;
675     for (i = 0; i < SILC_STACK_BLOCK_NUM; i++) {
676       if (!e->data[i])
677         continue;
678       stack_size += e->data[i]->bytes_left;
679       c++;
680     }
681     fprintf(stdout, "\n  Size of stack           : %u\n",
682             (unsigned int)stack_size);
683     fprintf(stdout, "  Number of blocks        : %u\n", c);
684   }
685 }
686 #endif /* SILC_DIST_INPLACE */