195b7b453SJohn Marino /* Safe automatic memory allocation.
2*09d4459fSDaniel Fojt Copyright (C) 2003, 2006-2007, 2009-2020 Free Software Foundation, Inc.
3*09d4459fSDaniel Fojt Written by Bruno Haible <bruno@clisp.org>, 2003, 2018.
495b7b453SJohn Marino
595b7b453SJohn Marino This program is free software; you can redistribute it and/or modify
695b7b453SJohn Marino it under the terms of the GNU General Public License as published by
795b7b453SJohn Marino the Free Software Foundation; either version 3, or (at your option)
895b7b453SJohn Marino any later version.
995b7b453SJohn Marino
1095b7b453SJohn Marino This program is distributed in the hope that it will be useful,
1195b7b453SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
1295b7b453SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1395b7b453SJohn Marino GNU General Public License for more details.
1495b7b453SJohn Marino
1595b7b453SJohn Marino You should have received a copy of the GNU General Public License
16*09d4459fSDaniel Fojt along with this program; if not, see <https://www.gnu.org/licenses/>. */
1795b7b453SJohn Marino
18200fbe8dSJohn Marino #define _GL_USE_STDLIB_ALLOC 1
1995b7b453SJohn Marino #include <config.h>
2095b7b453SJohn Marino
2195b7b453SJohn Marino /* Specification. */
2295b7b453SJohn Marino #include "malloca.h"
2395b7b453SJohn Marino
24200fbe8dSJohn Marino #include "verify.h"
2595b7b453SJohn Marino
2695b7b453SJohn Marino /* The speed critical point in this file is freea() applied to an alloca()
2795b7b453SJohn Marino result: it must be fast, to match the speed of alloca(). The speed of
2895b7b453SJohn Marino mmalloca() and freea() in the other case are not critical, because they
29*09d4459fSDaniel Fojt are only invoked for big memory sizes.
30*09d4459fSDaniel Fojt Here we use a bit in the address as an indicator, an idea by Ondřej Bílka.
31*09d4459fSDaniel Fojt malloca() can return three types of pointers:
32*09d4459fSDaniel Fojt - Pointers ≡ 0 mod 2*sa_alignment_max come from stack allocation.
33*09d4459fSDaniel Fojt - Pointers ≡ sa_alignment_max mod 2*sa_alignment_max come from heap
34*09d4459fSDaniel Fojt allocation.
35*09d4459fSDaniel Fojt - NULL comes from a failed heap allocation. */
3695b7b453SJohn Marino
37*09d4459fSDaniel Fojt /* Type for holding very small pointer differences. */
38*09d4459fSDaniel Fojt typedef unsigned char small_t;
39*09d4459fSDaniel Fojt /* Verify that it is wide enough. */
40*09d4459fSDaniel Fojt verify (2 * sa_alignment_max - 1 <= (small_t) -1);
4195b7b453SJohn Marino
4295b7b453SJohn Marino void *
mmalloca(size_t n)4395b7b453SJohn Marino mmalloca (size_t n)
4495b7b453SJohn Marino {
4595b7b453SJohn Marino #if HAVE_ALLOCA
46*09d4459fSDaniel Fojt /* Allocate one more word, used to determine the address to pass to freea(),
47*09d4459fSDaniel Fojt and room for the alignment ≡ sa_alignment_max mod 2*sa_alignment_max. */
48*09d4459fSDaniel Fojt size_t nplus = n + sizeof (small_t) + 2 * sa_alignment_max - 1;
4995b7b453SJohn Marino
5095b7b453SJohn Marino if (nplus >= n)
5195b7b453SJohn Marino {
52*09d4459fSDaniel Fojt char *mem = (char *) malloc (nplus);
5395b7b453SJohn Marino
54*09d4459fSDaniel Fojt if (mem != NULL)
5595b7b453SJohn Marino {
56*09d4459fSDaniel Fojt char *p =
57*09d4459fSDaniel Fojt (char *)((((uintptr_t)mem + sizeof (small_t) + sa_alignment_max - 1)
58*09d4459fSDaniel Fojt & ~(uintptr_t)(2 * sa_alignment_max - 1))
59*09d4459fSDaniel Fojt + sa_alignment_max);
60*09d4459fSDaniel Fojt /* Here p >= mem + sizeof (small_t),
61*09d4459fSDaniel Fojt and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1
62*09d4459fSDaniel Fojt hence p + n <= mem + nplus.
63*09d4459fSDaniel Fojt So, the memory range [p, p+n) lies in the allocated memory range
64*09d4459fSDaniel Fojt [mem, mem + nplus). */
65*09d4459fSDaniel Fojt ((small_t *) p)[-1] = p - mem;
66*09d4459fSDaniel Fojt /* p ≡ sa_alignment_max mod 2*sa_alignment_max. */
6795b7b453SJohn Marino return p;
6895b7b453SJohn Marino }
6995b7b453SJohn Marino }
7095b7b453SJohn Marino /* Out of memory. */
7195b7b453SJohn Marino return NULL;
7295b7b453SJohn Marino #else
7395b7b453SJohn Marino # if !MALLOC_0_IS_NONNULL
7495b7b453SJohn Marino if (n == 0)
7595b7b453SJohn Marino n = 1;
7695b7b453SJohn Marino # endif
7795b7b453SJohn Marino return malloc (n);
7895b7b453SJohn Marino #endif
7995b7b453SJohn Marino }
8095b7b453SJohn Marino
8195b7b453SJohn Marino #if HAVE_ALLOCA
8295b7b453SJohn Marino void
freea(void * p)8395b7b453SJohn Marino freea (void *p)
8495b7b453SJohn Marino {
85*09d4459fSDaniel Fojt /* Check argument. */
86*09d4459fSDaniel Fojt if ((uintptr_t) p & (sa_alignment_max - 1))
8795b7b453SJohn Marino {
88*09d4459fSDaniel Fojt /* p was not the result of a malloca() call. Invalid argument. */
89*09d4459fSDaniel Fojt abort ();
9095b7b453SJohn Marino }
91*09d4459fSDaniel Fojt /* Determine whether p was a non-NULL pointer returned by mmalloca(). */
92*09d4459fSDaniel Fojt if ((uintptr_t) p & sa_alignment_max)
93*09d4459fSDaniel Fojt {
94*09d4459fSDaniel Fojt void *mem = (char *) p - ((small_t *) p)[-1];
95*09d4459fSDaniel Fojt free (mem);
9695b7b453SJohn Marino }
9795b7b453SJohn Marino }
9895b7b453SJohn Marino #endif
99*09d4459fSDaniel Fojt
100*09d4459fSDaniel Fojt /*
101*09d4459fSDaniel Fojt * Hey Emacs!
102*09d4459fSDaniel Fojt * Local Variables:
103*09d4459fSDaniel Fojt * coding: utf-8
104*09d4459fSDaniel Fojt * End:
105*09d4459fSDaniel Fojt */
106