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