xref: /dflybsd-src/contrib/cvs-1.12/lib/allocsa.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /* Safe automatic memory allocation.
2*86d7f5d3SJohn Marino    Copyright (C) 2003 Free Software Foundation, Inc.
3*86d7f5d3SJohn Marino    Written by Bruno Haible <bruno@clisp.org>, 2003.
4*86d7f5d3SJohn Marino 
5*86d7f5d3SJohn Marino    This program is free software; you can redistribute it and/or modify
6*86d7f5d3SJohn Marino    it under the terms of the GNU General Public License as published by
7*86d7f5d3SJohn Marino    the Free Software Foundation; either version 2, or (at your option)
8*86d7f5d3SJohn Marino    any later version.
9*86d7f5d3SJohn Marino 
10*86d7f5d3SJohn Marino    This program is distributed in the hope that it will be useful,
11*86d7f5d3SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
12*86d7f5d3SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13*86d7f5d3SJohn Marino    GNU General Public License for more details.
14*86d7f5d3SJohn Marino 
15*86d7f5d3SJohn Marino    You should have received a copy of the GNU General Public License
16*86d7f5d3SJohn Marino    along with this program; if not, write to the Free Software Foundation,
17*86d7f5d3SJohn Marino    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18*86d7f5d3SJohn Marino 
19*86d7f5d3SJohn Marino #ifdef HAVE_CONFIG_H
20*86d7f5d3SJohn Marino # include <config.h>
21*86d7f5d3SJohn Marino #endif
22*86d7f5d3SJohn Marino 
23*86d7f5d3SJohn Marino /* Specification.  */
24*86d7f5d3SJohn Marino #include "allocsa.h"
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino /* The speed critical point in this file is freesa() applied to an alloca()
27*86d7f5d3SJohn Marino    result: it must be fast, to match the speed of alloca().  The speed of
28*86d7f5d3SJohn Marino    mallocsa() and freesa() in the other case are not critical, because they
29*86d7f5d3SJohn Marino    are only invoked for big memory sizes.  */
30*86d7f5d3SJohn Marino 
31*86d7f5d3SJohn Marino #if HAVE_ALLOCA
32*86d7f5d3SJohn Marino 
33*86d7f5d3SJohn Marino /* Store the mallocsa() results in a hash table.  This is needed to reliably
34*86d7f5d3SJohn Marino    distinguish a mallocsa() result and an alloca() result.
35*86d7f5d3SJohn Marino 
36*86d7f5d3SJohn Marino    Although it is possible that the same pointer is returned by alloca() and
37*86d7f5d3SJohn Marino    by mallocsa() at different times in the same application, it does not lead
38*86d7f5d3SJohn Marino    to a bug in freesa(), because:
39*86d7f5d3SJohn Marino      - Before a pointer returned by alloca() can point into malloc()ed memory,
40*86d7f5d3SJohn Marino        the function must return, and once this has happened the programmer must
41*86d7f5d3SJohn Marino        not call freesa() on it anyway.
42*86d7f5d3SJohn Marino      - Before a pointer returned by mallocsa() can point into the stack, it
43*86d7f5d3SJohn Marino        must be freed.  The only function that can free it is freesa(), and
44*86d7f5d3SJohn Marino        when freesa() frees it, it also removes it from the hash table.  */
45*86d7f5d3SJohn Marino 
46*86d7f5d3SJohn Marino #define MAGIC_NUMBER 0x1415fb4a
47*86d7f5d3SJohn Marino #define MAGIC_SIZE sizeof (int)
48*86d7f5d3SJohn Marino /* This is how the header info would look like without any alignment
49*86d7f5d3SJohn Marino    considerations.  */
50*86d7f5d3SJohn Marino struct preliminary_header { void *next; char room[MAGIC_SIZE]; };
51*86d7f5d3SJohn Marino /* But the header's size must be a multiple of sa_alignment_max.  */
52*86d7f5d3SJohn Marino #define HEADER_SIZE \
53*86d7f5d3SJohn Marino   (((sizeof (struct preliminary_header) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max)
54*86d7f5d3SJohn Marino struct header { void *next; char room[HEADER_SIZE - sizeof (struct preliminary_header) + MAGIC_SIZE]; };
55*86d7f5d3SJohn Marino /* Verify that HEADER_SIZE == sizeof (struct header).  */
56*86d7f5d3SJohn Marino typedef int verify1[2 * (HEADER_SIZE == sizeof (struct header)) - 1];
57*86d7f5d3SJohn Marino /* We make the hash table quite big, so that during lookups the probability
58*86d7f5d3SJohn Marino    of empty hash buckets is quite high.  There is no need to make the hash
59*86d7f5d3SJohn Marino    table resizable, because when the hash table gets filled so much that the
60*86d7f5d3SJohn Marino    lookup becomes slow, it means that the application has memory leaks.  */
61*86d7f5d3SJohn Marino #define HASH_TABLE_SIZE 257
62*86d7f5d3SJohn Marino static void * mallocsa_results[HASH_TABLE_SIZE];
63*86d7f5d3SJohn Marino 
64*86d7f5d3SJohn Marino #endif
65*86d7f5d3SJohn Marino 
66*86d7f5d3SJohn Marino void *
mallocsa(size_t n)67*86d7f5d3SJohn Marino mallocsa (size_t n)
68*86d7f5d3SJohn Marino {
69*86d7f5d3SJohn Marino #if HAVE_ALLOCA
70*86d7f5d3SJohn Marino   /* Allocate one more word, that serves as an indicator for malloc()ed
71*86d7f5d3SJohn Marino      memory, so that freesa() of an alloca() result is fast.  */
72*86d7f5d3SJohn Marino   size_t nplus = n + HEADER_SIZE;
73*86d7f5d3SJohn Marino 
74*86d7f5d3SJohn Marino   if (nplus >= n)
75*86d7f5d3SJohn Marino     {
76*86d7f5d3SJohn Marino       char *p = (char *) malloc (nplus);
77*86d7f5d3SJohn Marino 
78*86d7f5d3SJohn Marino       if (p != NULL)
79*86d7f5d3SJohn Marino 	{
80*86d7f5d3SJohn Marino 	  size_t slot;
81*86d7f5d3SJohn Marino 
82*86d7f5d3SJohn Marino 	  p += HEADER_SIZE;
83*86d7f5d3SJohn Marino 
84*86d7f5d3SJohn Marino 	  /* Put a magic number into the indicator word.  */
85*86d7f5d3SJohn Marino 	  ((int *) p)[-1] = MAGIC_NUMBER;
86*86d7f5d3SJohn Marino 
87*86d7f5d3SJohn Marino 	  /* Enter p into the hash table.  */
88*86d7f5d3SJohn Marino 	  slot = (unsigned long) p % HASH_TABLE_SIZE;
89*86d7f5d3SJohn Marino 	  ((struct header *) (p - HEADER_SIZE))->next = mallocsa_results[slot];
90*86d7f5d3SJohn Marino 	  mallocsa_results[slot] = p;
91*86d7f5d3SJohn Marino 
92*86d7f5d3SJohn Marino 	  return p;
93*86d7f5d3SJohn Marino 	}
94*86d7f5d3SJohn Marino     }
95*86d7f5d3SJohn Marino   /* Out of memory.  */
96*86d7f5d3SJohn Marino   return NULL;
97*86d7f5d3SJohn Marino #else
98*86d7f5d3SJohn Marino # if !MALLOC_0_IS_NONNULL
99*86d7f5d3SJohn Marino   if (n == 0)
100*86d7f5d3SJohn Marino     n = 1;
101*86d7f5d3SJohn Marino # endif
102*86d7f5d3SJohn Marino   return malloc (n);
103*86d7f5d3SJohn Marino #endif
104*86d7f5d3SJohn Marino }
105*86d7f5d3SJohn Marino 
106*86d7f5d3SJohn Marino #if HAVE_ALLOCA
107*86d7f5d3SJohn Marino void
freesa(void * p)108*86d7f5d3SJohn Marino freesa (void *p)
109*86d7f5d3SJohn Marino {
110*86d7f5d3SJohn Marino   /* mallocsa() may have returned NULL.  */
111*86d7f5d3SJohn Marino   if (p != NULL)
112*86d7f5d3SJohn Marino     {
113*86d7f5d3SJohn Marino       /* Attempt to quickly distinguish the mallocsa() result - which has
114*86d7f5d3SJohn Marino 	 a magic indicator word - and the alloca() result - which has an
115*86d7f5d3SJohn Marino 	 uninitialized indicator word.  It is for this test that sa_increment
116*86d7f5d3SJohn Marino 	 additional bytes are allocated in the alloca() case.  */
117*86d7f5d3SJohn Marino       if (((int *) p)[-1] == MAGIC_NUMBER)
118*86d7f5d3SJohn Marino 	{
119*86d7f5d3SJohn Marino 	  /* Looks like a mallocsa() result.  To see whether it really is one,
120*86d7f5d3SJohn Marino 	     perform a lookup in the hash table.  */
121*86d7f5d3SJohn Marino 	  size_t slot = (unsigned long) p % HASH_TABLE_SIZE;
122*86d7f5d3SJohn Marino 	  void **chain = &mallocsa_results[slot];
123*86d7f5d3SJohn Marino 	  for (; *chain != NULL;)
124*86d7f5d3SJohn Marino 	    {
125*86d7f5d3SJohn Marino 	      if (*chain == p)
126*86d7f5d3SJohn Marino 		{
127*86d7f5d3SJohn Marino 		  /* Found it.  Remove it from the hash table and free it.  */
128*86d7f5d3SJohn Marino 		  char *p_begin = (char *) p - HEADER_SIZE;
129*86d7f5d3SJohn Marino 		  *chain = ((struct header *) p_begin)->next;
130*86d7f5d3SJohn Marino 		  free (p_begin);
131*86d7f5d3SJohn Marino 		  return;
132*86d7f5d3SJohn Marino 		}
133*86d7f5d3SJohn Marino 	      chain = &((struct header *) ((char *) *chain - HEADER_SIZE))->next;
134*86d7f5d3SJohn Marino 	    }
135*86d7f5d3SJohn Marino 	}
136*86d7f5d3SJohn Marino       /* At this point, we know it was not a mallocsa() result.  */
137*86d7f5d3SJohn Marino     }
138*86d7f5d3SJohn Marino }
139*86d7f5d3SJohn Marino #endif
140