Blame gnulib/lib/malloca.c

Packit Service 51e54d
/* Safe automatic memory allocation.
Packit Service 51e54d
   Copyright (C) 2003, 2006-2007, 2009-2014 Free Software Foundation, Inc.
Packit Service 51e54d
   Written by Bruno Haible <bruno@clisp.org>, 2003.
Packit Service 51e54d
Packit Service 51e54d
   This program is free software; you can redistribute it and/or modify
Packit Service 51e54d
   it under the terms of the GNU General Public License as published by
Packit Service 51e54d
   the Free Software Foundation; either version 3, or (at your option)
Packit Service 51e54d
   any later version.
Packit Service 51e54d
Packit Service 51e54d
   This program is distributed in the hope that it will be useful,
Packit Service 51e54d
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 51e54d
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 51e54d
   GNU General Public License for more details.
Packit Service 51e54d
Packit Service 51e54d
   You should have received a copy of the GNU General Public License
Packit Service 51e54d
   along with this program; if not, see <http://www.gnu.org/licenses/>.  */
Packit Service 51e54d
Packit Service 51e54d
#define _GL_USE_STDLIB_ALLOC 1
Packit Service 51e54d
#include <config.h>
Packit Service 51e54d
Packit Service 51e54d
/* Specification.  */
Packit Service 51e54d
#include "malloca.h"
Packit Service 51e54d
Packit Service 51e54d
#include <stdint.h>
Packit Service 51e54d
Packit Service 51e54d
#include "verify.h"
Packit Service 51e54d
Packit Service 51e54d
/* The speed critical point in this file is freea() applied to an alloca()
Packit Service 51e54d
   result: it must be fast, to match the speed of alloca().  The speed of
Packit Service 51e54d
   mmalloca() and freea() in the other case are not critical, because they
Packit Service 51e54d
   are only invoked for big memory sizes.  */
Packit Service 51e54d
Packit Service 51e54d
#if HAVE_ALLOCA
Packit Service 51e54d
Packit Service 51e54d
/* Store the mmalloca() results in a hash table.  This is needed to reliably
Packit Service 51e54d
   distinguish a mmalloca() result and an alloca() result.
Packit Service 51e54d
Packit Service 51e54d
   Although it is possible that the same pointer is returned by alloca() and
Packit Service 51e54d
   by mmalloca() at different times in the same application, it does not lead
Packit Service 51e54d
   to a bug in freea(), because:
Packit Service 51e54d
     - Before a pointer returned by alloca() can point into malloc()ed memory,
Packit Service 51e54d
       the function must return, and once this has happened the programmer must
Packit Service 51e54d
       not call freea() on it anyway.
Packit Service 51e54d
     - Before a pointer returned by mmalloca() can point into the stack, it
Packit Service 51e54d
       must be freed.  The only function that can free it is freea(), and
Packit Service 51e54d
       when freea() frees it, it also removes it from the hash table.  */
Packit Service 51e54d
Packit Service 51e54d
#define MAGIC_NUMBER 0x1415fb4a
Packit Service 51e54d
#define MAGIC_SIZE sizeof (int)
Packit Service 51e54d
/* This is how the header info would look like without any alignment
Packit Service 51e54d
   considerations.  */
Packit Service 51e54d
struct preliminary_header { void *next; int magic; };
Packit Service 51e54d
/* But the header's size must be a multiple of sa_alignment_max.  */
Packit Service 51e54d
#define HEADER_SIZE \
Packit Service 51e54d
  (((sizeof (struct preliminary_header) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max)
Packit Service 51e54d
union header {
Packit Service 51e54d
  void *next;
Packit Service 51e54d
  struct {
Packit Service 51e54d
    char room[HEADER_SIZE - MAGIC_SIZE];
Packit Service 51e54d
    int word;
Packit Service 51e54d
  } magic;
Packit Service 51e54d
};
Packit Service 51e54d
verify (HEADER_SIZE == sizeof (union header));
Packit Service 51e54d
/* We make the hash table quite big, so that during lookups the probability
Packit Service 51e54d
   of empty hash buckets is quite high.  There is no need to make the hash
Packit Service 51e54d
   table resizable, because when the hash table gets filled so much that the
Packit Service 51e54d
   lookup becomes slow, it means that the application has memory leaks.  */
Packit Service 51e54d
#define HASH_TABLE_SIZE 257
Packit Service 51e54d
static void * mmalloca_results[HASH_TABLE_SIZE];
Packit Service 51e54d
Packit Service 51e54d
#endif
Packit Service 51e54d
Packit Service 51e54d
void *
Packit Service 51e54d
mmalloca (size_t n)
Packit Service 51e54d
{
Packit Service 51e54d
#if HAVE_ALLOCA
Packit Service 51e54d
  /* Allocate one more word, that serves as an indicator for malloc()ed
Packit Service 51e54d
     memory, so that freea() of an alloca() result is fast.  */
Packit Service 51e54d
  size_t nplus = n + HEADER_SIZE;
Packit Service 51e54d
Packit Service 51e54d
  if (nplus >= n)
Packit Service 51e54d
    {
Packit Service 51e54d
      void *p = malloc (nplus);
Packit Service 51e54d
Packit Service 51e54d
      if (p != NULL)
Packit Service 51e54d
        {
Packit Service 51e54d
          size_t slot;
Packit Service 51e54d
          union header *h = p;
Packit Service 51e54d
Packit Service 51e54d
          p = h + 1;
Packit Service 51e54d
Packit Service 51e54d
          /* Put a magic number into the indicator word.  */
Packit Service 51e54d
          h->magic.word = MAGIC_NUMBER;
Packit Service 51e54d
Packit Service 51e54d
          /* Enter p into the hash table.  */
Packit Service 51e54d
          slot = (uintptr_t) p % HASH_TABLE_SIZE;
Packit Service 51e54d
          h->next = mmalloca_results[slot];
Packit Service 51e54d
          mmalloca_results[slot] = p;
Packit Service 51e54d
Packit Service 51e54d
          return p;
Packit Service 51e54d
        }
Packit Service 51e54d
    }
Packit Service 51e54d
  /* Out of memory.  */
Packit Service 51e54d
  return NULL;
Packit Service 51e54d
#else
Packit Service 51e54d
# if !MALLOC_0_IS_NONNULL
Packit Service 51e54d
  if (n == 0)
Packit Service 51e54d
    n = 1;
Packit Service 51e54d
# endif
Packit Service 51e54d
  return malloc (n);
Packit Service 51e54d
#endif
Packit Service 51e54d
}
Packit Service 51e54d
Packit Service 51e54d
#if HAVE_ALLOCA
Packit Service 51e54d
void
Packit Service 51e54d
freea (void *p)
Packit Service 51e54d
{
Packit Service 51e54d
  /* mmalloca() may have returned NULL.  */
Packit Service 51e54d
  if (p != NULL)
Packit Service 51e54d
    {
Packit Service 51e54d
      /* Attempt to quickly distinguish the mmalloca() result - which has
Packit Service 51e54d
         a magic indicator word - and the alloca() result - which has an
Packit Service 51e54d
         uninitialized indicator word.  It is for this test that sa_increment
Packit Service 51e54d
         additional bytes are allocated in the alloca() case.  */
Packit Service 51e54d
      if (((int *) p)[-1] == MAGIC_NUMBER)
Packit Service 51e54d
        {
Packit Service 51e54d
          /* Looks like a mmalloca() result.  To see whether it really is one,
Packit Service 51e54d
             perform a lookup in the hash table.  */
Packit Service 51e54d
          size_t slot = (uintptr_t) p % HASH_TABLE_SIZE;
Packit Service 51e54d
          void **chain = &mmalloca_results[slot];
Packit Service 51e54d
          for (; *chain != NULL;)
Packit Service 51e54d
            {
Packit Service 51e54d
              union header *h = p;
Packit Service 51e54d
              if (*chain == p)
Packit Service 51e54d
                {
Packit Service 51e54d
                  /* Found it.  Remove it from the hash table and free it.  */
Packit Service 51e54d
                  union header *p_begin = h - 1;
Packit Service 51e54d
                  *chain = p_begin->next;
Packit Service 51e54d
                  free (p_begin);
Packit Service 51e54d
                  return;
Packit Service 51e54d
                }
Packit Service 51e54d
              h = *chain;
Packit Service 51e54d
              chain = &h[-1].next;
Packit Service 51e54d
            }
Packit Service 51e54d
        }
Packit Service 51e54d
      /* At this point, we know it was not a mmalloca() result.  */
Packit Service 51e54d
    }
Packit Service 51e54d
}
Packit Service 51e54d
#endif