Blame lib/malloca.c

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