Blame lib/malloca.c

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