Blame gnulib/lib/malloca.c

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