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