Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b

Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/mem/mem.c b/src/mem/mem.c
new file mode 100644
index 0000000..1f634c6
--- /dev/null
+++ b/src/mem/mem.c
@@ -0,0 +1,509 @@
+/**
+ * @file mem.c  Memory management with reference counting
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+#include <re_types.h>
+#include <re_list.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_mem.h>
+
+
+#define DEBUG_MODULE "mem"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+#ifndef RELEASE
+#define MEM_DEBUG 1  /**< Enable memory debugging */
+#endif
+
+
+/** Defines a reference-counting memory object */
+struct mem {
+	uint32_t nrefs;     /**< Number of references  */
+	mem_destroy_h *dh;  /**< Destroy handler       */
+#if MEM_DEBUG
+	struct le le;       /**< Linked list element   */
+	uint32_t magic;     /**< Magic number          */
+	size_t size;        /**< Size of memory object */
+#endif
+};
+
+#if MEM_DEBUG
+/* Memory debugging */
+static struct list meml = LIST_INIT;
+static const uint32_t mem_magic = 0xe7fb9ac4;
+static ssize_t threshold = -1;  /**< Memory threshold, disabled by default */
+
+static struct memstat memstat = {
+	0,0,0,0,~0,0
+};
+
+#ifdef HAVE_PTHREAD
+
+static pthread_mutex_t mem_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static inline void mem_lock(void)
+{
+	pthread_mutex_lock(&mem_mutex);
+}
+
+
+static inline void mem_unlock(void)
+{
+	pthread_mutex_unlock(&mem_mutex);
+}
+
+#else
+
+#define mem_lock()    /**< Stub */
+#define mem_unlock()  /**< Stub */
+
+#endif
+
+/** Update statistics for mem_zalloc() */
+#define STAT_ALLOC(m, size) \
+	mem_lock(); \
+	memstat.bytes_cur += (size); \
+	memstat.bytes_peak = max(memstat.bytes_cur, memstat.bytes_peak); \
+	++memstat.blocks_cur; \
+	memstat.blocks_peak = max(memstat.blocks_cur, memstat.blocks_peak); \
+	memstat.size_min = min(memstat.size_min, size);	\
+	memstat.size_max = max(memstat.size_max, size);	\
+	mem_unlock(); \
+	(m)->size = (size); \
+	(m)->magic = mem_magic;
+
+/** Update statistics for mem_realloc() */
+#define STAT_REALLOC(m, size) \
+	mem_lock(); \
+	memstat.bytes_cur += ((size) - (m)->size); \
+	memstat.bytes_peak = max(memstat.bytes_cur, memstat.bytes_peak); \
+	memstat.size_min = min(memstat.size_min, size);	\
+	memstat.size_max = max(memstat.size_max, size); \
+	mem_unlock(); \
+	(m)->size = (size)
+
+/** Update statistics for mem_deref() */
+#define STAT_DEREF(m) \
+	mem_lock(); \
+	memstat.bytes_cur -= (m)->size; \
+	--memstat.blocks_cur; \
+	mem_unlock(); \
+	memset((m), 0xb5, sizeof(struct mem) + (m)->size)
+
+/** Check magic number in memory object */
+#define MAGIC_CHECK(m) \
+	if (mem_magic != (m)->magic) { \
+		DEBUG_WARNING("%s: magic check failed 0x%08x (%p)\n", \
+			      __REFUNC__, (m)->magic, (m)+1);	      \
+		BREAKPOINT;					      \
+	}
+#else
+#define STAT_ALLOC(m, size)
+#define STAT_REALLOC(m, size)
+#define STAT_DEREF(m)
+#define MAGIC_CHECK(m)
+#endif
+
+
+/**
+ * Allocate a new reference-counted memory object
+ *
+ * @param size Size of memory object
+ * @param dh   Optional destructor, called when destroyed
+ *
+ * @return Pointer to allocated object
+ */
+void *mem_alloc(size_t size, mem_destroy_h *dh)
+{
+	struct mem *m;
+
+#if MEM_DEBUG
+	mem_lock();
+	if (-1 != threshold && (memstat.blocks_cur >= (size_t)threshold)) {
+		mem_unlock();
+		return NULL;
+	}
+	mem_unlock();
+#endif
+
+	m = malloc(sizeof(*m) + size);
+	if (!m)
+		return NULL;
+
+#if MEM_DEBUG
+	memset(&m->le, 0, sizeof(struct le));
+	mem_lock();
+	list_append(&meml, &m->le, m);
+	mem_unlock();
+#endif
+
+	m->nrefs = 1;
+	m->dh    = dh;
+
+	STAT_ALLOC(m, size);
+
+	return (void *)(m + 1);
+}
+
+
+/**
+ * Allocate a new reference-counted memory object. Memory is zeroed.
+ *
+ * @param size Size of memory object
+ * @param dh   Optional destructor, called when destroyed
+ *
+ * @return Pointer to allocated object
+ */
+void *mem_zalloc(size_t size, mem_destroy_h *dh)
+{
+	void *p;
+
+	p = mem_alloc(size, dh);
+	if (!p)
+		return NULL;
+
+	memset(p, 0, size);
+
+	return p;
+}
+
+
+/**
+ * Re-allocate a reference-counted memory object
+ *
+ * @param data Memory object
+ * @param size New size of memory object
+ *
+ * @return New pointer to allocated object
+ *
+ * @note Realloc NULL pointer is not supported
+ */
+void *mem_realloc(void *data, size_t size)
+{
+	struct mem *m, *m2;
+
+	if (!data)
+		return NULL;
+
+	m = ((struct mem *)data) - 1;
+
+	MAGIC_CHECK(m);
+
+#if MEM_DEBUG
+	mem_lock();
+
+	/* Simulate OOM */
+	if (-1 != threshold && size > m->size) {
+		if (memstat.blocks_cur >= (size_t)threshold) {
+			mem_unlock();
+			return NULL;
+		}
+	}
+
+	list_unlink(&m->le);
+	mem_unlock();
+#endif
+
+	m2 = realloc(m, sizeof(*m2) + size);
+
+#if MEM_DEBUG
+	mem_lock();
+	list_append(&meml, m2 ? &m2->le : &m->le, m2 ? m2 : m);
+	mem_unlock();
+#endif
+
+	if (!m2) {
+		return NULL;
+	}
+
+	STAT_REALLOC(m2, size);
+
+	return (void *)(m2 + 1);
+}
+
+
+#ifndef SIZE_MAX
+#define SIZE_MAX    (~((size_t)0))
+#endif
+
+
+/**
+ * Re-allocate a reference-counted array
+ *
+ * @param ptr      Pointer to existing array, NULL to allocate a new array
+ * @param nmemb    Number of members in array
+ * @param membsize Number of bytes in each member
+ * @param dh       Optional destructor, only used when ptr is NULL
+ *
+ * @return New pointer to allocated array
+ */
+void *mem_reallocarray(void *ptr, size_t nmemb, size_t membsize,
+		       mem_destroy_h *dh)
+{
+	size_t tsize;
+
+	if (membsize && nmemb > SIZE_MAX / membsize) {
+		return NULL;
+	}
+
+	tsize = nmemb * membsize;
+
+	if (ptr) {
+		return mem_realloc(ptr, tsize);
+	}
+	else {
+		return mem_alloc(tsize, dh);
+	}
+}
+
+
+/**
+ * Reference a reference-counted memory object
+ *
+ * @param data Memory object
+ *
+ * @return Memory object (same as data)
+ */
+void *mem_ref(void *data)
+{
+	struct mem *m;
+
+	if (!data)
+		return NULL;
+
+	m = ((struct mem *)data) - 1;
+
+	MAGIC_CHECK(m);
+
+	++m->nrefs;
+
+	return data;
+}
+
+
+/**
+ * Dereference a reference-counted memory object. When the reference count
+ * is zero, the destroy handler will be called (if present) and the memory
+ * will be freed
+ *
+ * @param data Memory object
+ *
+ * @return Always NULL
+ */
+void *mem_deref(void *data)
+{
+	struct mem *m;
+
+	if (!data)
+		return NULL;
+
+	m = ((struct mem *)data) - 1;
+
+	MAGIC_CHECK(m);
+
+	if (--m->nrefs > 0)
+		return NULL;
+
+	if (m->dh)
+		m->dh(data);
+
+	/* NOTE: check if the destructor called mem_ref() */
+	if (m->nrefs > 0)
+		return NULL;
+
+#if MEM_DEBUG
+	mem_lock();
+	list_unlink(&m->le);
+	mem_unlock();
+#endif
+
+	STAT_DEREF(m);
+
+	free(m);
+
+	return NULL;
+}
+
+
+/**
+ * Get number of references to a reference-counted memory object
+ *
+ * @param data Memory object
+ *
+ * @return Number of references
+ */
+uint32_t mem_nrefs(const void *data)
+{
+	struct mem *m;
+
+	if (!data)
+		return 0;
+
+	m = ((struct mem *)data) - 1;
+
+	MAGIC_CHECK(m);
+
+	return m->nrefs;
+}
+
+
+#if MEM_DEBUG
+static bool debug_handler(struct le *le, void *arg)
+{
+	struct mem *m = le->data;
+	const uint8_t *p = (const uint8_t *)(m + 1);
+	size_t i;
+
+	(void)arg;
+
+	(void)re_fprintf(stderr, "  %p: nrefs=%-2u", p, m->nrefs);
+
+	(void)re_fprintf(stderr, " size=%-7u", m->size);
+
+	(void)re_fprintf(stderr, " [");
+
+	for (i=0; i<16; i++) {
+		if (i >= m->size)
+			(void)re_fprintf(stderr, "   ");
+		else
+			(void)re_fprintf(stderr, "%02x ", p[i]);
+	}
+
+	(void)re_fprintf(stderr, "] [");
+
+	for (i=0; i<16; i++) {
+		if (i >= m->size)
+			(void)re_fprintf(stderr, " ");
+		else
+			(void)re_fprintf(stderr, "%c",
+					 isprint(p[i]) ? p[i] : '.');
+	}
+
+	(void)re_fprintf(stderr, "]");
+
+	MAGIC_CHECK(m);
+
+	(void)re_fprintf(stderr, "\n");
+
+	return false;
+}
+#endif
+
+
+/**
+ * Debug all allocated memory objects
+ */
+void mem_debug(void)
+{
+#if MEM_DEBUG
+	uint32_t n;
+
+	mem_lock();
+	n = list_count(&meml);
+	mem_unlock();
+
+	if (!n)
+		return;
+
+	DEBUG_WARNING("Memory leaks (%u):\n", n);
+
+	mem_lock();
+	(void)list_apply(&meml, true, debug_handler, NULL);
+	mem_unlock();
+#endif
+}
+
+
+/**
+ * Set the memory allocation threshold. This is only used for debugging
+ * and out-of-memory simulation
+ *
+ * @param n Threshold value
+ */
+void mem_threshold_set(ssize_t n)
+{
+#if MEM_DEBUG
+	mem_lock();
+	threshold = n;
+	mem_unlock();
+#else
+	(void)n;
+#endif
+}
+
+
+/**
+ * Print memory status
+ *
+ * @param pf     Print handler for debug output
+ * @param unused Unused parameter
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int mem_status(struct re_printf *pf, void *unused)
+{
+#if MEM_DEBUG
+	struct memstat stat;
+	uint32_t c;
+	int err = 0;
+
+	(void)unused;
+
+	mem_lock();
+	memcpy(&stat, &memstat, sizeof(stat));
+	c = list_count(&meml);
+	mem_unlock();
+
+	err |= re_hprintf(pf, "Memory status: (%u bytes overhead pr block)\n",
+			  sizeof(struct mem));
+	err |= re_hprintf(pf, " Cur:  %u blocks, %u bytes (total %u bytes)\n",
+			  stat.blocks_cur, stat.bytes_cur,
+			  stat.bytes_cur
+			  +(stat.blocks_cur*sizeof(struct mem)));
+	err |= re_hprintf(pf, " Peak: %u blocks, %u bytes (total %u bytes)\n",
+			  stat.blocks_peak, stat.bytes_peak,
+			  stat.bytes_peak
+			  +(stat.blocks_peak*sizeof(struct mem)));
+	err |= re_hprintf(pf, " Block size: min=%u, max=%u\n",
+			  stat.size_min, stat.size_max);
+	err |= re_hprintf(pf, " Total %u blocks allocated\n", c);
+
+	return err;
+#else
+	(void)pf;
+	(void)unused;
+	return 0;
+#endif
+}
+
+
+/**
+ * Get memory statistics
+ *
+ * @param mstat Returned memory statistics
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int mem_get_stat(struct memstat *mstat)
+{
+	if (!mstat)
+		return EINVAL;
+#if MEM_DEBUG
+	mem_lock();
+	memcpy(mstat, &memstat, sizeof(*mstat));
+	mem_unlock();
+	return 0;
+#else
+	return ENOSYS;
+#endif
+}