/*
 * Copyright (C) 1997-2004, Michael Jennings
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * @file mem.c
 * Memory Management Subsystem Source File
 *
 * This file contains the memory management subsystem.
 *
 * @author Michael Jennings <mej@eterm.org>
 * $Revision: 51650 $
 * $Date: 2010-08-26 01:34:13 +0000 (Thu, 26 Aug 2010) $
 */

static const char __attribute__((unused)) cvs_ident[] = "$Id: mem.c 51650 2010-08-26 01:34:13Z lucas $";

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "libast_internal.h"

static void memrec_add_var(memrec_t *, const spif_charptr_t, unsigned long, void *, size_t);
static ptr_t *memrec_find_var(memrec_t *, const void *);
static void memrec_rem_var(memrec_t *, const spif_charptr_t, const spif_charptr_t, unsigned long, const void *);
static void memrec_chg_var(memrec_t *, const spif_charptr_t, const spif_charptr_t, unsigned long, const void *, void *, size_t);
static void memrec_dump_pointers(memrec_t *);
static void memrec_dump_resources(memrec_t *);

#if MALLOC_CALL_DEBUG
/*@{*/
/**
 * @name Memory Management Call Tracking
 * Count calls to memory management functions.
 *
 * This group of variables is used to count calls to the memory
 * management functions.  Call counting is controlled by the
 * #MALLOC_CALL_DEBUG symbol, and is off by default.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink
 * @ingroup DOXGRP_MEM
 */

/** Count calls to MALLOC().  Count calls to MALLOC(). */
static int malloc_count = 0;
/** Count calls to CALLOC().  Count calls to CALLOC(). */
static int calloc_count = 0;
/** Count calls to REALLOC().  Count calls to REALLOC(). */
static int realloc_count = 0;
/** Count calls to FREE().  Count calls to FREE(). */
static int free_count = 0;
/*@}*/
#endif

/**
 * Allocated pointers.
 *
 * This structure keeps track of the pointer array which represents
 * pointers allocated via the memory management interface.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, memrec_t_struct
 * @ingroup DOXGRP_MEM
 */
static memrec_t malloc_rec;
/**
 * Allocated pixmaps.
 *
 * This structure keeps track of the pixmap array which represents
 * pixmaps allocated via the memory management interface.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, memrec_t_struct
 * @ingroup DOXGRP_MEM
 */
static memrec_t pixmap_rec;
/**
 * Allocated GC's.
 *
 * This structure keeps track of the GC array which represents
 * X11 Graphics Context objects, or GC's, allocated via the memory
 * management interface.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, memrec_t_struct
 * @ingroup DOXGRP_MEM
 */
static memrec_t gc_rec;

/**
 * Initialize memory management system.
 *
 * A call to this function must occur before any other part of the
 * memory management system is used.  This function initializes the
 * pointer lists.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink
 * @ingroup DOXGRP_MEM
 */
void
spifmem_init(void)
{
    D_MEM(("Constructing memory allocation records\n"));
    malloc_rec.ptrs = (ptr_t *) malloc(sizeof(ptr_t));
    pixmap_rec.ptrs = (ptr_t *) malloc(sizeof(ptr_t));
    gc_rec.ptrs = (ptr_t *) malloc(sizeof(ptr_t));
}

/**
 * Add a variable to a record set.
 *
 * This is the static, internal-use-only function that does the actual
 * work of recording information on a variable to be tracked.  This
 * information includes file and line number information and is stored
 * as a #ptr_t.
 *
 * @param memrec   Address of the #memrec_t we're adding to.
 * @param filename The filename where the variable was allocated.
 * @param line     The line number of @a filename where the variable
 *                 was allocated.
 * @param ptr      The allocated variable.
 * @param size     The number of bytes requested.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC(), libast_malloc()
 * @ingroup DOXGRP_MEM
 */
static void
memrec_add_var(memrec_t *memrec, const spif_charptr_t filename, unsigned long line, void *ptr, size_t size)
{
    register ptr_t *p;

    ASSERT(memrec != NULL);
    memrec->cnt++;
    if (!(memrec->ptrs = (ptr_t *)realloc(memrec->ptrs, sizeof(ptr_t) * memrec->cnt))) {
        D_MEM(("Unable to reallocate pointer list -- %s\n", strerror(errno)));
    }
    p = memrec->ptrs + memrec->cnt - 1;
    D_MEM(("Adding variable (%10p, %lu bytes) from %s:%lu.\n", ptr, size, filename, line));
    D_MEM(("Storing as pointer #%lu at %10p (from %10p).\n", memrec->cnt, p, memrec->ptrs));
    p->ptr = ptr;
    p->size = size;
    spiftool_safe_strncpy(p->file, (const spif_charptr_t) filename, LIBAST_FNAME_LEN);
    p->file[LIBAST_FNAME_LEN] = 0;
    p->line = line;
}

/**
 * Find a variable within a record set.
 *
 * This function searches through the pointer list of the specified
 * @a memrec object for a given pointer.
 *
 * @param memrec Address of the #memrec_t we're searching.
 * @param ptr    The value of the requested pointer.
 * @return       A pointer to the #ptr_t object within @a memrec
 *               that matches @a ptr, or NULL if not found.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC(), libast_malloc()
 * @ingroup DOXGRP_MEM
 */
static ptr_t *
memrec_find_var(memrec_t *memrec, const void *ptr)
{
    register ptr_t *p;
    register unsigned long i;

    ASSERT_RVAL(memrec != NULL, NULL);
    REQUIRE_RVAL(ptr != NULL, NULL);

    for (i = 0, p = memrec->ptrs; i < memrec->cnt; i++, p++) {
        if (p->ptr == ptr) {
            D_MEM(("Found pointer #%lu stored at %10p (from %10p)\n", i + 1, p, memrec->ptrs));
            return p;
        }
    }
    return NULL;
}

/**
 * Remove a variable from a record set.
 *
 * This is the static, internal-use-only function that does the actual
 * work of freeing recorded information for a deleted pointer.
 *
 * @param memrec   Address of the #memrec_t we're removing from.
 * @param var      The variable name being freed (for diagnostic
 *                 purposes only).
 * @param filename The filename where the variable was freed.
 * @param line     The line number of @a filename where the variable
 *                 was freed.
 * @param ptr      The freed variable.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, FREE(), libast_free()
 * @ingroup DOXGRP_MEM
 */
static void
memrec_rem_var(memrec_t *memrec, const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, const void *ptr)
{
    register ptr_t *p;

    ASSERT(memrec != NULL);
    USE_VAR(var);
    USE_VAR(filename);
    USE_VAR(line);

    if (!(p = memrec_find_var(memrec, ptr))) {
        D_MEM(("ERROR:  File %s, line %d attempted to free variable %s (%10p) which was not allocated with MALLOC/REALLOC\n",
               filename, line, var, ptr));
        return;
    }
    D_MEM(("Removing variable %s (%10p) of size %lu\n", var, ptr, p->size));
    if ((--memrec->cnt) > 0) {
        memmove(p, p + 1, sizeof(ptr_t) * (memrec->cnt - (p - memrec->ptrs)));
        memrec->ptrs = (ptr_t *) realloc(memrec->ptrs, sizeof(ptr_t) * memrec->cnt);
    }
}

/**
 * Resize a variable in a record set.
 *
 * This is the static, internal-use-only function that does the actual
 * work of altering information on a tracked variable.
 *
 * @param memrec   Address of the #memrec_t we're modifying.
 * @param var      The variable name being resized (for diagnostic
 *                 purposes only).
 * @param filename The filename where the variable was resized.
 * @param line     The line number of @a filename where the variable
 *                 was resized.
 * @param oldp     The old value of the pointer.
 * @param newp     The new value of the pointer.
 * @param size     The new size in bytes.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, REALLOC(), libast_realloc()
 * @ingroup DOXGRP_MEM
 */
static void
memrec_chg_var(memrec_t *memrec, const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, const void *oldp, void *newp, size_t size)
{
    register ptr_t *p;

    ASSERT(memrec != NULL);
    USE_VAR(var);

    if (!(p = memrec_find_var(memrec, oldp))) {
        D_MEM(("ERROR:  File %s, line %d attempted to realloc variable %s (%10p) which was not allocated with MALLOC/REALLOC\n", filename,
               line, var, oldp));
        return;
    }
    D_MEM(("Changing variable %s (%10p, %lu -> %10p, %lu)\n", var, oldp, p->size, newp, size));
    p->ptr = newp;
    p->size = size;
    spiftool_safe_strncpy(p->file, (const spif_charptr_t) filename, LIBAST_FNAME_LEN);
    p->line = line;
}

/**
 * Dump listing of tracked pointers.
 *
 * This function dumps a listing of all pointers in @a memrec along
 * with the filename, line number, address, size, and contents for
 * each.  Contents are displayed in both hex and ASCII, the latter
 * having non-printable characters replaced with periods ('.').
 *
 * @param memrec Address of the #memrec_t we're dumping.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC_DUMP(), libast_dump_mem_tables()
 * @ingroup DOXGRP_MEM
 */
static void
memrec_dump_pointers(memrec_t *memrec)
{
    register ptr_t *p;
    unsigned long i, j, k, l, total = 0;
    unsigned long len;
    spif_char_t buff[9];

    ASSERT(memrec != NULL);
    fprintf(LIBAST_DEBUG_FD, "PTR:  %lu pointers stored.\n", (unsigned long) memrec->cnt);
    fprintf(LIBAST_DEBUG_FD,
            "PTR:   Pointer |       Filename       |  Line  |  Address |  Size  | Offset  | 00 01 02 03 04 05 06 07 |  ASCII  \n");
    fprintf(LIBAST_DEBUG_FD,
            "PTR:  ---------+----------------------+--------+----------+--------+---------+-------------------------+---------\n");
    fflush(LIBAST_DEBUG_FD);
    len = sizeof(ptr_t) * memrec->cnt;
    memset(buff, 0, sizeof(buff));

    /* First, dump the contents of the memrec->ptrs[] array. */
    for (p = memrec->ptrs, j = 0; j < len; j += 8) {
        fprintf(LIBAST_DEBUG_FD, "PTR:   %07lu | %20s | %6lu | %10p | %06lu | %07x | ",
                (unsigned long) 0, "", (unsigned long) 0,
                (spif_ptr_t) memrec->ptrs,
                (unsigned long) (sizeof(ptr_t) * memrec->cnt), (unsigned int) j);
        /* l is the number of characters we're going to output */
        l = ((len - j < 8) ? (len - j) : (8));
        /* Copy l bytes (up to 8) from memrec->ptrs[] (p) to buffer */
        memcpy(buff, ((spif_charptr_t) p) + j, l);
        buff[l] = 0;
        for (k = 0; k < l; k++) {
            fprintf(LIBAST_DEBUG_FD, "%02x ", buff[k]);
        }
        /* If we printed less than 8 bytes worth, pad with 3 spaces per byte */
        for (; k < 8; k++) {
            fprintf(LIBAST_DEBUG_FD, "   ");
        }
        /* Finally, print the printable ASCII string for those l bytes */
        fprintf(LIBAST_DEBUG_FD, "| %-8s\n", spiftool_safe_str((spif_charptr_t) buff, l));
        /* Flush after every line in case we crash */
        fflush(LIBAST_DEBUG_FD);
    }

    /* Now print out each pointer and its contents. */
    for (i = 0; i < memrec->cnt; p++, i++) {
        /* Add this pointer's size to our total */
        total += p->size;
        for (j = 0; j < p->size; j += 8) {
            fprintf(LIBAST_DEBUG_FD, "PTR:   %07lu | %20s | %6lu | %10p | %06lu | %07x | ",
                    i + 1, NONULL(p->file), (unsigned long) p->line,
                    p->ptr, (unsigned long) p->size, (unsigned) j);
            /* l is the number of characters we're going to output */
            l = ((p->size - j < 8) ? (p->size - j) : (8));
            /* Copy l bytes (up to 8) from p->ptr to buffer */
            memcpy(buff, ((spif_charptr_t) p->ptr) + j, l);
            buff[l] = 0;
            for (k = 0; k < l; k++) {
                fprintf(LIBAST_DEBUG_FD, "%02x ", buff[k]);
            }
            /* If we printed less than 8 bytes worth, pad with 3 spaces per byte */
            for (; k < 8; k++) {
                fprintf(LIBAST_DEBUG_FD, "   ");
            }
            /* Finally, print the printable ASCII string for those l bytes */
            fprintf(LIBAST_DEBUG_FD, "| %-8s\n", spiftool_safe_str(buff, l));
            /* Flush after every line in case we crash */
            fflush(LIBAST_DEBUG_FD);
        }
    }
    fprintf(LIBAST_DEBUG_FD, "PTR:  Total allocated memory: %10lu bytes\n", total);
    fflush(LIBAST_DEBUG_FD);
}

/**
 * Dump listing of tracked resources.
 *
 * This function is very similar to memrec_dump_pointers() but is
 * intended for use with non-pointer data.
 *
 * @param memrec Address of the #memrec_t we're dumping.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC_DUMP(), libast_dump_mem_tables(),
 *      memrec_dump_pointers()
 * @ingroup DOXGRP_MEM
 */
static void
memrec_dump_resources(memrec_t *memrec)
{
    register ptr_t *p;
    unsigned long i, total;
    unsigned long len;

    ASSERT(memrec != NULL);
    len = memrec->cnt;
    fprintf(LIBAST_DEBUG_FD, "RES:  %lu resources stored.\n",
            (unsigned long) memrec->cnt);
    fprintf(LIBAST_DEBUG_FD, "RES:   Index | Resource ID |       Filename       |  Line  |  Size  \n");
    fprintf(LIBAST_DEBUG_FD, "RES:  -------+-------------+----------------------+--------+--------\n");
    fflush(LIBAST_DEBUG_FD);

    for (p = memrec->ptrs, i = 0, total = 0; i < len; i++, p++) {
        total += p->size;
        fprintf(LIBAST_DEBUG_FD, "RES:   %5lu |  0x%08lx | %20s | %6lu | %6lu\n",
                i, (unsigned long) p->ptr, NONULL(p->file),
                (unsigned long) p->line,
                (unsigned long) p->size);
        /* Flush after every line in case we crash */
        fflush(LIBAST_DEBUG_FD);
    }
    fprintf(LIBAST_DEBUG_FD, "RES:  Total size: %lu bytes\n", (unsigned long) total);
    fflush(LIBAST_DEBUG_FD);
}

/******************** MEMORY ALLOCATION INTERFACE ********************/

/**
 * LibAST implementation of malloc().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * MALLOC() macro are routed here.  The macro allows filename and line
 * number information to be provided thanks to the __FILE__ and
 * __LINE__ symbols pre-defined by cpp.
 *
 * @param filename The filename where the variable is being
 *                 allocated.
 * @param line     The line number of @a filename where the variable
 *                 is being allocated.
 * @param size     The requested size in bytes (as passed to MALLOC()).
 * @return         A pointer to the newly-allocated memory.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC()
 * @ingroup DOXGRP_MEM
 */
void *
spifmem_malloc(const spif_charptr_t filename, unsigned long line, size_t size)
{
    void *temp;

#if MALLOC_CALL_DEBUG
    ++malloc_count;
    if (!(malloc_count % MALLOC_MOD)) {
        fprintf(LIBAST_DEBUG_FD, "Calls to malloc(): %d\n", malloc_count);
    }
#endif

    D_MEM(("%lu bytes requested at %s:%lu\n", size, NONULL(filename), line));

    temp = (void *) malloc(size);
    ASSERT_RVAL(!SPIF_PTR_ISNULL(temp), (spif_ptr_t) NULL);
    if (DEBUG_LEVEL >= DEBUG_MEM) {
        memrec_add_var(&malloc_rec, (spif_charptr_t) NONULL(filename), line, temp, size);
    }
    return (temp);
}

/**
 * LibAST implementation of realloc().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * REALLOC() macro are routed here.  The macro allows variable name,
 * filename, and line number information to be provided.
 *
 * @param var      The variable name being resized.
 * @param filename The filename where the variable is being
 *                 reallocated.
 * @param line     The line number of @a filename where the variable
 *                 is being reallocated.
 * @param ptr      The old value of the pointer being resized.
 * @param size     The new requested size in bytes (as passed to
 *                 REALLOC()). 
 * @return         The new value (possibly moved) of the pointer.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, REALLOC()
 * @ingroup DOXGRP_MEM
 */
void *
spifmem_realloc(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, void *ptr, size_t size)
{
    void *temp;

#if MALLOC_CALL_DEBUG
    ++realloc_count;
    if (!(realloc_count % REALLOC_MOD)) {
        D_MEM(("Calls to realloc(): %d\n", realloc_count));
    }
#endif

    D_MEM(("Variable %s (%10p -> %lu) at %s:%lu\n", var, ptr, (unsigned long) size, NONULL(filename), line));
    if (!ptr) {
        temp = (void *) spifmem_malloc(filename, line, size);
    } else if (size == 0) {
        spifmem_free(var, filename, line, ptr);
        temp = NULL;
    } else {
        temp = (void *) realloc(ptr, size);
        ASSERT_RVAL(!SPIF_PTR_ISNULL(temp), (spif_ptr_t) NULL);
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            memrec_chg_var(&malloc_rec, var, (spif_charptr_t) NONULL(filename), line, ptr, temp, size);
        }
    }
    return (temp);
}

/**
 * LibAST implementation of calloc().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * CALLOC() macro are routed here.  The macro allows filename and line
 * number information to be provided thanks to the __FILE__ and
 * __LINE__ symbols pre-defined by cpp.
 *
 * @param filename The filename where the variable is being
 *                 allocated.
 * @param line     The line number of @a filename where the variable
 *                 is being allocated.
 * @param count    The number of objects being allocated.
 * @param size     The size in bytes of each object (as passed to
 *                 CALLOC()).
 * @return         A pointer to the newly-allocated, zeroed memory.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, CALLOC()
 * @ingroup DOXGRP_MEM
 */
void *
spifmem_calloc(const spif_charptr_t filename, unsigned long line, size_t count, size_t size)
{
    void *temp;

#if MALLOC_CALL_DEBUG
    ++calloc_count;
    if (!(calloc_count % CALLOC_MOD)) {
        fprintf(LIBAST_DEBUG_FD, "Calls to calloc(): %d\n", calloc_count);
    }
#endif

    D_MEM(("%lu units of %lu bytes each requested at %s:%lu\n", count, size, NONULL(filename), line));
    temp = (void *) calloc(count, size);
    ASSERT_RVAL(!SPIF_PTR_ISNULL(temp), (spif_ptr_t) NULL);
    if (DEBUG_LEVEL >= DEBUG_MEM) {
        memrec_add_var(&malloc_rec, (spif_charptr_t) NONULL(filename), line, temp, size * count);
    }
    return (temp);
}

/**
 * LibAST implementation of free().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * FREE() macro are routed here.  The macro allows variable name,
 * filename, and line number information to be provided.
 *
 * @param var      The variable name being freed.
 * @param filename The filename where the variable is being freed.
 * @param line     The line number of @a filename where the variable
 *                 is being freed.
 * @param ptr      The pointer being freed (as passed to FREE()).
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, FREE()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_free(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, void *ptr)
{
#if MALLOC_CALL_DEBUG
    ++free_count;
    if (!(free_count % FREE_MOD)) {
        fprintf(LIBAST_DEBUG_FD, "Calls to free(): %d\n", free_count);
    }
#endif

    D_MEM(("Variable %s (%10p) at %s:%lu\n", var, ptr, NONULL(filename), line));
    if (ptr) {
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            memrec_rem_var(&malloc_rec, var, (spif_charptr_t) NONULL(filename), line, ptr);
        }
        free(ptr);
    } else {
        D_MEM(("ERROR:  Caught attempt to free NULL pointer\n"));
    }
}

/**
 * LibAST implementation of strdup().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * STRDUP() macro are routed here.  The macro allows variable name,
 * filename, and line number information to be provided.
 *
 * @param var      The variable name being duplicated.
 * @param filename The filename where the variable is being duplicated.
 * @param line     The line number of @a filename where the variable
 *                 is being duplicated.
 * @param str      The string being duplicated (as passed to STRDUP()).
 * @return         A pointer to a newly-allocated copy of @a str.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, STRDUP()
 * @ingroup DOXGRP_MEM
 */
spif_charptr_t 
spifmem_strdup(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, const spif_charptr_t str)
{
    register spif_charptr_t newstr;
    register size_t len;

    ASSERT_RVAL(!SPIF_PTR_ISNULL(str), (spif_charptr_t) NULL);
    USE_VAR(var);
    D_MEM(("Variable %s (%10p) at %s:%lu\n", var, str, NONULL(filename), line));

    len = strlen((char *) str) + 1;      /* Copy NUL byte also */
    newstr = (spif_charptr_t) spifmem_malloc((spif_charptr_t) NONULL(filename), line, len);
    ASSERT_RVAL(!SPIF_PTR_ISNULL(newstr), (spif_ptr_t) NULL);
    strcpy((char *) newstr, (char *) str);
    return (newstr);
}

/**
 * Dump listing of tracked pointers.
 *
 * This function simply calls memrec_dump_pointers() and passes in the
 * address of the #malloc_rec variable.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, MALLOC_DUMP(), memrec_dump_pointers()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_dump_mem_tables(void)
{
    fprintf(LIBAST_DEBUG_FD, "Dumping memory allocation table:\n");
    memrec_dump_pointers(&malloc_rec);
}

#if LIBAST_X11_SUPPORT

/******************** PIXMAP ALLOCATION INTERFACE ********************/

/**
 * LibAST implementation of XCreatePixmap().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * X_CREATE_PIXMAP() macro are routed here.  The macro allows filename
 * and line number information to be provided thanks to the __FILE__
 * and __LINE__ symbols pre-defined by cpp.
 *
 * @param filename The filename where the pixmap is being created.
 * @param line     The line number of @a filename where the pixmap is
 *                 being created.
 * @param d        The Display for the new pixmap.
 * @param win      The Drawable for the new pixmap.
 * @param w        Width of the pixmap, in pixels.
 * @param h        Height of the pixmap, in pixels.
 * @param depth    The color depth for the new pixmap.
 * @return         A newly-created Pixmap.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, X_CREATE_PIXMAP()
 * @ingroup DOXGRP_MEM
 */
Pixmap
spifmem_x_create_pixmap(const spif_charptr_t filename, unsigned long line, Display * d, Drawable win, unsigned int w, unsigned int h,
                       unsigned int depth)
{
    Pixmap p;

    p = XCreatePixmap(d, win, w, h, depth);
    D_MEM(("Created %ux%u pixmap 0x%08x of depth %u for window 0x%08x at %s:%lu\n", w, h, p, depth, win, NONULL(filename), line));
    ASSERT_RVAL(p != None, None);
    if (DEBUG_LEVEL >= DEBUG_MEM) {
        memrec_add_var(&pixmap_rec, (spif_charptr_t) NONULL(filename), line, (void *) p, w * h * (depth / 8));
    }
    return (p);
}

/**
 * LibAST implementation of XFreePixmap().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * X_FREE_PIXMAP() macro are routed here.  The macro allows variable
 * name, filename, and line number information to be provided.
 *
 * @param var      The variable name of the pixmap being freed.
 * @param filename The filename where the pixmap is being freed.
 * @param line     The line number of @a filename where the pixmap is
 *                 being freed.
 * @param d        The Display for the pixmap.
 * @param p        The Pixmap to be freed.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, X_FREE_PIXMAP()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_x_free_pixmap(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, Display * d, Pixmap p)
{
    D_MEM(("Freeing pixmap %s (0x%08x) at %s:%lu\n", var, p, NONULL(filename), line));
    if (p) {
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            memrec_rem_var(&pixmap_rec, var, (spif_charptr_t) NONULL(filename), line, (void *) p);
        }
        XFreePixmap(d, p);
    } else {
        D_MEM(("ERROR:  Caught attempt to free NULL pixmap\n"));
    }
}

# if LIBAST_IMLIB2_SUPPORT
/**
 * Register a pixmap for tracking.
 *
 * Imlib has its own mechanism for creating pixmaps internally.  In
 * order to keep track of these pixmaps, they must be registered with
 * LibAST using this function (via the IMLIB_REGISTER_PIXMAP() macro).
 *
 * @param var      The variable name of the pixmap being registered.
 * @param filename The filename where the pixmap is being registered.
 * @param line     The line number of @a filename where the pixmap is
 *                 being registered.
 * @param p        The Pixmap being registered.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, IMLIB_REGISTER_PIXMAP()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_imlib_register_pixmap(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, Pixmap p)
{
    USE_VAR(var);
    D_MEM(("Registering pixmap %s (0x%08x) created by Imlib2 at %s:%lu\n", var, p, NONULL(filename), line));
    if (p) {
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            if (!memrec_find_var(&pixmap_rec, (void *) p)) {
                memrec_add_var(&pixmap_rec, (spif_charptr_t) NONULL(filename), line, (void *) p, 1);
            } else {
                D_MEM(("Pixmap 0x%08x already registered.\n"));
            }
        }
    } else {
        D_MEM(("ERROR:  Refusing to register a NULL pixmap\n"));
    }
}

/**
 * Free a pixmap created by Imlib.
 *
 * Imlib has its own mechanism for freeing pixmaps, and their
 * associated mask (if any), internally.  All pixmaps created by Imlib
 * must also be freed by Imlib.  It is safe, albeit a bit slower, to
 * free all pixmaps via Imlib, regardless of how they were created.
 *
 * @param var      The variable name of the pixmap being freed.
 * @param filename The filename where the pixmap is being freed.
 * @param line     The line number of @a filename where the pixmap is
 *                 being freed.
 * @param p        The Pixmap being freed.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, IMLIB_FREE_PIXMAP()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_imlib_free_pixmap(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, Pixmap p)
{
    D_MEM(("Freeing pixmap %s (0x%08x) at %s:%lu using Imlib2\n", var, p, NONULL(filename), line));
    if (p) {
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            memrec_rem_var(&pixmap_rec, var, (spif_charptr_t) NONULL(filename), line, (void *) p);
        }
        imlib_free_pixmap_and_mask(p);
    } else {
        D_MEM(("ERROR:  Caught attempt to free NULL pixmap\n"));
    }
}
# endif

/**
 * Dump listing of tracked pixmaps.
 *
 * This function simply calls memrec_dump_resources() and passes in
 * the address of the #pixmap_rec variable.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, PIXMAP_DUMP(), memrec_dump_resources()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_dump_pixmap_tables(void)
{
    fprintf(LIBAST_DEBUG_FD, "Dumping X11 Pixmap allocation table:\n");
    memrec_dump_resources(&pixmap_rec);
}



/********************** GC ALLOCATION INTERFACE **********************/

/**
 * LibAST implementation of XCreateGC().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * X_CREATE_GC() macro are routed here.  The macro allows filename
 * and line number information to be provided thanks to the __FILE__
 * and __LINE__ symbols pre-defined by cpp.
 *
 * @param filename The filename where the GC is being created.
 * @param line     The line number of @a filename where the GC is
 *                 being created.
 * @param d        The Display for the new GC.
 * @param win      The Drawable for the new GC.
 * @param mask     Bitwise OR of zero or more GC flags.
 * @param gcv      Pointer to XGCValues structure.
 * @return         A newly-created GC.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, X_CREATE_GC()
 * @ingroup DOXGRP_MEM
 */
GC
spifmem_x_create_gc(const spif_charptr_t filename, unsigned long line, Display * d, Drawable win, unsigned long mask, XGCValues * gcv)
{
    GC gc;

    D_MEM(("Creating gc for window 0x%08x at %s:%lu\n", win, NONULL(filename), line));

    gc = XCreateGC(d, win, mask, gcv);
    ASSERT_RVAL(gc != None, None);
    if (DEBUG_LEVEL >= DEBUG_MEM) {
        memrec_add_var(&gc_rec, (spif_charptr_t) NONULL(filename), line, (void *) gc, sizeof(XGCValues));
    }
    return (gc);
}

/**
 * LibAST implementation of XFreeGC().
 *
 * When memory debugging is active (via #DEBUG_MEM), all calls to the
 * X_FREE_GC() macro are routed here.  The macro allows variable
 * name, filename, and line number information to be provided.
 *
 * @param var      The variable name of the GC being freed.
 * @param filename The filename where the GC is being freed.
 * @param line     The line number of @a filename where the GC is
 *                 being freed.
 * @param d        The Display for the GC.
 * @param gc       The GC to be freed.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, X_FREE_GC()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_x_free_gc(const spif_charptr_t var, const spif_charptr_t filename, unsigned long line, Display * d, GC gc)
{
    D_MEM(("spifmem_x_free_gc() called for variable %s (0x%08x) at %s:%lu\n", var, gc, NONULL(filename), line));
    if (gc) {
        if (DEBUG_LEVEL >= DEBUG_MEM) {
            memrec_rem_var(&gc_rec, var, (spif_charptr_t) NONULL(filename), line, (void *) gc);
        }
        XFreeGC(d, gc);
    } else {
        D_MEM(("ERROR:  Caught attempt to free NULL GC\n"));
    }
}

/**
 * Dump listing of tracked GC's.
 *
 * This function simply calls memrec_dump_resources() and passes in
 * the address of the #gc_rec variable.
 *
 * @see @link DOXGRP_MEM Memory Management Subsystem @endlink, GC_DUMP(), memrec_dump_resources()
 * @ingroup DOXGRP_MEM
 */
void
spifmem_dump_gc_tables(void)
{
    fprintf(LIBAST_DEBUG_FD, "Dumping X11 GC allocation table:\n");
    memrec_dump_resources(&gc_rec);
}
#endif

/**
 * Free an array of pointers.
 *
 * This really doesn't relate to the memory management subsystem, per
 * se.  It is simply a convenience function which simplifies the
 * freeing of pointer arrays.  The first @a count pointers in the
 * array are freed, after which the array itself is freed.  If
 * @a count is 0, the array must be NULL-terminated.  All pointers up
 * to the first NULL pointer encountered will be freed.
 *
 * @param list  The pointer array to be freed.  This variable's value
 *              MUST NOT be used after being passed to this function.
 * @param count The number of pointers in the array, or 0 for a
 *              NULL-terminated array.
 *
 * @ingroup DOXGRP_MEM
 */
void
spiftool_free_array(void *list, size_t count)
{
    register size_t i;
    void **l = (void **) list;

    REQUIRE(list != NULL);

    if (count == 0) {
        count = (size_t) (-1);
    }
    for (i = 0; i < count && l[i]; i++) {
        FREE(l[i]);
    }
    FREE(list);
}

/**
 * @defgroup DOXGRP_MEM Memory Management Subsystem
 *
 * This group of functions/defines/macros implements the memory
 * management subsystem within LibAST.
 *
 * LibAST provides a robust mechanism for tracking memory allocations
 * and deallocations.  This system employs macro-based wrappers
 * around the standard libc malloc/realloc/calloc/free routines, other
 * libc fare such as strdup(), Xlib GC and Pixmap create/free
 * routines, and even Imlib2's own pixmap functions.
 *
 * To take advantage of this system, simply substitute the macro
 * versions in place of the standard versions throughout your code
 * (e.g., use MALLOC() instead of malloc(), X_FREE_GC() instead of
 * XFreeGC(), etc.).  If DEBUG is set to a value higher than
 * DEBUG_MEM, the LibAST-custom versions of these functions will be
 * used.  Of course, if memory debugging has not been requested, the
 * original libc/XLib/Imlib2 versions will be used instead, so that
 * you only incur the debugging overhead when you want it.
 *
 * LibAST has also been designed to work effectively with Gray
 * Watson's excellent malloc-debugging library, dmalloc
 * (http://dmalloc.com/), either instead of or in addition to its own
 * memory tracking routines.  Unlike LibAST, dmalloc supplements
 * memory allocation tracking with fence-post checking, freed pointer
 * reuse detection, and other very handy features.
 *
 * A small sample program demonstrating use of LibAST's memory
 * management system can be found
 * @link mem_example.c here @endlink.
 */

/**
 * @example mem_example.c
 * Example code for using the memory management subsystem.
 *
 * This small program demonstrates how to use LibAST's built-in memory
 * management system as well as a few of the errors it can catch for
 * you.
 *
 * The following shows output similar to what you can expect to
 * receive if you build and run this program:
 *
 * @code
 * $ ./mem_example 
 * [1045859036]        mem.c |  246: spifmem_malloc(): 500 bytes requested at mem_example.c:27
 * [1045859036]        mem.c |   74: memrec_add_var(): Adding variable (0x8049a20, 500 bytes) from mem_example.c:27.
 * [1045859036]        mem.c |   75: memrec_add_var(): Storing as pointer #1 at 0x8049c18 (from 0x8049c18).
 * [1045859036]        mem.c |  329: spifmem_strdup(): Variable pointer (0x8049a20) at mem_example.c:36
 * [1045859036]        mem.c |  246: spifmem_malloc(): 16 bytes requested at mem_example.c:36
 * [1045859036]        mem.c |   74: memrec_add_var(): Adding variable (0x8049c40, 16 bytes) from mem_example.c:36.
 * [1045859036]        mem.c |   75: memrec_add_var(): Storing as pointer #2 at 0x8049c7c (from 0x8049c58).
 * [1045859036]        mem.c |  312: spifmem_free(): Variable dup (0x8049c40) at mem_example.c:39
 * [1045859036]        mem.c |   94: memrec_find_var(): Found pointer #2 stored at 0x8049c7c (from 0x8049c58)
 * [1045859036]        mem.c |  113: memrec_rem_var(): Removing variable dup (0x8049c40) of size 16
 * [1045859036]        mem.c |  312: spifmem_free(): Variable dup (   (nil)) at mem_example.c:43
 * [1045859036]        mem.c |  319: spifmem_free(): ERROR:  Caught attempt to free NULL pointer
 * [1045859036]        mem.c |  268: spifmem_realloc(): Variable pointer (0x8049a20 -> 1000) at mem_example.c:46
 * [1045859036]        mem.c |   94: memrec_find_var(): Found pointer #1 stored at 0x8049c58 (from 0x8049c58)
 * [1045859036]        mem.c |  132: memrec_chg_var(): Changing variable pointer (0x8049a20, 500 -> 0x8049c80, 1000)
 * Dumping memory allocation table:
 * PTR:  1 pointers stored.
 * PTR:   Pointer |       Filename       |  Line  |  Address |  Size  | Offset  | 00 01 02 03 04 05 06 07 |  ASCII  
 * PTR:  ---------+----------------------+--------+----------+--------+---------+-------------------------+---------
 * PTR:   0000000 |                      |      0 | 0x8049c58 | 000036 | 0000000 | 80 9c 04 08 e8 03 00 00 | €œ..è...
 * PTR:   0000000 |                      |      0 | 0x8049c58 | 000036 | 0000008 | 6d 65 6d 5f 65 78 61 6d | mem_exam
 * PTR:   0000000 |                      |      0 | 0x8049c58 | 000036 | 0000010 | 70 6c 65 2e 63 00 00 00 | ple.c...
 * PTR:   0000000 |                      |      0 | 0x8049c58 | 000036 | 0000018 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000000 |                      |      0 | 0x8049c58 | 000036 | 0000020 | 2e 00 00 00             | ....    
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000000 | 54 68 69 73 20 69 73 20 | This is 
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000008 | 61 20 74 65 73 74 2e 00 | a test..
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000010 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000018 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000020 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000028 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000030 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000038 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000040 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000048 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000050 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000058 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000060 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000068 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000070 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000078 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000080 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000088 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000090 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000098 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000a0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000a8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000b0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000b8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000c0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000c8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000d0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000d8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000e0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000e8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000f0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00000f8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000100 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000108 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000110 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000118 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000120 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000128 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000130 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000138 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000140 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000148 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000150 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000158 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000160 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000168 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000170 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000178 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000180 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000188 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000190 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000198 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001a0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001a8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001b0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001b8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001c0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001c8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001d0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001d8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001e0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001e8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001f0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00001f8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000200 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000208 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000210 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000218 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000220 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000228 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000230 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000238 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000240 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000248 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000250 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000258 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000260 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000268 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000270 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000278 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000280 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000288 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000290 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000298 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002a0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002a8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002b0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002b8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002c0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002c8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002d0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002d8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002e0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002e8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002f0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00002f8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000300 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000308 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000310 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000318 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000320 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000328 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000330 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000338 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000340 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000348 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000350 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000358 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000360 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000368 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000370 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000378 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000380 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000388 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000390 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 0000398 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003a0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003a8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003b0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003b8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003c0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003c8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003d0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003d8 | 00 00 00 00 00 00 00 00 | ........
 * PTR:   0000001 |        mem_example.c |     46 | 0x8049c80 | 001000 | 00003e0 | 00 00 00 00 00 00 00 00 | ........
 * PTR:  Total allocated memory:       1000 bytes
 * @endcode
 *
 * Here is the source code:
 */
