Add Euro character.
[kopensolaris-gnu/glibc.git] / malloc / malloc.c
index ed24d5d..03d68b7 100644 (file)
@@ -1,7 +1,8 @@
 /* Malloc implementation for multiple threads without lock contention.
-   Copyright (C) 1996 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>, 1996.
+   Contributed by Wolfram Gloger <wmglo@dent.med.uni-muenchen.de>
+   and Doug Lea <dl@cs.oswego.edu>, 1996.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public License as
@@ -18,7 +19,7 @@
    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.  */
 
-/* VERSION 2.6.4-pt Wed Dec  4 00:35:54 MET 1996
+/* V2.6.4-pt3 Thu Feb 20 1997
 
   This work is mainly derived from malloc-2.6.4 by Doug Lea
   <dl@cs.oswego.edu>, which is available from:
@@ -51,8 +52,8 @@
   ptmalloc_init();
      Initialize global configuration.  When compiled for multiple threads,
      this function must be called once before any other function in the
-     package.  It is not required otherwise. It is called automatically
-     in the Linux/GNU C libray.
+     package.  It is not required otherwise.  It is called automatically
+     in the Linux/GNU C libray or when compiling with MALLOC_HOOKS.
   malloc(size_t n);
      Return a pointer to a newly allocated chunk of at least n bytes, or null
      if no space is available.
 
   Maximum overhead wastage per allocated chunk: normally 15 bytes
 
-       Alignnment demands, plus the minimum allocatable size restriction
+       Alignment demands, plus the minimum allocatable size restriction
        make the normal worst-case wastage 15 bytes (i.e., up to 15
        more bytes will be allocated than were requested in malloc), with
        two exceptions:
 
     Here are some features that are NOT currently supported
 
-    * No user-definable hooks for callbacks and the like.
     * No automated mechanism for fully checking that all accesses
       to malloced memory stay within their bounds.
     * No support for compaction.
      Define to enable debugging. Adds fairly extensive assertion-based
      checking to help track down memory errors, but noticeably slows down
      execution.
+  MALLOC_HOOKS             (default: NOT defined)
+     Define to enable support run-time replacement of the allocation
+     functions through user-defined `hooks'.
   REALLOC_ZERO_BYTES_FREES (default: NOT defined)
      Define this if you think that realloc(p, 0) should be equivalent
      to free(p). Otherwise, since malloc returns a unique pointer for
      These values may also be changed dynamically via mallopt(). The
      preset defaults are those that give best performance for typical
      programs/systems.
+  DEFAULT_CHECK_ACTION
+     When the standard debugging hooks are in place, and a pointer is
+     detected as corrupt, do nothing (0), print an error message (1),
+     or call abort() (2).
 
 
 */
 \f
 
 
-/* Macros for handling mutexes and thread-specific data.  This is
-   included first, because some thread-related header files (such as
-   pthread.h) should be included before any others. */
-#include "thread-m.h"
-
-
 /* Preliminaries */
 
 #ifndef __STD_C
 #endif /*Void_t*/
 
 #if __STD_C
-#include <stddef.h>   /* for size_t */
+# include <stddef.h>   /* for size_t */
+# if defined _LIBC || defined MALLOC_HOOKS
+#  include <stdlib.h>  /* for getenv(), abort() */
+# endif
 #else
-#include <sys/types.h>
+# include <sys/types.h>
 #endif
 
+/* Macros for handling mutexes and thread-specific data.  This is
+   included early, because some thread-related header files (such as
+   pthread.h) should be included before any others. */
+#include "thread-m.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -327,7 +336,7 @@ extern "C" {
     checking is fairly extensive, and will slow down execution
     noticeably. Calling malloc_stats or mallinfo with MALLOC_DEBUG set will
     attempt to check every non-mmapped allocated and free chunk in the
-    course of computing the summmaries. (By nature, mmapped regions
+    course of computing the summaries. (By nature, mmapped regions
     cannot be checked very much automatically.)
 
     Setting MALLOC_DEBUG may also be helpful if you are trying to modify
@@ -380,7 +389,7 @@ extern "C" {
 
 */
 
-#define HAVE_MEMCPY
+#define HAVE_MEMCPY 1
 
 #ifndef USE_MEMCPY
 #ifdef HAVE_MEMCPY
@@ -484,6 +493,10 @@ do {                                                                          \
 #endif
 
 
+#ifndef LACKS_UNISTD_H
+#  include <unistd.h>
+#endif
+
 /*
   Define HAVE_MMAP to optionally make malloc() use mmap() to
   allocate very large blocks.  These will be returned to the
@@ -491,7 +504,9 @@ do {                                                                          \
 */
 
 #ifndef HAVE_MMAP
-#define HAVE_MMAP 1
+# ifdef _POSIX_MAPPED_FILES
+#  define HAVE_MMAP 1
+# endif
 #endif
 
 /*
@@ -501,7 +516,7 @@ do {                                                                          \
 */
 
 #ifndef HAVE_MREMAP
-#define HAVE_MREMAP defined(__linux__)
+#define HAVE_MREMAP defined(__linux__) && !defined(__arm__)
 #endif
 
 #if HAVE_MMAP
@@ -514,6 +529,14 @@ do {                                                                          \
 #define MAP_ANONYMOUS MAP_ANON
 #endif
 
+#ifndef MAP_NORESERVE
+# ifdef MAP_AUTORESRV
+#  define MAP_NORESERVE MAP_AUTORESRV
+# else
+#  define MAP_NORESERVE 0
+# endif
+#endif
+
 #endif /* HAVE_MMAP */
 
 /*
@@ -524,10 +547,6 @@ do {                                                                          \
   bsd/gnu getpagesize.h
 */
 
-#ifndef LACKS_UNISTD_H
-#  include <unistd.h>
-#endif
-
 #ifndef malloc_getpagesize
 #  ifdef _SC_PAGESIZE         /* some SVR4 systems omit an underscore */
 #    ifndef _SC_PAGE_SIZE
@@ -597,9 +616,13 @@ do {                                                                          \
 /* #define HAVE_USR_INCLUDE_MALLOC_H */
 
 #if HAVE_USR_INCLUDE_MALLOC_H
-#include "/usr/include/malloc.h"
+# include "/usr/include/malloc.h"
 #else
-#include "malloc.h"
+# ifdef _LIBC
+#  include "malloc.h"
+# else
+#  include "ptmalloc.h"
+# endif
 #endif
 
 
@@ -634,7 +657,7 @@ do {                                                                          \
       might set to a value close to the average size of a process
       (program) running on your system.  Releasing this much memory
       would allow such a process to run in memory.  Generally, it's
-      worth it to tune for trimming rather tham memory mapping when a
+      worth it to tune for trimming rather than memory mapping when a
       program undergoes phases where several large chunks are
       allocated and released in ways that can reuse each other's
       storage, perhaps mixed with phases where there are no such
@@ -764,6 +787,16 @@ do {                                                                          \
 
 
 
+#ifndef DEFAULT_CHECK_ACTION
+#define DEFAULT_CHECK_ACTION 1
+#endif
+
+/* What to do if the standard debugging hooks are in place and a
+   corrupt pointer is detected: do nothing (0), print an error message
+   (1), or call abort() (2). */
+
+
+
 #define HEAP_MIN_SIZE (32*1024)
 #define HEAP_MAX_SIZE (1024*1024) /* must be a power of two */
 
@@ -797,18 +830,24 @@ do {                                                                          \
 #if __STD_C
 
 Void_t * __default_morecore (ptrdiff_t);
-static Void_t *(*__morecore)(ptrdiff_t) = __default_morecore;
+Void_t *(*__morecore)(ptrdiff_t) = __default_morecore;
 
 #else
 
 Void_t * __default_morecore ();
-static Void_t *(*__morecore)() = __default_morecore;
+Void_t *(*__morecore)() = __default_morecore;
 
 #endif
 
 #define MORECORE (*__morecore)
 #define MORECORE_FAILURE 0
 #define MORECORE_CLEARS 1
+#define mmap    __mmap
+#define munmap  __munmap
+#define mremap  __mremap
+#define mprotect __mprotect
+#undef malloc_getpagesize
+#define malloc_getpagesize __getpagesize()
 
 #else /* _LIBC */
 
@@ -832,7 +871,7 @@ extern Void_t*     sbrk();
 
 #endif /* _LIBC */
 
-#if 0 && defined(_LIBC)
+#ifdef _LIBC
 
 #define cALLOc          __libc_calloc
 #define fREe            __libc_free
@@ -843,17 +882,11 @@ extern Void_t*     sbrk();
 #define pvALLOc         __libc_pvalloc
 #define mALLINFo        __libc_mallinfo
 #define mALLOPt         __libc_mallopt
-
-#pragma weak calloc = __libc_calloc
-#pragma weak free = __libc_free
-#pragma weak cfree = __libc_free
-#pragma weak malloc = __libc_malloc
-#pragma weak memalign = __libc_memalign
-#pragma weak realloc = __libc_realloc
-#pragma weak valloc = __libc_valloc
-#pragma weak pvalloc = __libc_pvalloc
-#pragma weak mallinfo = __libc_mallinfo
-#pragma weak mallopt = __libc_mallopt
+#define mALLOC_STATs    __malloc_stats
+#define mALLOC_USABLE_SIZe __malloc_usable_size
+#define mALLOC_TRIm     __malloc_trim
+#define mALLOC_GET_STATe __malloc_get_state
+#define mALLOC_SET_STATe __malloc_set_state
 
 #else
 
@@ -866,6 +899,11 @@ extern Void_t*     sbrk();
 #define pvALLOc         pvalloc
 #define mALLINFo        mallinfo
 #define mALLOPt         mallopt
+#define mALLOC_STATs    malloc_stats
+#define mALLOC_USABLE_SIZe malloc_usable_size
+#define mALLOC_TRIm     malloc_trim
+#define mALLOC_GET_STATe malloc_get_state
+#define mALLOC_SET_STATe malloc_set_state
 
 #endif
 
@@ -884,12 +922,16 @@ Void_t* vALLOc(size_t);
 Void_t* pvALLOc(size_t);
 Void_t* cALLOc(size_t, size_t);
 void    cfree(Void_t*);
-int     malloc_trim(size_t);
-size_t  malloc_usable_size(Void_t*);
-void    malloc_stats(void);
+int     mALLOC_TRIm(size_t);
+size_t  mALLOC_USABLE_SIZe(Void_t*);
+void    mALLOC_STATs(void);
 int     mALLOPt(int, int);
 struct mallinfo mALLINFo(void);
-#else
+Void_t* mALLOC_GET_STATe(void);
+int     mALLOC_SET_STATe(Void_t*);
+
+#else /* !__STD_C */
+
 #ifndef _LIBC
 void    ptmalloc_init();
 #endif
@@ -901,12 +943,15 @@ Void_t* vALLOc();
 Void_t* pvALLOc();
 Void_t* cALLOc();
 void    cfree();
-int     malloc_trim();
-size_t  malloc_usable_size();
-void    malloc_stats();
+int     mALLOC_TRIm();
+size_t  mALLOC_USABLE_SIZe();
+void    mALLOC_STATs();
 int     mALLOPt();
 struct mallinfo mALLINFo();
-#endif
+Void_t* mALLOC_GET_STATe();
+int     mALLOC_SET_STATe();
+
+#endif /* __STD_C */
 
 
 #ifdef __cplusplus
@@ -969,7 +1014,7 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     the malloc code, but "mem" is the pointer that is returned to the
     user.  "Nextchunk" is the beginning of the next contiguous chunk.
 
-    Chunks always begin on even word boundries, so the mem portion
+    Chunks always begin on even word boundaries, so the mem portion
     (which is returned to the user) is also on an even word boundary, and
     thus double-word aligned.
 
@@ -1106,18 +1151,24 @@ typedef struct malloc_chunk* mbinptr;
 typedef struct _arena {
   mbinptr av[2*NAV + 2];
   struct _arena *next;
+  size_t size;
+#if THREAD_STATS
+  long stat_lock_direct, stat_lock_loop, stat_lock_wait;
+#endif
   mutex_t mutex;
 } arena;
 
 
-/* A heap is a single contiguous memory region holding (coalescable)
+/* A heap is a single contiguous memory region holding (coalesceable)
    malloc_chunks.  It is allocated with mmap() and always starts at an
    address aligned to HEAP_MAX_SIZE.  Not used unless compiling for
    multiple threads. */
 
 typedef struct _heap_info {
-  arena *ar_ptr;
-  size_t size;
+  arena *ar_ptr; /* Arena for this heap. */
+  struct _heap_info *prev; /* Previous heap. */
+  size_t size;   /* Current size in bytes. */
+  size_t pad;    /* Make sure the following data is properly aligned. */
 } heap_info;
 
 
@@ -1126,13 +1177,64 @@ typedef struct _heap_info {
 */
 
 #if __STD_C
-static void      chunk_free(arena *ar_ptr, mchunkptr p);
-static mchunkptr chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T size);
-static int       arena_trim(arena *ar_ptr, size_t pad);
+
+static void      chunk_free(arena *ar_ptr, mchunkptr p) internal_function;
+static mchunkptr chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T size)
+     internal_function;
+static mchunkptr chunk_realloc(arena *ar_ptr, mchunkptr oldp,
+                               INTERNAL_SIZE_T oldsize, INTERNAL_SIZE_T nb)
+     internal_function;
+static mchunkptr chunk_align(arena *ar_ptr, INTERNAL_SIZE_T nb,
+                             size_t alignment) internal_function;
+static int       main_trim(size_t pad) internal_function;
+#ifndef NO_THREADS
+static int       heap_trim(heap_info *heap, size_t pad) internal_function;
+#endif
+#if defined _LIBC || defined MALLOC_HOOKS
+static Void_t*   malloc_check(size_t sz, const Void_t *caller);
+static void      free_check(Void_t* mem, const Void_t *caller);
+static Void_t*   realloc_check(Void_t* oldmem, size_t bytes,
+                              const Void_t *caller);
+static Void_t*   memalign_check(size_t alignment, size_t bytes,
+                               const Void_t *caller);
+#ifndef NO_THREADS
+static Void_t*   malloc_starter(size_t sz, const Void_t *caller);
+static void      free_starter(Void_t* mem, const Void_t *caller);
+static Void_t*   malloc_atfork(size_t sz, const Void_t *caller);
+static void      free_atfork(Void_t* mem, const Void_t *caller);
+#endif
+#endif
+
 #else
+
 static void      chunk_free();
 static mchunkptr chunk_alloc();
-static int       arena_trim();
+static mchunkptr chunk_realloc();
+static mchunkptr chunk_align();
+static int       main_trim();
+#ifndef NO_THREADS
+static int       heap_trim();
+#endif
+#if defined _LIBC || defined MALLOC_HOOKS
+static Void_t*   malloc_check();
+static void      free_check();
+static Void_t*   realloc_check();
+static Void_t*   memalign_check();
+#ifndef NO_THREADS
+static Void_t*   malloc_starter();
+static void      free_starter();
+static Void_t*   malloc_atfork();
+static void      free_atfork();
+#endif
+#endif
+
+#endif
+
+/* On some platforms we can compile internal, not exported functions better.
+   Let the environment provide a macro and define it to be empty if it
+   is not available.  */
+#ifndef internal_function
+# define internal_function
 #endif
 
 \f
@@ -1297,13 +1399,13 @@ static int       arena_trim();
   Indexing into bins
 */
 
-#define bin_index(sz)                                                          \
-(((((unsigned long)(sz)) >> 9) ==    0) ?       (((unsigned long)(sz)) >>  3): \
- ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6): \
- ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9): \
- ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12): \
- ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15): \
- ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \
+#define bin_index(sz)                                                         \
+(((((unsigned long)(sz)) >> 9) ==    0) ?       (((unsigned long)(sz)) >>  3):\
+ ((((unsigned long)(sz)) >> 9) <=    4) ?  56 + (((unsigned long)(sz)) >>  6):\
+ ((((unsigned long)(sz)) >> 9) <=   20) ?  91 + (((unsigned long)(sz)) >>  9):\
+ ((((unsigned long)(sz)) >> 9) <=   84) ? 110 + (((unsigned long)(sz)) >> 12):\
+ ((((unsigned long)(sz)) >> 9) <=  340) ? 119 + (((unsigned long)(sz)) >> 15):\
+ ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18):\
                                           126)
 /*
   bins for chunks < 512 are all spaced 8 bytes apart, and hold
@@ -1370,7 +1472,11 @@ static arena main_arena = {
  IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119),
  IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127)
     },
-    NULL, /* next */
+    &main_arena, /* next */
+    0, /* size */
+#if THREAD_STATS
+    0, 0, 0, /* stat_lock_direct, stat_lock_loop, stat_lock_wait */
+#endif
     MUTEX_INITIALIZER /* mutex */
 };
 
@@ -1378,14 +1484,13 @@ static arena main_arena = {
 
 /* Thread specific data */
 
+#ifndef NO_THREADS
 static tsd_key_t arena_key;
 static mutex_t list_lock = MUTEX_INITIALIZER;
+#endif
 
 #if THREAD_STATS
-static int stat_n_arenas = 0;
 static int stat_n_heaps = 0;
-static long stat_lock_direct = 0;
-static long stat_lock_loop = 0;
 #define THREAD_STAT(x) x
 #else
 #define THREAD_STAT(x) do ; while(0)
@@ -1397,6 +1502,7 @@ static unsigned long trim_threshold   = DEFAULT_TRIM_THRESHOLD;
 static unsigned long top_pad          = DEFAULT_TOP_PAD;
 static unsigned int  n_mmaps_max      = DEFAULT_MMAP_MAX;
 static unsigned long mmap_threshold   = DEFAULT_MMAP_THRESHOLD;
+static int           check_action     = DEFAULT_CHECK_ACTION;
 
 /* The first value returned from sbrk */
 static char* sbrk_base = (char*)(-1);
@@ -1404,14 +1510,13 @@ static char* sbrk_base = (char*)(-1);
 /* The maximum memory obtained from system via sbrk */
 static unsigned long max_sbrked_mem = 0;
 
-/* The maximum via either sbrk or mmap */
+/* The maximum via either sbrk or mmap (too difficult to track with threads) */
+#ifdef NO_THREADS
 static unsigned long max_total_mem = 0;
-
-/* internal working copy of mallinfo */
-static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#endif
 
 /* The total memory obtained from system via sbrk */
-#define sbrked_mem  (current_mallinfo.arena)
+#define sbrked_mem (main_arena.size)
 
 /* Tracking mmaps */
 
@@ -1422,11 +1527,99 @@ static unsigned long max_mmapped_mem = 0;
 
 
 \f
+#ifndef _LIBC
+#define weak_variable
+#else
+/* In GNU libc we want the hook variables to be weak definitions to
+   avoid a problem with Emacs.  */
+#define weak_variable weak_function
+#endif
+
+/* Already initialized? */
+int __malloc_initialized = -1;
+
+
+#ifndef NO_THREADS
+
+/* The following two functions are registered via thread_atfork() to
+   make sure that the mutexes remain in a consistent state in the
+   fork()ed version of a thread.  Also adapt the malloc and free hooks
+   temporarily, because the `atfork' handler mechanism may use
+   malloc/free internally (e.g. in LinuxThreads). */
+
+#if defined _LIBC || defined MALLOC_HOOKS
+static __malloc_ptr_t (*save_malloc_hook) __MALLOC_P ((size_t __size,
+                                                      const __malloc_ptr_t));
+static void           (*save_free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
+                                                    const __malloc_ptr_t));
+static Void_t*        save_arena;
+#endif
+
+static void
+ptmalloc_lock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+  (void)mutex_lock(&list_lock);
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_lock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+#if defined _LIBC || defined MALLOC_HOOKS
+  save_malloc_hook = __malloc_hook;
+  save_free_hook = __free_hook;
+  __malloc_hook = malloc_atfork;
+  __free_hook = free_atfork;
+  /* Only the current thread may perform malloc/free calls now. */
+  tsd_getspecific(arena_key, save_arena);
+  tsd_setspecific(arena_key, (Void_t*)0);
+#endif
+}
+
+static void
+ptmalloc_unlock_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
 
+#if defined _LIBC || defined MALLOC_HOOKS
+  tsd_setspecific(arena_key, save_arena);
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_unlock(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+  (void)mutex_unlock(&list_lock);
+}
+
+static void
+ptmalloc_init_all __MALLOC_P((void))
+{
+  arena *ar_ptr;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  tsd_setspecific(arena_key, save_arena);
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
+  for(ar_ptr = &main_arena;;) {
+    (void)mutex_init(&ar_ptr->mutex);
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+  (void)mutex_init(&list_lock);
+}
+
+#endif
 
 /* Initialization routine. */
 #if defined(_LIBC)
+#if 0
 static void ptmalloc_init __MALLOC_P ((void)) __attribute__ ((constructor));
+#endif
 
 static void
 ptmalloc_init __MALLOC_P((void))
@@ -1435,23 +1628,158 @@ void
 ptmalloc_init __MALLOC_P((void))
 #endif
 {
-  static int first = 1;
+#if defined _LIBC || defined MALLOC_HOOKS
+  const char* s;
+#endif
 
-#if defined(_LIBC)
-  /* Initialize the pthread. */
+  if(__malloc_initialized >= 0) return;
+  __malloc_initialized = 0;
+#ifndef NO_THREADS
+#if defined _LIBC || defined MALLOC_HOOKS
+  /* With some threads implementations, creating thread-specific data
+     or initializing a mutex may call malloc() itself.  Provide a
+     simple starter version (realloc() won't work). */
+  save_malloc_hook = __malloc_hook;
+  save_free_hook = __free_hook;
+  __malloc_hook = malloc_starter;
+  __free_hook = free_starter;
+#endif
+#ifdef _LIBC
+  /* Initialize the pthreads interface. */
   if (__pthread_initialize != NULL)
-    __pthread_initialize ();
+    __pthread_initialize();
+#endif
+  mutex_init(&main_arena.mutex);
+  mutex_init(&list_lock);
+  tsd_key_create(&arena_key, NULL);
+  tsd_setspecific(arena_key, (Void_t *)&main_arena);
+  thread_atfork(ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_init_all);
+#endif /* !defined NO_THREADS */
+#if defined _LIBC || defined MALLOC_HOOKS
+  if((s = getenv("MALLOC_TRIM_THRESHOLD_")))
+    mALLOPt(M_TRIM_THRESHOLD, atoi(s));
+  if((s = getenv("MALLOC_TOP_PAD_")))
+    mALLOPt(M_TOP_PAD, atoi(s));
+  if((s = getenv("MALLOC_MMAP_THRESHOLD_")))
+    mALLOPt(M_MMAP_THRESHOLD, atoi(s));
+  if((s = getenv("MALLOC_MMAP_MAX_")))
+    mALLOPt(M_MMAP_MAX, atoi(s));
+  s = getenv("MALLOC_CHECK_");
+#ifndef NO_THREADS
+  __malloc_hook = save_malloc_hook;
+  __free_hook = save_free_hook;
+#endif
+  if(s) {
+    if(s[0]) mALLOPt(M_CHECK_ACTION, (int)(s[0] - '0'));
+    __malloc_check_init();
+  }
+  if(__malloc_initialize_hook != NULL)
+    (*__malloc_initialize_hook)();
+#endif
+  __malloc_initialized = 1;
+}
+
+/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
+#ifdef thread_atfork_static
+thread_atfork_static(ptmalloc_lock_all, ptmalloc_unlock_all, \
+                     ptmalloc_init_all)
+#endif
+
+#if defined _LIBC || defined MALLOC_HOOKS
+
+/* Hooks for debugging versions.  The initial hooks just call the
+   initialization routine, then do the normal work. */
+
+static Void_t*
+#if __STD_C
+malloc_hook_ini(size_t sz, const __malloc_ptr_t caller)
+#else
+malloc_hook_ini(sz, caller)
+     size_t sz; const __malloc_ptr_t caller;
+#endif
+{
+  __malloc_hook = NULL;
+  ptmalloc_init();
+  return mALLOc(sz);
+}
+
+static Void_t*
+#if __STD_C
+realloc_hook_ini(Void_t* ptr, size_t sz, const __malloc_ptr_t caller)
+#else
+realloc_hook_ini(ptr, sz, caller)
+     Void_t* ptr; size_t sz; const __malloc_ptr_t caller;
+#endif
+{
+  __malloc_hook = NULL;
+  __realloc_hook = NULL;
+  ptmalloc_init();
+  return rEALLOc(ptr, sz);
+}
+
+static Void_t*
+#if __STD_C
+memalign_hook_ini(size_t sz, size_t alignment, const __malloc_ptr_t caller)
+#else
+memalign_hook_ini(sz, alignment, caller)
+     size_t sz; size_t alignment; const __malloc_ptr_t caller;
 #endif
+{
+  __memalign_hook = NULL;
+  ptmalloc_init();
+  return mEMALIGn(sz, alignment);
+}
 
-  if(first) {
-    first = 0;
-    mutex_init(&main_arena.mutex);
-    mutex_init(&list_lock);
-    tsd_key_create(&arena_key, NULL);
-    tsd_setspecific(arena_key, (Void_t *)&main_arena);
+void weak_variable (*__malloc_initialize_hook) __MALLOC_P ((void)) = NULL;
+void weak_variable (*__free_hook) __MALLOC_P ((__malloc_ptr_t __ptr,
+                                              const __malloc_ptr_t)) = NULL;
+__malloc_ptr_t weak_variable (*__malloc_hook)
+ __MALLOC_P ((size_t __size, const __malloc_ptr_t)) = malloc_hook_ini;
+__malloc_ptr_t weak_variable (*__realloc_hook)
+ __MALLOC_P ((__malloc_ptr_t __ptr, size_t __size, const __malloc_ptr_t))
+     = realloc_hook_ini;
+__malloc_ptr_t weak_variable (*__memalign_hook)
+ __MALLOC_P ((size_t __size, size_t __alignment, const __malloc_ptr_t))
+     = memalign_hook_ini;
+void weak_variable (*__after_morecore_hook) __MALLOC_P ((void)) = NULL;
+
+/* Whether we are using malloc checking.  */
+static int using_malloc_checking;
+
+/* A flag that is set by malloc_set_state, to signal that malloc checking
+   must not be enabled on the request from the user (via the MALLOC_CHECK_
+   environment variable).  It is reset by __malloc_check_init to tell
+   malloc_set_state that the user has requested malloc checking.
+
+   The purpose of this flag is to make sure that malloc checking is not
+   enabled when the heap to be restored was constructed without malloc
+   checking, and thus does not contain the required magic bytes.
+   Otherwise the heap would be corrupted by calls to free and realloc.  If
+   it turns out that the heap was created with malloc checking and the
+   user has requested it malloc_set_state just calls __malloc_check_init
+   again to enable it.  On the other hand, reusing such a heap without
+   further malloc checking is safe.  */
+static int disallow_malloc_check;
+
+/* Activate a standard set of debugging hooks. */
+void
+__malloc_check_init()
+{
+  if (disallow_malloc_check) {
+    disallow_malloc_check = 0;
+    return;
   }
+  using_malloc_checking = 1;
+  __malloc_hook = malloc_check;
+  __free_hook = free_check;
+  __realloc_hook = realloc_check;
+  __memalign_hook = memalign_check;
+  if(check_action == 1)
+    fprintf(stderr, "malloc: using debugging hooks\n");
 }
 
+#endif
+
 
 \f
 
@@ -1464,22 +1792,28 @@ ptmalloc_init __MALLOC_P((void))
 
 static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
 
-#define MMAP(size, prot) ((dev_zero_fd < 0) ? \
+#define MMAP(addr, size, prot, flags) ((dev_zero_fd < 0) ? \
  (dev_zero_fd = open("/dev/zero", O_RDWR), \
-  mmap(0, (size), (prot), MAP_PRIVATE, dev_zero_fd, 0)) : \
-   mmap(0, (size), (prot), MAP_PRIVATE, dev_zero_fd, 0))
+  mmap((addr), (size), (prot), (flags), dev_zero_fd, 0)) : \
+   mmap((addr), (size), (prot), (flags), dev_zero_fd, 0))
 
 #else
 
-#define MMAP(size, prot) \
- (mmap(0, (size), (prot), MAP_PRIVATE|MAP_ANONYMOUS, -1, 0))
+#define MMAP(addr, size, prot, flags) \
+ (mmap((addr), (size), (prot), (flags)|MAP_ANONYMOUS, -1, 0))
 
 #endif
 
+#if defined __GNUC__ && __GNUC__ >= 2
+/* This function is only called from one place, inline it.  */
+inline
+#endif
+static mchunkptr
+internal_function
 #if __STD_C
-static mchunkptr mmap_chunk(size_t size)
+mmap_chunk(size_t size)
 #else
-static mchunkptr mmap_chunk(size) size_t size;
+mmap_chunk(size) size_t size;
 #endif
 {
   size_t page_mask = malloc_getpagesize - 1;
@@ -1492,8 +1826,8 @@ static mchunkptr mmap_chunk(size) size_t size;
    */
   size = (size + SIZE_SZ + page_mask) & ~page_mask;
 
-  p = (mchunkptr)MMAP(size, PROT_READ|PROT_WRITE);
-  if(p == (mchunkptr)-1) return 0;
+  p = (mchunkptr)MMAP(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE);
+  if(p == (mchunkptr) MAP_FAILED) return 0;
 
   n_mmaps++;
   if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps;
@@ -1511,8 +1845,10 @@ static mchunkptr mmap_chunk(size) size_t size;
   mmapped_mem += size;
   if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem)
     max_mmapped_mem = mmapped_mem;
+#ifdef NO_THREADS
   if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem)
     max_total_mem = mmapped_mem + sbrked_mem;
+#endif
   return p;
 }
 
@@ -1576,8 +1912,10 @@ static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size;
   mmapped_mem += new_size;
   if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem)
     max_mmapped_mem = mmapped_mem;
+#ifdef NO_THREADS
   if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem)
     max_total_mem = mmapped_mem + sbrked_mem;
+#endif
   return p;
 }
 
@@ -1595,6 +1933,7 @@ static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size;
    of the page size. */
 
 static heap_info *
+internal_function
 #if __STD_C
 new_heap(size_t size)
 #else
@@ -1606,13 +1945,22 @@ new_heap(size) size_t size;
   unsigned long ul;
   heap_info *h;
 
-  if(size < HEAP_MIN_SIZE)
+  if(size+top_pad < HEAP_MIN_SIZE)
     size = HEAP_MIN_SIZE;
-  size = (size + page_mask) & ~page_mask;
-  if(size > HEAP_MAX_SIZE)
+  else if(size+top_pad <= HEAP_MAX_SIZE)
+    size += top_pad;
+  else if(size > HEAP_MAX_SIZE)
     return 0;
-  p1 = (char *)MMAP(HEAP_MAX_SIZE<<1, PROT_NONE);
-  if(p1 == (char *)-1)
+  else
+    size = HEAP_MAX_SIZE;
+  size = (size + page_mask) & ~page_mask;
+
+  /* A memory region aligned to a multiple of HEAP_MAX_SIZE is needed.
+     No swap space needs to be reserved for the following large
+     mapping (on Linux, this is the case for all non-writable mappings
+     anyway). */
+  p1 = (char *)MMAP(0, HEAP_MAX_SIZE<<1, PROT_NONE, MAP_PRIVATE|MAP_NORESERVE);
+  if(p1 == MAP_FAILED)
     return 0;
   p2 = (char *)(((unsigned long)p1 + HEAP_MAX_SIZE) & ~(HEAP_MAX_SIZE-1));
   ul = p2 - p1;
@@ -1629,7 +1977,7 @@ new_heap(size) size_t size;
 }
 
 /* Grow or shrink a heap.  size is automatically rounded up to a
-   multiple of the page size. */
+   multiple of the page size if it is positive. */
 
 static int
 #if __STD_C
@@ -1650,32 +1998,39 @@ grow_heap(h, diff) heap_info *h; long diff;
       return -2;
   } else {
     new_size = (long)h->size + diff;
-    if(new_size < 0)
+    if(new_size < (long)sizeof(*h))
       return -1;
-    if(mprotect((char *)h + new_size, -diff, PROT_NONE) != 0)
+    /* Try to re-map the extra heap space freshly to save memory, and 
+       make it inaccessible. */ 
+    if((char *)MMAP((char *)h + new_size, -diff, PROT_NONE, 
+                   MAP_PRIVATE|MAP_FIXED) == (char *) MAP_FAILED)
       return -2;
   }
   h->size = new_size;
   return 0;
 }
 
+/* Delete a heap. */
+
+#define delete_heap(heap) munmap((char*)(heap), HEAP_MAX_SIZE)
+
 /* arena_get() acquires an arena and locks the corresponding mutex.
    First, try the one last locked successfully by this thread.  (This
    is the common case and handled with a macro for speed.)  Then, loop
-   over the singly linked list of arenas.  If no arena is readily
-   available, create a new one.  */
+   once over the circularly linked list of arenas.  If no arena is
+   readily available, create a new one. */
 
 #define arena_get(ptr, size) do { \
   Void_t *vptr = NULL; \
   ptr = (arena *)tsd_getspecific(arena_key, vptr); \
   if(ptr && !mutex_trylock(&ptr->mutex)) { \
-    THREAD_STAT(stat_lock_direct++); \
-  } else \
+    THREAD_STAT(++(ptr->stat_lock_direct)); \
+  } else \
     ptr = arena_get2(ptr, (size)); \
-  } \
 } while(0)
 
 static arena *
+internal_function
 #if __STD_C
 arena_get2(arena *a_tsd, size_t size)
 #else
@@ -1688,22 +2043,38 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   int i;
   unsigned long misalign;
 
-  /* Check the list for unlocked arenas. */
-  if(a_tsd) {
-    for(a = a_tsd->next; a; a = a->next) {
-      if(!mutex_trylock(&a->mutex))
-        goto done;
+  if(!a_tsd)
+    a = a_tsd = &main_arena;
+  else {
+    a = a_tsd->next;
+    if(!a) {
+      /* This can only happen while initializing the new arena. */
+      (void)mutex_lock(&main_arena.mutex);
+      THREAD_STAT(++(main_arena.stat_lock_wait));
+      return &main_arena;
     }
-    for(a = &main_arena; a != a_tsd; a = a->next) {
-      if(!mutex_trylock(&a->mutex))
-        goto done;
-    }
-  } else {
-    for(a = &main_arena; a; a = a->next) {
-      if(!mutex_trylock(&a->mutex))
-        goto done;
+  }
+
+  /* Check the global, circularly linked list for available arenas. */
+ repeat:
+  do {
+    if(!mutex_trylock(&a->mutex)) {
+      THREAD_STAT(++(a->stat_lock_loop));
+      tsd_setspecific(arena_key, (Void_t *)a);
+      return a;
     }
+    a = a->next;
+  } while(a != a_tsd);
+
+  /* If not even the list_lock can be obtained, try again.  This can
+     happen during `atfork', or for example on systems where thread
+     creation makes it temporarily impossible to obtain _any_
+     locks. */
+  if(mutex_trylock(&list_lock)) {
+    a = a_tsd;
+    goto repeat;
   }
+  (void)mutex_unlock(&list_lock);
 
   /* Nothing immediately available, so generate a new arena. */
   h = new_heap(size + (sizeof(*h) + sizeof(*a) + MALLOC_ALIGNMENT));
@@ -1712,6 +2083,9 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   a = h->ar_ptr = (arena *)(h+1);
   for(i=0; i<NAV; i++)
     init_bin(a, i);
+  a->next = NULL;
+  a->size = h->size;
+  tsd_setspecific(arena_key, (Void_t *)a);
   mutex_init(&a->mutex);
   i = mutex_lock(&a->mutex); /* remember result */
 
@@ -1721,21 +2095,18 @@ arena_get2(a_tsd, size) arena *a_tsd; size_t size;
   if (misalign > 0)
     ptr += MALLOC_ALIGNMENT - misalign;
   top(a) = (mchunkptr)ptr;
-  set_head(top(a), (h->size - (ptr-(char*)h)) | PREV_INUSE);
+  set_head(top(a), (((char*)h + h->size) - ptr) | PREV_INUSE);
 
   /* Add the new arena to the list. */
   (void)mutex_lock(&list_lock);
   a->next = main_arena.next;
   main_arena.next = a;
-  THREAD_STAT(stat_n_arenas++);
   (void)mutex_unlock(&list_lock);
 
   if(i) /* locking failed; keep arena for further attempts later */
     return 0;
 
-done:
-  THREAD_STAT(stat_lock_loop++);
-  tsd_setspecific(arena_key, (Void_t *)a);
+  THREAD_STAT(++(a->stat_lock_loop));
   return a;
 }
 
@@ -1788,7 +2159,10 @@ static void do_check_chunk(ar_ptr, p) arena *ar_ptr; mchunkptr p;
   if(ar_ptr != &main_arena) {
     heap_info *heap = heap_for_ptr(p);
     assert(heap->ar_ptr == ar_ptr);
-    assert((char *)p + sz <= (char *)heap + heap->size);
+    if(p != top(ar_ptr)) 
+      assert((char *)p + sz <= (char *)heap + heap->size); 
+    else 
+      assert((char *)p + sz == (char *)heap + heap->size);
     return;
   }
 #endif
@@ -1817,23 +2191,19 @@ static void do_check_free_chunk(ar_ptr, p) arena *ar_ptr; mchunkptr p;
   /* Check whether it claims to be free ... */
   assert(!inuse(p));
 
-  /* Unless a special marker, must have OK fields */
-  if ((long)sz >= (long)MINSIZE)
-  {
-    assert((sz & MALLOC_ALIGN_MASK) == 0);
-    assert(aligned_OK(chunk2mem(p)));
-    /* ... matching footer field */
-    assert(next->prev_size == sz);
-    /* ... and is fully consolidated */
-    assert(prev_inuse(p));
-    assert (next == top(ar_ptr) || inuse(next));
-
-    /* ... and has minimally sane links */
-    assert(p->fd->bk == p);
-    assert(p->bk->fd == p);
-  }
-  else /* markers are always of size SIZE_SZ */
-    assert(sz == SIZE_SZ);
+  /* Must have OK size and fields */
+  assert((long)sz >= (long)MINSIZE);
+  assert((sz & MALLOC_ALIGN_MASK) == 0);
+  assert(aligned_OK(chunk2mem(p)));
+  /* ... matching footer field */
+  assert(next->prev_size == sz);
+  /* ... and is fully consolidated */
+  assert(prev_inuse(p));
+  assert (next == top(ar_ptr) || inuse(next));
+
+  /* ... and has minimally sane links */
+  assert(p->fd->bk == p);
+  assert(p->bk->fd == p);
 }
 
 #if __STD_C
@@ -1848,6 +2218,9 @@ static void do_check_inuse_chunk(ar_ptr, p) arena *ar_ptr; mchunkptr p;
   /* Check whether it claims to be in use ... */
   assert(inuse(p));
 
+  /* ... whether its size is OK (it might be a fencepost) ... */
+  assert(chunksize(p) >= MINSIZE || next->size == (0|PREV_INUSE));
+
   /* ... and is surrounded by OK chunks.
     Since more things can be checked with free chunks than inuse ones,
     if an inuse chunk borders them and debug is on, it's worth doing them.
@@ -1988,10 +2361,16 @@ arena *ar_ptr; mchunkptr p; INTERNAL_SIZE_T s;
   Main interface to sbrk (but see also malloc_trim).
 */
 
+#if defined __GNUC__ && __GNUC__ >= 2
+/* This function is called only from one place, inline it.  */
+inline
+#endif
+static void
+internal_function
 #if __STD_C
-static void malloc_extend_top(arena *ar_ptr, INTERNAL_SIZE_T nb)
+malloc_extend_top(arena *ar_ptr, INTERNAL_SIZE_T nb)
 #else
-static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
+malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
 #endif
 {
   unsigned long pagesz   = malloc_getpagesize;
@@ -2026,6 +2405,12 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
         (brk < old_end && old_top != initial_top(&main_arena)))
       return;
 
+#if defined _LIBC || defined MALLOC_HOOKS
+    /* Call the `morecore' hook if necessary.  */
+    if (__after_morecore_hook)
+      (*__after_morecore_hook) ();
+#endif
+
     sbrked_mem += sbrk_size;
 
     if (brk == old_end) { /* can just add bytes to current top */
@@ -2054,6 +2439,12 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
       new_brk = (char*)(MORECORE (correction));
       if (new_brk == (char*)(MORECORE_FAILURE)) return;
 
+#if defined _LIBC || defined MALLOC_HOOKS
+      /* Call the `morecore' hook if necessary.  */
+      if (__after_morecore_hook)
+        (*__after_morecore_hook) ();
+#endif
+
       sbrked_mem += correction;
 
       top(&main_arena) = (mchunkptr)brk;
@@ -2066,14 +2457,16 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
 
     if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem)
       max_sbrked_mem = sbrked_mem;
+#ifdef NO_THREADS
     if ((unsigned long)(mmapped_mem + sbrked_mem) >
         (unsigned long)max_total_mem)
       max_total_mem = mmapped_mem + sbrked_mem;
+#endif
 
 #ifndef NO_THREADS
   } else { /* ar_ptr != &main_arena */
-
-    heap_info *heap;
+    heap_info *old_heap, *heap;
+    size_t old_heap_size;
 
     if(old_top_size < MINSIZE) /* this should never happen */
       return;
@@ -2081,18 +2474,22 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
     /* First try to extend the current heap. */
     if(MINSIZE + nb <= old_top_size)
       return;
-    heap = heap_for_ptr(old_top);
-    if(grow_heap(heap, MINSIZE + nb - old_top_size) == 0) {
-      top_size = heap->size - ((char *)old_top - (char *)heap);
+    old_heap = heap_for_ptr(old_top);
+    old_heap_size = old_heap->size;
+    if(grow_heap(old_heap, MINSIZE + nb - old_top_size) == 0) {
+      ar_ptr->size += old_heap->size - old_heap_size;
+      top_size = ((char *)old_heap + old_heap->size) - (char *)old_top;
       set_head(old_top, top_size | PREV_INUSE);
       return;
     }
 
     /* A new heap must be created. */
-    heap = new_heap(nb + top_pad + (MINSIZE + sizeof(*heap)));
+    heap = new_heap(nb + (MINSIZE + sizeof(*heap)));
     if(!heap)
       return;
     heap->ar_ptr = ar_ptr;
+    heap->prev = old_heap;
+    ar_ptr->size += heap->size;
 
     /* Set up the new top, so we can safely use chunk_free() below. */
     top(ar_ptr) = chunk_at_offset(heap, sizeof(*heap));
@@ -2106,19 +2503,19 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
 
   /* Setup fencepost and free the old top chunk. */
   if(old_top) {
-    /* Keep size a multiple of MALLOC_ALIGNMENT. */
-    old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK;
-    /* If possible, release the rest. */
-    if (old_top_size >= MINSIZE) {
-      set_head(chunk_at_offset(old_top, old_top_size        ),
-               SIZE_SZ|PREV_INUSE);
-      set_head(chunk_at_offset(old_top, old_top_size+SIZE_SZ),
-               SIZE_SZ|PREV_INUSE);
+    /* The fencepost takes at least MINSIZE bytes, because it might
+       become the top chunk again later.  Note that a footer is set
+       up, too, although the chunk is marked in use. */
+    old_top_size -= MINSIZE;
+    set_head(chunk_at_offset(old_top, old_top_size + 2*SIZE_SZ), 0|PREV_INUSE);
+    if(old_top_size >= MINSIZE) {
+      set_head(chunk_at_offset(old_top, old_top_size), (2*SIZE_SZ)|PREV_INUSE);
+      set_foot(chunk_at_offset(old_top, old_top_size), (2*SIZE_SZ));
       set_head_size(old_top, old_top_size);
       chunk_free(ar_ptr, old_top);
     } else {
-      set_head(old_top, SIZE_SZ|PREV_INUSE);
-      set_head(chunk_at_offset(old_top, SIZE_SZ), SIZE_SZ|PREV_INUSE);
+      set_head(old_top, (old_top_size + 2*SIZE_SZ)|PREV_INUSE);
+      set_foot(old_top, (old_top_size + 2*SIZE_SZ));
     }
   }
 }
@@ -2130,13 +2527,14 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
 
 
 /*
-  Malloc Algorthim:
+  Malloc Algorithm:
 
     The requested size is first converted into a usable form, `nb'.
     This currently means to add 4 bytes overhead plus possibly more to
     obtain 8-byte alignment and/or to obtain a size of at least
-    MINSIZE (currently 16 bytes), the smallest allocatable size.
-    (All fits are considered `exact' if they are within MINSIZE bytes.)
+    MINSIZE (currently 16, 24, or 32 bytes), the smallest allocatable
+    size.  (All fits are considered `exact' if they are within MINSIZE
+    bytes.)
 
     From there, the first successful of the following steps is taken:
 
@@ -2180,7 +2578,7 @@ static void malloc_extend_top(ar_ptr, nb) arena *ar_ptr; INTERNAL_SIZE_T nb;
          mallocs with other sbrk calls.
 
 
-      All allocations are made from the the `lowest' part of any found
+      All allocations are made from the `lowest' part of any found
       chunk. (The implementation invariant is that prev_inuse is
       always true of any allocated chunk; i.e., that each allocated
       chunk borders either a previously allocated and still in-use chunk,
@@ -2195,18 +2593,42 @@ Void_t* mALLOc(bytes) size_t bytes;
 #endif
 {
   arena *ar_ptr;
-  INTERNAL_SIZE_T nb = request2size(bytes);  /* padded request size; */
+  INTERNAL_SIZE_T nb; /* padded request size */
   mchunkptr victim;
 
-  arena_get(ar_ptr, nb + top_pad);
+#if defined _LIBC || defined MALLOC_HOOKS
+  if (__malloc_hook != NULL) {
+    Void_t* result;
+
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__malloc_hook)(bytes, __builtin_return_address (0));
+#else
+    result = (*__malloc_hook)(bytes, NULL);
+#endif
+    return result;
+  }
+#endif
+
+  nb = request2size(bytes);
+  arena_get(ar_ptr, nb);
   if(!ar_ptr)
     return 0;
   victim = chunk_alloc(ar_ptr, nb);
   (void)mutex_unlock(&ar_ptr->mutex);
-  return victim ? chunk2mem(victim) : 0;
+  if(!victim) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      victim = chunk_alloc(&main_arena, nb);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if(!victim) return 0;
+  }
+  return chunk2mem(victim);
 }
 
 static mchunkptr
+internal_function
 #if __STD_C
 chunk_alloc(arena *ar_ptr, INTERNAL_SIZE_T nb)
 #else
@@ -2472,6 +2894,17 @@ void fREe(mem) Void_t* mem;
   arena *ar_ptr;
   mchunkptr p;                          /* chunk corresponding to mem */
 
+#if defined _LIBC || defined MALLOC_HOOKS
+  if (__free_hook != NULL) {
+#if defined __GNUC__ && __GNUC__ >= 2
+    (*__free_hook)(mem, __builtin_return_address (0));
+#else
+    (*__free_hook)(mem, NULL);
+#endif
+    return;
+  }
+#endif
+
   if (mem == 0)                              /* free(0) has no effect */
     return;
 
@@ -2486,12 +2919,22 @@ void fREe(mem) Void_t* mem;
 #endif
 
   ar_ptr = arena_for_ptr(p);
+#if THREAD_STATS
+  if(!mutex_trylock(&ar_ptr->mutex))
+    ++(ar_ptr->stat_lock_direct);
+  else {
+    (void)mutex_lock(&ar_ptr->mutex);
+    ++(ar_ptr->stat_lock_wait);
+  }
+#else
   (void)mutex_lock(&ar_ptr->mutex);
+#endif
   chunk_free(ar_ptr, p);
   (void)mutex_unlock(&ar_ptr->mutex);
 }
 
 static void
+internal_function
 #if __STD_C
 chunk_free(arena *ar_ptr, mchunkptr p)
 #else
@@ -2528,13 +2971,27 @@ chunk_free(ar_ptr, p) arena *ar_ptr; mchunkptr p;
 
     set_head(p, sz | PREV_INUSE);
     top(ar_ptr) = p;
-    if ((unsigned long)(sz) >= (unsigned long)trim_threshold)
-      arena_trim(ar_ptr, top_pad);
+
+#ifndef NO_THREADS
+    if(ar_ptr == &main_arena) {
+#endif
+      if ((unsigned long)(sz) >= (unsigned long)trim_threshold)
+        main_trim(top_pad);
+#ifndef NO_THREADS
+    } else {
+      heap_info *heap = heap_for_ptr(p);
+
+      assert(heap->ar_ptr == ar_ptr);
+
+      /* Try to get rid of completely empty heaps, if possible. */
+      if((unsigned long)(sz) >= (unsigned long)trim_threshold ||
+         p == chunk_at_offset(heap, sizeof(*heap)))
+        heap_trim(heap, top_pad);
+    }
+#endif
     return;
   }
 
-  set_head(next, nextsz);                    /* clear inuse bit */
-
   islr = 0;
 
   if (!(hd & PREV_INUSE))                    /* consolidate backward */
@@ -2561,12 +3018,29 @@ chunk_free(ar_ptr, p) arena *ar_ptr; mchunkptr p;
     }
     else
       unlink(next, bck, fwd);
+
+    next = chunk_at_offset(p, sz);
   }
+  else
+    set_head(next, nextsz);                  /* clear inuse bit */
 
   set_head(p, sz | PREV_INUSE);
-  set_foot(p, sz);
+  next->prev_size = sz;
   if (!islr)
     frontlink(ar_ptr, p, sz, idx, bck, fwd);
+
+#ifndef NO_THREADS
+  /* Check whether the heap containing top can go away now. */
+  if(next->size < MINSIZE &&
+     (unsigned long)sz > trim_threshold &&
+     ar_ptr != &main_arena) {                /* fencepost */
+    heap_info* heap = heap_for_ptr(top(ar_ptr));
+
+    if(top(ar_ptr) == chunk_at_offset(heap, sizeof(*heap)) &&
+       heap->prev == heap_for_ptr(p))
+      heap_trim(heap, top_pad);
+  }
+#endif
 }
 
 
@@ -2622,38 +3096,37 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
   INTERNAL_SIZE_T    oldsize; /* its size */
 
   mchunkptr newp;             /* chunk to return */
-  INTERNAL_SIZE_T    newsize; /* its size */
-  Void_t*   newmem;           /* corresponding user mem */
-
-  mchunkptr next;             /* next contiguous chunk after oldp */
-  INTERNAL_SIZE_T  nextsize;  /* its size */
 
-  mchunkptr prev;             /* previous contiguous chunk before oldp */
-  INTERNAL_SIZE_T  prevsize;  /* its size */
-
-  mchunkptr remainder;        /* holds split off extra space from newp */
-  INTERNAL_SIZE_T  remainder_size;   /* its size */
+#if defined _LIBC || defined MALLOC_HOOKS
+  if (__realloc_hook != NULL) {
+    Void_t* result;
 
-  mchunkptr bck;              /* misc temp for linking */
-  mchunkptr fwd;              /* misc temp for linking */
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__realloc_hook)(oldmem, bytes, __builtin_return_address (0));
+#else
+    result = (*__realloc_hook)(oldmem, bytes, NULL);
+#endif
+    return result;
+  }
+#endif
 
 #ifdef REALLOC_ZERO_BYTES_FREES
   if (bytes == 0) { fREe(oldmem); return 0; }
 #endif
 
-
   /* realloc of null is supposed to be same as malloc */
   if (oldmem == 0) return mALLOc(bytes);
 
-  newp    = oldp    = mem2chunk(oldmem);
-  newsize = oldsize = chunksize(oldp);
-
+  oldp    = mem2chunk(oldmem);
+  oldsize = chunksize(oldp);
 
   nb = request2size(bytes);
 
 #if HAVE_MMAP
   if (chunk_is_mmapped(oldp))
   {
+    Void_t* newmem;
+
 #if HAVE_MREMAP
     newp = mremap_chunk(oldp, nb);
     if(newp) return chunk2mem(newp);
@@ -2670,9 +3143,52 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
 #endif
 
   ar_ptr = arena_for_ptr(oldp);
+#if THREAD_STATS
+  if(!mutex_trylock(&ar_ptr->mutex))
+    ++(ar_ptr->stat_lock_direct);
+  else {
+    (void)mutex_lock(&ar_ptr->mutex);
+    ++(ar_ptr->stat_lock_wait);
+  }
+#else
   (void)mutex_lock(&ar_ptr->mutex);
+#endif
+
+#ifndef NO_THREADS
   /* As in malloc(), remember this arena for the next allocation. */
   tsd_setspecific(arena_key, (Void_t *)ar_ptr);
+#endif
+
+  newp = chunk_realloc(ar_ptr, oldp, oldsize, nb);
+
+  (void)mutex_unlock(&ar_ptr->mutex);
+  return newp ? chunk2mem(newp) : NULL;
+}
+
+static mchunkptr
+internal_function
+#if __STD_C
+chunk_realloc(arena* ar_ptr, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
+              INTERNAL_SIZE_T nb)
+#else
+chunk_realloc(ar_ptr, oldp, oldsize, nb)
+arena* ar_ptr; mchunkptr oldp; INTERNAL_SIZE_T oldsize, nb;
+#endif
+{
+  mchunkptr newp = oldp;      /* chunk to return */
+  INTERNAL_SIZE_T newsize = oldsize; /* its size */
+
+  mchunkptr next;             /* next contiguous chunk after oldp */
+  INTERNAL_SIZE_T  nextsize;  /* its size */
+
+  mchunkptr prev;             /* previous contiguous chunk before oldp */
+  INTERNAL_SIZE_T  prevsize;  /* its size */
+
+  mchunkptr remainder;        /* holds split off extra space from newp */
+  INTERNAL_SIZE_T  remainder_size;   /* its size */
+
+  mchunkptr bck;              /* misc temp for linking */
+  mchunkptr fwd;              /* misc temp for linking */
 
   check_inuse_chunk(ar_ptr, oldp);
 
@@ -2695,8 +3211,7 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
           top(ar_ptr) = chunk_at_offset(oldp, nb);
           set_head(top(ar_ptr), (newsize - nb) | PREV_INUSE);
           set_head_size(oldp, nb);
-          (void)mutex_unlock(&ar_ptr->mutex);
-          return chunk2mem(oldp);
+          return oldp;
         }
       }
 
@@ -2733,13 +3248,11 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
             unlink(prev, bck, fwd);
             newp = prev;
             newsize += prevsize + nextsize;
-            newmem = chunk2mem(newp);
-            MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ);
+            MALLOC_COPY(chunk2mem(newp), chunk2mem(oldp), oldsize - SIZE_SZ);
             top(ar_ptr) = chunk_at_offset(newp, nb);
             set_head(top(ar_ptr), (newsize - nb) | PREV_INUSE);
             set_head_size(newp, nb);
-            (void)mutex_unlock(&ar_ptr->mutex);
-            return newmem;
+            return newp;
           }
         }
 
@@ -2750,8 +3263,7 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
           unlink(prev, bck, fwd);
           newp = prev;
           newsize += nextsize + prevsize;
-          newmem = chunk2mem(newp);
-          MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ);
+          MALLOC_COPY(chunk2mem(newp), chunk2mem(oldp), oldsize - SIZE_SZ);
           goto split;
         }
       }
@@ -2762,8 +3274,7 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
         unlink(prev, bck, fwd);
         newp = prev;
         newsize += prevsize;
-        newmem = chunk2mem(newp);
-        MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ);
+        MALLOC_COPY(chunk2mem(newp), chunk2mem(oldp), oldsize - SIZE_SZ);
         goto split;
       }
     }
@@ -2772,8 +3283,16 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
 
     newp = chunk_alloc (ar_ptr, nb);
 
-    if (newp == 0)  /* propagate failure */
-      return 0;
+    if (newp == 0) {
+      /* Maybe the failure is due to running out of mmapped areas. */
+      if (ar_ptr != &main_arena) {
+        (void)mutex_lock(&main_arena.mutex);
+        newp = chunk_alloc(&main_arena, nb);
+        (void)mutex_unlock(&main_arena.mutex);
+      }
+      if (newp == 0) /* propagate failure */
+        return 0;
+    }
 
     /* Avoid copy if newp is next chunk after oldp. */
     /* (This can only happen when new chunk is sbrk'ed.) */
@@ -2786,11 +3305,9 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
     }
 
     /* Otherwise copy, free, and exit */
-    newmem = chunk2mem(newp);
-    MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ);
+    MALLOC_COPY(chunk2mem(newp), chunk2mem(oldp), oldsize - SIZE_SZ);
     chunk_free(ar_ptr, oldp);
-    (void)mutex_unlock(&ar_ptr->mutex);
-    return newmem;
+    return newp;
   }
 
 
@@ -2812,8 +3329,7 @@ Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes;
   }
 
   check_inuse_chunk(ar_ptr, newp);
-  (void)mutex_unlock(&ar_ptr->mutex);
-  return chunk2mem(newp);
+  return newp;
 }
 
 
@@ -2846,14 +3362,21 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
 {
   arena *ar_ptr;
   INTERNAL_SIZE_T    nb;      /* padded  request size */
-  char*     m;                /* memory returned by malloc call */
-  mchunkptr p;                /* corresponding chunk */
-  char*     brk;              /* alignment point within p */
-  mchunkptr newp;             /* chunk to return */
-  INTERNAL_SIZE_T  newsize;   /* its size */
-  INTERNAL_SIZE_T  leadsize;  /* leading space befor alignment point */
-  mchunkptr remainder;        /* spare room at end to split off */
-  long      remainder_size;   /* its size */
+  mchunkptr p;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  if (__memalign_hook != NULL) {
+    Void_t* result;
+
+#if defined __GNUC__ && __GNUC__ >= 2
+    result = (*__memalign_hook)(alignment, bytes,
+                               __builtin_return_address (0));
+#else
+    result = (*__memalign_hook)(alignment, bytes, NULL);
+#endif
+    return result;
+  }
+#endif
 
   /* If need less alignment than we give anyway, just relay to malloc */
 
@@ -2863,18 +3386,46 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
 
   if (alignment <  MINSIZE) alignment = MINSIZE;
 
-  /* Call malloc with worst case padding to hit alignment. */
-
   nb = request2size(bytes);
   arena_get(ar_ptr, nb + alignment + MINSIZE);
   if(!ar_ptr)
     return 0;
-  p = chunk_alloc(ar_ptr, nb + alignment + MINSIZE);
+  p = chunk_align(ar_ptr, nb, alignment);
+  (void)mutex_unlock(&ar_ptr->mutex);
+  if(!p) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      p = chunk_align(&main_arena, nb, alignment);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if(!p) return 0;
+  }
+  return chunk2mem(p);
+}
 
-  if (p == 0) {
-    (void)mutex_unlock(&ar_ptr->mutex);
+static mchunkptr
+internal_function
+#if __STD_C
+chunk_align(arena* ar_ptr, INTERNAL_SIZE_T nb, size_t alignment)
+#else
+chunk_align(ar_ptr, nb, alignment)
+arena* ar_ptr; INTERNAL_SIZE_T nb; size_t alignment;
+#endif
+{
+  char*     m;                /* memory returned by malloc call */
+  mchunkptr p;                /* corresponding chunk */
+  char*     brk;              /* alignment point within p */
+  mchunkptr newp;             /* chunk to return */
+  INTERNAL_SIZE_T  newsize;   /* its size */
+  INTERNAL_SIZE_T  leadsize;  /* leading space befor alignment point */
+  mchunkptr remainder;        /* spare room at end to split off */
+  long      remainder_size;   /* its size */
+
+  /* Call chunk_alloc with worst case padding to hit alignment. */
+  p = chunk_alloc(ar_ptr, nb + alignment + MINSIZE);
+  if (p == 0)
     return 0; /* propagate failure */
-  }
 
   m = chunk2mem(p);
 
@@ -2882,8 +3433,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
   {
 #if HAVE_MMAP
     if(chunk_is_mmapped(p)) {
-      (void)mutex_unlock(&ar_ptr->mutex);
-      return chunk2mem(p); /* nothing more to do */
+      return p; /* nothing more to do */
     }
 #endif
   }
@@ -2899,7 +3449,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
     */
 
     brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -alignment);
-    if ((long)(brk - (char*)(p)) < (long) MINSIZE) brk = brk + alignment;
+    if ((long)(brk - (char*)(p)) < (long)MINSIZE) brk += alignment;
 
     newp = (mchunkptr)brk;
     leadsize = brk - (char*)(p);
@@ -2910,8 +3460,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
     {
       newp->prev_size = p->prev_size + leadsize;
       set_head(newp, newsize|IS_MMAPPED);
-      (void)mutex_unlock(&ar_ptr->mutex);
-      return chunk2mem(newp);
+      return newp;
     }
 #endif
 
@@ -2939,9 +3488,7 @@ Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes;
   }
 
   check_inuse_chunk(ar_ptr, p);
-  (void)mutex_unlock(&ar_ptr->mutex);
-  return chunk2mem(p);
-
+  return p;
 }
 
 \f
@@ -2980,7 +3527,7 @@ Void_t* pvALLOc(bytes) size_t bytes;
 
 /*
 
-  calloc calls malloc, then zeroes out the allocated chunk.
+  calloc calls chunk_alloc, then zeroes out the allocated chunk.
 
 */
 
@@ -2992,11 +3539,29 @@ Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size;
 {
   arena *ar_ptr;
   mchunkptr p, oldtop;
-  INTERNAL_SIZE_T csz, oldtopsize;
+  INTERNAL_SIZE_T sz, csz, oldtopsize;
   Void_t* mem;
 
-  INTERNAL_SIZE_T sz = request2size(n * elem_size);
+#if defined _LIBC || defined MALLOC_HOOKS
+  if (__malloc_hook != NULL) {
+    sz = n * elem_size;
+#if defined __GNUC__ && __GNUC__ >= 2
+    mem = (*__malloc_hook)(sz, __builtin_return_address (0));
+#else
+    mem = (*__malloc_hook)(sz, NULL);
+#endif
+    if(mem == 0)
+      return 0;
+#ifdef HAVE_MEMSET
+    return memset(mem, 0, sz);
+#else
+    while(sz > 0) ((char*)mem)[--sz] = 0; /* rather inefficient */
+    return mem;
+#endif
+  }
+#endif
 
+  sz = request2size(n * elem_size);
   arena_get(ar_ptr, sz);
   if(!ar_ptr)
     return 0;
@@ -3011,31 +3576,34 @@ Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size;
   /* Only clearing follows, so we can unlock early. */
   (void)mutex_unlock(&ar_ptr->mutex);
 
-  if (p == 0)
-    return 0;
-  else
-  {
-    mem = chunk2mem(p);
+  if (p == 0) {
+    /* Maybe the failure is due to running out of mmapped areas. */
+    if(ar_ptr != &main_arena) {
+      (void)mutex_lock(&main_arena.mutex);
+      p = chunk_alloc(&main_arena, sz);
+      (void)mutex_unlock(&main_arena.mutex);
+    }
+    if (p == 0) return 0;
+  }
+  mem = chunk2mem(p);
 
-    /* Two optional cases in which clearing not necessary */
+  /* Two optional cases in which clearing not necessary */
 
 #if HAVE_MMAP
-    if (chunk_is_mmapped(p)) return mem;
+  if (chunk_is_mmapped(p)) return mem;
 #endif
 
-    csz = chunksize(p);
+  csz = chunksize(p);
 
 #if MORECORE_CLEARS
-    if (p == oldtop && csz > oldtopsize)
-    {
-      /* clear only the bytes from non-freshly-sbrked memory */
-      csz = oldtopsize;
-    }
+  if (p == oldtop && csz > oldtopsize) {
+    /* clear only the bytes from non-freshly-sbrked memory */
+    csz = oldtopsize;
+  }
 #endif
 
-    MALLOC_ZERO(mem, csz - SIZE_SZ);
-    return mem;
-  }
+  MALLOC_ZERO(mem, csz - SIZE_SZ);
+  return mem;
 }
 
 /*
@@ -3082,24 +3650,27 @@ void cfree(mem) Void_t *mem;
 */
 
 #if __STD_C
-int malloc_trim(size_t pad)
+int mALLOC_TRIm(size_t pad)
 #else
-int malloc_trim(pad) size_t pad;
+int mALLOC_TRIm(pad) size_t pad;
 #endif
 {
   int res;
 
   (void)mutex_lock(&main_arena.mutex);
-  res = arena_trim(&main_arena, pad);
+  res = main_trim(pad);
   (void)mutex_unlock(&main_arena.mutex);
   return res;
 }
 
+/* Trim the main arena. */
+
 static int
+internal_function
 #if __STD_C
-arena_trim(arena *ar_ptr, size_t pad)
+main_trim(size_t pad)
 #else
-arena_trim(ar_ptr, pad) arena *ar_ptr; size_t pad;
+main_trim(pad) size_t pad;
 #endif
 {
   mchunkptr top_chunk;   /* The current top chunk */
@@ -3110,44 +3681,96 @@ arena_trim(ar_ptr, pad) arena *ar_ptr; size_t pad;
 
   unsigned long pagesz = malloc_getpagesize;
 
-  top_chunk = top(ar_ptr);
+  top_chunk = top(&main_arena);
   top_size = chunksize(top_chunk);
   extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz;
 
   if (extra < (long)pagesz) /* Not enough memory to release */
     return 0;
 
-#ifndef NO_THREADS
-  if(ar_ptr == &main_arena) {
-#endif
+  /* Test to make sure no one else called sbrk */
+  current_brk = (char*)(MORECORE (0));
+  if (current_brk != (char*)(top_chunk) + top_size)
+    return 0;     /* Apparently we don't own memory; must fail */
 
-    /* Test to make sure no one else called sbrk */
-    current_brk = (char*)(MORECORE (0));
-    if (current_brk != (char*)(top_chunk) + top_size)
-      return 0;     /* Apparently we don't own memory; must fail */
+  new_brk = (char*)(MORECORE (-extra));
 
-    new_brk = (char*)(MORECORE (-extra));
+#if defined _LIBC || defined MALLOC_HOOKS
+  /* Call the `morecore' hook if necessary.  */
+  if (__after_morecore_hook)
+    (*__after_morecore_hook) ();
+#endif
 
-    if (new_brk == (char*)(MORECORE_FAILURE)) { /* sbrk failed? */
-      /* Try to figure out what we have */
-      current_brk = (char*)(MORECORE (0));
-      top_size = current_brk - (char*)top_chunk;
-      if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */
-      {
-        sbrked_mem = current_brk - sbrk_base;
-        set_head(top_chunk, top_size | PREV_INUSE);
-      }
-      check_chunk(ar_ptr, top_chunk);
-      return 0;
+  if (new_brk == (char*)(MORECORE_FAILURE)) { /* sbrk failed? */
+    /* Try to figure out what we have */
+    current_brk = (char*)(MORECORE (0));
+    top_size = current_brk - (char*)top_chunk;
+    if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */
+    {
+      sbrked_mem = current_brk - sbrk_base;
+      set_head(top_chunk, top_size | PREV_INUSE);
     }
-    sbrked_mem -= extra;
+    check_chunk(&main_arena, top_chunk);
+    return 0;
+  }
+  sbrked_mem -= extra;
+
+  /* Success. Adjust top accordingly. */
+  set_head(top_chunk, (top_size - extra) | PREV_INUSE);
+  check_chunk(&main_arena, top_chunk);
+  return 1;
+}
 
 #ifndef NO_THREADS
-  } else {
-    if(grow_heap(heap_for_ptr(top_chunk), -extra) != 0)
-      return 0;
-  }
+
+static int
+internal_function
+#if __STD_C
+heap_trim(heap_info *heap, size_t pad)
+#else
+heap_trim(heap, pad) heap_info *heap; size_t pad;
 #endif
+{
+  unsigned long pagesz = malloc_getpagesize;
+  arena *ar_ptr = heap->ar_ptr;
+  mchunkptr top_chunk = top(ar_ptr), p, bck, fwd;
+  heap_info *prev_heap;
+  long new_size, top_size, extra;
+
+  /* Can this heap go away completely ? */
+  while(top_chunk == chunk_at_offset(heap, sizeof(*heap))) {
+    prev_heap = heap->prev;
+    p = chunk_at_offset(prev_heap, prev_heap->size - (MINSIZE-2*SIZE_SZ));
+    assert(p->size == (0|PREV_INUSE)); /* must be fencepost */
+    p = prev_chunk(p);
+    new_size = chunksize(p) + (MINSIZE-2*SIZE_SZ);
+    assert(new_size>0 && new_size<(long)(2*MINSIZE));
+    if(!prev_inuse(p))
+      new_size += p->prev_size;
+    assert(new_size>0 && new_size<HEAP_MAX_SIZE);
+    if(new_size + (HEAP_MAX_SIZE - prev_heap->size) < pad + MINSIZE + pagesz)
+      break;
+    ar_ptr->size -= heap->size;
+    delete_heap(heap);
+    heap = prev_heap;
+    if(!prev_inuse(p)) { /* consolidate backward */
+      p = prev_chunk(p);
+      unlink(p, bck, fwd);
+    }
+    assert(((unsigned long)((char*)p + new_size) & (pagesz-1)) == 0);
+    assert( ((char*)p + new_size) == ((char*)heap + heap->size) );
+    top(ar_ptr) = top_chunk = p;
+    set_head(top_chunk, new_size | PREV_INUSE);
+    check_chunk(ar_ptr, top_chunk);
+  }
+  top_size = chunksize(top_chunk);
+  extra = ((top_size - pad - MINSIZE + (pagesz-1))/pagesz - 1) * pagesz;
+  if(extra < (long)pagesz)
+    return 0;
+  /* Try to shrink. */
+  if(grow_heap(heap, -extra) != 0)
+    return 0;
+  ar_ptr->size -= extra;
 
   /* Success. Adjust top accordingly. */
   set_head(top_chunk, (top_size - extra) | PREV_INUSE);
@@ -3155,6 +3778,8 @@ arena_trim(ar_ptr, pad) arena *ar_ptr; size_t pad;
   return 1;
 }
 
+#endif
+
 \f
 
 /*
@@ -3169,9 +3794,9 @@ arena_trim(ar_ptr, pad) arena *ar_ptr; size_t pad;
 */
 
 #if __STD_C
-size_t malloc_usable_size(Void_t* mem)
+size_t mALLOC_USABLE_SIZe(Void_t* mem)
 #else
-size_t malloc_usable_size(mem) Void_t* mem;
+size_t mALLOC_USABLE_SIZe(mem) Void_t* mem;
 #endif
 {
   mchunkptr p;
@@ -3194,11 +3819,15 @@ size_t malloc_usable_size(mem) Void_t* mem;
 
 \f
 
-/* Utility to update current_mallinfo for malloc_stats and mallinfo() */
+/* Utility to update mallinfo for malloc_stats() and mallinfo() */
 
-static void malloc_update_mallinfo __MALLOC_P ((void))
+static void
+#if __STD_C
+malloc_update_mallinfo(arena *ar_ptr, struct mallinfo *mi)
+#else
+malloc_update_mallinfo(ar_ptr, mi) arena *ar_ptr; struct mallinfo *mi;
+#endif
 {
-  arena *ar_ptr = &main_arena;
   int i, navail;
   mbinptr b;
   mchunkptr p;
@@ -3219,7 +3848,7 @@ static void malloc_update_mallinfo __MALLOC_P ((void))
 #if MALLOC_DEBUG
       check_free_chunk(ar_ptr, p);
       for (q = next_chunk(p);
-           q < top(ar_ptr) && inuse(q) && (long)chunksize(q) >= (long)MINSIZE;
+           q != top(ar_ptr) && inuse(q) && (long)chunksize(q) > 0;
            q = next_chunk(q))
         check_inuse_chunk(ar_ptr, q);
 #endif
@@ -3228,62 +3857,145 @@ static void malloc_update_mallinfo __MALLOC_P ((void))
     }
   }
 
-  current_mallinfo.ordblks = navail;
-  current_mallinfo.uordblks = sbrked_mem - avail;
-  current_mallinfo.fordblks = avail;
-  current_mallinfo.hblks = n_mmaps;
-  current_mallinfo.hblkhd = mmapped_mem;
-  current_mallinfo.keepcost = chunksize(top(ar_ptr));
+  mi->arena = ar_ptr->size;
+  mi->ordblks = navail;
+  mi->smblks = mi->usmblks = mi->fsmblks = 0; /* clear unused fields */
+  mi->uordblks = ar_ptr->size - avail;
+  mi->fordblks = avail;
+  mi->hblks = n_mmaps;
+  mi->hblkhd = mmapped_mem;
+  mi->keepcost = chunksize(top(ar_ptr));
 
   (void)mutex_unlock(&ar_ptr->mutex);
 }
 
+#if !defined(NO_THREADS) && MALLOC_DEBUG > 1
+
+/* Print the complete contents of a single heap to stderr. */
+
+static void
+#if __STD_C
+dump_heap(heap_info *heap)
+#else
+dump_heap(heap) heap_info *heap;
+#endif
+{
+  char *ptr;
+  mchunkptr p;
+
+  fprintf(stderr, "Heap %p, size %10lx:\n", heap, (long)heap->size);
+  ptr = (heap->ar_ptr != (arena*)(heap+1)) ?
+    (char*)(heap + 1) : (char*)(heap + 1) + sizeof(arena);
+  p = (mchunkptr)(((unsigned long)ptr + MALLOC_ALIGN_MASK) &
+                  ~MALLOC_ALIGN_MASK);
+  for(;;) {
+    fprintf(stderr, "chunk %p size %10lx", p, (long)p->size);
+    if(p == top(heap->ar_ptr)) {
+      fprintf(stderr, " (top)\n");
+      break;
+    } else if(p->size == (0|PREV_INUSE)) {
+      fprintf(stderr, " (fence)\n");
+      break;
+    }
+    fprintf(stderr, "\n");
+    p = next_chunk(p);
+  }
+}
+
+#endif
+
 \f
 
 /*
 
   malloc_stats:
 
-    Prints on stderr the amount of space obtain from the system (both
-    via sbrk and mmap), the maximum amount (which may be more than
-    current if malloc_trim and/or munmap got called), the maximum
-    number of simultaneous mmap regions used, and the current number
+    For all arenas separately and in total, prints on stderr the
+    amount of space obtained from the system, and the current number
     of bytes allocated via malloc (or realloc, etc) but not yet
     freed. (Note that this is the number of bytes allocated, not the
     number requested. It will be larger than the number requested
-    because of alignment and bookkeeping overhead.)
+    because of alignment and bookkeeping overhead.)  When not compiled
+    for multiple threads, the maximum amount of allocated memory
+    (which may be more than current if malloc_trim and/or munmap got
+    called) is also reported.  When using mmap(), prints the maximum
+    number of simultaneous mmap regions used, too.
 
 */
 
-void malloc_stats()
+void mALLOC_STATs()
 {
-  malloc_update_mallinfo();
-  fprintf(stderr, "max system bytes = %10u\n",
-          (unsigned int)(max_total_mem));
-  fprintf(stderr, "system bytes     = %10u\n",
-          (unsigned int)(sbrked_mem + mmapped_mem));
-  fprintf(stderr, "in use bytes     = %10u\n",
-          (unsigned int)(current_mallinfo.uordblks + mmapped_mem));
+  int i;
+  arena *ar_ptr;
+  struct mallinfo mi;
+  unsigned int in_use_b = mmapped_mem, system_b = in_use_b;
+#if THREAD_STATS
+  long stat_lock_direct = 0, stat_lock_loop = 0, stat_lock_wait = 0;
+#endif
+
+  for(i=0, ar_ptr = &main_arena;; i++) {
+    malloc_update_mallinfo(ar_ptr, &mi);
+    fprintf(stderr, "Arena %d:\n", i);
+    fprintf(stderr, "system bytes     = %10u\n", (unsigned int)mi.arena);
+    fprintf(stderr, "in use bytes     = %10u\n", (unsigned int)mi.uordblks);
+    system_b += mi.arena;
+    in_use_b += mi.uordblks;
+#if THREAD_STATS
+    stat_lock_direct += ar_ptr->stat_lock_direct;
+    stat_lock_loop += ar_ptr->stat_lock_loop;
+    stat_lock_wait += ar_ptr->stat_lock_wait;
+#endif
+#if !defined(NO_THREADS) && MALLOC_DEBUG > 1
+    if(ar_ptr != &main_arena) {
+      heap_info *heap;
+      (void)mutex_lock(&ar_ptr->mutex);
+      heap = heap_for_ptr(top(ar_ptr));
+      while(heap) { dump_heap(heap); heap = heap->prev; }
+      (void)mutex_unlock(&ar_ptr->mutex);
+    }
+#endif
+    ar_ptr = ar_ptr->next;
+    if(ar_ptr == &main_arena) break;
+  }
+#if HAVE_MMAP
+  fprintf(stderr, "Total (incl. mmap):\n");
+#else
+  fprintf(stderr, "Total:\n");
+#endif
+  fprintf(stderr, "system bytes     = %10u\n", system_b);
+  fprintf(stderr, "in use bytes     = %10u\n", in_use_b);
+#ifdef NO_THREADS
+  fprintf(stderr, "max system bytes = %10u\n", (unsigned int)max_total_mem);
+#endif
 #if HAVE_MMAP
-  fprintf(stderr, "max mmap regions = %10u\n",
-          (unsigned int)max_n_mmaps);
+  fprintf(stderr, "max mmap regions = %10u\n", (unsigned int)max_n_mmaps);
+  fprintf(stderr, "max mmap bytes   = %10lu\n", max_mmapped_mem);
 #endif
 #if THREAD_STATS
-  fprintf(stderr, "arenas created   = %10d\n", stat_n_arenas);
-  fprintf(stderr, "heaps created    = %10d\n", stat_n_heaps);
+  fprintf(stderr, "heaps created    = %10d\n",  stat_n_heaps);
   fprintf(stderr, "locked directly  = %10ld\n", stat_lock_direct);
   fprintf(stderr, "locked in loop   = %10ld\n", stat_lock_loop);
+  fprintf(stderr, "locked waiting   = %10ld\n", stat_lock_wait);
+  fprintf(stderr, "locked total     = %10ld\n",
+          stat_lock_direct + stat_lock_loop + stat_lock_wait);
 #endif
 }
 
 /*
   mallinfo returns a copy of updated current mallinfo.
+  The information reported is for the arena last used by the thread.
 */
 
 struct mallinfo mALLINFo()
 {
-  malloc_update_mallinfo();
-  return current_mallinfo;
+  struct mallinfo mi;
+  Void_t *vptr = NULL;
+
+#ifndef NO_THREADS
+  tsd_getspecific(arena_key, vptr);
+#endif
+  malloc_update_mallinfo((vptr ? (arena*)vptr : &main_arena), &mi);
+  return mi;
 }
 
 
@@ -3326,29 +4038,580 @@ int mALLOPt(param_number, value) int param_number; int value;
 #else
       if (value != 0) return 0; else  n_mmaps_max = value; return 1;
 #endif
+    case M_CHECK_ACTION:
+      check_action = value; return 1;
 
     default:
       return 0;
   }
 }
 
-#if 0 && defined(_LIBC)
-weak_alias (__libc_calloc, calloc)
-weak_alias (__libc_free, cfree)
-weak_alias (__libc_free, free)
-weak_alias (__libc_malloc, malloc)
-weak_alias (__libc_memalign, memalign)
-weak_alias (__libc_realloc, realloc)
-weak_alias (__libc_valloc, valloc)
-weak_alias (__libc_pvalloc, pvalloc)
-weak_alias (__libc_mallinfo, mallinfo)
-weak_alias (__libc_mallopt, mallopt)
+\f
+
+/* Get/set state: malloc_get_state() records the current state of all
+   malloc variables (_except_ for the actual heap contents and `hook'
+   function pointers) in a system dependent, opaque data structure.
+   This data structure is dynamically allocated and can be free()d
+   after use.  malloc_set_state() restores the state of all malloc
+   variables to the previously obtained state.  This is especially
+   useful when using this malloc as part of a shared library, and when
+   the heap contents are saved/restored via some other method.  The
+   primary example for this is GNU Emacs with its `dumping' procedure.
+   `Hook' function pointers are never saved or restored by these
+   functions. */
+
+#define MALLOC_STATE_MAGIC   0x444c4541l
+#define MALLOC_STATE_VERSION (0*0x100l + 1l) /* major*0x100 + minor */
+
+struct malloc_state {
+  long          magic;
+  long          version;
+  mbinptr       av[NAV * 2 + 2];
+  char*         sbrk_base;
+  int           sbrked_mem_bytes;
+  unsigned long trim_threshold;
+  unsigned long top_pad;
+  unsigned int  n_mmaps_max;
+  unsigned long mmap_threshold;
+  int           check_action;
+  unsigned long max_sbrked_mem;
+  unsigned long max_total_mem;
+  unsigned int  n_mmaps;
+  unsigned int  max_n_mmaps;
+  unsigned long mmapped_mem;
+  unsigned long max_mmapped_mem;
+  int          using_malloc_checking;
+};
+
+Void_t*
+mALLOC_GET_STATe()
+{
+  struct malloc_state* ms;
+  int i;
+  mbinptr b;
+
+  ms = (struct malloc_state*)mALLOc(sizeof(*ms));
+  if (!ms)
+    return 0;
+  (void)mutex_lock(&main_arena.mutex);
+  ms->magic = MALLOC_STATE_MAGIC;
+  ms->version = MALLOC_STATE_VERSION;
+  ms->av[0] = main_arena.av[0];
+  ms->av[1] = main_arena.av[1];
+  for(i=0; i<NAV; i++) {
+    b = bin_at(&main_arena, i);
+    if(first(b) == b)
+      ms->av[2*i+2] = ms->av[2*i+3] = 0; /* empty bin (or initial top) */
+    else {
+      ms->av[2*i+2] = first(b);
+      ms->av[2*i+3] = last(b);
+    }
+  }
+  ms->sbrk_base = sbrk_base;
+  ms->sbrked_mem_bytes = sbrked_mem;
+  ms->trim_threshold = trim_threshold;
+  ms->top_pad = top_pad;
+  ms->n_mmaps_max = n_mmaps_max;
+  ms->mmap_threshold = mmap_threshold;
+  ms->check_action = check_action;
+  ms->max_sbrked_mem = max_sbrked_mem;
+#ifdef NO_THREADS
+  ms->max_total_mem = max_total_mem;
+#else
+  ms->max_total_mem = 0;
+#endif
+  ms->n_mmaps = n_mmaps;
+  ms->max_n_mmaps = max_n_mmaps;
+  ms->mmapped_mem = mmapped_mem;
+  ms->max_mmapped_mem = max_mmapped_mem;
+#if defined _LIBC || defined MALLOC_HOOKS
+  ms->using_malloc_checking = using_malloc_checking;
+#else
+  ms->using_malloc_checking = 0;
+#endif
+  (void)mutex_unlock(&main_arena.mutex);
+  return (Void_t*)ms;
+}
+
+int
+#if __STD_C
+mALLOC_SET_STATe(Void_t* msptr)
+#else
+mALLOC_SET_STATe(msptr) Void_t* msptr;
+#endif
+{
+  struct malloc_state* ms = (struct malloc_state*)msptr;
+  int i;
+  mbinptr b;
+
+#if defined _LIBC || defined MALLOC_HOOKS
+  disallow_malloc_check = 1;
+#endif
+  ptmalloc_init();
+  if(ms->magic != MALLOC_STATE_MAGIC) return -1;
+  /* Must fail if the major version is too high. */
+  if((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl)) return -2;
+  (void)mutex_lock(&main_arena.mutex);
+  main_arena.av[0] = ms->av[0];
+  main_arena.av[1] = ms->av[1];
+  for(i=0; i<NAV; i++) {
+    b = bin_at(&main_arena, i);
+    if(ms->av[2*i+2] == 0)
+      first(b) = last(b) = b;
+    else {
+      first(b) = ms->av[2*i+2];
+      last(b) = ms->av[2*i+3];
+      if(i > 0) {
+        /* Make sure the links to the `av'-bins in the heap are correct. */
+        first(b)->bk = b;
+        last(b)->fd = b;
+      }
+    }
+  }
+  sbrk_base = ms->sbrk_base;
+  sbrked_mem = ms->sbrked_mem_bytes;
+  trim_threshold = ms->trim_threshold;
+  top_pad = ms->top_pad;
+  n_mmaps_max = ms->n_mmaps_max;
+  mmap_threshold = ms->mmap_threshold;
+  check_action = ms->check_action;
+  max_sbrked_mem = ms->max_sbrked_mem;
+#ifdef NO_THREADS
+  max_total_mem = ms->max_total_mem;
+#endif
+  n_mmaps = ms->n_mmaps;
+  max_n_mmaps = ms->max_n_mmaps;
+  mmapped_mem = ms->mmapped_mem;
+  max_mmapped_mem = ms->max_mmapped_mem;
+  /* add version-dependent code here */
+  if (ms->version >= 1) {
+#if defined _LIBC || defined MALLOC_HOOKS
+    /* Check whether it is safe to enable malloc checking.  */
+    if (ms->using_malloc_checking && !using_malloc_checking &&
+       !disallow_malloc_check)
+      __malloc_check_init ();
+#endif
+  }
+
+  (void)mutex_unlock(&main_arena.mutex);
+  return 0;
+}
+
+\f
+
+#if defined _LIBC || defined MALLOC_HOOKS
+
+/* A simple, standard set of debugging hooks.  Overhead is `only' one
+   byte per chunk; still this will catch most cases of double frees or
+   overruns.  The goal here is to avoid obscure crashes due to invalid
+   usage, unlike in the MALLOC_DEBUG code. */
+
+#define MAGICBYTE(p) ( ( ((size_t)p >> 3) ^ ((size_t)p >> 11)) & 0xFF )
+
+/* Instrument a chunk with overrun detector byte(s) and convert it
+   into a user pointer with requested size sz. */
+
+static Void_t*
+#if __STD_C
+chunk2mem_check(mchunkptr p, size_t sz)
+#else
+chunk2mem_check(p, sz) mchunkptr p; size_t sz;
+#endif
+{
+  unsigned char* m_ptr = (unsigned char*)chunk2mem(p);
+  size_t i;
+
+  for(i = chunksize(p) - (chunk_is_mmapped(p) ? 2*SIZE_SZ+1 : SIZE_SZ+1);
+      i > sz;
+      i -= 0xFF) {
+    if(i-sz < 0x100) {
+      m_ptr[i] = (unsigned char)(i-sz);
+      break;
+    }
+    m_ptr[i] = 0xFF;
+  }
+  m_ptr[sz] = MAGICBYTE(p);
+  return (Void_t*)m_ptr;
+}
+
+/* Convert a pointer to be free()d or realloc()ed to a valid chunk
+   pointer.  If the provided pointer is not valid, return NULL. */
+
+static mchunkptr
+internal_function
+#if __STD_C
+mem2chunk_check(Void_t* mem)
+#else
+mem2chunk_check(mem) Void_t* mem;
+#endif
+{
+  mchunkptr p;
+  INTERNAL_SIZE_T sz, c;
+  unsigned char magic;
+
+  p = mem2chunk(mem);
+  if(!aligned_OK(p)) return NULL;
+  if( (char*)p>=sbrk_base && (char*)p<(sbrk_base+sbrked_mem) ) {
+    /* Must be a chunk in conventional heap memory. */
+    if(chunk_is_mmapped(p) ||
+       ( (sz = chunksize(p)), ((char*)p + sz)>=(sbrk_base+sbrked_mem) ) ||
+       sz<MINSIZE || sz&MALLOC_ALIGN_MASK || !inuse(p) ||
+       ( !prev_inuse(p) && (p->prev_size&MALLOC_ALIGN_MASK ||
+                            (long)prev_chunk(p)<(long)sbrk_base ||
+                            next_chunk(prev_chunk(p))!=p) ))
+      return NULL;
+    magic = MAGICBYTE(p);
+    for(sz += SIZE_SZ-1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+      if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+    }
+    ((unsigned char*)p)[sz] ^= 0xFF;
+  } else {
+    unsigned long offset, page_mask = malloc_getpagesize-1;
+
+    /* mmap()ed chunks have MALLOC_ALIGNMENT or higher power-of-two
+       alignment relative to the beginning of a page.  Check this
+       first. */
+    offset = (unsigned long)mem & page_mask;
+    if((offset!=MALLOC_ALIGNMENT && offset!=0 && offset!=0x10 &&
+        offset!=0x20 && offset!=0x40 && offset!=0x80 && offset!=0x100 &&
+        offset!=0x200 && offset!=0x400 && offset!=0x800 && offset!=0x1000 &&
+        offset<0x2000) ||
+       !chunk_is_mmapped(p) || (p->size & PREV_INUSE) ||
+       ( (((unsigned long)p - p->prev_size) & page_mask) != 0 ) ||
+       ( (sz = chunksize(p)), ((p->prev_size + sz) & page_mask) != 0 ) )
+      return NULL;
+    magic = MAGICBYTE(p);
+    for(sz -= 1; (c = ((unsigned char*)p)[sz]) != magic; sz -= c) {
+      if(c<=0 || sz<(c+2*SIZE_SZ)) return NULL;
+    }
+    ((unsigned char*)p)[sz] ^= 0xFF;
+  }
+  return p;
+}
+
+/* Check for corruption of the top chunk, and try to recover if
+   necessary. */
+
+static int
+#if __STD_C
+top_check(void)
+#else
+top_check()
+#endif
+{
+  mchunkptr t = top(&main_arena);
+  char* brk, * new_brk;
+  INTERNAL_SIZE_T front_misalign, sbrk_size;
+  unsigned long pagesz = malloc_getpagesize;
+
+  if((char*)t + chunksize(t) == sbrk_base + sbrked_mem ||
+     t == initial_top(&main_arena)) return 0;
+
+  switch(check_action) {
+  case 1:
+    fprintf(stderr, "malloc: top chunk is corrupt\n");
+    break;
+  case 2:
+    abort();
+  }
+  /* Try to set up a new top chunk. */
+  brk = MORECORE(0);
+  front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK;
+  if (front_misalign > 0)
+    front_misalign = MALLOC_ALIGNMENT - front_misalign;
+  sbrk_size = front_misalign + top_pad + MINSIZE;
+  sbrk_size += pagesz - ((unsigned long)(brk + sbrk_size) & (pagesz - 1));
+  new_brk = (char*)(MORECORE (sbrk_size));
+  if (new_brk == (char*)(MORECORE_FAILURE)) return -1;
+  sbrked_mem = (new_brk - sbrk_base) + sbrk_size;
+
+  top(&main_arena) = (mchunkptr)(brk + front_misalign);
+  set_head(top(&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
+
+  return 0;
+}
+
+static Void_t*
+#if __STD_C
+malloc_check(size_t sz, const Void_t *caller)
+#else
+malloc_check(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+  mchunkptr victim;
+  INTERNAL_SIZE_T nb = request2size(sz + 1);
+
+  (void)mutex_lock(&main_arena.mutex);
+  victim = (top_check() >= 0) ? chunk_alloc(&main_arena, nb) : NULL;
+  (void)mutex_unlock(&main_arena.mutex);
+  if(!victim) return NULL;
+  return chunk2mem_check(victim, sz);
+}
+
+static void
+#if __STD_C
+free_check(Void_t* mem, const Void_t *caller)
+#else
+free_check(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+  mchunkptr p;
+
+  if(!mem) return;
+  (void)mutex_lock(&main_arena.mutex);
+  p = mem2chunk_check(mem);
+  if(!p) {
+    (void)mutex_unlock(&main_arena.mutex);
+    switch(check_action) {
+    case 1:
+      fprintf(stderr, "free(): invalid pointer %p!\n", mem);
+      break;
+    case 2:
+      abort();
+    }
+    return;
+  }
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p)) {
+    (void)mutex_unlock(&main_arena.mutex);
+    munmap_chunk(p);
+    return;
+  }
+#endif
+#if 0 /* Erase freed memory. */
+  memset(mem, 0, chunksize(p) - (SIZE_SZ+1));
+#endif
+  chunk_free(&main_arena, p);
+  (void)mutex_unlock(&main_arena.mutex);
+}
+
+static Void_t*
+#if __STD_C
+realloc_check(Void_t* oldmem, size_t bytes, const Void_t *caller)
+#else
+realloc_check(oldmem, bytes, caller)
+     Void_t* oldmem; size_t bytes; const Void_t *caller;
+#endif
+{
+  mchunkptr oldp, newp;
+  INTERNAL_SIZE_T nb, oldsize;
+
+  if (oldmem == 0) return malloc_check(bytes, NULL);
+  (void)mutex_lock(&main_arena.mutex);
+  oldp = mem2chunk_check(oldmem);
+  if(!oldp) {
+    (void)mutex_unlock(&main_arena.mutex);
+    switch(check_action) {
+    case 1:
+      fprintf(stderr, "realloc(): invalid pointer %p!\n", oldmem);
+      break;
+    case 2:
+      abort();
+    }
+    return malloc_check(bytes, NULL);
+  }
+  oldsize = chunksize(oldp);
+
+  nb = request2size(bytes+1);
+
+#if HAVE_MMAP
+  if (chunk_is_mmapped(oldp)) {
+#if HAVE_MREMAP
+    newp = mremap_chunk(oldp, nb);
+    if(!newp) {
+#endif
+      /* Note the extra SIZE_SZ overhead. */
+      if(oldsize - SIZE_SZ >= nb) newp = oldp; /* do nothing */
+      else {
+        /* Must alloc, copy, free. */
+        newp = (top_check() >= 0) ? chunk_alloc(&main_arena, nb) : NULL;
+        if (newp) {
+          MALLOC_COPY(chunk2mem(newp), oldmem, oldsize - 2*SIZE_SZ);
+          munmap_chunk(oldp);
+        }
+      }
+#if HAVE_MREMAP
+    }
+#endif
+  } else {
+#endif /* HAVE_MMAP */
+    newp = (top_check() >= 0) ?
+      chunk_realloc(&main_arena, oldp, oldsize, nb) : NULL;
+#if 0 /* Erase freed memory. */
+    nb = chunksize(newp);
+    if(oldp<newp || oldp>=chunk_at_offset(newp, nb)) {
+      memset((char*)oldmem + 2*sizeof(mbinptr), 0,
+             oldsize - (2*sizeof(mbinptr)+2*SIZE_SZ+1));
+    } else if(nb > oldsize+SIZE_SZ) {
+      memset((char*)chunk2mem(newp) + oldsize, 0, nb - (oldsize+SIZE_SZ));
+    }
+#endif
+#if HAVE_MMAP
+  }
+#endif
+  (void)mutex_unlock(&main_arena.mutex);
+
+  if(!newp) return NULL;
+  return chunk2mem_check(newp, bytes);
+}
+
+static Void_t*
+#if __STD_C
+memalign_check(size_t alignment, size_t bytes, const Void_t *caller)
+#else
+memalign_check(alignment, bytes, caller)
+     size_t alignment; size_t bytes; const Void_t *caller;
+#endif
+{
+  INTERNAL_SIZE_T nb;
+  mchunkptr p;
+
+  if (alignment <= MALLOC_ALIGNMENT) return malloc_check(bytes, NULL);
+  if (alignment <  MINSIZE) alignment = MINSIZE;
+
+  nb = request2size(bytes+1);
+  (void)mutex_lock(&main_arena.mutex);
+  p = (top_check() >= 0) ? chunk_align(&main_arena, nb, alignment) : NULL;
+  (void)mutex_unlock(&main_arena.mutex);
+  if(!p) return NULL;
+  return chunk2mem_check(p, bytes);
+}
+
+#ifndef NO_THREADS
+
+/* The following hooks are used when the global initialization in
+   ptmalloc_init() hasn't completed yet. */
+
+static Void_t*
+#if __STD_C
+malloc_starter(size_t sz, const Void_t *caller)
+#else
+malloc_starter(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+  mchunkptr victim = chunk_alloc(&main_arena, request2size(sz));
+
+  return victim ? chunk2mem(victim) : 0;
+}
+
+static void
+#if __STD_C
+free_starter(Void_t* mem, const Void_t *caller)
+#else
+free_starter(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+  mchunkptr p;
+
+  if(!mem) return;
+  p = mem2chunk(mem);
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p)) {
+    munmap_chunk(p);
+    return;
+  }
+#endif
+  chunk_free(&main_arena, p);
+}
+
+/* The following hooks are used while the `atfork' handling mechanism
+   is active. */
+
+static Void_t*
+#if __STD_C
+malloc_atfork (size_t sz, const Void_t *caller)
+#else
+malloc_atfork(sz, caller) size_t sz; const Void_t *caller;
+#endif
+{
+  Void_t *vptr = NULL;
+
+  tsd_getspecific(arena_key, vptr);
+  if(!vptr) {
+    mchunkptr victim = chunk_alloc(&main_arena, request2size(sz));
+    return victim ? chunk2mem(victim) : 0;
+  } else {
+    /* Suspend the thread until the `atfork' handlers have completed.
+       By that time, the hooks will have been reset as well, so that
+       mALLOc() can be used again. */
+    (void)mutex_lock(&list_lock);
+    (void)mutex_unlock(&list_lock);
+    return mALLOc(sz);
+  }
+}
+
+static void
+#if __STD_C
+free_atfork(Void_t* mem, const Void_t *caller)
+#else
+free_atfork(mem, caller) Void_t* mem; const Void_t *caller;
+#endif
+{
+  Void_t *vptr = NULL;
+  arena *ar_ptr;
+  mchunkptr p;                          /* chunk corresponding to mem */
+
+  if (mem == 0)                              /* free(0) has no effect */
+    return;
+
+  p = mem2chunk(mem);
+
+#if HAVE_MMAP
+  if (chunk_is_mmapped(p))                       /* release mmapped memory. */
+  {
+    munmap_chunk(p);
+    return;
+  }
+#endif
+
+  ar_ptr = arena_for_ptr(p);
+  tsd_getspecific(arena_key, vptr);
+  if(vptr)
+    (void)mutex_lock(&ar_ptr->mutex);
+  chunk_free(ar_ptr, p);
+  if(vptr)
+    (void)mutex_unlock(&ar_ptr->mutex);
+}
+
+#endif
+
+#endif /* defined _LIBC || defined MALLOC_HOOKS */
+
+\f
+
+#ifdef _LIBC
+weak_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
+weak_alias (__libc_free, __cfree) weak_alias (__libc_free, cfree)
+weak_alias (__libc_free, __free) weak_alias (__libc_free, free)
+weak_alias (__libc_malloc, __malloc) weak_alias (__libc_malloc, malloc)
+weak_alias (__libc_memalign, __memalign) weak_alias (__libc_memalign, memalign)
+weak_alias (__libc_realloc, __realloc) weak_alias (__libc_realloc, realloc)
+weak_alias (__libc_valloc, __valloc) weak_alias (__libc_valloc, valloc)
+weak_alias (__libc_pvalloc, __pvalloc) weak_alias (__libc_pvalloc, pvalloc)
+weak_alias (__libc_mallinfo, __mallinfo) weak_alias (__libc_mallinfo, mallinfo)
+weak_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
+
+weak_alias (__malloc_stats, malloc_stats)
+weak_alias (__malloc_usable_size, malloc_usable_size)
+weak_alias (__malloc_trim, malloc_trim)
+weak_alias (__malloc_get_state, malloc_get_state)
+weak_alias (__malloc_set_state, malloc_set_state)
 #endif
 
 /*
 
 History:
 
+    V2.6.4-pt3 Thu Feb 20 1997 Wolfram Gloger (wmglo@dent.med.uni-muenchen.de)
+      * Added malloc_get/set_state() (mainly for use in GNU emacs),
+        using interface from Marcus Daniels
+      * All parameters are now adjustable via environment variables
+
+    V2.6.4-pt2 Sat Dec 14 1996 Wolfram Gloger (wmglo@dent.med.uni-muenchen.de)
+      * Added debugging hooks
+      * Fixed possible deadlock in realloc() when out of memory
+      * Don't pollute namespace in glibc: use __getpagesize, __mmap, etc.
+
     V2.6.4-pt Wed Dec  4 1996 Wolfram Gloger (wmglo@dent.med.uni-muenchen.de)
       * Very minor updates from the released 2.6.4 version.
       * Trimmed include file down to exported data structures.
@@ -3373,10 +4636,10 @@ History:
         Wolfram Gloger (Gloger@lrz.uni-muenchen.de).
       * Use last_remainder in more cases.
       * Pack bins using idea from  colin@nyx10.cs.du.edu
-      * Use ordered bins instead of best-fit threshhold
+      * Use ordered bins instead of best-fit threshold
       * Eliminate block-local decls to simplify tracing and debugging.
       * Support another case of realloc via move into top
-      * Fix error occuring when initial sbrk_base not word-aligned.
+      * Fix error occurring when initial sbrk_base not word-aligned.
       * Rely on page size for units instead of SBRK_UNIT to
         avoid surprises about sbrk alignment conventions.
       * Add mallinfo, mallopt. Thanks to Raymond Nijssen