/*
 * CRTDLL memory functions
 * 
 * Copyright 1996,1998 Marcus Meissner
 * Copyright 1996 Jukka Iivonen
 * Copyright 1997,2000 Uwe Bonnes
 * Copyright 2000 Jon Griffiths
 *
 * Implementation Notes:
 * MT Safe.
 * heapwalk from win does not work. This is most likely due to internal
 * differences between wine and win (see memory/heap.c comments). This
 * version works fine, however.
 */

#include <string.h>
#include "crtdll.h"


DEFAULT_DEBUG_CHANNEL(crtdll);

static new_handler_type new_handler;


/*********************************************************************
 *                  new           (CRTDLL.001)
 *
 * Allocate memory.
 */
LPVOID CDECL CRTDLL_new(DWORD size)
{
    VOID* result;
    if(!(result = HeapAlloc(GetProcessHeap(),0,size)) && new_handler)
	(*new_handler)();
    return result;
}


/*********************************************************************
 *                  delete       (CRTDLL.002)
 *
 * Free memory created with new.
 */
VOID CDECL CRTDLL_delete(LPVOID ptr)
{
    HeapFree(GetProcessHeap(),0,ptr);
}


/*********************************************************************
 *                  set_new_handler(CRTDLL.003)
 */
new_handler_type CDECL CRTDLL_set_new_handler(new_handler_type func)
{
    new_handler_type old_handler = new_handler;
    new_handler = func;
    return old_handler;
}


/*********************************************************************
 *                  _expand        (CRTDLL.088)
 *
 * Increase the size of a block of memory allocated with malloc()
 * or realloc().
 */
LPVOID CDECL CRTDLL__expand(LPVOID ptr, INT size)
{
    return HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, ptr, size );
}


/*********************************************************************
 *                  _heapchk        (CRTDLL.130)
 *
 * Check the consistency of the process heap.
 */
INT CDECL CRTDLL__heapchk(VOID)
{
  // return (_heapchk());
  
  if (!HeapValidate( GetProcessHeap(), 0, NULL))
  {
    __CRTDLL__set_errno(GetLastError());
    return _HEAPBADNODE;
  }
  return _HEAPOK;
}


/*********************************************************************
 *                  _heapmin        (CRTDLL.131)
 *
 * Minimise the size of the heap.
 */
INT CDECL CRTDLL__heapmin(VOID)
{
  // return (_heapmin());
  
  if (!HeapCompact( GetProcessHeap(), 0 ))
  {
    if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
      __CRTDLL__set_errno(GetLastError());
    return -1;
  }
  return 0;
}


/*********************************************************************
 *                  _heapset        (CRTDLL.132)
 *
 * Fill free memory in the heap with a given value.
 */
INT CDECL CRTDLL__heapset(UINT value)
{
  // return (_heapset(fill));
  
  INT retVal;
  struct _heapinfo heap;

  memset( &heap, 0, sizeof(heap) );

  while ((retVal = CRTDLL__heapwalk(&heap)) == _HEAPOK)
  {
    if (heap._useflag == _FREEENTRY)
      memset(heap._pentry, value, heap._size);
  }
  return retVal == _HEAPEND? _HEAPOK : retVal;
}


/*********************************************************************
 *                  _heapwalk        (CRTDLL.133)
 *
 * Walk the heap block by block.
 */
INT CDECL CRTDLL__heapwalk(struct _heapinfo *next)
{
  PROCESS_HEAP_ENTRY phe;

  phe.lpData = next->_pentry;
  phe.cbData = next->_size;
  phe.wFlags = next->_useflag == _USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0;

  if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY && 
      !HeapValidate( GetProcessHeap(), 0, phe.lpData ))
  {
    __CRTDLL__set_errno(GetLastError());
    return _HEAPBADNODE;
  }

  do
  {
    if (!HeapWalk( GetProcessHeap(), &phe ))
    {
      if (GetLastError() == ERROR_NO_MORE_ITEMS)
        return _HEAPEND;
      __CRTDLL__set_errno(GetLastError());
      if (!phe.lpData)
        return _HEAPBADBEGIN;
      return _HEAPBADNODE;
    }
  } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE));

  next->_pentry = phe.lpData;
  next->_size = phe.cbData;
  next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? _USEDENTRY : _FREEENTRY;
  return _HEAPOK;
}


/*********************************************************************
 *                  _msize     (CRTDLL.234)
 *
 * Return the actual used size of an allocated block of memory.
 *
 */
LONG CDECL CRTDLL__msize(LPVOID mem)
{
  // return (_msize(ptr));
  
  LONG size = HeapSize(GetProcessHeap(),0,mem);
  if (size == -1)
  {
    WARN(":Probably called with non wine-allocated memory, ret = -1\n");
    /* At least the win98/nt crtdlls also return -1 in this case */
  }
  return size;
}


/*********************************************************************
 *                  calloc        (CRTDLL.350)
 *
 * Allocate memory from the heap and initialise it to zero.
 */
LPVOID CDECL CRTDLL_calloc(DWORD size, DWORD count)
{
    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size * count );
}


/*********************************************************************
 *                  free          (CRTDLL.375)
 *
 * Free a block of memory allocated with malloc()
 */
VOID CDECL CRTDLL_free(LPVOID ptr)
{
    HeapFree(GetProcessHeap(),0,ptr);
}


/*********************************************************************
 *                  malloc        (CRTDLL.424)
 * 
 * Alocate memory from the heap.
 */
LPVOID CDECL CRTDLL_malloc(DWORD size)
{
  LPVOID ret = HeapAlloc(GetProcessHeap(),0,size);
  if (!ret)
    __CRTDLL__set_errno(GetLastError());
  return ret;
}


/*********************************************************************
 *                  realloc        (CRTDLL.444)
 *
 * Resize a block of memory allocated with malloc() or realloc().
 */
LPVOID CDECL CRTDLL_realloc( VOID *ptr, DWORD size )
{
    return HeapReAlloc( GetProcessHeap(), 0, ptr, size );
}
