Blame gl/malloca.c

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