Blame lib/malloca.c

Packit 8f70b4
/* Safe automatic memory allocation.
Packit 8f70b4
   Copyright (C) 2003, 2006-2007, 2009-2018 Free Software Foundation, Inc.
Packit 8f70b4
   Written by Bruno Haible <bruno@clisp.org>, 2003, 2018.
Packit 8f70b4
Packit 8f70b4
   This program is free software; you can redistribute it and/or modify
Packit 8f70b4
   it under the terms of the GNU General Public License as published by
Packit 8f70b4
   the Free Software Foundation; either version 3, or (at your option)
Packit 8f70b4
   any later version.
Packit 8f70b4
Packit 8f70b4
   This program is distributed in the hope that it will be useful,
Packit 8f70b4
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8f70b4
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 8f70b4
   GNU General Public License for more details.
Packit 8f70b4
Packit 8f70b4
   You should have received a copy of the GNU General Public License
Packit 8f70b4
   along with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit 8f70b4
Packit 8f70b4
#define _GL_USE_STDLIB_ALLOC 1
Packit 8f70b4
#include <config.h>
Packit 8f70b4
Packit 8f70b4
/* Specification.  */
Packit 8f70b4
#include "malloca.h"
Packit 8f70b4
Packit 8f70b4
#include "verify.h"
Packit 8f70b4
Packit 8f70b4
/* The speed critical point in this file is freea() applied to an alloca()
Packit 8f70b4
   result: it must be fast, to match the speed of alloca().  The speed of
Packit 8f70b4
   mmalloca() and freea() in the other case are not critical, because they
Packit 8f70b4
   are only invoked for big memory sizes.
Packit 8f70b4
   Here we use a bit in the address as an indicator, an idea by Ondřej Bílka.
Packit 8f70b4
   malloca() can return three types of pointers:
Packit 8f70b4
     - Pointers ≡ 0 mod 2*sa_alignment_max come from stack allocation.
Packit 8f70b4
     - Pointers ≡ sa_alignment_max mod 2*sa_alignment_max come from heap
Packit 8f70b4
       allocation.
Packit 8f70b4
     - NULL comes from a failed heap allocation.  */
Packit 8f70b4
Packit 8f70b4
/* Type for holding very small pointer differences.  */
Packit 8f70b4
typedef unsigned char small_t;
Packit 8f70b4
/* Verify that it is wide enough.  */
Packit 8f70b4
verify (2 * sa_alignment_max - 1 <= (small_t) -1);
Packit 8f70b4
Packit 8f70b4
void *
Packit 8f70b4
mmalloca (size_t n)
Packit 8f70b4
{
Packit 8f70b4
#if HAVE_ALLOCA
Packit 8f70b4
  /* Allocate one more word, used to determine the address to pass to freea(),
Packit 8f70b4
     and room for the alignment ≡ sa_alignment_max mod 2*sa_alignment_max.  */
Packit 8f70b4
  size_t nplus = n + sizeof (small_t) + 2 * sa_alignment_max - 1;
Packit 8f70b4
Packit 8f70b4
  if (nplus >= n)
Packit 8f70b4
    {
Packit 8f70b4
      char *mem = (char *) malloc (nplus);
Packit 8f70b4
Packit 8f70b4
      if (mem != NULL)
Packit 8f70b4
        {
Packit 8f70b4
          char *p =
Packit 8f70b4
            (char *)((((uintptr_t)mem + sizeof (small_t) + sa_alignment_max - 1)
Packit 8f70b4
                      & ~(uintptr_t)(2 * sa_alignment_max - 1))
Packit 8f70b4
                     + sa_alignment_max);
Packit 8f70b4
          /* Here p >= mem + sizeof (small_t),
Packit 8f70b4
             and p <= mem + sizeof (small_t) + 2 * sa_alignment_max - 1
Packit 8f70b4
             hence p + n <= mem + nplus.
Packit 8f70b4
             So, the memory range [p, p+n) lies in the allocated memory range
Packit 8f70b4
             [mem, mem + nplus).  */
Packit 8f70b4
          ((small_t *) p)[-1] = p - mem;
Packit 8f70b4
          /* p ≡ sa_alignment_max mod 2*sa_alignment_max.  */
Packit 8f70b4
          return p;
Packit 8f70b4
        }
Packit 8f70b4
    }
Packit 8f70b4
  /* Out of memory.  */
Packit 8f70b4
  return NULL;
Packit 8f70b4
#else
Packit 8f70b4
# if !MALLOC_0_IS_NONNULL
Packit 8f70b4
  if (n == 0)
Packit 8f70b4
    n = 1;
Packit 8f70b4
# endif
Packit 8f70b4
  return malloc (n);
Packit 8f70b4
#endif
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#if HAVE_ALLOCA
Packit 8f70b4
void
Packit 8f70b4
freea (void *p)
Packit 8f70b4
{
Packit 8f70b4
  /* Check argument.  */
Packit 8f70b4
  if ((uintptr_t) p & (sa_alignment_max - 1))
Packit 8f70b4
    {
Packit 8f70b4
      /* p was not the result of a malloca() call.  Invalid argument.  */
Packit 8f70b4
      abort ();
Packit 8f70b4
    }
Packit 8f70b4
  /* Determine whether p was a non-NULL pointer returned by mmalloca().  */
Packit 8f70b4
  if ((uintptr_t) p & sa_alignment_max)
Packit 8f70b4
    {
Packit 8f70b4
      void *mem = (char *) p - ((small_t *) p)[-1];
Packit 8f70b4
      free (mem);
Packit 8f70b4
    }
Packit 8f70b4
}
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
/*
Packit 8f70b4
 * Hey Emacs!
Packit 8f70b4
 * Local Variables:
Packit 8f70b4
 * coding: utf-8
Packit 8f70b4
 * End:
Packit 8f70b4
 */