/*
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.c	Memory allocation routines.

*/


#include "dk.h"

#if DK_HAVE_STRING_H
#include <string.h>
#else
#if DK_HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#if DK_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if DK_HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if DK_HAVE_UNISTD_H
#include <unistd.h>
#endif
#if DK_HAVE_ALLOC_H
#include <alloc.h>
#endif



/**	Inside the dkmem module.
*/
#define DK_MEM_C 1

#include "dkmem.h"




#line 77 "dkmem.ctr"




#if !DKMEM_NO_TRACK

/**	Maximum unsigned long value.
*/
#define MAXUL  0xFFFFFFFFUL



/**	Upper 32 bits of amount of memory allocated.
*/
static unsigned long hb = 0UL;

/**	Lower 32 bits of amount of memory allocated.
*/
static unsigned long lb = 0UL;



/**	Track the number of bytes allocated.
	@param	sz	Number of bytes to add.
*/
static void
track_bytes DK_P1(size_t,sz)
{
  unsigned long newval;
  newval = (unsigned long)sz;
  if(lb >= (MAXUL - newval)) {
    hb++;
  }
  lb += newval;
  lb &= MAXUL;
}
#endif



/**	Get number of allocated bytes.
	@param	usehb	Flag: Use higher 32 bits (1) or lower 32 bits (0).
	@return	32-bit value.
*/
unsigned long
dkmem_get_track DK_P1(int,usehb)
{
  unsigned long back = 0UL;
  back = (usehb ? hb : lb);
  return back;
}



/**	Check whether the product of elsize and
	nelem fits into a size_t.
	@param	elsize	Element size.
	@param	nelem	Number of elements.
	@return	Product elsize*nelem or 0 on overflow.
*/
static
size_t
check_size	DK_P2(size_t, elsize, size_t, nelem)
{
  size_t back = 0UL;
  unsigned long i, j, k;
  i = elsize; j = nelem;
  if(i < (0xFFFFFFFFUL / j)) {
    k = i * j;
    if(k <= ((size_t)0xFFFFFFFFUL)) {
      back = ((size_t)k);
    }
  } 
  return back;
}



/**	Reset memory.
	@param	ptr	Pointer to memory.
	@param	bytes	Number of bytes.
*/
void  dkmem_res		DK_P2(void *, ptr, size_t, bytes)
{
  if(ptr && bytes) {
#if DK_HAVE_MEMSET
    memset(ptr,0,bytes);
#else
#if DK_HAVE_BZERO
    bzero(ptr,bytes);
#else
    char *x; size_t y;
    x = (char *)ptr; y = bytes;
    while(y--) { *(x++) = '\0'; }
#endif
#endif
  }
}



/**	Copy memory region.
	@param	d	Destination.
	@param	s	Source.
	@param	n	Number of bytes to copy.
*/
void
dkmem_cpy		DK_P3(void *, d, void *, s, size_t, n)
{
  if(d && s && n) {
#if DK_HAVE_MEMCPY
    memcpy(d,s,n);
#else
#if DK_HAVE_BCOPY
    bcopy(s,d,n);
#else
    char *x, *y; size_t z;
    z = n; x = (char *)s; y = (char *)d;
    while(z--) { *(y++) = *(x++); }
#endif
#endif
  }
}



/**	Compare memory regions.
	@param	s1	Left memory region.
	@param	s2	Right memory region.
	@param	n	Number of bytes.
	@return	Comparison result.
*/
int   dkmem_cmp		DK_P3(void *, s1, void *, s2, size_t, n)
{
  int back = 0;
  if(s1 && s2 && n) {
#if DK_HAVE_MEMCMP
    back = memcmp(s1,s2,n);
#else
#if DK_HAVE_BCMP
    back = bcmp(s1,s2,n);
#else
    char *x, *y; size_t z;
    z = n; x = (char *)s1; y = (char *)s2;
    while((z--) && (!back)) {
      if((*(x++)) != (*(y++))) {
	back = 1;
      }
    }
#endif
#endif
  }
  return back;
}



/**	Allocate memory.
	@param	elsize	Element size.
	@param	nelem	Number of elements.
	@return	Pointer on success, NULL on error.
*/
void *
dkmem_alloc	DK_P2(size_t, elsize, size_t, nelem)
{
  void *back = NULL;
  size_t bytes;
  
  if(elsize && nelem) {
    bytes = check_size(elsize,nelem); 
    if(bytes) {
#if DK_HAVE_FARMALLOC
      
#if DK_HAVE_FARCORELEFT
      
      if(bytes < farcoreleft()) {
#endif
	
        back = farmalloc(bytes);
#if DK_HAVE_FARCORELEFT
      }
#endif
#else
      
#if DK_HAVE_MALLOC
#if DK_HAVE_CORELEFT
      
      if(bytes < coreleft()) {
#endif
	
        back = malloc(bytes);
#if DK_HAVE_CORELEFT
      }
#endif
#endif
#endif
    }
  }
  if(back) { 
#if DK_HAVE_MEMSET
    
    memset(back, 0, bytes);
#else
#if DK_HAVE_BZERO
    
    bzero(back,bytes);
#else
    
    dkmem_res(back,bytes);
#endif
#endif
  } 
  return back;
}



/**	Allocate memory, track memory usage.
	@param	elsize	Element size.
	@param	nelem	Number of elements.
	@return	Pointer on success, NULL on error.
*/
void *
dkmem_alloc_tracked	DK_P2(size_t, elsize, size_t, nelem)
{
  void *back = NULL;
  size_t bytes;
  
  if(elsize && nelem) {
    bytes = check_size(elsize,nelem); 
    if(bytes) {
      back = dkmem_alloc(elsize, nelem);
    }
  }
#if !DKMEM_NO_TRACK
  if(back) { 
    track_bytes(bytes);
  }
#endif
  
  return back;
}



/**	Release memory.
	@param	ptr	Memory to release.
*/
void  dkmem_free		DK_P1(void *, ptr)
{
  
  if(ptr) {
#if DK_HAVE_FARMALLOC
    farfree(ptr);
#else
#if DK_HAVE_MALLOC
    free(ptr);
#endif
#endif
  }
  
}



