/*
 * object.cxx
 *
 * Global object support.
 *
 * Portable Windows Library
 *
 * Copyright (c) 1993-1998 Equivalence Pty. Ltd.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
 *
 * Portions are Copyright (C) 1993 Free Software Foundation, Inc.
 * All Rights Reserved.
 *
 * Contributor(s): ______________________________________.
 *
 * $Log: object.cxx,v $
 * Revision 1.83  2005/12/12 00:24:26  dereksmithies
 * Add recursive mutex to Wrapper object, so one can successfully Dump Memory
 * usage Statistics & objects during program operation.
 *
 * Revision 1.82  2005/09/23 15:30:46  dominance
 * more progress to make mingw compile nicely. Thanks goes to Julien Puydt for pointing out to me how to do it properly. ;)
 *
 * Revision 1.81  2004/10/24 03:43:55  rjongbloed
 * Used correct test for inclusion of pragma implementation
 *
 * Revision 1.80  2004/10/23 10:55:40  ykiryanov
 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
 *
 * Revision 1.79  2004/07/11 07:56:36  csoutheren
 * Applied jumbo VxWorks patch, thanks to Eize Slange
 *
 * Revision 1.78  2004/07/01 11:41:30  csoutheren
 * Fixed compile and run problems on Linux
 *
 * Revision 1.77  2004/06/30 12:17:06  rjongbloed
 * Rewrite of plug in system to use single global variable for all factories to avoid all sorts
 *   of issues with startup orders and Windows DLL multiple instances.
 *
 * Revision 1.76  2004/06/29 08:20:46  rogerhardiman
 * The throws are needed on new and delete in GCC 2.95.x too.
 * This is now important as other code changes (factory and #include <string>)
 * now cause /usr/include/g++/new to be included too. Tested on FreeBSD 4.10
 *
 * Revision 1.75  2004/06/17 22:02:51  csoutheren
 * Added check to prevent allocationBreakpoint from causing unintentional break every 2^32 memory allocations
 *
 * Revision 1.74  2004/06/01 05:22:43  csoutheren
 * Restored memory check functionality
 *
 * Revision 1.73  2004/04/14 06:58:05  csoutheren
 * Fixed PAtomicInteger and PSmartPointer to use real atomic operations
 *
 * Revision 1.72  2004/04/13 11:47:52  csoutheren
 * Changed PSmartPtr to use PAtomicInteger
 *
 * Revision 1.71  2004/04/03 08:22:21  csoutheren
 * Remove pseudo-RTTI and replaced with real RTTI
 *
 * Revision 1.70  2004/04/03 08:09:16  csoutheren
 * Fixed compile problem on Windows
 *
 * Revision 1.69  2004/04/03 07:41:01  csoutheren
 * Fixed compile problem with ostringstream/ostrstream
 *
 * Revision 1.68  2004/04/03 06:54:28  rjongbloed
 * Many and various changes to support new Visual C++ 2003
 *
 * Revision 1.67  2003/09/17 09:02:14  csoutheren
 * Removed memory leak detection code
 *
 * Revision 1.66  2003/09/17 05:55:10  csoutheren
 * Removed recursive includes
 *
 * Revision 1.65  2003/02/11 08:05:55  robertj
 * Fixed problems in dumping memory leaks and statistics as PMemoryHeap
 *   is flagged deleted too early, thanks Diego Tártara
 *
 * Revision 1.64  2003/02/06 21:03:13  dereks
 * Patch from Klaus Kaempf to fix warning message on compiling with gcc 3.x Thanks!
 *
 * Revision 1.63  2002/11/22 10:14:58  robertj
 * Fixed correct free of memory blocks in exceptional circumstances. Partially
 *   trashed heap or objects etc.
 *
 * Revision 1.62  2002/10/21 12:52:27  rogerh
 * Add throw()s to new and delete. Error reported by FreeBSD 5.0 and GCC 3.2.1
 *
 * Revision 1.61  2002/10/10 04:43:44  robertj
 * VxWorks port, thanks Martijn Roest
 *
 * Revision 1.60  2002/09/06 05:29:42  craigs
 * Reversed order of memory block check on delete to improve performance in
 * Linux debug mode
 *
 * Revision 1.59  2002/09/04 05:23:53  robertj
 * Fixed crashable way of checking pointer is in PWLib heap check.
 *
 * Revision 1.58  2002/08/13 01:28:42  robertj
 * Added allocation number to memory statistics dump to aid in leak finding.
 *
 * Revision 1.57  2002/06/28 05:17:06  yurik
 * ifndef'd ostrstream as it does not yet exist on wince
 *
 * Revision 1.56  2002/06/27 06:10:39  robertj
 * Removed PAssert on allocation breakpoint as can often caus a deadlock.
 *
 * Revision 1.55  2002/06/25 10:33:55  robertj
 * REmoved usage of sstream for GNU v3 as it is completely different to the
 *   old ostrstream classes.
 *
 * Revision 1.54  2002/06/25 02:23:57  robertj
 * Improved assertion system to allow C++ class name to be displayed if
 *   desired, especially relevant to container classes.
 *
 * Revision 1.53  2002/06/13 08:50:11  rogerh
 * GCC 3.1 uses slightly different #includes
 *
 * Revision 1.52  2001/09/18 05:56:03  robertj
 * Fixed numerous problems with thread suspend/resume and signals handling.
 *
 * Revision 1.51  2001/08/16 11:58:22  rogerh
 * Add more Mac OS X changes from John Woods <jfw@jfwhome.funhouse.com>
 *
 * Revision 1.50  2001/06/20 06:05:47  rogerh
 * Updates for Mac OS X from Pai-Hsiang Hsiao <shawn@eecs.harvard.edu>
 *
 * Revision 1.49  2001/05/03 06:27:29  robertj
 * Added return value to PMemoryCheck::SetIgnoreAllocations() so get previous state.
 *
 * Revision 1.48  2001/02/18 23:16:02  robertj
 * Fixed possible NULL pointer reference in memory check validation
 *   function, thanks Peter Ehlin.
 *
 * Revision 1.47  2001/02/13 03:31:02  robertj
 * Added function to do heap validation.
 *
 * Revision 1.46  2001/02/07 04:47:49  robertj
 * Added changes for possible random crashes in multi DLL environment
 *   due to memory allocation wierdness, thanks Milan Dimitrijevic.
 *
 * Revision 1.45  2001/01/25 07:14:39  robertj
 * Fixed spurios memory leak message. Usual static global problem.
 *
 * Revision 1.44  2000/06/26 11:17:20  robertj
 * Nucleus++ port (incomplete).
 *
 * Revision 1.43  2000/01/05 00:29:12  robertj
 * Fixed alignment problems in memory checking debug functions.
 *
 * Revision 1.42  1999/11/01 00:17:20  robertj
 * Added override of new functions for MSVC memory check code.
 *
 * Revision 1.41  1999/08/22 13:38:39  robertj
 * Fixed termination hang up problem with memory check code under unix pthreads.
 *
 * Revision 1.40  1999/08/10 10:45:09  robertj
 * Added mutex in memory check detection code.
 *
 * Revision 1.39  1999/07/18 15:08:48  robertj
 * Fixed 64 bit compatibility
 *
 * Revision 1.38  1999/05/01 11:29:20  robertj
 * Alpha linux port changes.
 *
 * Revision 1.37  1999/03/09 10:30:17  robertj
 * Fixed ability to have PMEMORY_CHECK on/off on both debug/release versions.
 *
 * Revision 1.36  1999/02/22 10:48:14  robertj
 * Fixed delete operator prototypes for MSVC6 and GNU compatibility.
 *
 * Revision 1.35  1998/12/22 10:24:17  robertj
 * Fixed MSVC warnings caused by changes made in linux PPC support.
 *
 * Revision 1.34  1998/12/15 09:01:10  robertj
 * Fixed 8 byte alignment problem in memory leak check code for sparc.
 *
 * Revision 1.33  1998/11/30 05:33:00  robertj
 * Fixed duplicate debug stream class, ther can be only one.
 *
 * Revision 1.32  1998/11/03 03:11:53  robertj
 * Fixed memory leak question so correctly detects leaks and can be ^C'd.
 *
 * Revision 1.31  1998/11/03 00:55:31  robertj
 * Added allocation breakpoint variable.
 *
 * Revision 1.30  1998/10/15 07:48:56  robertj
 * Added hex dump to memory leak.
 * Added ability to ignore G++lib memory leaks.
 *
 * Revision 1.29  1998/10/15 01:53:35  robertj
 * GNU compatibility.
 *
 * Revision 1.28  1998/10/13 14:06:26  robertj
 * Complete rewrite of memory leak detection code.
 *
 * Revision 1.27  1998/09/23 06:22:22  robertj
 * Added open source copyright license.
 *
 * Revision 1.26  1998/05/30 13:27:02  robertj
 * Changed memory check code so global statics are not included in leak check.
 *
 * Revision 1.25  1997/07/08 13:07:07  robertj
 * DLL support.
 *
 * Revision 1.24  1997/02/09 03:45:28  robertj
 * Fixed unix/dos compatibility with include file.
 *
 * Revision 1.23  1997/02/05 11:54:12  robertj
 * Fixed problems with memory check and leak detection.
 *
 * Revision 1.22  1996/08/08 10:08:46  robertj
 * Directory structure changes for common files.
 *
 * Revision 1.21  1996/07/15 10:35:11  robertj
 * Changed memory leak dump to use static class rather than atexit for better portability.
 *
 * Revision 1.20  1996/06/17 11:35:47  robertj
 * Fixed display of memory leak info, needed flush and use of cin as getchar() does not work with services.
 *
 * Revision 1.19  1996/05/09 12:19:29  robertj
 * Fixed up 64 bit integer class for Mac platform.
 * Fixed incorrect use of memcmp/strcmp return value.
 *
 * Revision 1.18  1996/03/26 00:55:20  robertj
 * Added keypress before dumping memory leaks.
 *
 * Revision 1.17  1996/01/28 02:50:27  robertj
 * Added missing bit shift operators to 64 bit integer class.
 * Added assert into all Compare functions to assure comparison between compatible objects.
 *
 * Revision 1.16  1996/01/23 13:15:52  robertj
 * Mac Metrowerks compiler support.
 *
 * Revision 1.15  1996/01/02 12:52:02  robertj
 * Mac OS compatibility changes.
 *
 * Revision 1.14  1995/11/21 11:51:54  robertj
 * Improved streams compatibility.
 *
 * Revision 1.12  1995/04/25 11:30:34  robertj
 * Fixed Borland compiler warnings.
 * Fixed function hiding ancestors virtual.
 *
 * Revision 1.11  1995/03/12 04:59:53  robertj
 * Re-organisation of DOS/WIN16 and WIN32 platforms to maximise common code.
 * Used built-in equate for WIN32 API (_WIN32).
 *
 * Revision 1.10  1995/02/19  04:19:21  robertj
 * Added dynamically linked command processing.
 *
 * Revision 1.9  1995/01/15  04:52:02  robertj
 * Mac compatibility.
 * Added memory stats function.
 *
// Revision 1.8  1995/01/09  12:38:07  robertj
// Changed variable names around during documentation run.
// Fixed smart pointer comparison.
// Fixed serialisation stuff.
//
// Revision 1.7  1995/01/07  04:39:45  robertj
// Redesigned font enumeration code and changed font styles.
//
// Revision 1.6  1995/01/04  10:57:08  robertj
// Changed for HPUX and GNU2.6.x
//
// Revision 1.5  1995/01/03  09:39:10  robertj
// Put standard malloc style memory allocation etc into memory check system.
//
// Revision 1.4  1994/12/21  11:43:29  robertj
// Added extra memory stats.
//
// Revision 1.3  1994/12/13  11:54:54  robertj
// Added some memory usage statistics.
//
// Revision 1.2  1994/12/12  10:08:32  robertj
// Renamed PWrapper to PSmartPointer..
//
// Revision 1.1  1994/10/30  12:02:15  robertj
// Initial revision
//
 */

#ifdef __GNUC__
#pragma implementation "pfactory.h"
#endif // __GNUC__

#include <ptlib.h>
#include <ctype.h>
#ifdef _WIN32
#include <ptlib/msos/ptlib/debstrm.h>
#ifdef _MSC_VER
#include <crtdbg.h>
#endif
#elif defined(__NUCLEUS_PLUS__)
#include <ptlib/NucleusDebstrm.h>
#else
#include <signal.h>
#endif


PFactoryBase::FactoryMap & PFactoryBase::GetFactories()
{
  static FactoryMap factories;
  return factories;
}

PMutex & PFactoryBase::GetFactoriesMutex()
{
  static PMutex mutex;
  return mutex;
}

PFactoryBase::FactoryMap::~FactoryMap()
{
  FactoryMap::const_iterator entry;
  for (entry = begin(); entry != end(); ++entry)
    delete entry->second;
}

void PAssertFunc(const char * file,
                 int line,
                 const char * className,
                 PStandardAssertMessage msg)
{
  if (msg == POutOfMemory) {
    // Special case, do not use ostrstream in other PAssertFunc if have
    // a memory out situation as that would probably also fail!
    static const char fmt[] = "Out of memory at file %.100s, line %u, class %.30s";
    char msgbuf[sizeof(fmt)+100+10+30];
    sprintf(msgbuf, fmt, file, line, className);
    PAssertFunc(msgbuf);
    return;
  }

  static const char * const textmsg[PMaxStandardAssertMessage] = {
    NULL,
    "Out of memory",
    "Null pointer reference",
    "Invalid cast to non-descendant class",
    "Invalid array index",
    "Invalid array element",
    "Stack empty",
    "Unimplemented function",
    "Invalid parameter",
    "Operating System error",
    "File not open",
    "Unsupported feature",
    "Invalid or closed operating system window"
  };

  const char * theMsg;
  char msgbuf[20];
  if (msg < PMaxStandardAssertMessage)
    theMsg = textmsg[msg];
  else {
    sprintf(msgbuf, "Assertion %i", msg);
    theMsg = msgbuf;
  }
  PAssertFunc(file, line, className, theMsg);
}


void PAssertFunc(const char * file, int line, const char * className, const char * msg)
{
#if defined(_WIN32)
  DWORD err = GetLastError();
#else
  int err = errno;
#endif

#ifndef _WIN32_WCE

  #if (__GNUC__ >= 3) && defined (__USE_STL__)
  ostringstream str;
  #else
  ostrstream str;
  #endif
  str << "Assertion fail: ";
  if (msg != NULL)
    str << msg << ", ";
  str << "file " << file << ", line " << line;
  if (className != NULL)
    str << ", class " << className;
  if (err != 0)
    str << ", Error=" << err;
  str << ends;
  
  #if (__GNUC__ >= 3) && defined (__USE_STL__)
  const char * s = str.str().c_str();
  #else
  const char * s = str.str();
  #endif

  PAssertFunc(s);

#endif // !_WIN32_WCE
}

PObject::Comparison PObject::CompareObjectMemoryDirect(const PObject&obj) const
{
  int retval = memcmp(this, &obj, sizeof(PObject));
  if (retval < 0)
    return LessThan;
  if (retval > 0)
    return GreaterThan;
  return EqualTo;
}


PObject * PObject::Clone() const
{
  PAssertAlways(PUnimplementedFunction);
  return NULL;
}


PObject::Comparison PObject::Compare(const PObject & obj) const
{
  return (Comparison)CompareObjectMemoryDirect(obj);
}


void PObject::PrintOn(ostream & strm) const
{
  strm << GetClass();
}


void PObject::ReadFrom(istream &)
{
}


PINDEX PObject::HashFunction() const
{
  return 0;
}


///////////////////////////////////////////////////////////////////////////////
// General reference counting support

PSmartPointer::PSmartPointer(const PSmartPointer & ptr)
{
  object = ptr.object;
  if (object != NULL)
    ++object->referenceCount;
}


PSmartPointer & PSmartPointer::operator=(const PSmartPointer & ptr)
{
  if (object == ptr.object)
    return *this;

  if ((object != NULL) && (--object->referenceCount == 0))
      delete object;

  object = ptr.object;
  if (object != NULL)
    ++object->referenceCount;

  return *this;
}


PSmartPointer::~PSmartPointer()
{
  if ((object != NULL) && (--object->referenceCount == 0))
    delete object;
}


PObject::Comparison PSmartPointer::Compare(const PObject & obj) const
{
  PAssert(PIsDescendant(&obj, PSmartPointer), PInvalidCast);
  PSmartObject * other = ((const PSmartPointer &)obj).object;
  if (object == other)
    return EqualTo;
  return object < other ? LessThan : GreaterThan;
}



//////////////////////////////////////////////////////////////////////////////////////////

#if PMEMORY_CHECK

#undef malloc
#undef realloc
#undef free

#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void * operator new(size_t nSize) throw (std::bad_alloc)
#else
void * operator new(size_t nSize)
#endif
{
  return PMemoryHeap::Allocate(nSize, (const char *)NULL, 0, NULL);
}


#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void * operator new[](size_t nSize) throw (std::bad_alloc)
#else
void * operator new[](size_t nSize)
#endif
{
  return PMemoryHeap::Allocate(nSize, (const char *)NULL, 0, NULL);
}


#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void operator delete(void * ptr) throw()
#else
void operator delete(void * ptr)
#endif
{
  PMemoryHeap::Deallocate(ptr, NULL);
}


#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void operator delete[](void * ptr) throw()
#else
void operator delete[](void * ptr)
#endif
{
  PMemoryHeap::Deallocate(ptr, NULL);
}


DWORD PMemoryHeap::allocationBreakpoint = 0;
char PMemoryHeap::Header::GuardBytes[NumGuardBytes];


PMemoryHeap::Wrapper::Wrapper()
{
  // The following is done like this to get over brain dead compilers that cannot
  // guarentee that a static global is contructed before it is used.
  static PMemoryHeap real_instance;
  instance = &real_instance;
  if (instance->isDestroyed)
    return;

#if defined(_WIN32)
  EnterCriticalSection(&instance->mutex);
#elif defined(P_MAC_MPTHREADS)
  long err;
  PAssertOS((err = MPEnterCriticalRegion(instance->mutex, kDurationForever)) == 0);
#elif defined(P_PTHREADS)
  pthread_mutex_lock(&instance->mutex);
#elif defined(P_VXWORKS)
  semTake((SEM_ID)instance->mutex, WAIT_FOREVER);
#endif
}


PMemoryHeap::Wrapper::~Wrapper()
{
  if (instance->isDestroyed)
    return;

#if defined(_WIN32)
  LeaveCriticalSection(&instance->mutex);
#elif defined(P_MAC_MPTHREADS)
  long err;
  PAssertOS((err = MPExitCriticalRegion(instance->mutex)) == 0 || instance->isDestroyed);
#elif defined(P_PTHREADS)
  pthread_mutex_unlock(&instance->mutex);
#elif defined(P_VXWORKS)
  semGive((SEM_ID)instance->mutex);
#endif
}


PMemoryHeap::PMemoryHeap()
{
  isDestroyed = FALSE;

  listHead = NULL;
  listTail = NULL;

  allocationRequest = 1;
  firstRealObject = 0;
  flags = NoLeakPrint;

  allocFillChar = '\x5A';
  freeFillChar = '\xA5';

  currentMemoryUsage = 0;
  peakMemoryUsage = 0;
  currentObjects = 0;
  peakObjects = 0;
  totalObjects = 0;

  for (PINDEX i = 0; i < Header::NumGuardBytes; i++)
    Header::GuardBytes[i] = (i&1) == 0 ? '\x55' : '\xaa';

#if defined(_WIN32)
  InitializeCriticalSection(&mutex);
  static PDebugStream debug;
  leakDumpStream = &debug;
#else
#if defined(P_PTHREADS)
#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
  pthread_mutex_t recursiveMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
  mutex = recursiveMutex;
#else
 pthread_mutex_init(&mutex, NULL);
#endif
#elif defined(P_VXWORKS)
  mutex = semMCreate(SEM_Q_FIFO);
#endif
  leakDumpStream = &cerr;
#endif
}


PMemoryHeap::~PMemoryHeap()
{
  if (leakDumpStream != NULL) {
    InternalDumpStatistics(*leakDumpStream);
    InternalDumpObjectsSince(firstRealObject, *leakDumpStream);
  }

  isDestroyed = TRUE;

#if defined(_WIN32)
  DeleteCriticalSection(&mutex);
  extern void PWaitOnExitConsoleWindow();
  PWaitOnExitConsoleWindow();
#elif defined(P_PTHREADS)
  pthread_mutex_destroy(&mutex);
#elif defined(P_VXWORKS)
  semDelete((SEM_ID)mutex);
#endif
}


void * PMemoryHeap::Allocate(size_t nSize, const char * file, int line, const char * className)
{
  Wrapper mem;
  return mem->InternalAllocate(nSize, file, line, className);
}


void * PMemoryHeap::Allocate(size_t count, size_t size, const char * file, int line)
{
  Wrapper mem;

  char oldFill = mem->allocFillChar;
  mem->allocFillChar = '\0';

  void * data = mem->InternalAllocate(count*size, file, line, NULL);

  mem->allocFillChar = oldFill;

  return data;
}


void * PMemoryHeap::InternalAllocate(size_t nSize, const char * file, int line, const char * className)
{
  if (isDestroyed)
    return malloc(nSize);

  Header * obj = (Header *)malloc(sizeof(Header) + nSize + sizeof(Header::GuardBytes));
  if (obj == NULL) {
    PAssertAlways(POutOfMemory);
    return NULL;
  }

  // Ignore all allocations made before main() is called. This is indicated
  // by PProcess::PreInitialise() clearing the NoLeakPrint flag. Why do we do
  // this? because the GNU compiler is broken in the way it does static global
  // C++ object construction and destruction.
  if (firstRealObject == 0 && (flags&NoLeakPrint) == 0)
    firstRealObject = allocationRequest;

  if (allocationBreakpoint != 0 && allocationRequest == allocationBreakpoint) {
#ifdef _WIN32
    __asm int 3;
#elif defined(P_VXWORKS)
    kill(taskIdSelf(), SIGABRT);
#else
    kill(getpid(), SIGABRT);
#endif 
  }

  currentMemoryUsage += nSize;
  if (currentMemoryUsage > peakMemoryUsage)
    peakMemoryUsage = currentMemoryUsage;

  currentObjects++;
  if (currentObjects > peakObjects)
    peakObjects = currentObjects;
  totalObjects++;

  char * data = (char *)&obj[1];

  obj->prev      = listTail;
  obj->next      = NULL;
  obj->size      = nSize;
  obj->fileName  = file;
  obj->line      = (WORD)line;
  obj->className = className;
  obj->request   = allocationRequest++;
  obj->flags     = flags;
  memcpy(obj->guard, obj->GuardBytes, sizeof(obj->guard));
  memset(data, allocFillChar, nSize);
  memcpy(&data[nSize], obj->GuardBytes, sizeof(obj->guard));

  if (listTail != NULL)
    listTail->next = obj;

  listTail = obj;

  if (listHead == NULL)
    listHead = obj;

  return data;
}


void * PMemoryHeap::Reallocate(void * ptr, size_t nSize, const char * file, int line)
{
  if (ptr == NULL)
    return Allocate(nSize, file, line, NULL);

  if (nSize == 0) {
    Deallocate(ptr, NULL);
    return NULL;
  }

  Wrapper mem;

  if (mem->isDestroyed)
    return realloc(ptr, nSize);

  if (mem->InternalValidate(ptr, NULL, mem->leakDumpStream) != Ok)
    return NULL;

  Header * obj = (Header *)realloc(((Header *)ptr)-1, sizeof(Header) + nSize + sizeof(obj->guard));
  if (obj == NULL) {
    PAssertAlways(POutOfMemory);
    return NULL;
  }

  if (mem->allocationBreakpoint != 0 && mem->allocationRequest == mem->allocationBreakpoint) {
#ifdef _WIN32
    __asm int 3;
#elif defined(P_VXWORKS)
    kill(taskIdSelf(), SIGABRT);
#else
    kill(getpid(), SIGABRT);
#endif 
  }

  mem->currentMemoryUsage -= obj->size;
  mem->currentMemoryUsage += nSize;
  if (mem->currentMemoryUsage > mem->peakMemoryUsage)
    mem->peakMemoryUsage = mem->currentMemoryUsage;

  char * data = (char *)&obj[1];
  memcpy(&data[nSize], obj->GuardBytes, sizeof(obj->guard));

  obj->size      = nSize;
  obj->fileName  = file;
  obj->line      = (WORD)line;
  obj->request   = mem->allocationRequest++;
  if (obj->prev != NULL)
    obj->prev->next = obj;
  else
    mem->listHead = obj;
  if (obj->next != NULL)
    obj->next->prev = obj;
  else
    mem->listTail = obj;

  return data;
}


void PMemoryHeap::Deallocate(void * ptr, const char * className)
{
  if (ptr == NULL)
    return;

  Wrapper mem;
  Header * obj = ((Header *)ptr)-1;

  if (mem->isDestroyed) {
    free(obj);
    return;
  }

  switch (mem->InternalValidate(ptr, className, mem->leakDumpStream)) {
    case Ok :
      break;
    case Trashed :
      free(ptr);
      return;
    case Bad :
      free(obj);
      return;
  }

  if (obj->prev != NULL)
    obj->prev->next = obj->next;
  else
    mem->listHead = obj->next;
  if (obj->next != NULL)
    obj->next->prev = obj->prev;
  else
    mem->listTail = obj->prev;

  mem->currentMemoryUsage -= obj->size;
  mem->currentObjects--;

  memset(ptr, mem->freeFillChar, obj->size);  // Make use of freed data noticable
  free(obj);
}


PMemoryHeap::Validation PMemoryHeap::Validate(void * ptr,
                                              const char * className,
                                              ostream * error)
{
  Wrapper mem;
  return mem->InternalValidate(ptr, className, error);
}


PMemoryHeap::Validation PMemoryHeap::InternalValidate(void * ptr,
                                                      const char * className,
                                                      ostream * error)
{
  if (isDestroyed)
    return Bad;

  if (ptr == NULL)
    return Trashed;

  Header * obj = ((Header *)ptr)-1;

  Header * link = listTail;  
  while (link != NULL && link != obj) 
    link = link->prev;  

  if (link == NULL) {
    if (error != NULL)
      *error << "Block " << ptr << " not in heap!" << endl;
    return Trashed;
  }

  if (memcmp(obj->guard, obj->GuardBytes, sizeof(obj->guard)) != 0) {
    if (error != NULL)
      *error << "Underrun at " << ptr << '[' << obj->size << "] #" << obj->request << endl;
    return Bad;
  }
  
  if (memcmp((char *)ptr+obj->size, obj->GuardBytes, sizeof(obj->guard)) != 0) {
    if (error != NULL)
      *error << "Overrun at " << ptr << '[' << obj->size << "] #" << obj->request << endl;
    return Bad;
  }
  
  if (!(className == NULL && obj->className == NULL) &&
       (className == NULL || obj->className == NULL ||
       (className != obj->className && strcmp(obj->className, className) != 0))) {
    if (error != NULL)
      *error << "PObject " << ptr << '[' << obj->size << "] #" << obj->request
             << " allocated as \"" << (obj->className != NULL ? obj->className : "<NULL>")
             << "\" and should be \"" << (className != NULL ? className : "<NULL>")
             << "\"." << endl;
    return Bad;
  }

  return Ok;
}


BOOL PMemoryHeap::ValidateHeap(ostream * error)
{
  Wrapper mem;

  if (error == NULL)
    error = mem->leakDumpStream;

  Header * obj = mem->listHead;
  while (obj != NULL) {
    if (memcmp(obj->guard, obj->GuardBytes, sizeof(obj->guard)) != 0) {
      if (error != NULL)
        *error << "Underrun at " << (obj+1) << '[' << obj->size << "] #" << obj->request << endl;
      return FALSE;
    }
  
    if (memcmp((char *)(obj+1)+obj->size, obj->GuardBytes, sizeof(obj->guard)) != 0) {
      if (error != NULL)
        *error << "Overrun at " << (obj+1) << '[' << obj->size << "] #" << obj->request << endl;
      return FALSE;
    }

    obj = obj->next;
  }

#if defined(_WIN32) && defined(_DEBUG)
  if (!_CrtCheckMemory()) {
    if (error != NULL)
      *error << "Heap failed MSVCRT validation!" << endl;
    return FALSE;
  }
#endif
  if (error != NULL)
    *error << "Heap passed validation." << endl;
  return TRUE;
}


BOOL PMemoryHeap::SetIgnoreAllocations(BOOL ignore)
{
  Wrapper mem;

  BOOL ignoreAllocations = (mem->flags&NoLeakPrint) != 0;

  if (ignore)
    mem->flags |= NoLeakPrint;
  else
    mem->flags &= ~NoLeakPrint;

  return ignoreAllocations;
}


void PMemoryHeap::DumpStatistics()
{
  Wrapper mem;
  if (mem->leakDumpStream != NULL)
    mem->InternalDumpStatistics(*mem->leakDumpStream);
}


void PMemoryHeap::DumpStatistics(ostream & strm)
{
  Wrapper mem;
  mem->InternalDumpStatistics(strm);
}


void PMemoryHeap::InternalDumpStatistics(ostream & strm)
{
  strm << "\nCurrent memory usage: " << currentMemoryUsage << " bytes";
  if (currentMemoryUsage > 2048)
    strm << ", " << (currentMemoryUsage+1023)/1024 << "kb";
  if (currentMemoryUsage > 2097152)
    strm << ", " << (currentMemoryUsage+1048575)/1048576 << "Mb";

  strm << ".\nCurrent objects count: " << currentObjects
       << "\nPeak memory usage: " << peakMemoryUsage << " bytes";
  if (peakMemoryUsage > 2048)
    strm << ", " << (peakMemoryUsage+1023)/1024 << "kb";
  if (peakMemoryUsage > 2097152)
    strm << ", " << (peakMemoryUsage+1048575)/1048576 << "Mb";

  strm << ".\nPeak objects created: " << peakObjects
       << "\nTotal objects created: " << totalObjects
       << "\nNext allocation request: " << allocationRequest
       << '\n' << endl;
}


DWORD PMemoryHeap::GetAllocationRequest()
{
  Wrapper mem;
  return mem->allocationRequest;
}


void PMemoryHeap::SetAllocationBreakpoint(DWORD point)
{
  allocationBreakpoint = point;
}


void PMemoryHeap::DumpObjectsSince(DWORD objectNumber)
{
  Wrapper mem;
  if (mem->leakDumpStream != NULL)
    mem->InternalDumpObjectsSince(objectNumber, *mem->leakDumpStream);
}


void PMemoryHeap::DumpObjectsSince(DWORD objectNumber, ostream & strm)
{
  Wrapper mem;
  mem->InternalDumpObjectsSince(objectNumber, strm);
}


void PMemoryHeap::InternalDumpObjectsSince(DWORD objectNumber, ostream & strm)
{
  BOOL first = TRUE;
  for (Header * obj = listHead; obj != NULL; obj = obj->next) {
    if (obj->request < objectNumber || (obj->flags&NoLeakPrint) != 0)
      continue;

    if (first && isDestroyed) {
      *leakDumpStream << "\nMemory leaks detected, press Enter to display . . ." << flush;
#if !defined(_WIN32)
      cin.get();
#endif
      first = FALSE;
    }

    BYTE * data = (BYTE *)&obj[1];

    if (obj->fileName != NULL)
      strm << obj->fileName << '(' << obj->line << ") : ";

    strm << '#' << obj->request << ' ' << (void *)data << " [" << obj->size << "] ";

    if (obj->className != NULL)
      strm << '"' << obj->className << "\" ";

    strm << '\n' << hex << setfill('0') << PBYTEArray(data, PMIN(16, obj->size), FALSE)
                 << dec << setfill(' ') << endl;
  }
}


#else // PMEMORY_CHECK

#ifndef P_VXWORKS

#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void * operator new[](size_t nSize) throw (std::bad_alloc)
#else
void * operator new[](size_t nSize)
#endif
{
  return malloc(nSize);
}

#if (__GNUC__ >= 3) || ((__GNUC__ == 2)&&(__GNUC_MINOR__ >= 95)) //2.95.X & 3.X
void operator delete[](void * ptr) throw ()
#else
void operator delete[](void * ptr)
#endif
{
  free(ptr);
}

#endif // !P_VXWORKS

#endif // PMEMORY_CHECK




///////////////////////////////////////////////////////////////////////////////
// Large integer support

#ifdef P_NEEDS_INT64

void PInt64__::Add(const PInt64__ & v)
{
  unsigned long old = low;
  high += v.high;
  low += v.low;
  if (low < old)
    high++;
}


void PInt64__::Sub(const PInt64__ & v)
{
  unsigned long old = low;
  high -= v.high;
  low -= v.low;
  if (low > old)
    high--;
}


void PInt64__::Mul(const PInt64__ & v)
{
  DWORD p1 = (low&0xffff)*(v.low&0xffff);
  DWORD p2 = (low >> 16)*(v.low >> 16);
  DWORD p3 = (high&0xffff)*(v.high&0xffff);
  DWORD p4 = (high >> 16)*(v.high >> 16);
  low = p1 + (p2 << 16);
  high = (p2 >> 16) + p3 + (p4 << 16);
}


void PInt64__::Div(const PInt64__ & v)
{
  long double dividend = high;
  dividend *=  4294967296.0;
  dividend += low;
  long double divisor = high;
  divisor *=  4294967296.0;
  divisor += low;
  long double quotient = dividend/divisor;
  low = quotient;
  high = quotient/4294967296.0;
}


void PInt64__::Mod(const PInt64__ & v)
{
  PInt64__ t = *this;
  t.Div(v);
  t.Mul(t);
  Sub(t);
}


void PInt64__::ShiftLeft(int bits)
{
  if (bits >= 32) {
    high = low << (bits - 32);
    low = 0;
  }
  else {
    high <<= bits;
    high |= low >> (32 - bits);
    low <<= bits;
  }
}


void PInt64__::ShiftRight(int bits)
{
  if (bits >= 32) {
    low = high >> (bits - 32);
    high = 0;
  }
  else {
    low >>= bits;
    low |= high << (32 - bits);
    high >>= bits;
  }
}


BOOL PInt64::Lt(const PInt64 & v) const
{
  if ((long)high < (long)v.high)
    return TRUE;
  if ((long)high > (long)v.high)
    return FALSE;
  if ((long)high < 0)
    return (long)low > (long)v.low;
  return (long)low < (long)v.low;
}


BOOL PInt64::Gt(const PInt64 & v) const
{
  if ((long)high > (long)v.high)
    return TRUE;
  if ((long)high < (long)v.high)
    return FALSE;
  if ((long)high < 0)
    return (long)low < (long)v.low;
  return (long)low > (long)v.low;
}


BOOL PUInt64::Lt(const PUInt64 & v) const
{
  if (high < v.high)
    return TRUE;
  if (high > v.high)
    return FALSE;
  return low < high;
}


BOOL PUInt64::Gt(const PUInt64 & v) const
{
  if (high > v.high)
    return TRUE;
  if (high < v.high)
    return FALSE;
  return low > high;
}


static void Out64(ostream & stream, PUInt64 num)
{
  char buf[25];
  char * p = &buf[sizeof(buf)];
  *--p = '\0';

  switch (stream.flags()&ios::basefield) {
    case ios::oct :
      while (num != 0) {
        *--p = (num&7) + '0';
        num >>= 3;
      }
      break;

    case ios::hex :
      while (num != 0) {
        *--p = (num&15) + '0';
        if (*p > '9')
          *p += 7;
        num >>= 4;
      }
      break;

    default :
      while (num != 0) {
        *--p = num%10 + '0';
        num /= 10;
      }
  }

  if (*p == '\0')
    *--p = '0';

  stream << p;
}


ostream & operator<<(ostream & stream, const PInt64 & v)
{
  if (v >= 0)
    Out64(stream, v);
  else {
    int w = stream.width();
    stream.put('-');
    if (w > 0)
      stream.width(w-1);
    Out64(stream, -v);
  }

  return stream;
}


ostream & operator<<(ostream & stream, const PUInt64 & v)
{
  Out64(stream, v);
  return stream;
}


static PUInt64 Inp64(istream & stream)
{
  int base;
  switch (stream.flags()&ios::basefield) {
    case ios::oct :
      base = 8;
      break;
    case ios::hex :
      base = 16;
      break;
    default :
      base = 10;
  }

  if (isspace(stream.peek()))
    stream.get();

  PInt64 num = 0;
  while (isxdigit(stream.peek())) {
    int c = stream.get() - '0';
    if (c > 9)
      c -= 7;
    if (c > 9)
      c -= 32;
    num = num*base + c;
  }

  return num;
}


istream & operator>>(istream & stream, PInt64 & v)
{
  if (isspace(stream.peek()))
    stream.get();

  switch (stream.peek()) {
    case '-' :
      stream.ignore();
      v = -(PInt64)Inp64(stream);
      break;
    case '+' :
      stream.ignore();
    default :
      v = (PInt64)Inp64(stream);
  }

  return stream;
}


istream & operator>>(istream & stream, PUInt64 & v)
{
  v = Inp64(stream);
  return stream;
}


#endif


#ifdef P_TORNADO

// the library provided with Tornado 2.0 does not contain implementation 
// for the functions defined below, therefor the own implementation

ostream & ostream::operator<<(PInt64 v)
{
  return *this << (long)(v >> 32) << (long)(v & 0xFFFFFFFF);
}


ostream & ostream::operator<<(PUInt64 v)
{
  return *this << (long)(v >> 32) << (long)(v & 0xFFFFFFFF);
}

istream & istream::operator>>(PInt64 & v)
{
  return *this >> (long)(v >> 32) >> (long)(v & 0xFFFFFFFF);
}


istream & istream::operator>>(PUInt64 & v)
{
  return *this >> (long)(v >> 32) >> (long)(v & 0xFFFFFFFF);
}

#endif // P_TORNADO


// End Of File ///////////////////////////////////////////////////////////////


syntax highlighted by Code2HTML, v. 0.9.1