xref: /netbsd-src/external/gpl2/xcvs/dist/lib/allocsa.c (revision 5a6c14c844c4c665da5632061aebde7bb2cb5766)
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