Blame lib/malloca.c

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