#ifdef DMEM
# warning "DMEM already defined : something bad is happenning."
#else // !defined(DMEM)
# define DMEM
#endif // DMEM

#define DMEM_KEEP_STD 

#include "dnew.hh"

#ifdef _QNX
# include <time.h>
#else // !defined(_QNX)
# include <sys/time.h>
#endif // _QNX

#include <stdio.h>
#include <assert.h>
#include <semaphore.h> 

extern "C" {

  static size_t mem_used = 0l, n_allocs = 0l;
  static FILE *dmem_dot_f = NULL;
  static sem_t *dmem_lock = NULL;

  static void dmem_add_dot() {
    if( NULL!=dmem_dot_f ) {
#ifdef _QNX
      struct timespec cur_time;
      
      clock_gettime(CLOCK_REALTIME, &cur_time);
      fprintf(dmem_dot_f, "%lu.%09lu\t%lu\t%lu\n",
	      cur_time.tv_sec, cur_time.tv_nsec, mem_used, n_allocs); 
#else // !defined(_QNX)
      struct rusage cur_time;

      getrusage(RUSAGE_SELF, &cur_time);
      fprintf(dmem_dot_f, "%lu.%06lu\t%lu\t%lu\n",
	      cur_time.ru_utime.tv_sec, cur_time.ru_utime.tv_usec, 
	      mem_used, n_allocs);
#endif // _QNX      
    }
  } // dmem_add_dot()

  static void dmem_eop() {
    if( NULL!=dmem_lock ) {
      sem_wait(dmem_lock); // CS
      if( NULL!=dmem_dot_f ) {
	dmem_add_dot();
	fclose(dmem_dot_f);
	dmem_dot_f = NULL;
      }
      if( mem_used>0 )
	fprintf(stderr, "dmem: %ul bytes (%ul allocs) not freed at EOP.\n",
		mem_used, n_allocs);
#ifdef _QNX
      sem_destroy(dmem_lock); // End CS
      free(dmem_lock); 
#else // !defined(_QNX)
      sem_close(dmem_lock); // End CS
#endif // _QNX
      dmem_lock = NULL;
    } 
  } // dmem_eop()

  static void dmem_init() {
    if( NULL==dmem_lock ) {
#ifdef _QNX
      dmem_lock = (sem_t *)malloc(sizeof(sem_t));
      if( -1 == sem_init(dmem_lock, 1, 0) ) {
#else // !defined(_QNX)
      /*
       * Looks like MacOS X does not implement unnamed semaphores.
       * So I manage it with a named one ... it will be sufficient
       * for testing purpose.
       */
      dmem_lock = sem_open("dmem_lock", O_CREAT, S_IRUSR|S_IWUSR, 0);
      if( dmem_lock==((sem_t*)SEM_FAILED) ) { 
#endif // _QNX
	perror("dmem_init");
	exit(-1);
      }
      atexit(dmem_eop);
      sem_post(dmem_lock);  
    } 
  } // dmem_init

  void *dmalloc(size_t bytes) {
    size_t *result;

    dmem_init();
    result = (size_t *)malloc(bytes+sizeof(size_t));
    if( NULL!=result ) {
      *(result++) = bytes;
      sem_wait(dmem_lock); // CS
      ++n_allocs;
      mem_used += bytes;
      dmem_add_dot();
      sem_post(dmem_lock); // End CS
    }
    return result;
  } // dmalloc(size_t)

  void *dcalloc(size_t size, size_t bytes) {
    return dmalloc(size*bytes);
  } // dcalloc(size_t, size_t)

  void dfree(void *ptr) {
    assert(NULL!=dmem_lock); // If dmem_lock is NULL I never used malloc
    if( NULL!=ptr ) {
      size_t *tmp = (size_t *)ptr, freed;
      
      freed = *(--tmp);
      sem_wait(dmem_lock); // CS
      if( 0==n_allocs || freed>mem_used ) {
	fprintf(stderr, "dmem: freeing more than I have allocated.\n");
	sem_post(dmem_lock); // End CS
	exit(-2);
      }
      --n_allocs;
      mem_used -= freed;
      dmem_add_dot();
      sem_post(dmem_lock); // End CS
      free(tmp);
    }
  } // dfree(void *)

  void dmem_dot(char const *fileName) {
    dmem_init();
    sem_wait(dmem_lock); // CS
    if( NULL==dmem_dot_f ) {
      dmem_dot_f = fopen(fileName, "w");
      if( NULL==dmem_dot_f )
	fprintf(stderr, "dmem: cannot create file \"%s\"\n", fileName);
      else 
	dmem_add_dot();
    } 
    sem_post(dmem_lock); // End CS
  } // dmem_dot(char const *)

} // extern "C"

#ifndef _QNX

void *operator new(size_t size) throw(std::bad_alloc) {
  for(;;) {
    void *ptr = operator new(size, std::nothrow);
    
    if( ptr )
      return ptr;
    else {
      std::new_handler handle = std::set_new_handler(0);
      std::set_new_handler(handle);
      handle();
    }
  }
} // operator new(size_t)

void operator delete(void *ptr) throw() {
  operator delete(ptr, std::nothrow);
} // operator delete(void *)

void *operator new[](size_t size) throw(std::bad_alloc) {
  for(;;) {
    void *ptr = operator new[](size, std::nothrow);
    
    if( ptr )
      return ptr;
    else {
      std::new_handler handle = std::set_new_handler(0);
      std::set_new_handler(handle);
      handle();
    }
  }  
} // operator new[](size_t)

void operator delete[](void *ptr) throw() {
  operator delete[](ptr, std::nothrow);
} // operator delete[](void *)

#endif // _QNX
