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