xref: /dflybsd-src/contrib/grep/lib/malloca.c (revision 91b9ed38d3db6a8a8ac5b66da1d43e6e331e259a)
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