text: /*
   *  Example memory allocator with machine parseable logging.
   *
   *  Also sizes for reallocs and frees are logged so that the memory
   *  behavior can be essentially replayed to accurately determine e.g.
   *  optimal pool sizes for a pooled allocator.
   *
   *  Allocation structure:
   *
   *    [ alloc_hdr | user area ]
   *
   *     ^           ^
   *     |           `--- pointer returned to Duktape
   *     `--- underlying malloc ptr
   */
  
  #include "duktape.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <stdint.h>
  #include "duk_alloc_logging.h"
  
  #define  ALLOC_LOG_FILE  "/tmp/duk-alloc-log.txt"
  
  typedef struct {
  	/* The double value in the union is there to ensure alignment is
  	 * good for IEEE doubles too.  In many 32-bit environments 4 bytes
  	 * would be sufficiently aligned and the double value is unnecessary.
  	 */
  	union {
  		size_t sz;
  		double d;
  	} u;
  } alloc_hdr;
  
  static FILE *log_file = NULL;
  
  static void write_log(const char *fmt, ...) {
  	va_list ap;
  
  	if (!log_file) {
  		log_file = fopen(ALLOC_LOG_FILE, "wb");
  		if (!log_file) {
  			return;
  		}
  	}
  
  	va_start(ap, fmt);
  	vfprintf(log_file, fmt, ap);
  	va_end(ap);
  }
  
  void *duk_alloc_logging(void *udata, duk_size_t size) {
  	alloc_hdr *hdr;
  	void *ret;
  
  	(void) udata;  /* Suppress warning. */
  
  	if (size == 0) {
  		write_log("A NULL %ld\n", (long) size);
  		return NULL;
  	}
  
  	hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
  	if (!hdr) {
  		write_log("A FAIL %ld\n", (long) size);
  		return NULL;
  	}
  	hdr->u.sz = size;
  	ret = (void *) (hdr + 1);
  	write_log("A %p %ld\n", ret, (long) size);
  	return ret;
  }
  
  void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size) {
  	alloc_hdr *hdr;
  	size_t old_size;
  	void *t;
  	void *ret;
  
  	(void) udata;  /* Suppress warning. */
  
  	/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
  	 * platform assumptions.  You can get away with much less in specific
  	 * well-behaving environments.
  	 */
  
  	if (ptr) {
  		hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr));
  		old_size = hdr->u.sz;
  
  		if (size == 0) {
  			write_log("R %p %ld NULL 0\n", ptr, (long) old_size);
  			free((void *) hdr);
  			return NULL;
  		} else {
  			t = realloc((void *) hdr, size + sizeof(alloc_hdr));
  			if (!t) {
  				write_log("R %p %ld FAIL %ld\n", ptr, (long) old_size, (long) size);
  				return NULL;
  			}
  			hdr = (alloc_hdr *) t;
  			hdr->u.sz = size;
  			ret = (void *) (hdr + 1);
  			write_log("R %p %ld %p %ld\n", ptr, (long) old_size, ret, (long) size);
  			return ret;
  		}
  	} else {
  		if (size == 0) {
  			write_log("R NULL 0 NULL 0\n");
  			return NULL;
  		} else {
  			hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr));
  			if (!hdr) {
  				write_log("R NULL 0 FAIL %ld\n", (long) size);
  				return NULL;
  			}
  			hdr->u.sz = size;
  			ret = (void *) (hdr + 1);
  			write_log("R NULL 0 %p %ld\n", ret, (long) size);
  			return ret;
  		}
  	}
  }
  
  void duk_free_logging(void *udata, void *ptr) {
  	alloc_hdr *hdr;
  
  	(void) udata;  /* Suppress warning. */
  
  	if (!ptr) {
  		write_log("F NULL 0\n");
  		return;
  	}
  	hdr = (alloc_hdr *) (void *) ((unsigned char *) ptr - sizeof(alloc_hdr));
  	write_log("F %p %ld\n", ptr, (long) hdr->u.sz);
  	free((void *) hdr);
  }
  