/**************************************************************************
 *{@C
 *	Copyright:	1988-2020 Paul Obermeier (obermeier@poSoft.de)
 *
 *	Module:		Utilities
 *	Filename:	UT_MemUserLevel.c
 *
 *	Author:		Paul Obermeier
 *
 *	Description:	The memory management library provides all the
 *			functions for dynamically allocating and deallocating
 *			memory.
 *			
 *			The memory management library consists of two layers:
 *
 *			Level one gets memory from the operating system, cares
 *			about fragmentation, and eventually returns memory
 *			to the operating system.
 *
 *			Level two is an interface to the "outside world",
 *			consisting of a set of functions that (hopefully) make
 *			if easier to handle dynamically allocated memory.
 *			The functions in level two can be compiled so
 *			that they detect violation of block boundaries and
 *			memory blocks that are never or more than once
 *			returned to the operating system.
 *
 *	Additional documentation:
 *			None.
 *
 *	Exported functions:
 *			UT_MemAlignPos
 *			UT_MemAlignNeg
 *			UT_MemTempAlloc
 *			UT_MemPermAlloc
 *			UT_MemFree
 *			UT_MemRemember
 *			UT_MemRestore
 *			UT_MemSave
 *			UT_MemDestroy
 *			UT_MemCheck
 *			UT_MemCheckBlock
 *			UT_MemGetUsed
 *
 **************************************************************************/

#include <stdio.h>

#include "UT_Compat.h"

#if defined (OS_IS_WIN32)
    #include <windows.h>
    #define DPSAPI_VERSION 1
    #include <psapi.h>
#elif defined (Darwin)
    #include <mach/mach_init.h>
    #include <mach/task.h>
#elif defined (OS_IS_SYS5_UNIX)
    #include <unistd.h>
#endif

#include "UT_Error.h"

#include "UT_Memory.h"
#include "UT_MemPrivate.h"

static UT_MemHdr
	*firstperm = NULL,		/* First block in permanent list */
	*lastperm = NULL,		/* Last block in permanent list */
	*firsttemp = NULL,		/* First block in temporary list */
	*lasttemp = NULL;		/* Last block in temporary list */

static UT_Bool
	initialized = UT_False,		/* Set to UT_True upon initialization */
	destroymode = UT_False;		/* UT_True while destroy mode is on */

#define	destroypattern	0xaa		/* Bit pattern used to overwrite a
					   memory block when destroy mode is
					   switched on */

#define checkbytebegin	~0x07041966	/* Bit pattern prepended to the
					   beginning of a memory block */

#define checkbyteend	~0x02081968	/* Bit pattern appended at the end
					   of a memory block */

#define checkbytefree	~0x14091935	/* Bit pattern used to mark free
					   memory blocks */


/* Initialize the memory management library's internal data structures. */

static void init (void)
{
    initialized = UT_True;
    return;
}

/* Wrapper around standard memory management functions malloc and free. */

static void *memAlloc (size_t size)
{
    void *ptr;

    if (!(ptr = malloc (size))) {
	UT_ErrSetNum (UT_ErrFromOs (), str_try_alloc, size);
    }
    return ptr;
}

static void memFree (void *ptr, size_t size)
{
    (void) size;
    free (ptr);
    return;
}

/* Given the size of the user data area of a memory block, compute the
total amount of memory needed for the block, including the block header,
and the check bytes at the beginning and at the end. */

#if defined(DEBUG_MEM)

    static size_t totalsize (size_t usize)
    {
	return MEM_ALIGN_POS (usize) + memheadersize + 2 * checkbytesize;
    }

#else

    static size_t totalsize (size_t usize)
    {
	return MEM_ALIGN_POS (usize) + memheadersize;
    }

#endif


/* Given the total size of a memory block (including block header etc.),
determine the size of the user data area. */

#if defined(DEBUG_MEM)

    static size_t usersize (size_t tsize)
    {
	return tsize - memheadersize - 2 * checkbytesize;
    }

#else

    static size_t usersize (size_t tsize)
    {
	return tsize - memheadersize;
    }

#endif


/* Given the address of a block header, compute the address of the
user data area and the adresses of the check bytes at the beginning
and at the end of the block. */

#if defined (DEBUG_MEM)

    static void header_to_user
	    (UT_MemHdr *header,
	     char **user,
	     size_t **checkbegin,
	     size_t **checkend)
    {
	*checkbegin = (size_t *)((char *)header + memheadersize);
	*user = (char *)*checkbegin + checkbytesize;
	*checkend = (size_t *)(*user + usersize (header->size));
	return;
    }

#else

    static void header_to_user (UT_MemHdr *header, char **user)
    {
	*user = (char *)header + memheadersize;
	return;
    }

#endif


/* Given the address of the user data area of a memory block, compute the
address of the block header and the adresses of the check bytes at the
beginning and at the end of the block. */

#if defined (DEBUG_MEM)

    static void user_to_header
	    (char *user,
	     UT_MemHdr **header,
	     size_t **checkbegin,
	     size_t **checkend)
    {
	*checkbegin = (size_t *)(user - checkbytesize);
	*header = (UT_MemHdr *)((char *)*checkbegin - memheadersize);
	*checkend = (size_t *)(user + usersize ((*header)->size));
	return;
    }

#else

    static void user_to_header (char *user, UT_MemHdr **header)
    {
	*header = (UT_MemHdr *)(user - memheadersize);
	return;
    }

#endif


/* Destroy the contents of the user data area of a memory block by
overwriting with a constant bit pattern. */

static void destroy_data (char *data, size_t nbytes)
{
    char *stop = data + nbytes;
    while (data < stop)
	*(data++) = (char) destroypattern;
    return;
}


/* Print name and size of a memory block to stderr. */

#if defined(DEBUG_MEM)

    static void printblockinfo (UT_MemHdr *header)
    {
	size_t *checkbegin, *checkend;
	char *user;

	fprintf (stderr, "\tname: ");
        fprintf (stderr, "'%s' ", header->name);
	fprintf (stderr, " - address: 0x%lx - size: %ld bytes\n",
	    (size_t)header, header->size);
	header_to_user (header, &user, &checkbegin, &checkend);
	if (*checkbegin != checkbytebegin)
	    fprintf (stderr, "\t\tcheckbytebegin changed\n");
	if (*checkend != checkbyteend)
	    fprintf (stderr, "\t\tcheckbyteend changed\n");
	return;
    }

#endif


/* Print name and size of all blocks in a linked list. */

#if defined(DEBUG_MEM)

    static void printblocklist (UT_MemHdr *header)
    {
	while (header)
	{
	    printblockinfo (header);
	    header = header->next;
	}
	return;
    }

#endif


/* Print name and size of a memory block to stderr, then call UT_ErrFatal. */

#if defined(DEBUG_MEM)

    static void fatal (UT_MemHdr *header, char func[], char message[])
    {
	fprintf (stderr, "\n\nmemory block - name: ");
        fprintf (stderr, "'%s' ", header->name);
	fprintf (stderr, " - address: 0x%lx - size: %ld bytes\n",
	    (size_t)header, header->size);
	UT_ErrFatal (func, message);
    }

#endif

/***************************************************************************
 *[@e
 *	Name:		UT_MemAlignPos
 *
 *	Usage:		Find next positive aligned address.
 *
 *	Synopsis:	void *UT_MemAlignPos (const void *addr)
 *
 *	Description:	Find the first address that is greater than or equal
 *			to a given address and that is aligned so that it can
 *			be used as the address of any data object.
 *
 *	Return value:	The aligned address.
 *
 *	See also:
 *
 ***************************************************************************/

void *UT_MemAlignPos (const void *addr)
{
    return (void *)MEM_ALIGN_POS ((size_t)addr);
}


/***************************************************************************
 *[@e
 *	Name:		UT_MemAlignNeg
 *
 *	Usage:		Find next negative aligned address.
 *
 *	Synopsis:	void *UT_MemAlignNeg (const void *addr)
 *
 *	Description:	Find the first address that is less than or equal to
 *			a given address and that is aligned so that it can be
 *			used as the address of any data object.
 *
 *	Return value:	The aligned address.
 *
 *	See also:
 *
 ***************************************************************************/

void *UT_MemAlignNeg (const void *addr)
{
    return (void *)MEM_ALIGN_NEG ((size_t)addr);
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemTempAlloc
 *
 *	Usage:		Allocate a temporary memory block from the
 *			operating system.
 *
 *	Synopsis:	void *UT_MemTempAlloc (size_t size, const char *name)
 *
 *	Description:	"Size" is the number of bytes to allocate. "Name"
 *			should be a pointer to a static string constant
 *			explaining for what the memory block is needed
 *			(this is quite helpful when debugging programs
 *			which forget to free unused memory blocks; a list
 *			of all temporary and permanent memory blocks can
 *			be printed with UT_MemCheck).
 *
 *			Notes:
 *			
 *			- The newly allocated temporary memory block is not
 *			  cleared; it contains random data.
 *
 *			- The new temporary block can be deallocated with
 *			  UT_MemRestore or UT_MemFree; it can also be converted
 *			  into a permanent block with UT_MemSave.
 *
 *			- The UT_MemTemp and UT_MemTempArray macros
 *			  provide a slightly more comfortable interface
 *			  to UT_MemTempAlloc.
 *
 *	Return value:	A pointer to a temporary block of "size" bytes,
 *			suitably aligned for any use, is returned, if enough
 *			memory is available, otherwise the result is NULL.
 *
 *	See also:
 *
 ***************************************************************************/

void *UT_MemTempAlloc (size_t size, const char *name)
{
    UT_MemHdr	*header;
    char	*user;

    #if defined(DEBUG_MEM)
	size_t *checkbegin,
		   *checkend;
    #endif

    if (!initialized) {
	init ();
    }

    /* Determine the total size of the block, including the header and
    the checkbytes, and allocate memory for it. */

    size = totalsize (size);
    if (!(header = memAlloc (size))) {
	return NULL;
    }

    /* Link the block to the end of the temporary block list. */

    header->previous = lasttemp;
    lasttemp = header;
    header->next = NULL;
    if (firsttemp) {
	header->previous->next = header;
    } else {
	firsttemp = header;
    }

    /* Initialize the block header and the checkbytes. */

    header->size = size;

    #if defined(DEBUG_MEM)
	header_to_user (header, &user, &checkbegin, &checkend);
	header->name = name;
	*checkbegin = checkbytebegin;
	*checkend = checkbyteend;
    #else
	header_to_user (header, &user);
    #endif

    return (void *)user;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemPermAlloc
 *
 *	Usage:		Allocate a permanent memory block from the
 *			operating system.
 *
 *	Synopsis:	void *UT_MemPermAlloc (size_t size, const char *name)
 *
 *	Description:	"Size" is the number of bytes to allocate. "Name"
 *			should be a pointer to a static string constant
 *			explaining for what the memory block is needed,
 *			as in UT_MemTempAlloc.
 *
 *			Notes:
 *			
 *			- The newly allocated permanent memory block is not
 *			  cleared; it contains random data.
 *
 *			- The new temporary block can be deallocated only
 *			  with UT_MemFree; calls to UT_MemRestore and UT_MemSave
 *			  have no effect on the block.
 *
 *			- The UT_MemPerm and UT_MemPermArray macros
 *			  provide a slightly more comfortable interface
 *			  to UT_MemPermAlloc.
 *
 *	Return value:	A pointer to a permanent block of "size" bytes,
 *			suitably aligned for any use, is returned, if enough
 *			memory is available, otherwise the result is NULL.
 *			The newly allocated block is not affected by any
 *			calls to UT_MemRestore or UT_MemSave.
 *
 *	See also:
 *
 ***************************************************************************/

void *UT_MemPermAlloc (size_t size, const char *name)
{
    UT_MemHdr	*header;
    char	*user;

    #if defined(DEBUG_MEM)
	size_t *checkbegin,
		   *checkend;
    #endif

    if (!initialized) {
	init ();
    }

    /* Determine the total size of the block, including the header and
    the checkbytes, and allocate memory for it. */

    size = totalsize (size);
    if (!(header = memAlloc (size))) {
	return NULL;
    }

    /* Link the block to the end of the permanent block list. */

    header->previous = lastperm;
    lastperm = header;
    header->next = NULL;
    if (firstperm) {
	header->previous->next = header;
    } else {
	firstperm = header;
    }

    /* Initialize the block header and the checkbytes. */

    header->size = size;

    #if defined(DEBUG_MEM)
	header_to_user (header, &user, &checkbegin, &checkend);
	header->name = name;
	*checkbegin = checkbytebegin;
	*checkend = checkbyteend;
    #else
	header_to_user (header, &user);
    #endif

    return (void *)user;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemFree
 *
 *	Usage:		Return a block of memory to the operating system.
 *
 *	Synopsis:	void UT_MemFree (void *blk)
 *
 *	Description:	"Blk" must be a pointer to a temporary or a permanent
 *			memory block previously allocated with UT_MemTempAlloc
 *			or UT_MemPermAlloc.
 *
 *	Return value:	None.
 *
 *	See also:
 *
 ***************************************************************************/

void UT_MemFree (void *blk)
{
    UT_MemHdr	*header;

    #if defined(DEBUG_MEM)
	size_t	*checkbegin,
		*checkend;
    #endif

    if (!initialized) {
	init ();
    }

    /* Find the address of the memory block header and the checkbytes.
    Then do some consistency checks. */

    #if defined(DEBUG_MEM)
	user_to_header (blk, &header, &checkbegin, &checkend);
	if (*checkbegin == checkbytefree)
	    fatal (header, "UT_MemFree",
		   "block freed twice");
	if (*checkbegin != checkbytebegin)
	    fatal (header, "UT_MemFree",
		   "block boundary violation; "
		   "write before start of block");
	if (*checkend != checkbyteend)
	    fatal (header, "UT_MemFree",
		   "block boundary violation; "
		   "write after end of block");
    #else
	user_to_header (blk, &header);
    #endif

    if (header->size < memheadersize) {
	UT_ErrFatal ("UT_MemFree", "invalid memory block size");
    }

    /* Determine in which of the memory block lists the block is,
    remove it from that list, and deallocate the block. */

    if (header->previous) {
	header->previous->next = header->next;
    } else {
	if (firsttemp == header) {
	    firsttemp = header->next;
	} else {
	    firstperm = header->next;
	}
    }
    if (header->next) {
	header->next->previous = header->previous;
    } else {
	if (lasttemp == header) {
	    lasttemp = header->previous;
	} else {
	    lastperm = header->previous;
	}
    }

    /* If destroy mode is switched on, we erase the contents of the
    user data area of the block. */

    if (destroymode) {
	destroy_data (blk, usersize (header->size));
    }

    /* Before we deallocate the block, we change the checkbytes at the
    beginning, so that we can detect if a memory block is freed twice. */

    #if defined(DEBUG_MEM)
	*checkbegin = checkbytefree;
    #endif

    memFree (header, header->size);
    return;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemRemember
 *
 *	Usage:		Save the status of the memory allocation list.
 *
 *	Synopsis:	UT_MemState UT_MemRemember (void)
 *
 *	Description:	Any function which has to temporary allocate memory,
 *			should call UT_MemRemember on entry. While it performs
 *			its task, it may allocate and deallocate temporary
 *			memory as it likes. Just before the function returns,
 *			it decides whether it wants to keep the allocated
 *			memory (and make it permanent by calling UT_MemSave),
 *			or whether it wants to deallocate all of it (by
 *			calling UT_MemRestore).
 *
 *	Return value:	A descriptor for the current internal status of the
 *			memory allocation list for later use with UT_MemRestore
 *			and/or UT_MemSave.
 *			Note that this descriptor becomes invalid as soon as
 *			you deallocate any temporary memory block that was
 *			allocated before the call to UT_MemRemember!
 *
 *	See also:
 *
 ***************************************************************************/

UT_MemState UT_MemRemember (void)
{
    if (!initialized) {
	init ();
    }
    return lasttemp;
}


/***************************************************************************
 *[@e
 *	Name:		UT_MemRestore
 *
 *	Usage:		Restore the memory allocation list to a state
 *			previously saved with UT_MemRemember.
 *
 *	Synopsis:	void UT_MemRestore (UT_MemState mh)
 *
 *	Description:	"Mh" must be a descriptor which was previously
 *			obtained from UT_MemRemember. All temporary memory
 *			blocks which were allocated but not freed, and all
 *			descriptors returned by UT_MemRemember after the call
 *			to UT_MemRemember which returned "mh", are deallocated.
 *			"Mh" itself remains valid.
 *
 *	Return value:	None.
 *
 *	See also:
 *
 ***************************************************************************/

void UT_MemRestore (UT_MemState mh)
{
    UT_MemHdr	*header,
		*next;
    char	*user;

    #if defined(DEBUG_MEM)
	size_t	*checkbegin,
		*checkend;
    #endif

    if (!initialized) {
	init ();
    }

    /* Check if mh can possibly be a valid pointer. */

    if (mh && mh->size < memheadersize) {
	UT_ErrFatal ("UT_MemRestore", "called with invalid argument");
    }

    /* Find the first block we have to deallocate. */

    header = mh? mh->next: firsttemp;

    /* Remove this and all following memory blocks from the
    temporary block list. */

    if (mh) {
	mh->next = NULL;
    } else {
	firsttemp = NULL;
    }
    lasttemp = mh;

    /* Go through the list of blocks that have been removed from the
    temporary block list, perform some consistency checks and deallocate
    the blocks. */

    while (header) {
	#if defined(DEBUG_MEM)
	    header_to_user (header, &user, &checkbegin, &checkend);
	    if (*checkbegin == checkbytefree)
		fatal (header, "UT_MemRestore",
		       "block freed twice");
	    if (*checkbegin != checkbytebegin)
		fatal (header, "UT_MemRestore",
		       "block boundary violation; "
		       "write before start of block");
	    if (*checkend != checkbyteend)
		fatal (header, "UT_MemRestore",
		       "block boundary violation; "
		       "write after end of block");
	#else
	    header_to_user (header, &user);
	#endif

	if (header->size < memheadersize) {
	    UT_ErrFatal ("UT_MemRestore", "memory block size invalid");
	}

	/* If destroy mode is switched on, we erase the contents of
	the block's user data area. */

	if (destroymode) {
	    destroy_data (user, usersize (header->size));
	}

	/* Before we deallocate the block, we change the checkbytes
	at the beginning, so that we can detect if a memory block is
	freed twice. */

	#if defined(DEBUG_MEM)
	    *checkbegin = checkbytefree;
	#endif

	next = header->next;
	memFree (header, header->size);
	header = next;
    }
    return;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemSave
 *
 *	Usage:		Protect memory blocks from being deallocated
 *			by UT_MemRestore.
 *
 *	Synopsis:	void UT_MemSave (UT_MemState mh)
 *
 *	Description:	"Mh" must be a descriptor which was previously obtained
 *			from UT_MemRemember. All temporary memory blocks which
 *			were allocated but not freed after the call to
 *			UT_MemRemember, which returned "mh", are made permanent,
 *			and all descriptors returned by UT_MemRemember after the
 *			call to UT_MemRemember, which returned "mh", become
 *			invalid. "Mh" remains valid.
 *
 *	Return value:	None.
 *
 *	See also:
 *
 ***************************************************************************/

void UT_MemSave (UT_MemState mh)
{
    UT_MemHdr	*first,
		*last;

    if (!initialized) {
	init ();
    }

    /* Check if mh can possibly be a valid pointer. */

    if (mh && mh->size < memheadersize) {
	UT_ErrFatal ("UT_MemSave", "called with invalid argument");
    }

    /* Check if any blocks were allocated and not freed since
    the last call to UT_MemRemember. */

    if (mh == lasttemp) {
	return;
    }

    /* Find the first and the last block we have to move. */

    first = mh? mh->next: firsttemp;
    last = lasttemp;

    /* Remove the blocks from "first" to "last" from
    the temporary memory block list. */

    if (mh) {
	mh->next = NULL;
    } else {
	firsttemp = NULL;
    }
    lasttemp = mh;

    /* Attach them to the beginning of the permanent
    memory block list. */

    if ((first && !last) || (last && !first)) {
	UT_ErrFatal ("UT_MemSave", "inconsistent temporary block list");
    }
    if (first) {
	if (firstperm) {
	    firstperm->previous = last;
	} else {
	    lastperm = last;
	}
	last->next = firstperm;
	first->previous = NULL;
	firstperm = first;
    }
    return;
}


/***************************************************************************
 *[@e
 *	Name:		UT_MemDestroy
 *
 *	Usage:		Switch destroy mode on or off.
 *
 *	Synopsis:	void UT_MemDestroy (UT_Bool onoff)
 *
 *	Description:	If "onoff" is UT_True, destroy mode is switched on, i.e.
 *			the contents of all memory blocks deallocated using
 *			UT_MemFree or UT_MemRestore are immediately destroyed
 *			by overwriting them with a constant bit pattern. 
 *			If "onoff" is UT_False, destroy mode is switched off.
 *			Destroy mode can help to detect read accesses to a
 *			memory block after that block has been deallocated.
 *
 *	Return value:	None.
 *
 *	See also:
 *
 ***************************************************************************/

void UT_MemDestroy (UT_Bool onoff)
{
    if (!initialized) {
	init ();
    }
    destroymode = onoff;
    return;
}


/***************************************************************************
 *[@e
 *	Name:		UT_MemCheck
 *
 *	Usage:		Check if all memory blocks allocated by UT_MemTempAlloc
 *			were deallocated (using UT_MemFree or UT_MemRestore).
 *
 *	Synopsis:	UT_Bool UT_MemCheck (void)
 *
 *	Description:	This function is intended for debugging memory
 *			handling of a program. It should be called just
 *			before a program exits, to check if the program
 *			forgot to deallocate any memory.
 *
 *	Return value:	UT_True if there are no more dynamically allocated
 *			memory blocks around, otherwise UT_False.
 *
 *			Note: If the memory management library has been
 *			compiled with the preprocessor macro DEBUG_MEM defined,
 *			UT_MemCheck prints a list of the names of all memory
 *			blocks to stderr.
 *
 *	See also:
 *
 ***************************************************************************/

UT_Bool UT_MemCheck (void)
{
    if (!initialized) {
	init ();
    }

    if ((firsttemp && !lasttemp) || (lasttemp && !firsttemp)) {
	UT_ErrFatal ("UT_MemCheck", "temporary block list inconsistent");
    }
    if ((firstperm && !lastperm) || (lastperm && !firstperm)) {
	UT_ErrFatal ("UT_MemCheck", "permanent block list inconsistent");
    }
    if (!firsttemp && !firstperm) {
	#if defined(DEBUG_MEM)
	    fprintf (stderr, "\nUT_MemCheck OK\n");
        #endif
	return UT_True;
    }

    #if defined(DEBUG_MEM)
	fprintf (stderr, "\nCURRENTLY ALLOCATED MEMORY BLOCKS:\n\n");
	fprintf (stderr, "Temporary:\n");
	printblocklist (firsttemp);
	fprintf (stderr, "Permanent:\n");
	printblocklist (firstperm);
	fprintf (stderr, "\nUT_MemCheck FAILED\n");
    #endif

    return UT_False;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemCheckBlock
 *
 *	Usage:		Check for block boundary violations.
 *
 *	Synopsis:	UT_Bool UT_MemCheckBlock (void *blk)
 *
 *	Description:	UT_MemCheckBlock tests if there were any write accesses
 *			to the bytes just before the first byte of memory
 *			block "blk" or right after the last byte of the block.
 *			This function is intended for debugging a program's
 *			memory handling.
 *
 *	Return value:	UT_False if write accesses outside the boundaries of
 *			the block are detected, otherwise UT_True.
 *
 *			Note: If the memory management library has been
 *			compiled without defining the preprocessor macro
 *			DEBUG_MEM, UT_MemCheckBlock always returns UT_True.
 *
 *	See also:
 *
 ***************************************************************************/

UT_Bool UT_MemCheckBlock (void *blk)
{
    #if defined (DEBUG_MEM)
	UT_MemHdr	*header;
	size_t		*checkbegin,
			*checkend;

	/* Calculate the addresses of the memory block header
	and the checkbytes. */

	user_to_header (blk, &header, &checkbegin, &checkend);

	/* Check if the size of the memory block equal to -1, which
	means that the block has already been freed. */

	if (*checkbegin == checkbytefree) {
	    UT_ErrFatal ("UT_MemCheckBlock", "block already freed");
	}

	/* See if the check bytes at both ends of the block still
	have their original values. If they have changed, the
	boundaries of the memory block were obviously violated. */

	if (*checkbegin != checkbytebegin) {
	    fprintf (stdout, "checkbytebegin changed\n");
            fflush (stdout);
	    return UT_False;
	}
	if (*checkend != checkbyteend) {
	    fprintf (stdout, "checkbyteend changed\n");
            fflush (stdout);
	    return UT_False;
	}
    #endif /* DEBUG_MEM */
    return UT_True;
}

/***************************************************************************
 *[@e
 *	Name:		UT_MemGetUsed
 *
 *	Usage:		Get used memory in bytes.
 *
 *	Synopsis:	size_t UT_MemGetUsed (void)
 *
 *	Description:	Get the memory occupied by the calling process.
 *
 *			Note: The returned value is the total memory of the process,
 *                      not only the memory allocated by the UT_Mem library.
 *
 *	Return value:	Return the memory occupied by the calling process in bytes.
 *                      If the value could not be obtained, an error message is set
 *                      and the return value is zero.
 *
 *	See also:
 *
 ***************************************************************************/

size_t UT_MemGetUsed (void)
{
    size_t size;

    #if defined (OS_IS_WIN32)
        PROCESS_MEMORY_COUNTERS pmc;
        if (0 == GetProcessMemoryInfo (GetCurrentProcess(), & pmc, sizeof (pmc))) {
	    UT_ErrSetNum (UT_ErrFromOs (), str_get_mem);
            return 0;
        }
        size = pmc.WorkingSetSize;
    #elif defined (Linux)
        FILE *fp;
        
        if (!(fp = fopen ("/proc/self/statm", "r"))) {
	    UT_ErrSetNum (UT_ErrFromOs (), str_get_mem);
            return 0;
        }
        fscanf (fp, "%ld", &size);
        fclose (fp);

        /* Size is returned in pages. Convert to bytes. */
        long pageSize = sysconf(_SC_PAGESIZE);
        size *= pageSize;
    #elif defined (Darwin)
        struct task_basic_info t_info;
        mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;

        if (KERN_SUCCESS != task_info(mach_task_self(),
            TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count)) {
	    UT_ErrSetNum (UT_ErrFromOs (), str_get_mem);
            return 0;
        }
        size = t_info.resident_size;
    #else
        /* Fall-back to Posix getrusage. This typically does not work correctly, sigh. */
        struct rusage r;
        getrusage (RUSAGE_SELF, &r); 
        size = r.ru_maxrss;
    #endif
    return size;
}
