Back to index

nagios-plugins  1.4.16
malloca.c
Go to the documentation of this file.
00001 /* Safe automatic memory allocation.
00002    Copyright (C) 2003, 2006-2007, 2009-2010 Free Software Foundation, Inc.
00003    Written by Bruno Haible <bruno@clisp.org>, 2003.
00004 
00005    This program is free software; you can redistribute it and/or modify
00006    it under the terms of the GNU General Public License as published by
00007    the Free Software Foundation; either version 3, or (at your option)
00008    any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013    GNU General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; if not, write to the Free Software Foundation,
00017    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
00018 
00019 #include <config.h>
00020 
00021 /* Specification.  */
00022 #include "malloca.h"
00023 
00024 /* Use the system functions, not the gnulib overrides in this file.  */
00025 #undef malloc
00026 
00027 /* The speed critical point in this file is freea() applied to an alloca()
00028    result: it must be fast, to match the speed of alloca().  The speed of
00029    mmalloca() and freea() in the other case are not critical, because they
00030    are only invoked for big memory sizes.  */
00031 
00032 #if HAVE_ALLOCA
00033 
00034 /* Store the mmalloca() results in a hash table.  This is needed to reliably
00035    distinguish a mmalloca() result and an alloca() result.
00036 
00037    Although it is possible that the same pointer is returned by alloca() and
00038    by mmalloca() at different times in the same application, it does not lead
00039    to a bug in freea(), because:
00040      - Before a pointer returned by alloca() can point into malloc()ed memory,
00041        the function must return, and once this has happened the programmer must
00042        not call freea() on it anyway.
00043      - Before a pointer returned by mmalloca() can point into the stack, it
00044        must be freed.  The only function that can free it is freea(), and
00045        when freea() frees it, it also removes it from the hash table.  */
00046 
00047 #define MAGIC_NUMBER 0x1415fb4a
00048 #define MAGIC_SIZE sizeof (int)
00049 /* This is how the header info would look like without any alignment
00050    considerations.  */
00051 struct preliminary_header { void *next; char room[MAGIC_SIZE]; };
00052 /* But the header's size must be a multiple of sa_alignment_max.  */
00053 #define HEADER_SIZE \
00054   (((sizeof (struct preliminary_header) + sa_alignment_max - 1) / sa_alignment_max) * sa_alignment_max)
00055 struct header { void *next; char room[HEADER_SIZE - sizeof (struct preliminary_header) + MAGIC_SIZE]; };
00056 /* Verify that HEADER_SIZE == sizeof (struct header).  */
00057 typedef int verify1[2 * (HEADER_SIZE == sizeof (struct header)) - 1];
00058 /* We make the hash table quite big, so that during lookups the probability
00059    of empty hash buckets is quite high.  There is no need to make the hash
00060    table resizable, because when the hash table gets filled so much that the
00061    lookup becomes slow, it means that the application has memory leaks.  */
00062 #define HASH_TABLE_SIZE 257
00063 static void * mmalloca_results[HASH_TABLE_SIZE];
00064 
00065 #endif
00066 
00067 void *
00068 mmalloca (size_t n)
00069 {
00070 #if HAVE_ALLOCA
00071   /* Allocate one more word, that serves as an indicator for malloc()ed
00072      memory, so that freea() of an alloca() result is fast.  */
00073   size_t nplus = n + HEADER_SIZE;
00074 
00075   if (nplus >= n)
00076     {
00077       char *p = (char *) malloc (nplus);
00078 
00079       if (p != NULL)
00080         {
00081           size_t slot;
00082 
00083           p += HEADER_SIZE;
00084 
00085           /* Put a magic number into the indicator word.  */
00086           ((int *) p)[-1] = MAGIC_NUMBER;
00087 
00088           /* Enter p into the hash table.  */
00089           slot = (unsigned long) p % HASH_TABLE_SIZE;
00090           ((struct header *) (p - HEADER_SIZE))->next = mmalloca_results[slot];
00091           mmalloca_results[slot] = p;
00092 
00093           return p;
00094         }
00095     }
00096   /* Out of memory.  */
00097   return NULL;
00098 #else
00099 # if !MALLOC_0_IS_NONNULL
00100   if (n == 0)
00101     n = 1;
00102 # endif
00103   return malloc (n);
00104 #endif
00105 }
00106 
00107 #if HAVE_ALLOCA
00108 void
00109 freea (void *p)
00110 {
00111   /* mmalloca() may have returned NULL.  */
00112   if (p != NULL)
00113     {
00114       /* Attempt to quickly distinguish the mmalloca() result - which has
00115          a magic indicator word - and the alloca() result - which has an
00116          uninitialized indicator word.  It is for this test that sa_increment
00117          additional bytes are allocated in the alloca() case.  */
00118       if (((int *) p)[-1] == MAGIC_NUMBER)
00119         {
00120           /* Looks like a mmalloca() result.  To see whether it really is one,
00121              perform a lookup in the hash table.  */
00122           size_t slot = (unsigned long) p % HASH_TABLE_SIZE;
00123           void **chain = &mmalloca_results[slot];
00124           for (; *chain != NULL;)
00125             {
00126               if (*chain == p)
00127                 {
00128                   /* Found it.  Remove it from the hash table and free it.  */
00129                   char *p_begin = (char *) p - HEADER_SIZE;
00130                   *chain = ((struct header *) p_begin)->next;
00131                   free (p_begin);
00132                   return;
00133                 }
00134               chain = &((struct header *) ((char *) *chain - HEADER_SIZE))->next;
00135             }
00136         }
00137       /* At this point, we know it was not a mmalloca() result.  */
00138     }
00139 }
00140 #endif