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