Blame src/gl/malloca.c

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