/*
Copyright (c) 2000-2010, Dirk Krause
All rights reserved.

Redistribution and use in source and binary forms,
with or without modification, are permitted provided
that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.
* Redistributions in binary form must reproduce the above 
  opyright notice, this list of conditions and the following
  disclaimer in the documentation and/or other materials
  provided with the distribution.
* Neither the name of the Dirk Krause nor the names of
  contributors may be used to endorse or promote
  products derived from this software without specific
  prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
*/

/**	@file dkmem.h
	Memory allocation functions.
	This module provides macros and functions for portable
	memory allocation.

	Use the dk_new() and dk_delete() macros to allocate and
	release memory, not the dkmem_alloc(), dkmem_alloc_tracked() and
	dkmem_free() functions.

	Use the DK_MEMRES(), DK_MEMCPY() and DK_MEMCMP() macros to
	reset, copy and compare memory buffers, not the dkmem_res(),
	dkmem_cpy() and dkmem_cmp() functions.
	The macros invoke the functions provided by the system.
	I.e. DM_MEMRES() is mapped to either memset() or bzero() or --
	if these functions are not available -- dkmem_res().
	Typically the functions in the C runtime library are implemented
	more efficiently than the fallback functions provided by the dkmem
	module.

	The only function in the dkmem module for direct use is
	dkmem_get_track().
*/

#ifndef DK_MEM_INCLUDED

/** Make sure to include the file only once. */
#define DK_MEM_INCLUDED 1

#include <dk.h>

#if DK_HAVE_STRING_H
#include <string.h>
#else
#if DK_HAVE_STRINGS_H
#include <strings.h>
#endif
#endif

#if defined(EXTERN)
#undef EXTERN
#endif
#ifndef DK_MEM_C
#if !DK_HAVE_PROTOTYPES
#define EXTERN extern
#else
#define EXTERN /* nix */
#endif
#else
#define EXTERN /* nix */
#endif

#if defined(__cplusplus)
extern "C" {
#endif

/**	Reset memory.
Set all bytes in buffer \a ptr (\a n bytes)  to 0x00.
@param	ptr	Pointer to buffer.
@param	bytes	Number of bytes in buffer.
*/
EXTERN void  dkmem_res		DK_PR((void *ptr, size_t bytes));

/**	Copy memory.
Copy \a n bytes from \a s to \a d.
@param	d	Pointer to destination buffer.
@param	s	Pointer to source buffer.
@param	n	Number of bytes to copy.
*/
EXTERN void  dkmem_cpy		DK_PR((void *d, void *s, size_t n));

/**	Compare memory.
Compare \a n bytes in buffers \a s1 and \a s2.
@param	s1	Pointer to one buffer.
@param	s2	Pointer to the other buffer.
@param	n	Number of bytes to compare.
@return
- 0 if both buffers are equal,
- positive value if \a s1 is "larger" than \a s2.
- negative value if \a s1 is "smaller" than \a s2.
*/
EXTERN int   dkmem_cmp		DK_PR((void *s1, void *s2, size_t n));

/**	Allocate memory.
Attempt to allocate \a nelem elements of element size \a elsize.
@param	elsize	Size of one element to allocate.
@param	nelem	Number of elements to allocate.
@return
- Valid pointer on success.
- NULL on errors.
*/
EXTERN void *dkmem_alloc	DK_PR((size_t elsize, size_t nelem));

/**	Allocate memory, keep track of allocated size.
Attempt to allocate \a nelem elements of element size \a elsize and
keep track of the summary size of allocated memory.
@param	elsize	Size of one element to allocate.
@param	nelem	Number of elements to allocate.
@return
- Valid pointer on success.
- NULL on errors.
*/
EXTERN void *dkmem_alloc_tracked	DK_PR((size_t elsize, size_t nelem));

/**	Release memory.
Release memory previously allocated by dkmem_alloc() or dkmem_alloc_tracked().
@param	ptr	Pointer to memory.
*/
EXTERN void  dkmem_free		DK_PR((void *ptr));

/**	Get amount of memory allocated.
Get number of bytes already allocated.
@param	usehb	Flag to indicate whether the higher or lower
32 bits of the number are requested.
@return	The numeric value.
*/
EXTERN unsigned long dkmem_get_track DK_PR((int usehb));

#if defined(__cplusplus)
}
#endif

/** Reset \a sz bytes in buffer \a ptr. */
#if DK_HAVE_MEMSET
#define DK_MEMRES(ptr,sz)	memset(((void *)(ptr)),0,((size_t)(sz)))
#else
#if DK_HAVE_BZERO
#define DK_MEMRES(ptr,sz)	bzero(((void *)(ptr)),((size_t)(sz)))
#else
#define DK_MEMRES(ptr,sz)	dkmem_res(((void *)(ptr)),((size_t)(sz)))
#endif
#endif

/** Copy \a n bytes from buffer \a s to buffer \a d. */
#if DK_HAVE_MEMCPY
#define DK_MEMCPY(d,s,n) memcpy(((void *)(d)),((void *)(s)),((size_t)(n)))
#else
#if DK_HAVE_BCOPY
#define DK_MEMCPY(d,s,n) bcopy(((void *)(s)),((void *)(d)),((size_t)(n)))
#else
#define DK_MEMCPY(d,s,n) dkmem_cpy(((void *)(d)),((void *)(s)),((size_t)(n)))
#endif
#endif

/** Compare \a n bytes in buffers \a a and \a b, return 0 for equal buffers. */
#if DK_HAVE_MEMCMP
#define DK_MEMCMP(a,b,n) memcmp(((void *)(a)),((void *)(b)),((size_t)(n)))
#else
#if DK_HAVE_BCMP
#define DK_MEMCMP(a,b,n) bcmp(((void *)(a)),((void *)(b)),((size_t)(n)))
#else
#define DK_MEMCMP(a,b,n) dkmem_cmp(((void *)(a)),((void *)(b)),((size_t)(n)))
#endif
#endif

#if DK_MEM_NOTRACK
#define dk_new(t,s)	(t *)dkmem_alloc(sizeof(t),((size_t)s))
#else
/** Allocate memory, \a s bytes of type \a t, return pointer on success. */
#define dk_new(t,s)	(t *)dkmem_alloc_tracked(sizeof(t),((size_t)s))
#endif
/** Release memory, buffer \a p. */
#define dk_delete(p)	dkmem_free((void *)(p))

#endif
/* DK_MEM_INCLUDED */

