Parameter estimate matched with appropriate variable during estimation
[pspp] / src / pool.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "pool.h"
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "command.h"
25 #include "error.h"
26 #include "size_max.h"
27 #include "str.h"
28
29 /* Fast, low-overhead memory block suballocator. */
30 struct pool
31   {
32     struct pool *parent;        /* Pool of which this pool is a subpool. */
33     struct pool_block *blocks;  /* Blocks owned by the pool. */
34     struct pool_gizmo *gizmos;  /* Other stuff owned by the pool. */
35   };
36
37 /* Pool block. */
38 struct pool_block 
39   {
40     struct pool_block *prev;
41     struct pool_block *next;
42     size_t ofs;
43   };
44
45 /* Gizmo types. */
46 enum
47   {
48     POOL_GIZMO_MALLOC,
49     POOL_GIZMO_FILE,
50     POOL_GIZMO_SUBPOOL,
51     POOL_GIZMO_REGISTERED,
52   };
53
54 /* Pool routines can maintain objects (`gizmos') as well as doing
55    suballocation.  
56    This structure is used to keep track of them. */
57 struct pool_gizmo
58   {
59     struct pool *pool;
60     struct pool_gizmo *prev;
61     struct pool_gizmo *next;
62
63     long serial;                /* Serial number. */
64     int type;                   /* Type of this gizmo. */
65
66     /* Type-dependent info. */
67     union
68       {
69         FILE *file;             /* POOL_GIZMO_FILE. */
70         struct pool *subpool;   /* POOL_GIZMO_SUBPOOL. */
71
72         /* POOL_GIZMO_REGISTERED. */
73         struct
74           {
75             void (*free) (void *p);
76             void *p;
77           }
78         registered;
79       }
80     p;
81   };
82
83 /* Rounds X up to the next multiple of Y. */
84 #ifndef ROUND_UP
85 #define ROUND_UP(X, Y)                          \
86         (((X) + ((Y) - 1)) / (Y) * (Y))
87 #endif
88
89 /* Types that provide typically useful alignment sizes. */
90 union align
91   {
92     void *op;
93     void (*fp) (void);
94     long l;
95     double d;
96   };
97
98 /* This should be the alignment size used by malloc().  The size of
99    the union above is correct, if not optimal, in all known cases. */
100 #if defined (i386) || defined (__i386__)
101 #define ALIGN_SIZE 4            /* Save some extra memory. */
102 #else
103 #define ALIGN_SIZE sizeof (union align)
104 #endif
105
106 /* DISCRETE_BLOCKS may be declared as nonzero to prevent
107    suballocation of blocks.  This is useful under memory
108    debuggers like Checker or valgrind because it allows the
109    source location of bugs to be more accurately pinpointed.
110
111    On the other hand, if we're testing the library, then we want to
112    test the library's real functionality, not its crippled, slow,
113    simplified functionality. */
114 /*#define DISCRETE_BLOCKS 1*/
115
116 /* Size of each block allocated in the pool, in bytes.
117    Should be at least 1k. */
118 #ifndef BLOCK_SIZE
119 #define BLOCK_SIZE 1024
120 #endif
121
122 /* Maximum size of a suballocated block.  Larger blocks are allocated
123    directly with malloc() to avoid memory wastage at the end of a
124    suballocation block. */
125 #ifndef MAX_SUBALLOC
126 #define MAX_SUBALLOC 64
127 #endif
128
129 /* Sizes of some structures with alignment padding included. */
130 #define POOL_BLOCK_SIZE ROUND_UP (sizeof (struct pool_block), ALIGN_SIZE)
131 #define POOL_GIZMO_SIZE ROUND_UP (sizeof (struct pool_gizmo), ALIGN_SIZE)
132 #define POOL_SIZE ROUND_UP (sizeof (struct pool), ALIGN_SIZE)
133
134 /* Serial number used to keep track of gizmos for mark/release. */
135 static long serial = 0;
136
137 /* Prototypes. */
138 static void add_gizmo (struct pool *, struct pool_gizmo *);
139 static void free_gizmo (struct pool_gizmo *);
140 static void free_all_gizmos (struct pool *pool);
141 static void delete_gizmo (struct pool *, struct pool_gizmo *);
142 static void check_gizmo (struct pool *, struct pool_gizmo *);
143 \f
144 /* General routines. */
145
146 /* Creates and returns a new memory pool, which allows malloc()'d
147    blocks to be suballocated in a time- and space-efficient manner.
148    The entire contents of the memory pool are freed at once.
149
150    In addition, other objects can be associated with a memory pool.
151    These are released when the pool is destroyed. */
152 struct pool *
153 pool_create (void)
154 {
155   struct pool_block *block;
156   struct pool *pool;
157
158   block = xmalloc (BLOCK_SIZE);
159   block->prev = block->next = block;
160   block->ofs = POOL_BLOCK_SIZE + POOL_SIZE;
161   
162   pool = (struct pool *) (((char *) block) + POOL_BLOCK_SIZE);
163   pool->parent = NULL;
164   pool->blocks = block;
165   pool->gizmos = NULL;
166
167   return pool;
168 }
169
170 /* Destroy the specified pool, including all subpools. */
171 void
172 pool_destroy (struct pool *pool)
173 {
174   if (pool == NULL)
175     return;
176
177   /* Remove this pool from its parent's list of gizmos. */
178   if (pool->parent) 
179     delete_gizmo (pool->parent, (void *) (((char *) pool) + POOL_SIZE));
180   
181   free_all_gizmos (pool);
182
183   /* Free all the memory. */
184   {
185     struct pool_block *cur, *next;
186
187     pool->blocks->prev->next = NULL;
188     for (cur = pool->blocks; cur; cur = next)
189       {
190         next = cur->next;
191         free (cur);
192       }
193   }
194 }
195
196 /* Release all the memory and gizmos in POOL.
197    Blocks are not given back with free() but kept for later
198    allocations.  To give back memory, use a subpool instead. */ 
199 void
200 pool_clear (struct pool *pool) 
201 {
202   free_all_gizmos (pool);
203
204   /* Zero out block sizes. */
205   {
206     struct pool_block *cur;
207     
208     cur = pool->blocks;
209     do
210       {
211         cur->ofs = POOL_BLOCK_SIZE;
212         if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
213           {
214             cur->ofs += POOL_SIZE;
215             if (pool->parent != NULL)
216               cur->ofs += POOL_GIZMO_SIZE; 
217           }
218         cur = cur->next;
219       }
220     while (cur != pool->blocks);
221   }
222 }
223 \f
224 /* Suballocation routines. */
225
226 /* Allocates a memory region AMT bytes in size from POOL and returns a
227    pointer to the region's start. */
228 void *
229 pool_alloc (struct pool *pool, size_t amt)
230 {
231   assert (pool != NULL);
232   
233 #ifndef DISCRETE_BLOCKS
234   if (amt <= MAX_SUBALLOC)
235     {
236       /* If there is space in this block, take it. */
237       struct pool_block *b = pool->blocks;
238       b->ofs = ROUND_UP (b->ofs, ALIGN_SIZE);
239       if (b->ofs + amt <= BLOCK_SIZE)
240         {
241           void *const p = ((char *) b) + b->ofs;
242           b->ofs += amt;
243           return p;
244         }
245
246       /* No space in this block, so we must make other
247          arrangements. */
248       if (b->next->ofs == 0) 
249         {
250           /* The next block is empty.  Use it. */
251           b = b->next;
252           b->ofs = POOL_BLOCK_SIZE;
253           if ((char *) b + POOL_BLOCK_SIZE == (char *) pool)
254             b->ofs += POOL_SIZE;
255         }
256       else 
257         {
258           /* Create a new block at the start of the list. */
259           b = xmalloc (BLOCK_SIZE);
260           b->next = pool->blocks;
261           b->prev = pool->blocks->prev;
262           b->ofs = POOL_BLOCK_SIZE;
263           pool->blocks->prev->next = b;
264           pool->blocks->prev = b;
265         }
266       pool->blocks = b;
267
268       /* Allocate space from B. */
269       b->ofs += amt;
270       return ((char *) b) + b->ofs - amt;
271     }
272   else
273 #endif
274     return pool_malloc (pool, amt);
275 }
276
277 /* Allocates a memory region N * S bytes in size from POOL and
278    returns a pointer to the region's start.
279    N must be nonnegative, S must be positive.
280    Terminates the program if the memory cannot be obtained,
281    including the case where N * S overflows the range of size_t. */
282 void *
283 pool_nalloc (struct pool *pool, size_t n, size_t s) 
284 {
285   if (xalloc_oversized (n, s))
286     xalloc_die ();
287   return pool_alloc (pool, n * s);
288 }
289
290 /* Allocates SIZE bytes in POOL, copies BUFFER into it, and
291    returns the new copy. */
292 void *
293 pool_clone (struct pool *pool, const void *buffer, size_t size)
294 {
295   void *block = pool_alloc (pool, size);
296   memcpy (block, buffer, size);
297   return block;
298 }
299
300 /* Duplicates STRING, which has LENGTH characters, within POOL,
301    and returns a pointer to the duplicate.  LENGTH should not
302    include the null terminator, which is always added to the
303    duplicate.  For use only with strings, because the returned
304    pointere may not be aligned properly for other types. */
305 char *
306 pool_strndup (struct pool *pool, const char *string, size_t length)
307 {
308   size_t size;
309   char *copy;
310
311   assert (pool && string);
312   size = length + 1;
313
314   /* Note that strings need not be aligned on any boundary. */
315 #ifndef DISCRETE_BLOCKS
316   {
317     struct pool_block *const b = pool->blocks;
318
319     if (b->ofs + size <= BLOCK_SIZE)
320       {
321         copy = ((char *) b) + b->ofs;
322         b->ofs += size;
323       }
324     else
325       copy = pool_alloc (pool, size);
326   }
327 #else
328   copy = pool_alloc (pool, size);
329 #endif
330
331   memcpy (copy, string, length);
332   copy[length] = '\0';
333   return copy;
334 }
335
336 /* Duplicates null-terminated STRING, within POOL, and returns a
337    pointer to the duplicate.  For use only with strings, because
338    the returned pointere may not be aligned properly for other
339    types. */
340 char *
341 pool_strdup (struct pool *pool, const char *string) 
342 {
343   return pool_strndup (pool, string, strlen (string));
344 }
345 \f
346 /* Standard allocation routines. */
347
348 /* Allocates AMT bytes using malloc(), to be managed by POOL, and
349    returns a pointer to the beginning of the block.
350    If POOL is a null pointer, then allocates a normal memory block
351    with xmalloc().  */
352 void *
353 pool_malloc (struct pool *pool, size_t amt)
354 {
355   if (pool != NULL)
356     {
357       if (amt != 0)
358         {
359           struct pool_gizmo *g = xmalloc (amt + POOL_GIZMO_SIZE);
360           g->type = POOL_GIZMO_MALLOC;
361           add_gizmo (pool, g);
362
363           return ((char *) g) + POOL_GIZMO_SIZE;
364         }
365       else
366         return NULL;
367     }
368   else
369     return xmalloc (amt);
370 }
371
372 /* Allocates and returns N elements of S bytes each, to be
373    managed by POOL.
374    If POOL is a null pointer, then allocates a normal memory block
375    with malloc().
376    N must be nonnegative, S must be positive.
377    Terminates the program if the memory cannot be obtained,
378    including the case where N * S overflows the range of size_t. */
379 void *
380 pool_nmalloc (struct pool *pool, size_t n, size_t s) 
381 {
382   if (xalloc_oversized (n, s))
383     xalloc_die ();
384   return pool_malloc (pool, n * s);
385 }
386
387 /* Changes the allocation size of the specified memory block P managed
388    by POOL to AMT bytes and returns a pointer to the beginning of the
389    block.
390    If POOL is a null pointer, then the block is reallocated in the
391    usual way with realloc(). */
392 void *
393 pool_realloc (struct pool *pool, void *p, size_t amt)
394 {
395   if (pool != NULL)
396     {
397       if (p != NULL)
398         {
399           if (amt != 0)
400             {
401               struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
402               check_gizmo (pool, g);
403
404               g = xrealloc (g, amt + POOL_GIZMO_SIZE);
405               if (g->next)
406                 g->next->prev = g;
407               if (g->prev)
408                 g->prev->next = g;
409               else
410                 pool->gizmos = g;
411               check_gizmo (pool, g);
412
413               return ((char *) g) + POOL_GIZMO_SIZE;
414             }
415           else
416             {
417               pool_free (pool, p);
418               return NULL;
419             }
420         }
421       else
422         return pool_malloc (pool, amt);
423     }
424   else
425     return xrealloc (p, amt);
426 }
427
428 /* Changes the allocation size of the specified memory block P
429    managed by POOL to N * S bytes and returns a pointer to the
430    beginning of the block.
431    N must be nonnegative, S must be positive.
432    If POOL is a null pointer, then the block is reallocated in
433    the usual way with xrealloc().
434    Terminates the program if the memory cannot be obtained,
435    including the case where N * S overflows the range of size_t. */
436 void *
437 pool_nrealloc (struct pool *pool, void *p, size_t n, size_t s)
438 {
439   if (xalloc_oversized (n, s))
440     xalloc_die ();
441   return pool_realloc (pool, p, n * s);
442 }
443
444 /* If P is null, allocate a block of at least *PN such objects;
445    otherwise, reallocate P so that it contains more than *PN
446    objects each of S bytes.  *PN must be nonzero unless P is
447    null, and S must be nonzero.  Set *PN to the new number of
448    objects, and return the pointer to the new block.  *PN is
449    never set to zero, and the returned pointer is never null.
450
451    The block returned is managed by POOL.  If POOL is a null
452    pointer, then the block is reallocated in the usual way with
453    x2nrealloc().
454
455    Terminates the program if the memory cannot be obtained,
456    including the case where the memory required overflows the
457    range of size_t.
458
459    Repeated reallocations are guaranteed to make progress, either by
460    allocating an initial block with a nonzero size, or by allocating a
461    larger block.
462
463    In the following implementation, nonzero sizes are doubled so that
464    repeated reallocations have O(N log N) overall cost rather than
465    O(N**2) cost, but the specification for this function does not
466    guarantee that sizes are doubled.
467
468    Here is an example of use:
469
470      int *p = NULL;
471      struct pool *pool;
472      size_t used = 0;
473      size_t allocated = 0;
474
475      void
476      append_int (int value)
477        {
478          if (used == allocated)
479            p = pool_2nrealloc (pool, p, &allocated, sizeof *p);
480          p[used++] = value;
481        }
482
483    This causes x2nrealloc to allocate a block of some nonzero size the
484    first time it is called.
485
486    To have finer-grained control over the initial size, set *PN to a
487    nonzero value before calling this function with P == NULL.  For
488    example:
489
490      int *p = NULL;
491      struct pool *pool;
492      size_t used = 0;
493      size_t allocated = 0;
494      size_t allocated1 = 1000;
495
496      void
497      append_int (int value)
498        {
499          if (used == allocated)
500            {
501              p = pool_2nrealloc (pool, p, &allocated1, sizeof *p);
502              allocated = allocated1;
503            }
504          p[used++] = value;
505        }
506
507    This function implementation is from gnulib. */
508 void *
509 pool_2nrealloc (struct pool *pool, void *p, size_t *pn, size_t s)
510 {
511   size_t n = *pn;
512
513   if (p == NULL)
514     {
515       if (n == 0)
516         {
517           /* The approximate size to use for initial small allocation
518              requests, when the invoking code specifies an old size of
519              zero.  64 bytes is the largest "small" request for the
520              GNU C library malloc.  */
521           enum { DEFAULT_MXFAST = 64 };
522
523           n = DEFAULT_MXFAST / s;
524           n += !n;
525         }
526     }
527   else
528     {
529       if (SIZE_MAX / 2 / s < n)
530         xalloc_die ();
531       n *= 2;
532     }
533
534   *pn = n;
535   return pool_realloc (pool, p, n * s);
536 }
537
538 /* Frees block P managed by POOL.
539    If POOL is a null pointer, then the block is freed as usual with
540    free(). */
541 void
542 pool_free (struct pool *pool, void *p)
543 {
544   if (pool != NULL && p != NULL)
545     {
546       struct pool_gizmo *g = (void *) (((char *) p) - POOL_GIZMO_SIZE);
547       check_gizmo (pool, g);
548       delete_gizmo (pool, g);
549       free (g);
550     }
551   else
552     free (p);
553 }
554 \f
555 /* Gizmo allocations. */
556
557 /* Creates and returns a pool as a subpool of POOL.
558    The subpool will be destroyed automatically when POOL is destroyed.
559    It may also be destroyed explicitly in advance. */
560 struct pool *
561 pool_create_subpool (struct pool *pool)
562 {
563   struct pool *subpool;
564   struct pool_gizmo *g;
565
566   assert (pool != NULL);
567   subpool = pool_create ();
568   subpool->parent = pool;
569
570   g = (void *) (((char *) subpool->blocks) + subpool->blocks->ofs);
571   subpool->blocks->ofs += POOL_GIZMO_SIZE;
572   
573   g->type = POOL_GIZMO_SUBPOOL;
574   g->p.subpool = subpool;
575
576   add_gizmo (pool, g);
577
578   return subpool;
579 }
580
581 /* Opens file FILENAME with mode MODE and returns a handle to it
582    if successful or a null pointer if not.
583    The file will be closed automatically when POOL is destroyed, or it
584    may be closed explicitly in advance using pool_fclose. */
585 FILE *
586 pool_fopen (struct pool *pool, const char *filename, const char *mode)
587 {
588   FILE *f;
589
590   assert (pool && filename && mode);
591   f = fopen (filename, mode);
592   if (f == NULL)
593     return NULL;
594
595   {
596     struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
597     g->type = POOL_GIZMO_FILE;
598     g->p.file = f;
599     add_gizmo (pool, g);
600   }
601
602   return f;
603 }
604
605 /* Closes file FILE managed by POOL. */
606 int
607 pool_fclose (struct pool *pool, FILE *file)
608 {
609   assert (pool && file);
610   if (fclose (file) == EOF)
611     return EOF;
612   
613   {
614     struct pool_gizmo *g;
615
616     for (g = pool->gizmos; g; g = g->next)
617       if (g->type == POOL_GIZMO_FILE && g->p.file == file)
618         {
619           delete_gizmo (pool, g);
620           break;
621         }
622   }
623   
624   return 0;
625 }
626 \f
627 /* Registers FREE to be called with argument P.
628    P should be unique among those registered in POOL so that it can be
629    uniquely identified by pool_unregister().
630    If not unregistered, FREE will be called with argument P when POOL
631    is destroyed. */
632 void
633 pool_register (struct pool *pool, void (*free) (void *), void *p)
634 {
635   assert (pool && free && p);
636
637   {
638     struct pool_gizmo *g = pool_alloc (pool, sizeof *g);
639     g->type = POOL_GIZMO_REGISTERED;
640     g->p.registered.free = free;
641     g->p.registered.p = p;
642     add_gizmo (pool, g);
643   }
644 }
645
646 /* Unregisters previously registered P from POOL.
647    Returns nonzero only if P was found to be registered in POOL. */
648 int
649 pool_unregister (struct pool *pool, void *p)
650 {
651   assert (pool && p);
652   
653   {
654     struct pool_gizmo *g;
655
656     for (g = pool->gizmos; g; g = g->next)
657       if (g->type == POOL_GIZMO_REGISTERED && g->p.registered.p == p)
658         {
659           delete_gizmo (pool, g);
660           return 1;
661         }
662   }
663   
664   return 0;
665 }
666 \f
667 /* Partial freeing. */
668
669 /* Notes the state of POOL into MARK so that it may be restored
670    by a call to pool_release(). */
671 void
672 pool_mark (struct pool *pool, struct pool_mark *mark)
673 {
674   assert (pool && mark);
675
676   mark->block = pool->blocks;
677   mark->ofs = pool->blocks->ofs;
678
679   mark->serial = serial;
680 }
681
682 /* Restores to POOL the state recorded in MARK.
683    Emptied blocks are not given back with free() but kept for
684    later allocations.  To get that behavior, use a subpool
685    instead. */ 
686 void
687 pool_release (struct pool *pool, const struct pool_mark *mark)
688 {
689   assert (pool && mark);
690   
691   {
692     struct pool_gizmo *cur, *next;
693
694     for (cur = pool->gizmos; cur && cur->serial >= mark->serial; cur = next)
695       {
696         next = cur->next;
697         free_gizmo (cur);
698       }
699
700     if (cur != NULL)
701       {
702         cur->prev = NULL;
703         pool->gizmos = cur;
704       }
705     else
706       pool->gizmos = NULL;
707   }
708   
709   {
710     struct pool_block *cur;
711
712     for (cur = pool->blocks; cur != mark->block; cur = cur->next) 
713       {
714         cur->ofs = POOL_BLOCK_SIZE;
715         if ((char *) cur + POOL_BLOCK_SIZE == (char *) pool) 
716           {
717             cur->ofs += POOL_SIZE;
718             if (pool->parent != NULL)
719               cur->ofs += POOL_GIZMO_SIZE; 
720           }
721       }
722     pool->blocks = mark->block;
723     pool->blocks->ofs = mark->ofs;
724   }
725 }
726 \f
727 /* Private functions. */
728
729 /* Adds GIZMO at the beginning of POOL's gizmo list. */
730 static void
731 add_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
732 {
733   assert (pool && gizmo);
734
735   gizmo->pool = pool;
736   gizmo->next = pool->gizmos;
737   gizmo->prev = NULL;
738   if (pool->gizmos)
739     pool->gizmos->prev = gizmo;
740   pool->gizmos = gizmo;
741
742   gizmo->serial = serial++;
743
744   check_gizmo (pool, gizmo);
745 }
746  
747 /* Removes GIZMO from POOL's gizmo list. */
748 static void
749 delete_gizmo (struct pool *pool, struct pool_gizmo *gizmo)
750 {
751   assert (pool && gizmo);
752
753   check_gizmo (pool, gizmo);
754
755   if (gizmo->prev)
756     gizmo->prev->next = gizmo->next;
757   else
758     pool->gizmos = gizmo->next;
759   if (gizmo->next)
760     gizmo->next->prev = gizmo->prev;
761 }
762
763 /* Frees any of GIZMO's internal state.
764    GIZMO's data must not be referenced after calling this function. */
765 static void
766 free_gizmo (struct pool_gizmo *gizmo)
767 {
768   assert (gizmo != NULL);
769
770   switch (gizmo->type)
771     {
772     case POOL_GIZMO_MALLOC:
773       free (gizmo);
774       break;
775     case POOL_GIZMO_FILE:
776       fclose (gizmo->p.file);   /* Ignore errors. */
777       break;
778     case POOL_GIZMO_SUBPOOL:
779       gizmo->p.subpool->parent = NULL;
780       pool_destroy (gizmo->p.subpool);
781       break;
782     case POOL_GIZMO_REGISTERED:
783       gizmo->p.registered.free (gizmo->p.registered.p);
784       break;
785     default:
786       assert (0);
787     }
788 }
789
790 /* Free all the gizmos in POOL. */
791 static void
792 free_all_gizmos (struct pool *pool) 
793 {
794   struct pool_gizmo *cur, *next;
795
796   for (cur = pool->gizmos; cur; cur = next)
797     {
798       next = cur->next;
799       free_gizmo (cur);
800     }
801   pool->gizmos = NULL;
802 }
803
804 static void
805 check_gizmo (struct pool *p, struct pool_gizmo *g) 
806 {
807   assert (g->pool == p);
808   assert (g->next == NULL || g->next->prev == g);
809   assert ((g->prev != NULL && g->prev->next == g)
810           || (g->prev == NULL && p->gizmos == g));
811
812 }
813 \f
814 /* Self-test routine. */
815
816 #include <errno.h>
817 #include <stdio.h>
818 #include <stdlib.h>
819 #include <string.h>
820 #include <time.h>
821
822 #define N_ITERATIONS 8192
823 #define N_FILES 16
824
825 /* Self-test routine.
826    This is not exhaustive, but it can be useful. */
827 int
828 cmd_debug_pool (void)
829 {
830   int seed = time (0) * 257 % 32768;
831
832   for (;;)
833     {
834       struct pool *pool;
835       struct pool_mark m1, m2;
836       FILE *files[N_FILES];
837       int cur_file;
838       long i;
839
840       printf ("Random number seed: %d\n", seed);
841       srand (seed++);
842
843       printf ("Creating pool...\n");
844       pool = pool_create ();
845
846       printf ("Marking pool state...\n");
847       pool_mark (pool, &m1);
848
849       printf ("    Populating pool with random-sized small objects...\n");
850       for (i = 0; i < N_ITERATIONS; i++)
851         {
852           size_t size = rand () % MAX_SUBALLOC;
853           void *p = pool_alloc (pool, size);
854           memset (p, 0, size);
855         }
856
857       printf ("    Marking pool state...\n");
858       pool_mark (pool, &m2);
859       
860       printf ("       Populating pool with random-sized small "
861               "and large objects...\n");
862       for (i = 0; i < N_ITERATIONS; i++)
863         {
864           size_t size = rand () % (2 * MAX_SUBALLOC);
865           void *p = pool_alloc (pool, size);
866           memset (p, 0, size);
867         }
868
869       printf ("    Releasing pool state...\n");
870       pool_release (pool, &m2);
871
872       printf ("    Populating pool with random objects and gizmos...\n");
873       for (i = 0; i < N_FILES; i++)
874         files[i] = NULL;
875       cur_file = 0;
876       for (i = 0; i < N_ITERATIONS; i++)
877         {
878           int type = rand () % 32;
879
880           if (type == 0)
881             {
882               if (files[cur_file] != NULL
883                   && EOF == pool_fclose (pool, files[cur_file]))
884                 printf ("error on fclose: %s\n", strerror (errno));
885
886               files[cur_file] = pool_fopen (pool, "/dev/null", "r");
887
888               if (++cur_file >= N_FILES)
889                 cur_file = 0;
890             }
891           else if (type == 1)
892             pool_create_subpool (pool);
893           else 
894             {
895               size_t size = rand () % (2 * MAX_SUBALLOC);
896               void *p = pool_alloc (pool, size);
897               memset (p, 0, size);
898             }
899         }
900       
901       printf ("Releasing pool state...\n");
902       pool_release (pool, &m1);
903
904       printf ("Destroying pool...\n");
905       pool_destroy (pool);
906
907       putchar ('\n');
908     }
909
910   return CMD_SUCCESS;
911 }
912