ldmud-3.3.719/
ldmud-3.3.719/doc/
ldmud-3.3.719/doc/efun.de/
ldmud-3.3.719/doc/efun/
ldmud-3.3.719/doc/man/
ldmud-3.3.719/doc/other/
ldmud-3.3.719/mud/
ldmud-3.3.719/mud/heaven7/
ldmud-3.3.719/mud/lp-245/
ldmud-3.3.719/mud/lp-245/banish/
ldmud-3.3.719/mud/lp-245/doc/
ldmud-3.3.719/mud/lp-245/doc/examples/
ldmud-3.3.719/mud/lp-245/doc/sefun/
ldmud-3.3.719/mud/lp-245/log/
ldmud-3.3.719/mud/lp-245/obj/Go/
ldmud-3.3.719/mud/lp-245/players/lars/
ldmud-3.3.719/mud/lp-245/room/death/
ldmud-3.3.719/mud/lp-245/room/maze1/
ldmud-3.3.719/mud/lp-245/room/sub/
ldmud-3.3.719/mud/lp-245/secure/
ldmud-3.3.719/mud/sticklib/
ldmud-3.3.719/mud/sticklib/src/
ldmud-3.3.719/mudlib/deprecated/
ldmud-3.3.719/mudlib/uni-crasher/
ldmud-3.3.719/pkg/
ldmud-3.3.719/pkg/debugger/
ldmud-3.3.719/pkg/diff/
ldmud-3.3.719/pkg/misc/
ldmud-3.3.719/src/
ldmud-3.3.719/src/autoconf/
ldmud-3.3.719/src/ptmalloc/
ldmud-3.3.719/src/util/
ldmud-3.3.719/src/util/erq/
ldmud-3.3.719/src/util/indent/hosts/next/
ldmud-3.3.719/src/util/xerq/
ldmud-3.3.719/src/util/xerq/lpc/
ldmud-3.3.719/src/util/xerq/lpc/www/
ldmud-3.3.719/test/generic/
ldmud-3.3.719/test/inc/
ldmud-3.3.719/test/t-0000398/
ldmud-3.3.719/test/t-0000548/
ldmud-3.3.719/test/t-030925/
ldmud-3.3.719/test/t-040413/
ldmud-3.3.719/test/t-041124/
ldmud-3.3.719/test/t-language/
/*---------------------------------------------------------------------------
 * The Memory Allocator wrapper.
 *
 *---------------------------------------------------------------------------
 * This file #includes the source for the memory allocator selected in
 * in config.h. If sysmalloc is selected, this file provides the basic
 * xalloc()... implementation using malloc()... .
 *
 * It is the task of the memory allocator source included to provide
 * simulations of malloc()... where required.
 *---------------------------------------------------------------------------
 */

#include "driver.h"

#include <ctype.h>
#include <stddef.h>
#include <stdio.h>

#include "xalloc.h"

#include "backend.h"
#include "gcollect.h"
#include "interpret.h"
#include "simulate.h"

#include "exec.h"
#include "object.h"
#include "mstrings.h"

/*-------------------------------------------------------------------------*/

/* Minimum boundary between stack and heap, should be sufficient for
 * error handling (which needs about 10..15KByte).
 * If the gap falls below this value, an error is generated and the
 * gap is no longer checked except for true overlap with the heap.
 */

#define HEAP_STACK_GAP (10 * ERROR_FMT_LEN)


/* A counter type for statistics and its functions.
 */
typedef struct { unsigned long counter, size; } t_stat;
  /* A counter type for statistics and its functions: */

static inline void count_add(t_stat *a, unsigned long b) {
  a->size += b;
}

static inline void count(t_stat *a, unsigned long b) {
  count_add(a, b);
  if (b < 0)
    --a->counter;
  else
    ++a->counter;
}

static inline void count_up(t_stat *a, unsigned long b) {
  count_add(a, b);
  ++a->counter;
}

static inline void count_up_n(t_stat *a, unsigned long b, unsigned long c) {
  count_add(a, b * c);
  a->counter += b;
}

static inline void count_back(t_stat *a, unsigned long b) {
  count_add(a, -b);
  --a->counter;
}

static inline void count_back_n(t_stat *a, unsigned long b, unsigned long c) {
  count_add(a, -(b * c));
  a->counter -= b;
}

typedef p_uint word_t;
  /* Our 'word' type. This should not be changed unless you check the allocators
   * first for assumptions that this is a p_uint.
   */

/*-------------------------------------------------------------------------*/

/* The extra xalloc header fields.
 * GC's write_malloc_trace() expects XM_FILE and XM_LINE at the end 
 * of the header.
 * TODO: Let the GC use the symbolic constants.
 */

enum xalloc_header_fields {
#ifdef MALLOC_LPC_TRACE
    XM_OBJ  = 0,  /* (object_t*) the allocating object */
    XM_PROG = 1,  /* (int32) allocating program's id-number */
    XM_PC   = 2,  /* (bytecode_p) inter_pc at the allocation */
#    ifdef MALLOC_TRACE
         XM_FILE = 3,  /* (const char*) allocating source file */
         XM_LINE = 4,  /* (word_t) allocating line in source file */
         XM_OVERHEAD = 5,
#    else
         XM_OVERHEAD = 3,
#    endif /* MALLOC_TRACE */
#else /* !MALLOC_LPC_TRACE */
#    ifdef MALLOC_TRACE
         XM_FILE = 0,  /* (const char*) allocating source file */
         XM_LINE = 1,  /* (word_t) allocating line in source file */
         XM_OVERHEAD = 2,
#    else
         XM_OVERHEAD = 0,
#    endif /* MALLOC_TRACE */
#endif /* MALLOC_LPC_TRACE */
    XM_OVERHEAD_SIZE = XM_OVERHEAD * sizeof(word_t),
};

/*-------------------------------------------------------------------------*/

/* -- Global Variables/Arguments dealing with memory -- */

Bool out_of_memory = MY_FALSE;              /* True: we are out of memory */
int malloc_privilege = MALLOC_USER; /* Privilege for next allocation */

char *reserved_user_area   = NULL;  /* Reserved memory areas */
char *reserved_master_area = NULL;
char *reserved_system_area = NULL;

mp_int reserved_user_size   = RESERVED_USER_SIZE;   /* The reserved sizes */
mp_int reserved_master_size = RESERVED_MASTER_SIZE;
mp_int reserved_system_size = RESERVED_SYSTEM_SIZE;

/* at startup reserve these amounts of memory for large and small blocks */
mp_int min_malloced       = MIN_MALLOCED;
mp_int min_small_malloced = MIN_SMALL_MALLOCED;
/* this is the hard limit for memory allocations. */
static mp_int max_malloced       = HARD_MALLOC_LIMIT_DEFAULT;
/* this is a soft limit for memory allocations. It serves as a kind of low
 * watermark. If exceeded, the game driver will inform the mudlib by calling
 * low_memory() in the master. */
static mp_int soft_malloc_limit  = SOFT_MALLOC_LIMIT_DEFAULT;

/* Was the low_memory() already called in the master`*/
static Bool low_memory_applied = MY_FALSE;

int stack_direction = 0; /*  0: Unknown stack behaviour
                          * +1: Stack grows upward
                          * -1: Stack grows downward
                          */

static char * initial_stack = NULL; /* The stack at the start of the program */

static int in_malloc = 0;
  /* >0 when the core code in the allocator is executed.
   * This variable serves as primitive safeguard against re-entrant
   * calls to the allocator, for example by threads.
   */

static int going_to_exit = MY_FALSE;
  /* When the allocator detected a fatal error, it sets this
   * variable before starting the fatal() handling in order to
   * prevent recursions.
   */

#ifdef MALLOC_SBRK_TRACE

static size_t mdb_size;
static object_t *mdb_object;
#    if defined(MALLOC_TRACE)
        static const char * mdb_file;
        static int mdb_line;
#    endif
  /* Persistent copy of the latest memory request information,
   * so that the SBRK_TRACE can print it.
   */

#endif /* MALLOC_SBRK_TRACE */

/* --- Statistics --- */

static t_stat xalloc_stat = {0,0};
  /* Total number and size of allocations done by the driver (incl overhead).
   */

#if defined(MALLOC_SBRK) && (defined(SBRK_OK) || defined(HAVE_MMAP))
static t_stat clib_alloc_stat = {0,0};
  /* Number and size of allocations done through the clib emulation
   * functions (incl overhead).
   */
#endif

/*-------------------------------------------------------------------------*/
/* Forward declarations */

#ifdef MALLOC_LPC_TRACE
static void write_lpc_trace (int d, word_t *p, int oneline) __attribute__((nonnull(2)));
#endif

#ifdef GC_SUPPORT
static void print_block (int d, word_t *block);
#endif /* GC_SUPPORT */

#ifdef MALLOC_SBRK_TRACE

/*-------------------------------------------------------------------------*/
static void
mem_debug_log (const char * name, p_int size)

/* Mem debug log function. Log the given <size> for function <name>
 * to stdout together with the original allocation request size.
 */

{
#if defined(MALLOC_TRACE)
      dprintf4(1, "%s %s(%d) for %d"
                , (p_int)current_time_stamp, (p_int)name
                , (p_int)size, (p_int)mdb_size
                );
      dprintf3(1, " , '%s':%d , obj %s\n"
                , (p_int)mdb_file, (p_int)mdb_line
                , (p_int)(mdb_object ? ( mdb_object->name ? get_txt(mdb_object->name) : "<?>"): "<null>")
                );
#else
      dprintf4(1, "%s %s(%d) for %d"
                , (p_int)current_time_stamp, (p_int)name
                , (p_int)size, (p_int)mdb_size
                );
      dprintf1(1, " , obj %s\n"
                , (p_int)(mdb_object ? ( mdb_object->name ? get_txt(mdb_object->name) : "<?>"): "<null>")
                );
#endif /* MALLOC_TRACE */
} /* mem_debug_log() */

/*-------------------------------------------------------------------------*/
static void
mdb_log_sbrk (p_int size)

/* esbrk() log function: esbrk() is called to allocate <size> bytes
 * from the system. Log this information to stdout together with
 * the original allocation request size.
 */

{
    mem_debug_log("esbrk", size);
} /* mdb_log_sbrk() */

#else 

#define mdb_log_sbrk(size) NOOP

#endif /* MALLOC_SBRK_TRACE */

/*-------------------------------------------------------------------------*/

/* Include the allocator source.
 *
 * The allocator is free to use any information found in xalloc.
 * In return, the allocator has to provide this interface:
 *
 *   static POINTER mem_alloc(size_t size)
 *     to allocate a memory block
 *
 *   static void mem_free(POINTER p)
 *     to deallocate a memory block
 *
 *   static POINTER mem_realloc (POINTER p, size_t size)
 *     reallocate a block.
 *
 *   static void * mem_increment_size (void *vp, size_t size)
 *     Increase a block size in place. If not possible, return NULL.
 *
 *   static static size_t mem_block_size (POINTER p)
 *     Return the size of an allocated block.
 *     If this value is not available, implement this function as a dummy,
 *     but also #define NO_MEM_BLOCK_SIZE.
 *     For Garbage Collection or replacing malloc() this function must be
 *     valid!
 *
 *   static static size_t mem_overhead ()
 *     Return the size of the allocators internal overhead.
 *
 *   static void mem_mark_permanent (POINTER p)
 *   static void mem_mark_collectable (POINTER p)
 *     Mark a block as permanent resp. collectable
 *
 *   void mem_consolidate (bool force)
 *     Do whatever consolidation is useful.
 *     <force> is true after a GC, and false when called from the backend.
 *
 *   void mem_dump_data (strbuf_t *sbuf)
 *   void mem_dump_extdata (strbuf_t *sbuf)
 *   void mem_dinfo_data (svalue_t *svp, int value)
 *     Return the statistics data.
 *
 *   Bool mem_dump_memory (int fd)
 *     Dump the memory layout to <fd>, or return FALSE.
 *     If <fd> is -1, just return TRUE or FALSE (this is used to check if
 *     the allocator supports memory dumps).
 *
#ifdef MALLOC_EXT_STATISTICS
 *   void mem_update_stats (void)
 *     Update whatever extended statistics are available.
 *     Called every backend cycle or so to allow for the calculation of
 *     averages over time.
 *
#endif
#ifdef GC_SUPPORT
 *   static void mem_clear_ref (POINTER p)
 *   static void mem_mark_ref (POINTER p)
 *   static Bool mem_test_ref (POINTER p)
 *     Clear, set, test the 'referenced' marker.
 *
 *   void mem_clear_ref_flags()
 *     Clear all 'referenced' markers.
 *
 *   void mem_free_unrefed_memory()
 *     Free all memory marked as 'unreferenced'.
 *     This routine also has to accordingly adjust xalloc_stat.
 *
#ifdef MALLOC_TRACE
 *   static Bool mem_is_freed (POINTER p, size_t minsize)
 *     Return true if <p> is a free block.
#endif
#endif
 *
 *   #define REPLACE_MALLOC
 *     if the allocator's mem_alloc()/mem_free() can be used to replace the
 *     libc allocation routines (ie. the allocator doesn't use malloc()
 *     itself).  The actual replacement is provided by xalloc.
 *   #define MEM_MAIN_THREADSAFE
 *     The allocator is safe to use from the main thread.
 *   #define MEM_THREADSAFE
 *     The allocator is altogether threadsafe.
 */

#if defined(MALLOC_smalloc)
#  include "smalloc.c"
#elif defined(MALLOC_slaballoc)
#  include "slaballoc.c"
#elif defined(MALLOC_sysmalloc)
#  include "sysmalloc.c"
#elif defined(MALLOC_ptmalloc)
#  include "xptmalloc.c"
#else
#  error "No allocator specified."
#endif

#ifdef NO_MEM_BLOCK_SIZE
#  if defined(GC_SUPPORT) || defined(REPLACE_MALLOC)
#    error "For GC_SUPPORT or REPLACE_MALLOC, mem_block_size() must exist!"
#  endif
#endif

#if defined(USE_SQLITE) && defined(SQLITE3_USES_PTHREADS) && !defined(MEM_THREADSAFE) && !defined(MEM_MAIN_THREADSAFE)
#    warning ""
#    warning "-----------------------------------"
#    warning "SQLite3 uses PThreads, but the allocator"
#    warning "is not threadsafe!"
#    warning "-----------------------------------"
#    warning ""
#endif

#if defined(MALLOC_ptmalloc) && defined(GC_SUPPORT) && defined(__FreeBSD__)
#    warning ""
#    warning "-----------------------------------"
#    warning "PTMalloc selected, but the allocator"
#    warning "doesn't support GC under FreeBSD!"
#    warning "-----------------------------------"
#    warning ""
#endif

/*-------------------------------------------------------------------------*/
size_t
xalloced_size (POINTER p
#ifdef NO_MEM_BLOCK_SIZE
                         UNUSED
#endif /* NO_MEM_BLOCK_SIZE */
              )

/* Return the allocation size (incl. overhead) of the block <p>.
 */

{
#ifndef NO_MEM_BLOCK_SIZE
    return mem_block_size((word_t*)p - XM_OVERHEAD);
#else
#   ifdef __MWERKS__
#       pragma unused(p)
#   endif
    return 0;
#endif
} /* xalloced_size() */

/*-------------------------------------------------------------------------*/
size_t
xalloc_overhead (void)

/* Return the total overhead of an allocation - xalloc and allocator.
 */

{
    return mem_overhead() + XM_OVERHEAD_SIZE;
} /* xalloc_overhead() */

/*-------------------------------------------------------------------------*/
static Bool
retry_alloc (size_t size MTRACE_DECL)

/* An allocation for <size> bytes just failed - try to free the reserves.
 * Return TRUE if the allocation can be retried, FALSE is not.
 * If allocation privilege is SYSTEM and the allocation can't be retried,
 * abort the driver.
 */

{
    /* Out of memory - try to recover */

    static char mess1[] =
        "Temporarily out of MEMORY. Freeing user reserve.\n";
    static char mess2[] =
        "Temporarily out of MEMORY. Freeing master reserve.\n";
    static char mess3[] =
        "Temporarily out of MEMORY. Freeing system reserve.\n";
    static char mess4[] =
        "Totally out of MEMORY.\n";
    static char mess_d1[] =
        "Low on MEMORY: Trying to allocate ";
    static char mess_d2[] =
        " bytes for ";
    static char mess_d3[] =
        " bytes request";
    static char mess_d4[] =
        " (";
    static char mess_d7[] =
        ", prog ";
    static char mess_d6[] =
        ")";
#if defined(MALLOC_TRACE)
    static char mess_d5[] =
        " line ";
#endif
    static char mess_nl[] =
        "\n";

    /* Print the Out-Of-Mem diagnostic */
    writes(2, mess_d1);
    writed(2, size);
    writes(2, mess_d2);
    writed(2, size - XM_OVERHEAD_SIZE);
    writes(2, mess_d3);
#ifdef MALLOC_TRACE
    writes(2, mess_d4);
    writes(2, malloc_trace_file);
    writes(2, mess_d5);
    writed(2, malloc_trace_line);
    writes(2, mess_d6);
#endif
    writes(2, mess_d4);
    writes(2, current_object ? get_txt(current_object->name) : "<null>");
    writes(2, mess_d7);
    writes(2, current_prog ? get_txt(current_prog->name) : "<null>");
    writes(2, mess_d6);
    writes(2, mess_nl);

    /* Free the next reserve, the try again */

    if (gc_request == gcDont)
        gc_request = gcMalloc;
    extra_jobs_to_do = MY_TRUE;
    if (reserved_user_area)
    {
        xfree(reserved_user_area);
        reserved_user_area = NULL;
        writes(2, mess1);
        return MY_TRUE;
    }

    if (malloc_privilege >= MALLOC_MASTER && reserved_master_area)
    {
        xfree(reserved_master_area);
        reserved_master_area = NULL;
        writes(2, mess2);
        return MY_TRUE;
    }
    if (malloc_privilege >= MALLOC_SYSTEM && reserved_system_area)
    {
        xfree(reserved_system_area);
        reserved_system_area = 0;
        writes(2, mess3);
        return MY_TRUE;
    }

    if (malloc_privilege < MALLOC_SYSTEM)
    {
        out_of_memory = MY_TRUE;
        return MY_FALSE;
    }

    /* Totally out of memory: exit */
    max_malloced = 0; /* Disable the checking for a clean exit */
    going_to_exit = MY_TRUE; /* Prevent recursions */
    writes(2, mess4);
    (void)dump_trace(MY_FALSE, NULL);
    fatal("Out of memory (%lu bytes)\n", (unsigned long)size);
    /* NOTREACHED */
    return MY_FALSE;
} /* retry_alloc() */

/*-------------------------------------------------------------------------*/
static INLINE Bool
check_max_malloced (void)

/* If max_malloced is set, check if the allocated memory exceeds it.
 * If not, return FALSE.
 * If yes, and malloc_privilege < MALLOC_SYSTEM: return TRUE.
 * If yes, and malloc_privilege == MALLOC_SYSTEM: abort.
 */

{
#ifndef NO_MEM_BLOCK_SIZE
    if (max_malloced > 0 && (mp_int)xalloc_stat.size > max_malloced)
    {
        static const char mess[] = "HARD_MALLOC_LIMIT reached.\n";
        writes(2, mess);
        if (malloc_privilege < MALLOC_SYSTEM)
            return MY_TRUE;

        /* Totally out of memory: exit */
        max_malloced = 0; /* Disable the checking for a clean exit */
        going_to_exit = MY_TRUE; /* Prevent recursions */
        (void)dump_trace(MY_FALSE, NULL);
        fatal("Out of memory.\n");
        /* NOTREACHED */
    }
#endif
    return MY_FALSE;
} /* check_max_malloced() */

/*-------------------------------------------------------------------------*/
POINTER
xalloc_traced (size_t size MTRACE_DECL)

/* Allocate <size> bytes of memory like malloc() does.
 * This function catches out of memory situations and tries to recover
 * from them by using the reserved memory areas. If it totally runs
 * out of memory, the program exit()s with code 3.
 */

{
    word_t *p;

    if (going_to_exit) /* A recursive call while we're exiting */
        exit(3);

#ifdef MALLOC_SBRK_TRACE
    mdb_size = size;
    mdb_object = current_object;
#ifdef MALLOC_TRACE
        mdb_file = malloc_trace_file;
        mdb_line = malloc_trace_line;
#endif
#endif /* MALLOC_SBRK_TRACE */

    size += XM_OVERHEAD_SIZE;

    do {
        p = mem_alloc(size);
    } while (p == NULL && retry_alloc(size MTRACE_PASS));

    if (p == NULL)
    {
        return NULL;
    }

#ifdef MALLOC_TRACE
        p[XM_FILE] = (word_t)malloc_trace_file;
        p[XM_LINE] = (word_t)malloc_trace_line;
#endif
#ifdef MALLOC_LPC_TRACE
        p[XM_OBJ]  = (word_t)current_object;
        p[XM_PROG] = current_prog ? current_prog->id_number : 0;
        p[XM_PC]   = (word_t)inter_pc;
#endif
#ifdef NO_MEM_BLOCK_SIZE
    count_up(&xalloc_stat, XM_OVERHEAD_SIZE);
#else
    count_up(&xalloc_stat, mem_block_size(p));
    if (check_max_malloced())
        return NULL;
#endif
    return (POINTER)(p + XM_OVERHEAD);
} /* xalloc_traced() */

/*-------------------------------------------------------------------------*/
void
xfree (POINTER p)

/* Free the memory block <p>.
 */

{
    if (NULL != p)
    {
        word_t *q = (word_t*)p - XM_OVERHEAD;
#ifdef NO_MEM_BLOCK_SIZE
        count_back(&xalloc_stat, XM_OVERHEAD_SIZE);
#else
        count_back(&xalloc_stat, mem_block_size(q));
#endif
        mem_free(q);
    }
} /* xfree() */

/*-------------------------------------------------------------------------*/
POINTER
pxalloc_traced (size_t size MTRACE_DECL)

/* Allocate a block of <size> bytes - like xalloc(), just that the
 * memory is not subject to GC.
 */

{
    POINTER temp;

    temp = xalloc_traced(size MTRACE_PASS);
    if (temp)
    {
        mem_mark_permanent((word_t *)temp - XM_OVERHEAD);
    }
    return temp;
} /* pxalloc_traced() */

/*-------------------------------------------------------------------------*/
void
pfree (POINTER p)

/* Deallocate a permanent block <p>.
 */

{
    if (p)
    {
        mem_mark_collectable((word_t *)p - XM_OVERHEAD);
    }
    xfree(p);
} /* pfree() */

/*-------------------------------------------------------------------------*/
POINTER
prexalloc_traced (POINTER p, size_t size MTRACE_DECL)

/* Reallocate block <p> to the new size of <size> and return the pointer.
 * The memory is not subject to GC.
#ifdef MALLOC_TRACE
 * The trace arguments are admittedly unused in the function, but come
 * in handy if the allocation code needs to be instrumented for debugging
 * purposes.
#endif
 */

{
    POINTER temp;

    if (p)
    {
        mem_mark_collectable((word_t *)p - XM_OVERHEAD);
    }
    temp = rexalloc_traced(p, size MTRACE_PASS);
    if (temp)
    {
        mem_mark_permanent((word_t *)temp - XM_OVERHEAD);
    }
    return temp;
} /* prexalloc_traced() */

/*-------------------------------------------------------------------------*/
void *
malloc_increment_size (void *vp, size_t size)

/* Try to extent the allocation block for <vp> in place to hold <size> more
 * bytes. If this is not possible, return NULL, otherwise return a pointer
 * to the start of the block extension.
 */
{
    word_t * block = (word_t*)vp - XM_OVERHEAD;
#ifndef NO_MEM_BLOCK_SIZE
    size_t old_size;
#endif
    void * rc;

    if (going_to_exit) /* A recursive call while we're exiting */
        exit(3);

#ifndef NO_MEM_BLOCK_SIZE
    old_size = mem_block_size(block);
#endif

    rc = mem_increment_size(block, size);

#ifndef NO_MEM_BLOCK_SIZE
    if (rc != NULL)
    {
        count_back(&xalloc_stat, old_size);
        count_up(&xalloc_stat, mem_block_size(block));
        if (check_max_malloced())
            return NULL;
    }
#endif

    return rc;
} /* malloc_increment_size() */

/*-------------------------------------------------------------------------*/
POINTER
rexalloc_traced (POINTER p, size_t size MTRACE_DECL
#ifndef MALLOC_SBRK_TRACE
                                                    UNUSED
#endif /* MALLOC_SBRK_TRACE */
                )

/* Reallocate block <p> to the new size of <size> and return the pointer.
 * The memory is not aligned and subject to GC.
#ifdef MALLOC_TRACE
 * The trace arguments are admittedly unused in the function, but come
 * in handy if the allocation code needs to be instrumented for debugging
 * purposes.
#endif
 */

{
#ifndef MALLOC_SBRK_TRACE
#ifdef MALLOC_TRACE
#   ifdef __MWERKS__
#       pragma unused(malloc_trace_file)
#       pragma unused(malloc_trace_line)
#   endif
#endif /* MALLOC_TRACE */
#endif /* MALLOC_SBRK_TRACE */

    word_t *block, *t;
#ifndef NO_MEM_BLOCK_SIZE
    size_t old_size;
#endif

    if (going_to_exit) /* A recursive call while we're exiting */
        exit(3);

    if (!p)
    {
        return xalloc_traced(size MTRACE_ARG);
    }

#ifdef MALLOC_SBRK_TRACE
    mdb_size = size;
    mdb_object = current_object;
#ifdef MALLOC_TRACE
        mdb_file = malloc_trace_file;
        mdb_line = malloc_trace_line;
#endif
#endif /* MALLOC_SBRK_TRACE */

    size += XM_OVERHEAD_SIZE;
    block = (word_t *)p - XM_OVERHEAD;

#ifndef NO_MEM_BLOCK_SIZE
    old_size = mem_block_size(block);
    t = malloc_increment_size(p, size - old_size);
    if (t)
        return p;
#endif

    do {
        t = mem_realloc(block, size);
    } while (t == NULL && retry_alloc(size MTRACE_ARG));

    if (t)
    {
#ifndef NO_MEM_BLOCK_SIZE
        count_back(&xalloc_stat, old_size);
        count_up(&xalloc_stat, mem_block_size(t));
        if (check_max_malloced())
            return NULL;
#endif
        t += XM_OVERHEAD;
    }
    
    return (POINTER)t;
} /* rexalloc() */

/*=========================================================================*/

/*                          GARBAGE COLLECTOR                              */

#ifdef GC_SUPPORT
/*-------------------------------------------------------------------------*/
void
x_clear_ref (POINTER p)

/* GC Support: Clear the 'referenced' flag for block <p>.
 */

{
    mem_clear_ref((word_t *)p - XM_OVERHEAD);
} /* x_clear_ref() */

/*-------------------------------------------------------------------------*/
int
x_mark_ref (POINTER p)

/* GC Support: Set the 'referenced' flag for block <p>.
 * This function returns a value (1) so that it can be used in macros
 * more easily.
 */
{
    mem_mark_ref((word_t *)p - XM_OVERHEAD);
    return 1;
} /* x_mark_ref() */

/*-------------------------------------------------------------------------*/
Bool
x_test_ref (POINTER p)

/* GC Support: Check the memory block marker for <p>, return TRUE if _not_
 * set.
 */

{
    return mem_test_ref((word_t *)p - XM_OVERHEAD);
} /* x_test_ref() */

/*-------------------------------------------------------------------------*/

#ifdef MALLOC_TRACE

static int num_dispatched_types = 0;
  /* Used size of the dispatch_table
   */

static struct {
    char *file;
    word_t line;
    void (*func)(int, void *, int);
} dispatch_table[12];
  /* The dispatch table used to recognize and print datablocks.
   * The recognition is simple and uses the file/line information received
   * from sample allocations done by gcollect. If an allocation matches
   * one entry in the table, the function is called with (filedescriptor,
   * blockaddress, 0).
   */

#ifdef CHECK_OBJECT_GC_REF
/*-------------------------------------------------------------------------*/
/* Some extra variables to explicitely store the location of program
 * and object allocations.
 */

static char * object_file;
static word_t object_line;
static char * program_file;
static word_t program_line;

void
note_object_allocation_info ( void *block )
{
    object_file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
    object_line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
}
void
note_program_allocation_info ( void *block )
{
    program_file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
    program_line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
}

Bool
is_object_allocation ( void *block )
{
    return (object_file == (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD])
        && (object_line == ((word_t*)block)[XM_LINE-XM_OVERHEAD]);
}
Bool
is_program_allocation ( void *block )
{
    return (program_file == (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD])
        && (program_line == ((word_t*)block)[XM_LINE-XM_OVERHEAD]);
}
#endif

/*-------------------------------------------------------------------------*/
void
store_print_block_dispatch_info (void *block
                                , void (*func)(int, void *, int)
                                )

/* Add a new block type: get the file/line information from the
 * allocation <block> and store it with the <func>tion.
 */

{
    int i;

    i = num_dispatched_types++;
    if (i >= (int)(sizeof(dispatch_table)/sizeof(dispatch_table[0])))
    {
        writes(2, "dispatch_table for print_block() to small\n");
        return;
    }

    dispatch_table[i].file = (char *)((word_t*)block)[XM_FILE-XM_OVERHEAD];
    dispatch_table[i].line = ((word_t*)block)[XM_LINE-XM_OVERHEAD];
    dispatch_table[i].func = func;
} /* store_print_block_dispatch_info() */

/*-------------------------------------------------------------------------*/
Bool
is_freed (void *p, p_uint minsize)

/* Check if block for the allocation <p> is a free block of at least
 * <minsize>. Blocks outside the heap always qualify.
 * The function might return false for joined blocks.
 */

{
    word_t *block;

    block = (word_t *) p - XM_OVERHEAD;

    return mem_is_freed(block, minsize + XM_OVERHEAD_SIZE);
} /* is_freed() */

#endif /* MALLOC_TRACE */

/*-------------------------------------------------------------------------*/
static void
print_block (int d, word_t *block)

/* Block <block> was recognized as lost - print it onto file <d>.
 * If possible, use the information in the dispatch_table, otherwise
 * print the first characters as they are.
 */

{
    word_t size;
    int i;

#ifdef MALLOC_TRACE
    char *file = (char *)block[XM_FILE];
    word_t line = block[XM_LINE];

    for (i = num_dispatched_types; --i >= 0; )
    {
        if (dispatch_table[i].file == file
         && dispatch_table[i].line == line)
        {
            (*dispatch_table[i].func)(d, (char *)(block+XM_OVERHEAD), 0);
            write(d, "\n", 1);
            return;
        }
    }
#endif

    /* Print a hexdump, but not more than 80 bytes */
    {
        int limit = 80;
        char * cp;

        size = mem_block_size(block) - XM_OVERHEAD;
        cp = (char *)(block + XM_OVERHEAD);

        while (size > 0 && limit > 0)
        {
            /* Start of line: print the address */
            dprintf1(d, "%x:", (p_int)cp);

            /* Print the up to 16 bytes after cp as hex values */
            for (i = 0; i < 16 && i < (int)size && i < limit; i++)
                dprintf1(d, " %X", cp[i]);

            /* Align foward to the character interpretation */
            for (; i < 16; i++)
                writes(d, "   ");

            writes(d, "  ");

            /* Print the same data as characters */
            for (i = 0; i < 16 && i < (int)size && i < limit; i++)
            {
                if (isprint((unsigned char)cp[i]))
                    write(d, cp+i, 1);
                else
                    writes(d, ".");
            }

            writes(d, "\n");

            cp += i;
            size -= i;
            limit -= i;
        }
    }

    writes(d, "\n");
} /* print_block() */

#endif /* GC_SUPPORT */

/*-------------------------------------------------------------------------*/
#ifdef MALLOC_LPC_TRACE

static void
write_lpc_trace (int d, word_t *p, int oneline)

/* Write the object and program which allocated the memory block <p>
 * onto file <d>.
 * if <oneline> is true, all information is printed in one line.
 */

{
    object_t *obj, *o;
    bytecode_p pc;
    program_t *prog;
    int line;
    int32 id;

    /* Try to find the object which allocated this block */
    if (!oneline)
    {
        if ( NULL != (obj = (object_t *)p[XM_OBJ]) )
        {
            writes(d, "  By object: ");
            if (obj->flags & O_DESTRUCTED)
                writes(d, "(destructed) ");
            for (o = obj_list; o && o != obj; o = o->next_all) NOOP;
            if (!o)
                writes(d, "(not in list) ");
            else if (o->name)
                writes(d, get_txt(o->name)); /* TODO: If this cores, it has to go again */
        }
        else
            writes(d, "  No object.");
        writes(d, "\n");
    }
    else
    {
        if ( NULL != (obj = (object_t *)p[XM_OBJ]) )
        {
            for (o = obj_list; o && o != obj; o = o->next_all) NOOP;
            if (!o || !o->name)
                writes(d, "?");
            else
                writes(d, get_txt(o->name)); /* TODO: If this cores, it has to go again */

            if (obj->flags & O_DESTRUCTED)
                writes(d, " (destructed)");
        }
        else
            writes(d, "-");
    }

    /* Try to find the program which allocated this block */
    if ( 0 != (id = p[XM_PROG]) )
    {
        pc = NULL;
        prog = NULL;

        for ( o = obj_list
            ;    o
              && !(o->flags & O_DESTRUCTED)
              && ((p_int)o->prog&1 || o->prog->id_number != id); )
            o = o->next_all;

        /* Unlikely, but possible: ids might have been renumbered. */
        if (o)
        {
            pc = (bytecode_p)p[XM_PC];
            prog = o->prog;
            if (prog->program > pc || pc > PROGRAM_END(*prog))
                o = NULL;
        }

        if (o)
        {
            string_t *file;

            line = get_line_number(pc, prog, &file);
            if (!oneline)
            {
                dprintf2(d, "  By program: %s line:%d\n", (p_int)get_txt(file), line);
            }
            else
            {
                dprintf2(d, " , %s %d", (p_int)get_txt(file), line);
            }

            if (file)
                free_mstring(file);
        }
        else
        {
            if (!oneline)
                writes(d, "  By program: Not found at old address.\n");
        }
    }

    if (oneline)
        writes(d, "\n");
} /* write_lpc_trace() */

#endif /* MALLOC_LPC_TRACE */

/*-------------------------------------------------------------------------*/
void
dump_lpc_trace (int d
               , void *p
#ifndef MALLOC_LPC_TRACE
                         UNUSED
#endif
               )

/* Write the object and program which allocated the memory block <p>
 * onto file <d>.
 * In contrast to write_lpc_trace(), the address of the memory block is
 * the one returned by xalloc(), ie. pointing after the memory block
 * header.
 */

{
#if defined(MALLOC_LPC_TRACE)
    write_lpc_trace(d, ((word_t *)p) - XM_OVERHEAD, MY_FALSE);
#else
#   ifdef __MWERKS__
#       pragma unused(p)
#   endif
    writes(d, "No malloc lpc trace.\n");
#endif /* MALLOC_LPC_TRACE */
} /* dump_lpc_trace() */

/*-------------------------------------------------------------------------*/
void
dump_malloc_trace (int d
                  , void *adr
#if !defined(MALLOC_TRACE) && !defined(MALLOC_LPC_TRACE)
                              UNUSED
#endif
                  )

/* Write the allocation information (file, linenumber, object and such) of
 * the memory block <adr> onto file <d>.
 * <adr> is the address returned by xalloc(), ie. pointing after the memory
 * block header.
 */

{
#if !defined(MALLOC_TRACE) && !defined(MALLOC_LPC_TRACE)
#   ifdef __MWERKS__
#       pragma unused(adr)
#   endif
    writes(d, "No malloc trace.\n");
#else
    word_t *p = ((word_t *)adr) - XM_OVERHEAD;

#    ifdef MALLOC_TRACE
        word_t size = mem_block_size(p);

        dprintf3(d, " %s %d size 0x%x\n",
                  p[XM_FILE], p[XM_LINE], size
                );
#    endif
#    ifdef MALLOC_LPC_TRACE
        write_lpc_trace(d, p, MY_FALSE);
#    endif
#endif
} /* dump_malloc_trace() */

/*=========================================================================*/

/*                     CLIB ALLOCATION FUNCTIONS                           */

#if defined(REPLACE_MALLOC) && (defined(SBRK_OK) || defined(HAVE_MMAP))

/*-------------------------------------------------------------------------*/
POINTER
malloc (size_t size)

/* Allocate an empty memory block of size <sizel>.
 * The memory is aligned and not subject to GC.
 */

{
    POINTER result = pxalloc(size);
    if (!result)
    {
        int save_privilege = malloc_privilege;
        malloc_privilege = MALLOC_SYSTEM;
        result = pxalloc(size);
        malloc_privilege = save_privilege;
    }
    
    if (result)
    {
        count_up(&clib_alloc_stat, xalloced_size(result) + mem_overhead());
    }

    return result;
} /* malloc() */

/*-------------------------------------------------------------------------*/
FREE_RETURN_TYPE
free (POINTER ptr)

/* Free the memory block <ptr> which was allocated with malloc().
 */

{
    if (ptr)
    {
        count_back(&clib_alloc_stat, xalloced_size(ptr) + mem_overhead());
    }

    pfree(ptr);
    FREE_RETURN
} /* free() */

/*-------------------------------------------------------------------------*/
POINTER
calloc (size_t nelem, size_t sizel)

/* Allocate an empty memory block for <nelem> objects of size <sizel>.
 * The memory is aligned and not subject to GC.
 */

{
    char *p;

    if (nelem == 0 || sizel == 0)
        return NULL;
    p = malloc(nelem * sizel);
    if (p == NULL)
        return NULL;
    memset(p, '\0', nelem * sizel);
    return p;
} /* calloc() */

/*-------------------------------------------------------------------------*/
POINTER
realloc (POINTER p, size_t size)

/* Reallocate block <p> to the new size of <size> and return the pointer.
 * The memory is aligned and not subject to GC.
 */

{
   size_t old_size;
   POINTER t;

   if (!p)
        return malloc(size);

   old_size = xalloced_size(p) - XM_OVERHEAD_SIZE; // usable size

   if (old_size >= size)
      return p;

   t = malloc(size);
   if (t == NULL)
       return NULL;

   memcpy(t, p, old_size);
   free(p);

   return t;
} /* realloc() */

#endif /* REPLACE_MALLOC */

/* ======================================================================= */

/*-------------------------------------------------------------------------*/
void
get_stack_direction (void)

/* Find the direction of the stackgrowth and store the result (+1 or -1)
 * into the global stack_direction.
 */

{
    char local;  /* to get stack address */

    if (initial_stack == NULL)  /* initial call */
    {
        initial_stack = &local;
        get_stack_direction ();  /* recurse once */
    }
    else  /* recursive call */
    if (&local > initial_stack)
        stack_direction = 1;    /* stack grew upward */
    else
        stack_direction = -1;   /* stack grew downward */
} /* get_stack_direction() */

/*-------------------------------------------------------------------------*/
void
assert_stack_gap (void)

/* Test if the stack is far enough away from the heap area and throw
 * an error if not.
 */

{
    static enum { Initial, Normal, Error, Fatal } condition = Initial;
    char * stack_start, * stack_end;
    ptrdiff_t gap;
    char local; /* used to yield a stack address */

    /* Don't check the gap after a Fatal error or if the system is
     * not fully initialised yet.
     */
    if (stack_direction == 0 || condition == Fatal || heap_end == NULL)
        return;

    /* On the first call, test if checking the gap actually makes sense.
     * If the stack-gap check is not necessary, the 'condition' will be set to
     * Fatal, otherwise the check will be enabled by setting condition to
     * 'Normal'.
     */
    if (condition == Initial)
    {
        /* Currently there are no limitations on checking the heap/stack gap.
         */
        condition = Normal;
    }

    /* Determine the stack limits */
    if (stack_direction < 0)
    {
        stack_start = &local;
        stack_end = initial_stack;
    }
    else
    {
        stack_start = initial_stack;
        stack_end = &local;
    }

    /* Check if the heap and stack overlap */

    if ((stack_end > (char *)heap_end && stack_start < (char *)heap_end)
     || (stack_end > (char *)heap_start && stack_start < (char *)heap_start)
       )
    {
        if (condition != Fatal)
        {
            condition = Fatal;
            fatal("Out of memory: Stack (%p..%p) overlaps heap (%p..%p).\n"
                 , stack_start, stack_end, heap_start, heap_end);
            /* NOTREACHED */
        }
        return; /* else: Recursive call during fatal() handling */
    }

    /* Check if the stack grows towards the heap. If it doesn't
     * we don't have to worry about the gap.
     */
    if ((stack_direction > 0) ? (stack_start > (char *)heap_end)
                              : (stack_end < (char *)heap_start)
       )
    {
        /* No worries about the gap */
        condition = Normal;
        return;
    }

    /* Heap and stack may overlap - do the normal gap checking.
     * Note that on machines with big address spaces the computation
     * may overflow.
     */
    if (stack_direction < 0)
        gap = (char *)stack_start - (char *)heap_end;
    else
        gap = (char *)heap_start - stack_end;

    if (gap < 0)
        gap = HEAP_STACK_GAP + 1;

    /* If the gap is big enough, mark that condition and return */
    if (gap >= HEAP_STACK_GAP)
    {
        condition = Normal;
        return;
    }

    /* The gap is too small.
     * Throw an error only if the condition was normal before,
     * otherwise the error handling would again get an error.
     */
    if (condition == Normal)
    {
        condition = Error;
        errorf("Out of memory: Gap between stack and heap: %ld.\n"
             , (long)gap);
        /* NOTREACHED */
    }
} /* assert_stack_gap() */

/*-------------------------------------------------------------------------*/
void
reserve_memory (void)

/* Reserve the memory blocks according to reserved_xxx_area, and the
 * min_{small}_malloced limits.
 */

{
    void * ptr;

    /* First, check if max_malloced is a sensible value.
     * We overestimate the requirement a bit...
     */
    if (max_malloced > 0)
    {
        mp_int required_mem = 0;
        mp_int required_reserve = 0;

        if (reserved_user_size > 0) required_reserve += reserved_user_size;
        if (reserved_master_size > 0) required_reserve += reserved_master_size;
        if (reserved_system_size > 0) required_reserve += reserved_system_size;

        if (min_malloced > 0)
        {
            if (min_malloced > required_reserve)
                required_mem += min_malloced;
            else
                required_mem += required_reserve + min_malloced;
        }
        else
            required_mem += required_reserve;

        if (min_small_malloced > 0) required_mem += min_small_malloced;

        if (max_malloced < required_mem)
        {
            printf("%s max_malloced is %ld bytes, "
                   "but driver requires %ld bytes.\n"
                  , time_stamp(), (long)max_malloced, (long)required_mem
                  );
            debug_message("%s max_malloced is %ld bytes, "
                          "but driver requires %ld bytes.\n"
                         , time_stamp(), (long)max_malloced, (long)required_mem
                         );
            max_malloced = required_mem;
        }
    }

    if (min_malloced > 0)
    {
        ptr = xalloc(min_malloced);

        if (ptr)
            xfree(ptr);
        else
        {
            printf("%s Failed to allocate MIN_MALLOCED block of %ld bytes.\n"
                  , time_stamp(), (long)min_malloced);
            debug_message("%s Failed to allocate MIN_MALLOCED block of %ld bytes.\n"
                  , time_stamp(), (long)min_malloced);
        }
    }

#ifdef MALLOC_smalloc

    if (min_small_malloced > 0)
        small_chunk_size = min_small_malloced;

#endif /* MALLOC_smalloc */

    if (reserved_system_size > 0)
    {
        reserved_system_area = xalloc((size_t)reserved_system_size);
        if (reserved_system_area == NULL)
        {
            printf("%s Failed to allocate system reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_system_area);
            debug_message("%s Failed to allocate system reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_system_area);
        }
    }
    if (reserved_master_size > 0)
    {
        reserved_master_area = xalloc((size_t)reserved_master_size);
        if (reserved_master_area == NULL)
        {
            printf("%s Failed to allocate master reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_master_area);
            debug_message("%s Failed to allocate master reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_master_area);
        }
    }
    if (reserved_user_size > 0)
    {
        reserved_user_area = xalloc((size_t)reserved_user_size);
        if (reserved_user_area == NULL)
        {
            printf("%s Failed to allocate user reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_user_area);
            debug_message("%s Failed to allocate user reserve of %ld bytes.\n"
                  , time_stamp(), (long)reserved_user_area);
        }
    }
} /* reserve_memory() */

/*-------------------------------------------------------------------------*/
void
reallocate_reserved_areas (void)

/* Try to reallocate the reserved memory areas. If this is possible,
 * a pending slow-shutdown is canceled and the out_of_memory flag is reset.
 */

{
    char *p;
    malloc_privilege = MALLOC_USER;
    if (reserved_system_size && !reserved_system_area) {
        if ( !(reserved_system_area = xalloc((size_t)reserved_system_size)) ) {
            slow_shut_down_to_do = 1;
            return;
        }
        else {
            p = "Reallocated System reserve.\n";
            write(1, p, strlen(p));
        }
    }
    if (reserved_master_size && !reserved_master_area) {
        if ( !(reserved_master_area = xalloc((size_t)reserved_master_size)) ) {
            slow_shut_down_to_do = 1;
            return;
        }
        else {
            p = "Reallocated Master reserve.\n";
            write(1, p, strlen(p));
        }
    }
    if (reserved_user_size && !reserved_user_area) {
        if ( !(reserved_user_area = xalloc((size_t)reserved_user_size)) ) {
            slow_shut_down_to_do = 6;
            return;
        }
        else {
            p = "Reallocated User reserve.\n";
            write(1, p, strlen(p));
        }
    }
    slow_shut_down_to_do = 0;
    out_of_memory = MY_FALSE;
} /* reallocate_reserved_areas() */

/*-------------------------------------------------------------------------*/
char *
string_copy_traced (const char *str MTRACE_DECL)

/* string_copy() acts like strdup() with the additional bonus that it can
 * trace file/line of the calling place if MALLOC_TRACE is defined.
 */

{
    char *p;
    size_t len;

    len = strlen(str)+1;
    memsafe(p = xalloc_traced(len MTRACE_PASS), len, "string_copy");
    if (p)
    {
        (void)memcpy(p, str, len);
    }
    return p;
} /* string_copy_traced() */

/*-------------------------------------------------------------------------*/
void
notify_lowmemory_condition(enum memory_limit_types what)
/* Calls low_memory(what, <limit>, <memory_consumption>, <reserves> ) 
 * in the master.
 */
{
    short reservestate = 0;
    push_number(inter_sp, what);
    if (what == SOFT_MALLOC_LIMIT_EXCEEDED)
        push_number(inter_sp, soft_malloc_limit);
    else if (what == HARD_MALLOC_LIMIT_EXCEEDED)
        push_number(inter_sp, max_malloced);
    else
        push_number(inter_sp, 0);
    push_number(inter_sp, xalloc_stat.size);
    if (reserved_user_area)
        reservestate |= USER_RESERVE_AVAILABLE;
    if (reserved_master_area)
        reservestate |= MASTER_RESERVE_AVAILABLE;
    if (reserved_system_area)
        reservestate |= SYSTEM_RESERVE_AVAILABLE;
    push_number(inter_sp, reservestate);
    callback_master(STR_LOW_MEMORY, 4);
} /* notify_lowmemory_condition */

/*-------------------------------------------------------------------------*/
void
check_for_soft_malloc_limit (void)
/* If soft_malloc_limit is set, check if the allocated memory exceeds it.
 * If yes and the master was not notified until now, low_memory() is called 
 * in the mudlib master and low_memory_applied ist set to prevent any further
 * notifications.
 * If the limit is not exceeded, low_memory_applied is reset.
 * Should be called from the backend.
 */
{
#ifndef NO_MEM_BLOCK_SIZE
    if (soft_malloc_limit > 0)
    {
        // check first, if the soft limit is > than the hard limit and issue
        // a debug message. (Gnomi wanted to check this here to be able to set
        // a soft limit before a hard limit without error.)
        if (soft_malloc_limit >= max_malloced)
        {
            debug_message("%s The soft memory limit (%"PRIdMPINT") is bigger "
                          "than the hard memory limit (%"PRIdMPINT") - "
                          "disabling the soft limit!",
                          time_stamp(), soft_malloc_limit, max_malloced);
            soft_malloc_limit = 0;
        }
        else if ((mp_int)xalloc_stat.size > soft_malloc_limit)
        {
            if (!low_memory_applied)
            {
                /* call low_memory(malloced_memory) in the master but first
                 * set the flag to prevent calling every backend cycle in case
                 * of errors. */
                low_memory_applied = MY_TRUE;
                notify_lowmemory_condition(SOFT_MALLOC_LIMIT_EXCEEDED);
            }
        }
        else if (low_memory_applied)
        {
            /* OK, memory consumption shrunk below the soft limit. Reset the 
             * warning, so that the next time the limit is exceeded,
             * the master apply is done again. */
            low_memory_applied = MY_FALSE;
        }
    }
#endif
} /* check_for_soft_malloc_limit() */

/*-------------------------------------------------------------------------*/
Bool
set_memory_limit(enum memory_limit_types what, mp_int limit)
/* Sets the <what> memory limit to <limit>.
 * <limit> for SOFT_MLIMIT has to be < HARD_MLIMIT.
 * return TRUE on success and FALSE otherwise.
 * If the current memory allocation is smaller than a new soft limit, the flag
 * low_memory_applied is reset.
 */
{
#ifndef NO_MEM_BLOCK_SIZE
    // limits smaller than the sum of reserves (and some more) are harmful and
    // lead anyway to immediate crashes... But we ignore this here, because
    // reserve_memory() will deal with the needed sizes for the reserves and the
    // minimum allocations. And later on in the game we will just prevent to
    // set the limit smaller then the already allocated memory.
    
    if (what == MALLOC_SOFT_LIMIT)
    {
        if (limit < 0)
            return MY_FALSE;
        
        soft_malloc_limit = limit;
        // reset flag if appropriate.
        if ((mp_int)xalloc_stat.size < soft_malloc_limit
            && low_memory_applied)
            low_memory_applied = MY_FALSE;
    }
    else 
    {
        // setting the limit below the currently allocated memory seems to be 
        // a very bad idea.
        if (limit && limit <= (mp_int)xalloc_stat.size)
            return MY_FALSE;
        
        max_malloced = limit;
    }
    return MY_TRUE;
#else
    return MY_FALSE;
#endif // NO_MEM_BLOCK_SIZE
} /* set_memory_limit */

/*-------------------------------------------------------------------------*/
mp_int
get_memory_limit(enum memory_limit_types what)
/* Return the value of limit <what>. */
{
#ifndef NO_MEM_BLOCK_SIZE
    if (what == MALLOC_SOFT_LIMIT)
        return soft_malloc_limit;
    else
        return max_malloced;
#else
    return 0;
#endif
}
/***************************************************************************/