/*-
 * Copyright (c) 2003-2004 Andrey Simonenko
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"

#ifndef lint
static const char rcsid[] ATTR_UNUSED =
  "@(#)$Id: memfunc.c,v 1.2.2.5 2006/12/15 16:31:22 simon Exp $";
#endif /* !lint */

#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <unistd.h>

#include "ipa_mod.h"

#ifdef WITH_PTHREAD
# include <pthread.h>
# define MEMFUNC_INTERNAL_FLAGS IPA_MEMFUNC_FLAG_PTHREAD
#else
# define MEMFUNC_INTERNAL_FLAGS 0
#endif

#include "memfunc.h"

#define MEMFLAG_OPTIMIZE	IPA_MEMFUNC_FLAG_OPTIMIZE

/*
 * All functions from this file call memfunc_panic() if some
 * critical error occurred.
 */

/*
 * All functions from this file do as many as possible checks,
 * but these checks should not decrease speed a lot.
 */

/*
 * If some pool in a mzone becomes free, then it is probably will
 * be returned to free memory.
 */

#define MEM_SIGNATURE	0x1a77bc01	/* Anything, except all 0s or 1s. */
#define MEM_NOSIGNATURE	0x00000000	/* Anything, but not MEM_SIGNATURE. */

#define MEM_ALIGN_T	double		/* Is it good for all archs? */

struct ipa_mem_type_struct {
	STAILQ_ENTRY(ipa_mem_type_struct) link; /* For list of all mem_types. */
	const char	*name;		/* Name of type.		*/
	const char	*desc;		/* Description.			*/
	u_int		flags;		/* Flags.			*/
	size_t		size;		/* Amount of allocated memory.	*/
	u_int		reqs;		/* Number of requests.		*/
	int		local;		/* Non-zero if not owned by module. */
#ifdef WITH_PTHREAD
	pthread_mutex_t	mutex;		/* Mutex for this type.		*/
#endif
};

static STAILQ_HEAD(, ipa_mem_type_struct) mem_type_list; /* List of all mem_types. */

#define MTYPE_NAME(x) "memfunc:m_" #x

static ipa_mzone *mem_type_mzone;	/* Mzone of mem_type structures. */

struct mem_desc {
	int		signature;	/* MEM_SIGNATURE & size.	*/
	size_t		size;		/* Actual size.			*/
	union {
		MEM_ALIGN_T align;
		char	buf[1];		/* Actual buffer.		*/
	} u;
};

#define MEM_TYPE_NSIZE	30
#define MEM_TYPE_NALLOC	10

/* Offset of actual buffer in struct mem_desc. */
#define MEM_DESC_BUF_OFFSET (offsetof(struct mem_desc, u.buf[0]))

/* Pointer to struct mem_desc for the given buffer address. */
#define MEM_DESC_PTR(ptr) ((struct mem_desc *)((char *)(ptr) - MEM_DESC_BUF_OFFSET))

/* Pointer to the buffer in struct mem_desc. */
#define MEM_DESC_BUF(ptr) ((ptr)->u.buf)

#define MZONE_NAME(x)	"memfunc:" #x

static int memfunc_inited = 0;		/* Non-zero if memfunc_init() was called. */

/*
 * Nitems and nalloc in marrays are rounded to BIT_WORD_BIT, so each
 * bitword is used completely.
 */

/* Pool's items array alignment. */
#define MZONE_POOL_ITEMS_ALIGN_T	MEM_ALIGN_T

/* Rounding for size of each item. */
#define MZONE_POOL_ITEM_ROUNDING	(sizeof(MZONE_POOL_ITEMS_ALIGN_T))

/* Address of pool's items array. */
#define MZONE_POOL_ITEMS(pool)		((void *)(pool)->u.buf)

/* Offset of the start of items array in a pool. */
#define MZONE_POOL_ITEMS_OFFSET		(offsetof(struct mzone_pool, u.buf[0]))

/* Return real size of item without pool index. */
#define MZONE_REAL_ISIZE(mzone)		((mzone)->isize - sizeof(u_int))

/* Return pointer to index of pool for item. */
#define MZONE_POOL_INDEX(mzone, item)	((u_int *)((char *)(item) + MZONE_REAL_ISIZE(mzone)))

/* Pointer to next free item. */
#define MZONE_FREE_ITEM_NEXT(mzone, item) (*(void **)(item))

/*
 * Item should be in pool's items addresses and should be
 * correctly aligned.
 */
#define MZONE_POOL_VALID_ITEM(mzone, pool, item) (			\
	(item) >= MZONE_POOL_ITEMS(pool) &&				\
	(item) <= (pool)->last_item &&					\
	((char *)(item) - (char *)MZONE_POOL_ITEMS(pool)) % (mzone)->isize == 0	\
)

/* Return index of item in pool's items array. */
#define MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item)			\
	(((char *)(item) - (char *)MZONE_POOL_ITEMS(pool)) / (mzone)->isize)

/*
 * One pool of mzone's items.
 */
struct mzone_pool {
	TAILQ_ENTRY(mzone_pool) link;	/* Link for free pools list.	*/
	void		*last_item;	/* Pointer to last item.	*/
	void		*free_items;	/* List of free items.		*/
	BITMAP_TYPE	*bitmap;	/* Pointer to items bitmap.	*/
	u_int		nitems;		/* Number of all items.		*/
	u_int		nfree;		/* Number of free items.	*/
	size_t		pool_size;	/* Size of pool.		*/
	u_int		pool_idx;	/* Index of pool.		*/
	union {
		MZONE_POOL_ITEMS_ALIGN_T align;
		char	buf[1];		/* Array of items + bitmap.	*/
	} u;
};

TAILQ_HEAD(mzone_pool_list, mzone_pool);

struct ipa_mzone_struct {
	TAILQ_ENTRY(ipa_mzone_struct) link; /* For list of all mzones.	*/
	struct mzone_pool_list free_pools; /* List of free pools.	*/
	size_t		isize;		/* Size of one item.		*/
	u_int		nalloc;		/* How may items to allocate.	*/
	u_int		nused;		/* Number of used items.	*/
	u_int		nfree;		/* Number of free items.	*/
	const char	*name;		/* Name of the mzone.		*/
	const char	*desc;		/* Description.			*/
	size_t		pools_size;	/* Used memory by pools.	*/
	u_int		flags;		/* Mzone flags.			*/
	u_int		reqs;		/* Number of requests.		*/
	struct mzone_pool **pool_ptr;	/* Pointers to pools.		*/
	u_int		pool_ptr_size;	/* Size of pool_ptr in entries.	*/
#ifdef WITH_PTHREAD
	pthread_mutex_t	mzone_mutex;	/* Mutex for whole mzone.	*/
#endif
};

static TAILQ_HEAD(, ipa_mzone_struct) mzone_list; /* List of all mzones. */

struct ipa_marray_struct {
	TAILQ_ENTRY(ipa_marray_struct) link; /* Linked list of all marrays. */
	void		**arr;		/* Pointer to pointer of array of items. */
	BITMAP_TYPE	*bitmap;	/* Bitmap of used/unused items. */
	BITMAP_TYPE	*bitmap_hint;	/* Hint for free items search.	*/
	u_int		nitems;		/* Number of all items.		*/
	u_int		nfree;		/* Number of free items		*/
	u_int		nalloc;		/* How many items to allocate.	*/
	size_t		isize;		/* Size of one item.		*/
	const char	*name;		/* Name of the marray.		*/
	const char	*desc;		/* Description.			*/
	size_t		arr_size;	/* Used memory by *arr.		*/
	size_t		bitmap_size;	/* Used memory by bitmap.	*/
	u_int		flags;		/* Marray flags.		*/
	u_int		reqs;		/* Number of requests.		*/
#ifdef WITH_PTHREAD
	pthread_mutex_t	marray_mutex;	/* Mutex for marray.		*/
#endif
};

static TAILQ_HEAD(, ipa_marray_struct) marray_list; /* List of all marrays. */

#define MZONE_NSIZE	20
#define MZONE_NALLOC	10

static ipa_mzone mzone_mzone;		/* Mzone of mzones. */
static ipa_mem_type m_mzone;		/* Mem_type for mzones. */

#define MARRAY_NSIZE	20
#define MARRAY_NALLOC	10

static ipa_mzone *marray_mzone;		/* Mzone of marrays. */
static ipa_mem_type *m_marray;		/* Mem_type for marrays. */

#define PAGESIZE_DEFAULT 4096		/* Default size of a VM page. */

static long	pagesize;		/* Size of a VM page. */

/*
 * Implementation of mvlogmsgx() should be provided by the
 * function, which uses any of exported functions from this file.
 */
void	(*mvlogmsgx)(const char *, va_list);

extern ipa_mem_type *m_parser;		/* Declared in parser.c */

#ifdef WITH_MEMFUNC_DEBUG
# ifndef FILL_ALLOC
#  define FILL_ALLOC	0xfc		/* Fill byte for just allocated memory. */
# endif
# ifndef FILL_FREE
#  define FILL_FREE	0xa7		/* Fill byte for just freed memory. */
# endif
#endif

static const char gbytes[] = { 0x27, 0x17, 0x4b, 0xe7 };

/*
 * This variables has values only after memfunc_deinit_1() and
 * memfunc_deinit_2() invocations.
 */
static size_t mem_size_cnt_all = 0;	/* Amount of allocated memory after start. */
static size_t mem_size_cnt;		/* Amount of allocated memory after reconfiguration. */

#ifdef WITH_PTHREAD

static pthread_mutexattr_t mutexattr;	/* Default attributes for mutexes. */

# define IS_PTHREADED(x)	((x)->flags & IPA_MEMFUNC_FLAG_PTHREAD)

# define LOCK_MZONE(x) do {					\
       if (IS_PTHREADED(x))					\
		lock_mzone(x);					\
} while (/* CONSTCOND */ 0)

# define UNLOCK_MZONE(x) do {					\
	if (IS_PTHREADED(x))					\
		unlock_mzone(x);				\
} while (/* CONSTCOND */ 0)

# define LOCK_MARRAY(x) do {					\
	if (IS_PTHREADED(x))					\
		lock_marray(x);					\
} while (/* CONSTCOND */ 0)

# define UNLOCK_MARRAY(x) do {					\
	if (IS_PTHREADED(x))					\
		unlock_marray(x);				\
} while (/* CONSTCOND */ 0)

# define LOCK_MEM_TYPE(x) do {					\
	if (IS_PTHREADED(x))					\
		lock_mem_type(x);				\
} while (/* CONSTCOND */ 0)

# define UNLOCK_MEM_TYPE(x) do {				\
	if (IS_PTHREADED(x))					\
		unlock_mem_type(x);				\
} while (/* CONSTCOND */ 0)

#define LOCK_MEM_TYPE_LIST()	LOCK_MZONE(mem_type_mzone)
#define UNLOCK_MEM_TYPE_LIST()	UNLOCK_MZONE(mem_type_mzone)

#define LOCK_MZONE_LIST()	LOCK_MZONE(&mzone_mzone)
#define UNLOCK_MZONE_LIST()	UNLOCK_MZONE(&mzone_mzone)

#define LOCK_MARRAY_LIST()	LOCK_MZONE(marray_mzone)
#define UNLOCK_MARRAY_LIST()	UNLOCK_MZONE(marray_mzone)

#define STRERRBUF_SIZE		128	/* Size of buffer for strerror_r(). */

#define my_strerror(e)	safe_strerror(e, strerrbuf)

#else

# define LOCK_MZONE(x)
# define UNLOCK_MZONE(x)

# define LOCK_MARRAY(x)
# define UNLOCK_MARRAY(x)

# define LOCK_MEM_TYPE(x)
# define UNLOCK_MEM_TYPE(x)

#define LOCK_MEM_TYPE_LIST()
#define UNLOCK_MEM_TYPE_LIST()

#define LOCK_MZONE_LIST()
#define UNLOCK_MZONE_LIST()

#define LOCK_MARRAY_LIST()
#define UNLOCK_MARRAY_LIST()

#define my_strerror(e)	strerror(e)

#endif /* WITH_PTHREAD */

static void	logmsgx(const char *, ...) ATTR_FORMAT(printf, 1, 2);
static void	memfunc_panic(const char *, ...) ATTR_FORMAT(printf, 1, 2);

/*
 * This log function is used mostly for checking printf-like format,
 * since the external implementation of log function is vprintf-like.
 */
static void
logmsgx(const char *format, ...)
{
	va_list ap;

	va_start(ap, format);
	mvlogmsgx(format, ap);
	va_end(ap);
}

/*
 * Send last panic message and exit by sending SIGABRT signal
 * to itself.
 */
static void
memfunc_panic(const char *format, ...)
{
	va_list ap;

	va_start(ap, format);
	mvlogmsgx(format, ap);
	va_end(ap);
	logmsgx("!!! PANIC EXIT FROM MEMFUNC !!!");
	abort();
	exit(1);
}

#ifdef WITH_PTHREAD
/*
 * Any buffer passed to this function should be at least STRERRBUF_SIZE
 * bytes in size.
 */
static const char *
safe_strerror(int error, char *buf)
{
	if (strerror_r(error, buf, STRERRBUF_SIZE) != 0)
		if (snprintf(buf, STRERRBUF_SIZE, "error code %d", error) < 0)
			return "";
	return buf;
	
}

static int
mutex_init(const char *name, pthread_mutex_t *mutex)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_init(mutex, &mutexattr)) != 0) {
		logmsgx("mutex_init: pthread_mutex_init(%s) failed: %s!",
		    name, my_strerror(error));
		return -1;
	}

	return 0;
}

/*
 * Just init mutexattr with default settings and set PTHREAD_MUTEX_ERRORCHECK
 * type for it, to prevent recursive locking, etc.
 */
static int
memfunc_mutexattr_init(void)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutexattr_init(&mutexattr)) != 0) {
		logmsgx("memfunc_mutexattr_init: pthread_mutexattr_init: %s",
		    my_strerror(error));
		return -1;
	}

	if ( (error = pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK)) != 0)
		memfunc_panic("memfunc_mutexattr_init: pthread_mutexattr_settype: %s",
		    my_strerror(error));

	return 0;
}

/*
 * These two functions are used for locking and unlocking mzone
 * structure, they should not fail.
 */
static void
lock_mzone(ipa_mzone *mzone)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_lock(&mzone->mzone_mutex)) != 0)
		memfunc_panic("lock_mzone(%s): pthread_mutex_lock failed: %s!",
		    mzone->name, my_strerror(error));
}

static void
unlock_mzone(ipa_mzone *mzone)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_unlock(&mzone->mzone_mutex)) != 0)
		memfunc_panic("unlock_mzone(%s): pthread_mutex_unlock failed: %s!",
		    mzone->name, my_strerror(error));
}

/*
 * These two functions are used for locking and unlocking marray
 * structure, they should not fail.
 */
static void
lock_marray(ipa_marray *marray)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_lock(&marray->marray_mutex)) != 0)
		memfunc_panic("lock_marray(%s): pthread_mutex_lock failed: %s!",
		    marray->name, my_strerror(error));
}

static void
unlock_marray(ipa_marray *marray)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_unlock(&marray->marray_mutex)) != 0)
		memfunc_panic("unlock_marray(%s): pthread_mutex_unlock failed: %s!",
		    marray->name, my_strerror(error));
}

/*
 * These two functions are used for locking and unlocking mem_type
 * structure, they should not fail.
 */
static void
lock_mem_type(ipa_mem_type *mem_type)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_lock(&mem_type->mutex)) != 0)
		memfunc_panic("lock_mem_type(%s): pthread_mutex_lock failed: %s!",
		    mem_type->name, my_strerror(error));
}

static void
unlock_mem_type(ipa_mem_type *mem_type)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if ( (error = pthread_mutex_unlock(&mem_type->mutex)) != 0)
		memfunc_panic("unlock_mem_type(%s): pthread_mutex_unlock failed: %s!",
		    mem_type->name, my_strerror(error));
}
#endif /* WITH_PTHREAD */

static int
mem_type_init_internal(ipa_mem_type *mem_type, const char *name,
    const char *desc, u_int flags)
{
	if (name == NULL || desc == NULL) {
		logmsgx("mem_type_init_internal: name or desc is NULL");
		return -1;
	}

	mem_type->name = name;
	mem_type->desc = desc;
	mem_type->flags = flags;
	mem_type->size = 0;
	mem_type->reqs = 0;

#ifdef WITH_PTHREAD
	if (IS_PTHREADED(mem_type))
		if (mutex_init(name, &mem_type->mutex) < 0) {
			logmsgx("mem_type_init_internal: mutex_init failed!");
			return -1;
		}
#endif

	return 0;
}

#ifdef WITH_PTHREAD
static void
mem_type_deinit_internal(ipa_mem_type *mem_type)
{
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];

	if (IS_PTHREADED(mem_type))
		if ( (error = pthread_mutex_destroy(&mem_type->mutex)) != 0)
			memfunc_panic("mem_type_deinit_internal(%s): pthread_mutex_destroy failed: %s!",
			    mem_type->name, my_strerror(error));
}
#else
static void
mem_type_deinit_internal(ipa_mem_type *mem_type ATTR_UNUSED)
{
}
#endif /* WITH_PTHREAD */

static ipa_mem_type *
mem_type_new(const char *name, const char *desc, u_int flags)
{
	ipa_mem_type *mem_type;

	if ( (mem_type = mzone_alloc(mem_type_mzone)) == NULL) {
		logmsgx("mem_type_new(%s): mzone_alloc failed", name);
		return NULL;
	}

	if (mem_type_init_internal(mem_type, name, desc, flags) < 0) {
		logmsgx("mem_type_new(%s): mem_type_init_internal failed",
		    name);
		mzone_free(mem_type_mzone, mem_type);
		return NULL;
	}

	mem_type->local = 0;

	LOCK_MEM_TYPE_LIST();
	STAILQ_INSERT_TAIL(&mem_type_list, mem_type, link);
	UNLOCK_MEM_TYPE_LIST();

	return mem_type;
}

ipa_mem_type *
mem_type_new_local(const char *name, const char *desc, u_int flags)
{
	ipa_mem_type *mem_type;

	if ( (mem_type = mem_type_new(name, desc, flags)) == NULL)
		return NULL;

	mem_type->local = 1;

	return mem_type;
}

/*
 * Allocate buffer of size bytes.
 */
void *
mem_malloc(size_t size, ipa_mem_type *mem_type)
{
	struct mem_desc *mem;
#ifdef WITH_PTHREAD
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if (size == 0)
		memfunc_panic("mem_malloc(%s): size is zero",
		    mem_type->name);

	if ( (mem = malloc(size + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) {
		logmsgx("mem_malloc(%lu + %lu, %s) failed: malloc: %s",
		    (u_long)size, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name,
		    my_strerror(errno));
		return NULL;
	}
	mem->signature = MEM_SIGNATURE & (int)size;
	mem->size = size;

#ifdef WITH_MEMFUNC_DEBUG
	memset(MEM_DESC_BUF(mem), FILL_ALLOC, size);
#endif

	memcpy(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes);

	LOCK_MEM_TYPE(mem_type);
	mem_type->size += size;
	mem_type->reqs++;
	UNLOCK_MEM_TYPE(mem_type);

	return MEM_DESC_BUF(mem);
}

/*
 * Allocate buffer of number * size bytes and fill it with zeros.
 */
void *
mem_calloc(size_t number, size_t size, ipa_mem_type *mem_type)
{
	struct mem_desc *mem;
#ifdef WITH_PTHREAD
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if (number == 0 || size == 0)
		memfunc_panic("mem_calloc(%s): wrong number %lu or size %lu",
		    mem_type->name, (u_long)number, (u_long)size);

	size *= number;
	if ( (mem = malloc(size + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) {
		logmsgx("mem_calloc(%lu + %lu, %s) failed: %s",
		    (u_long)size, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name,
		    my_strerror(errno));
		return NULL;
	}
	mem->signature = MEM_SIGNATURE & (int)size;
	mem->size = size;

	memset(MEM_DESC_BUF(mem), 0, size);

	memcpy(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes);

	LOCK_MEM_TYPE(mem_type);
	mem_type->size += size;
	mem_type->reqs++;
	UNLOCK_MEM_TYPE(mem_type);

	return MEM_DESC_BUF(mem);
}

/*
 * Free previously allocated buffer.
 */
void
mem_free(void *ptr, ipa_mem_type *mem_type)
{
	size_t	size;
	struct mem_desc *mem;

	if (ptr == NULL)
		return;

	mem = MEM_DESC_PTR(ptr);

	if (mem->signature != (MEM_SIGNATURE & (int)(mem->size)))
		memfunc_panic("mem_free(%p, %p): wrong signature",
		    ptr, mem_type);

	size = mem->size;

	if (memcmp(MEM_DESC_BUF(mem) + size, gbytes, sizeof gbytes) != 0)
		memfunc_panic("mem_free(%p, %s): guard bytes were changed",
		    ptr, mem_type->name);

	LOCK_MEM_TYPE(mem_type);
	mem_type->size -= size;
	UNLOCK_MEM_TYPE(mem_type);

#ifdef WITH_MEMFUNC_DEBUG
	memset(mem, FILL_FREE, size + MEM_DESC_BUF_OFFSET);
#endif

	mem->signature = MEM_NOSIGNATURE;

	free(mem);
}

/*
 * Reallocate buffer, if can't reallocate then keep previous buffer
 * unchanged and return NULL.
 */
void *
mem_realloc(void *ptr, size_t size2, ipa_mem_type *mem_type)
{
	size_t	size1;
	struct mem_desc *mem1, *mem2;
#ifdef WITH_PTHREAD
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if (size2 == 0)
		memfunc_panic("mem_realloc(%s): size is zero",
		    mem_type->name);

	if (ptr == NULL) {
		/* realloc(NULL, size2) is the same as malloc(size2) */
		if ( (ptr = mem_malloc(size2, mem_type)) == NULL)
			logmsgx("mem_realloc(%s): mem_malloc failed",
			    mem_type->name);
		return ptr;
	}

	mem1 = MEM_DESC_PTR(ptr);

	size1 = mem1->size;

	if (mem1->signature != (MEM_SIGNATURE & (int)size1))
		memfunc_panic("mem_realloc(%p, %p): wrong signature",
		    ptr, mem_type);

	if (memcmp(MEM_DESC_BUF(mem1) + size1, gbytes, sizeof gbytes) != 0)
		memfunc_panic("mem_realloc(%p, %s): guard bytes were changed",
		    ptr, mem_type->name);

#ifdef WITH_MEMFUNC_DEBUG
	/* malloc() --> memcpy() --> free() */
	if ( (mem2 = malloc(size2 + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) {
		logmsgx("mem_realloc(%s): malloc(%lu + %lu) failed: %s",
		    mem_type->name, (u_long)size2, (u_long)MEM_DESC_BUF_OFFSET,
		    my_strerror(errno));
		return NULL;
	}
	if (size2 > size1) {
		memcpy(MEM_DESC_BUF(mem2), ptr, size1);
		memset(MEM_DESC_BUF(mem2) + size1, FILL_ALLOC, size2 - size1);
	} else
		memcpy(MEM_DESC_BUF(mem2), ptr, size2);
	mem_free(ptr, mem_type);
	LOCK_MEM_TYPE(mem_type);
	mem_type->size += size2;
	mem_type->reqs++;
	UNLOCK_MEM_TYPE(mem_type);
#else
	/* True realloc(). */
	mem1->signature = MEM_NOSIGNATURE;
	if ( (mem2 = realloc(mem1, size2 + MEM_DESC_BUF_OFFSET + sizeof(gbytes))) == NULL) {
		logmsgx("mem_realloc(%lu + %lu, %s) failed: %s",
		    (u_long)size2, (u_long)MEM_DESC_BUF_OFFSET, mem_type->name,
		    my_strerror(errno));
		mem1->signature = MEM_SIGNATURE & (int)size1;
		return NULL;
	}
	LOCK_MEM_TYPE(mem_type);
	mem_type->size -= size1, mem_type->size += size2;
	mem_type->reqs++;
	UNLOCK_MEM_TYPE(mem_type);
#endif /* WITH_MEMFUNC_DEBUG */

	mem2->signature = MEM_SIGNATURE & (int)size2;

	memcpy(MEM_DESC_BUF(mem2) + size2, gbytes, sizeof gbytes);

	mem2->size = size2;

	return MEM_DESC_BUF(mem2);
}

/*
 * Allocate memory for a string and copy it.
 */
char *
mem_strdup(const char *src, ipa_mem_type *mem_type)
{
	char	*dst;
	size_t	size;

	if (src == NULL)
		memfunc_panic("mem_strdup: NULL pointer argument");

	size = strlen(src);

	if ( (dst = mem_malloc(size + 1, mem_type)) == NULL) {
		logmsgx("mem_strdup(%s): mem_malloc failed", mem_type->name);
		return NULL;
	}

	return strncpy(dst, src, size + 1);
}

/*
 * Analog of vasprintf(3) function, found on some systems.
 */
int
mem_vasprintf(ipa_mem_type *mem_type, char **bufp, const char *format,
    va_list ap)
{
	int	size, rv;
	char	*ptr;
	char	buf[128];	/* Try to use buffer in stack. */
#ifdef WITH_PTHREAD
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if ( (size = vsnprintf(buf, sizeof buf, format, ap)) < 0) {
		logmsgx("mem_vasprintf: vsnprintf failed (1): %s",
		    my_strerror(errno));
		*bufp = NULL;
		return -1;
	}
	if ( (ptr = mem_malloc(++size, mem_type)) == NULL) {
		logmsgx("mem_vasprintf(%s): mem_malloc failed", mem_type->name);
		return -1;
	}
	/* Since size was incremented, so use '>' operator here. */
	if (size > sizeof buf) {
		if ( (rv = vsnprintf(ptr, size, format, ap)) < 0) {
			logmsgx("mem_vasprintf(%s): vsnprintf failed (2): %s",
			    mem_type->name, my_strerror(errno));
			goto failed;
		}
		if (rv >= size) {
			logmsgx("mem_vasprintf(%s): not enough space in vsnprintf",
			    mem_type->name);
			goto failed;
		}
	} else
		memcpy(ptr, buf, size);
	*bufp = ptr;
	return 0;

failed:
	mem_free(ptr, mem_type);
	*bufp = NULL;
	return -1;
}

/*
 * Analog of asprintf(3) function, found on some systems.
 */
int
mem_asprintf(ipa_mem_type *mem_type, char **bufp, const char *format, ...)
{
	int	ret;
	va_list ap;

	va_start(ap, format);
	ret = mem_vasprintf(mem_type, bufp, format, ap);
	va_end(ap);

	return ret;
}

/*
 * Adjust pools_size and nfree in mzone and release memory
 * used by the given pool of items.
 */
static void
mzone_pool_free(ipa_mzone *mzone, struct mzone_pool *pool)
{
	mzone->pools_size -= pool->pool_size;
	mzone->nfree -= pool->nfree;
	mem_free(pool->bitmap, &m_mzone);
	mem_free(pool, &m_mzone);
}

/*
 * Create one mzone_pool with nitems items.
 */
static struct mzone_pool *
mzone_pool_create(ipa_mzone *mzone, u_int nitems)
{
	u_int	i, pool_idx;
	void	*item, *item_next;
	size_t	pool_size;
	struct mzone_pool *pool, **pool_ptr;

	/* Check if free_pools list is empty. */
	if (!TAILQ_EMPTY(&mzone->free_pools))
		memfunc_panic("mzone_pool_create(%s): there are already free pools",
		    mzone->name);

	pool_size = MZONE_POOL_ITEMS_OFFSET + nitems * mzone->isize;

	/* Allocate pool. */
	if ( (pool = mem_malloc(pool_size, &m_mzone)) == NULL) {
		logmsgx("mzone_pool_create(%s): mem_malloc failed for pool", mzone->name);
		return NULL;
	}

	/* Allocate memory for bitmap. */
	if ( (pool->bitmap = mem_calloc(BITMAP_SIZE(nitems), 1, &m_mzone)) == NULL) {
		mem_free(pool, &m_mzone);
		logmsgx("mzone_pool_create(%s): mem_calloc failed for bitmap", mzone->name);
		return NULL;
	}

	mzone->pools_size += pool->pool_size = pool_size;
	mzone->nfree += pool->nitems = pool->nfree = nitems;

	/* Find first unused pool index. */
	for (pool_idx = 0; pool_idx < mzone->pool_ptr_size; ++pool_idx)
		if (mzone->pool_ptr[pool_idx] == NULL)
			break;
	if (pool_idx == mzone->pool_ptr_size) {
		/* No free index. */
		if ( (pool_ptr = mem_realloc(mzone->pool_ptr, (mzone->pool_ptr_size + 1) * sizeof *mzone->pool_ptr,
		    &m_mzone)) == NULL) {
			logmsgx("mzone_pool_create(%s): mem_realloc failed, cannot extend pool_ptr",
			    mzone->name);
			mzone_pool_free(mzone, pool);
			return NULL;
		}
		mzone->pool_ptr = pool_ptr;
		mzone->pool_ptr_size++;
	}

	pool->pool_idx = pool_idx;
	mzone->pool_ptr[pool_idx] = pool;

	pool->free_items = MZONE_POOL_ITEMS(pool);

	/*
	 * Let's keep free items in a list sorted by addresses,
	 * at least until mzone_free() is called.
	 */
	for (i = 0, item = pool->free_items; i < nitems; ++i) {
		item_next = (char *)item + mzone->isize;
		MZONE_FREE_ITEM_NEXT(mzone, item) = item_next;
		*MZONE_POOL_INDEX(mzone, item) = pool_idx;
		item = item_next;
	}
	pool->last_item = (char *)item - mzone->isize;

	/* And insert this pool to free_pools list. */
	TAILQ_INSERT_HEAD(&mzone->free_pools, pool, link);

	return pool;
}

/*
 * Reserve memory for header struct mzone_pool{} and for
 * header struct mem_desc{} and return number of items which
 * fit to (n * pagesize).
 */
static u_int
round_to_pagesize(u_int nitems, u_int isize)
{
	size_t size;

	size = (nitems * isize + pagesize - 1) / pagesize;
	size *= pagesize;
	nitems = (size - MEM_DESC_BUF_OFFSET - MZONE_POOL_ITEMS_OFFSET) / isize;
	return nitems != 0 ? nitems : 1;
}

/*
 * Init fields in mzone and allocate one pool.
 */
static int
mzone_init_internal(ipa_mzone *mzone, const char *name, const char *desc,
    u_int flags, size_t isize, u_int nitems, u_int nalloc)
{
	struct mzone_pool *pool;

	/* Validate arguments. */
	if (isize == 0 || nitems == 0 || nalloc == 0)
		memfunc_panic("mzone_init_internal(%s): isize, nitems and nalloc must be greater than zero",
		    mzone->name);

	/* There should be enough space for storing (void *) inside one item. */
	if (isize < sizeof(void *))
		isize = sizeof(void *);

	/*
	 * Reserve memory for pool index, this value will also can help
	 * to find buffer overflow errors, also we keep not a pointer
	 * to make additional tests.  Alternatively pools can be allocated
	 * to page boundary and in this case it is easy to access pool's
	 * description from item's address, but this optimization is system
	 * specific.
	 */
	isize += sizeof(u_int);

	/* Round isize to MZONE_POOL_ITEM_ROUNDING. */
	isize = (isize + MZONE_POOL_ITEM_ROUNDING - 1) / MZONE_POOL_ITEM_ROUNDING;
	isize *= MZONE_POOL_ITEM_ROUNDING;

	if (flags & IPA_MEMFUNC_FLAG_OPTIMIZE) {
		/*
		 * Adjust nitems and nalloc, so one pool should use
		 * (pagesize * n) bytes.
		 */
		nitems = round_to_pagesize(nitems, isize);
		nalloc = round_to_pagesize(nalloc, isize);
	}

	TAILQ_INIT(&mzone->free_pools);
	mzone->isize = isize;
	mzone->nalloc = nalloc;
	mzone->nused = mzone->nfree = 0;
	mzone->name = name;
	mzone->desc = desc;
	mzone->pools_size = 0;

	mzone->pool_ptr = NULL;
	mzone->pool_ptr_size = 0;

	if ( (pool = mzone_pool_create(mzone, nitems)) == NULL) {
		logmsgx("mzone_init_internal(%s): mzone_pool_create failed", name);
		return -1;
	}
	mzone->flags = flags;
	mzone->reqs = 0;

#ifdef WITH_PTHREAD
	if (IS_PTHREADED(mzone))
		if (mutex_init(name, &mzone->mzone_mutex) < 0) {
			logmsgx("mzone_init_internal: mutex_init failed!");
			mzone_pool_free(mzone, pool);
			return -1;
		}
#endif

	return 0;
}

/*
 * Init mzone, should be called before any other mzone_*() function.
 */
ipa_mzone *
mzone_init(const char *name, const char *desc, u_int flags, size_t isize,
    u_int nitems, u_int nalloc)
{
	ipa_mzone *mzone;

	if (name == NULL || desc == NULL)
		memfunc_panic("mzone_init: name or description of mzone must be given");

	if ( (mzone = mzone_alloc(&mzone_mzone)) == NULL) {
		logmsgx("mzone_init(%s): mzone_alloc failed", name);
		return NULL;
	}

	if (mzone_init_internal(mzone, name, desc, flags, isize, nitems, nalloc) < 0) {
		logmsgx("mzone_init(%s): mzone_init_internal failed", name);
		mzone_free(&mzone_mzone, mzone);
		return NULL;
	}

	LOCK_MZONE_LIST();
	TAILQ_INSERT_TAIL(&mzone_list, mzone, link);
	UNLOCK_MZONE_LIST();

	return mzone;
}

/*
 * Free all mzone_pools in mzone and destroy mutex.
 */
static void
mzone_deinit_internal(ipa_mzone *mzone)
{
	u_int	pool_idx;
	struct mzone_pool *pool;
#ifdef WITH_PTHREAD
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	for (pool_idx = 0; pool_idx < mzone->pool_ptr_size; ++pool_idx) {
		pool = mzone->pool_ptr[pool_idx];
		if (pool != NULL) {
			if (pool->pool_idx != pool_idx)
				memfunc_panic("mzone_deinit_internal(%s): pool index is wrong",
				    mzone->name);
			mzone_pool_free(mzone, pool);
		}
	}

	if (mzone->pools_size != 0)
		memfunc_panic("mzone_deinit_internal(%s): pools_size is not equal to zero",
		    mzone->name);

	if (mzone->nfree != 0)
		memfunc_panic("mzone_deinit_internal(%s): nfree is not equal to zero",
		    mzone->name);

	mem_free(mzone->pool_ptr, &m_mzone);

#ifdef WITH_PTHREAD
	if (IS_PTHREADED(mzone))
		if ( (error = pthread_mutex_destroy(&mzone->mzone_mutex)) != 0)
			memfunc_panic("mzone_deinit_internal(%s): pthread_mutex_destroy failed: %s!",
			    mzone->name, my_strerror(error));
#endif
}

/*
 * Free all memory used by mzone and by mzone structure as well.
 */
void
mzone_deinit(ipa_mzone *mzone)
{
	if (mzone == NULL)
		return;

	mzone_deinit_internal(mzone);

	LOCK_MZONE_LIST();
	TAILQ_REMOVE(&mzone_list, mzone, link);
	UNLOCK_MZONE_LIST();

	mzone_free(&mzone_mzone, mzone);
}

/*
 * Allocate one item in mzone, possibly allocate new pool if needed.
 */
void *
mzone_alloc(ipa_mzone *mzone)
{
	u_int	idx;
	void	*item;
	BITMAP_TYPE *bitword, bitmask;
	struct mzone_pool *pool;

	if (mzone == NULL)
		memfunc_panic("mzone_alloc: NULL mzone");

	LOCK_MZONE(mzone);

	/* Get pool with free items. */
	if (mzone->nfree > 0) {
		pool = TAILQ_FIRST(&mzone->free_pools);
		if (pool == NULL)
			memfunc_panic("mzone_alloc(%s): free pool is NULL",
			    mzone->name);
		if (pool->nfree == 0)
			memfunc_panic("mzone_alloc(%s): free pool does not have free items",
			    mzone->name);
	} else {
		if ( (pool = mzone_pool_create(mzone, mzone->nalloc)) == NULL) {
			logmsgx("mzone_alloc(%s): mzone_pool_create failed",
			    mzone->name);
			item = NULL;
			goto out;
		}
	}

	/* Decrement number of free items. */
	pool->nfree--;
	mzone->nfree--;

	/* Increment number of used items. */
	mzone->nused++;

	/* Get item from free items list. */
	item = pool->free_items;
	pool->free_items = MZONE_FREE_ITEM_NEXT(mzone, item);

	/* Check if just allocated item has the same pool index as its pool. */
	if (*MZONE_POOL_INDEX(mzone, item) != pool->pool_idx)
		memfunc_panic("mzone_alloc(%s): just allocated item %p does not have the same pool index as its pool",
		    mzone->name, item);

	/* Check if just allocated item belongs to pool. */
	if (!MZONE_POOL_VALID_ITEM(mzone, pool, item))
		memfunc_panic("mzone_alloc(%s): just allocated item %p does not belong to pool",
		    mzone->name, item);

	/* Get index of just allocated item in its pool. */
	idx = MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item);

	bitword = BIT_WORD(pool->bitmap, idx);
	bitmask = BIT_MASK(idx);

	/* Check if just allocated item is used. */
	if (BIT_TEST(bitword, bitmask))
		memfunc_panic("mzone_alloc(%s): just allocated item %p already is used",
		    mzone->name, item);

	/* Mark just allocated item as used. */
	BIT_SET(bitword, bitmask);

#ifdef WITH_MEMFUNC_DEBUG
	memset(item, FILL_ALLOC, MZONE_REAL_ISIZE(mzone));
#endif

	/* Pool became empty. */
	if (pool->nfree == 0)
		TAILQ_REMOVE(&mzone->free_pools, pool, link);

out:
	mzone->reqs++;

	UNLOCK_MZONE(mzone);

	return item;
}

/*
 * Return previously allocated item to mzone.
 * Check if we can completely free its pool.
 */
void
mzone_free(ipa_mzone *mzone, void *item)
{
	u_int	idx, pool_idx;
	BITMAP_TYPE *bitword, bitmask;
	struct mzone_pool *pool, **pool_ptr;

	if (mzone == NULL)
		memfunc_panic("mzone_free: NULL mzone");

	if (item == NULL)
		return;

	LOCK_MZONE(mzone);

	pool_idx = *MZONE_POOL_INDEX(mzone, item);

	/* Out of pools? */
	if (pool_idx >= mzone->pool_ptr_size)
		memfunc_panic("mzone_free(%s): index of item's pool is %u, last pool's index is %u",
		    mzone->name, pool_idx, mzone->pool_ptr_size - 1);

	pool = mzone->pool_ptr[pool_idx];

	/* Check if this pool really exist. */
	if (pool == NULL)
		memfunc_panic("mzone_free(%s): index of returned item's pool is %u, but this pool does not exist",
		    mzone->name, pool_idx);

	/* Check if returned item has the same pool index as its pool. */
	if (pool_idx != pool->pool_idx)
		memfunc_panic("mzone_alloc(%s): returned item %p does not have the same pool index as its pool",
		    mzone->name, item);

	/* Check if returned item belongs to pool. */
	if (!MZONE_POOL_VALID_ITEM(mzone, pool, item))
		memfunc_panic("mzone_alloc(%s): returned item %p does not belong to its pool",
		    mzone->name, item);

	idx = MZONE_POOL_ITEM_TO_INDEX(mzone, pool, item);
	bitword = BIT_WORD(pool->bitmap, idx);
	bitmask = BIT_MASK(idx);

	/* Test if this item is marked as used. */
	if (!BIT_TEST(bitword, bitmask))
		memfunc_panic("mzone_free(%s): returned item %p is marked as free",
		    mzone->name, item);

#ifdef WITH_MEMFUNC_DEBUG
	memset(item, FILL_FREE, MZONE_REAL_ISIZE(mzone));
#endif

	/* Return item to free items list. */
	BIT_CLEAR(bitword, bitmask);
	MZONE_FREE_ITEM_NEXT(mzone, item) = pool->free_items;
	pool->free_items = item;

	/* Increment number of free items. */
	pool->nfree++;
	mzone->nfree++;

	/* Decrement number of used items. */
	mzone->nused--;

	if (pool->nfree == pool->nitems) {
		/* If pool has more than one free item, then it was in free_pools list. */
		if (pool->nfree > 1)
			TAILQ_REMOVE(&mzone->free_pools, pool, link);

		if (mzone->nfree - pool->nfree >= mzone->nalloc) {
			/* Mzone has enough unused items. */
free_pool:
			/* Mark this pool as not existent. */
			mzone->pool_ptr[pool_idx] = NULL;

			/* Actually release memory used by pool. */
			mzone_pool_free(mzone, pool);

			/* Can we reduce pool_ptr? */
			if (pool_idx + 1 == mzone->pool_ptr_size) {
				/* Last pool in pool_ptr --> find first used pool from the end. */
				for (--pool_idx;; --pool_idx)
					if (mzone->pool_ptr[pool_idx] != NULL)
						break;
				if ( (pool_ptr = mem_realloc(mzone->pool_ptr, (pool_idx + 1) * sizeof *mzone->pool_ptr,
				    &m_mzone)) == NULL)
					logmsgx("mzone_free(%s): mem_realloc failed, cannot reduce pool_ptr",
					    mzone->name);
				else {
					/* Pool_ptr was reduced. */
					mzone->pool_ptr = pool_ptr;
					mzone->pool_ptr_size = pool_idx + 1;
				}
			}
		} else {
			/* Link completely free pool to the end of free_pools list. */
			TAILQ_INSERT_TAIL(&mzone->free_pools, pool, link);
		}
	} else {
		/* If pool has only one free item, then it is not in free_pools list. */
		if (pool->nfree == 1)
			TAILQ_INSERT_HEAD(&mzone->free_pools, pool, link);

		/*
		 * If last pool in free_pools list is completely free and
		 * mzone has enough unused items, then free it.
		 */
		pool = TAILQ_LAST(&mzone->free_pools, mzone_pool_list);
		if (pool->nfree == pool->nitems &&
		    mzone->nfree - pool->nfree >= mzone->nalloc) {
			TAILQ_REMOVE(&mzone->free_pools, pool, link);
			pool_idx = pool->pool_idx;
			goto free_pool;
		}
	}

	UNLOCK_MZONE(mzone);
}

/*
 * Return number of allocated items in mzone.
 */
u_int
mzone_nused(ipa_mzone *mzone)
{
	u_int ret;

	if (mzone == NULL)
		memfunc_panic("mzone_nused: NULL mzone");

	LOCK_MZONE(mzone);

	ret = mzone->nused;

	UNLOCK_MZONE(mzone);

	return ret;
}

/*
 * Init marray, should be called before any other marray_*() function.
 */
ipa_marray *
marray_init(const char *name, const char *desc, u_int flags, void **arr,
    size_t isize, u_int nitems, u_int nalloc)
{
	size_t	arr_size, bitmap_size;
	BITMAP_TYPE *bitmap;
	ipa_marray *marray;

	/* Validate arguments. */
	if (name == NULL || desc == NULL)
		memfunc_panic("marray_init: name or description of marray must be given");

	if (isize == 0 || nitems == 0 || nalloc == 0)
		memfunc_panic("marray_init(%s): isize, nitems and nalloc must be greater than zero",
		    name);

	/* To simplify checks let nitems will be >= than nalloc. */
	if (nitems < nalloc)
		nitems = nalloc;

	/* Round nitems and nalloc to BIT_WORD_NBITS. */
	nitems = (nitems + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1);
	nalloc = (nalloc + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1);

	/* Allocate memory for actual array. */
	arr_size = isize * nitems;
	if ( (*arr = mem_malloc(arr_size, m_marray)) == NULL) {
		logmsgx("marray_init(%s): mem_malloc for array failed", name);
		return NULL;
	}

	/* Allocate memory for bitmap. */
	bitmap_size = BITMAP_SIZE(nitems);
	if ( (bitmap = mem_calloc(bitmap_size, 1, m_marray)) == NULL) {
		logmsgx("marray_init(%s): mem_calloc for bitmap failed", name);
		goto fail_dealloc;
	}

	/* Allocate memory for marray structure and fill it. */
	if ( (marray = mzone_alloc(marray_mzone)) == NULL) {
		logmsgx("marray_init(%s): mzone_alloc failed", name);
		goto fail_dealloc;
	}
	marray->arr = arr;
	marray->bitmap = marray->bitmap_hint = bitmap;
	marray->nitems = marray->nfree = nitems;
	marray->nalloc = nalloc;
	marray->isize = isize;
	marray->name = name;
	marray->desc = desc;
	marray->arr_size = arr_size;
	marray->bitmap_size = bitmap_size;
	marray->flags = flags;
	marray->reqs = 0;

#ifdef WITH_PTHREAD
	if (IS_PTHREADED(marray))
		if (mutex_init(name, &marray->marray_mutex) < 0) {
			logmsgx("marray_init: mutex_init failed!");
			mzone_free(marray_mzone, marray);
			goto fail_dealloc;
		}
#endif

	LOCK_MARRAY_LIST();
	TAILQ_INSERT_TAIL(&marray_list, marray, link);
	UNLOCK_MARRAY_LIST();

	return marray;

fail_dealloc:
	mem_free(*arr, m_marray);
	mem_free(bitmap, m_marray);
	return NULL;
}

/*
 * Free all memory used by marray and by marray structure as well.
 */
void
marray_deinit(ipa_marray *marray)
{
#ifdef WITH_PTHREAD
	int	error;
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if (marray == NULL)
		return;

	mem_free(*marray->arr, m_marray);
	mem_free(marray->bitmap, m_marray);

#ifdef WITH_PTHREAD
	if (IS_PTHREADED(marray))
		if ( (error = pthread_mutex_destroy(&marray->marray_mutex)) != 0)
			memfunc_panic("marray_deinit(%s): pthread_mutex_destroy failed: %s!",
			    marray->name, my_strerror(error));
#endif

	LOCK_MARRAY_LIST();
	TAILQ_REMOVE(&marray_list, marray, link);
	UNLOCK_MARRAY_LIST();

	mzone_free(marray_mzone, marray);
}

/*
 * Allocate item in marray: if fixed is non-zero, then allocate
 * item with index *idxp, else find a free item with the lowest
 * index and allocate it and return index in *idxp.
 */
int
marray_alloc(ipa_marray *marray, u_int *idxp, int fixed)
{
	int	error;
	u_int	idx;
	BITMAP_TYPE *bitword, bitmask;
#ifdef WITH_PTHREAD
	char	strerrbuf[STRERRBUF_SIZE];
#endif

	if (marray == NULL)
		memfunc_panic("marray_alloc: NULL marray");

	if (idxp == NULL)
		memfunc_panic("marray_alloc(%s): NULL idxp", marray->name);

	LOCK_MARRAY(marray);

	error = 0;

	if (fixed) {
		idx = *idxp;
		if (idx < marray->nitems) {
			bitword = BIT_WORD(marray->bitmap, idx);
			bitmask = BIT_MASK(idx);
			if (BIT_TEST(bitword, bitmask))
				memfunc_panic("marray_alloc(%s, %u, 1): index is already used",
				    marray->name, idx);
		} else
			bitword = NULL;
	} else {
		if (marray->nfree > 0) {
			int bit;

			for (bitword = marray->bitmap_hint; *bitword == BIT_WORD_ALL_BITS; ++bitword)
				;
			marray->bitmap_hint = bitword;
#if 0
			for (bit = 0, bitmask = 1; BIT_TEST(bitword, bitmask); ++bit, bitmask <<= 1)
				;
#else
			bit = ffs(~*bitword) - 1;
			bitmask = 1 << bit;
#endif
			idx = BIT_WORD_NBITS * (bitword - marray->bitmap) + bit;
		} else
			bitword = NULL;
	}

	if (bitword == NULL) {
		u_int	new_nitems, new_nfree;
		size_t	size1, size2;
		void	*arr;

		new_nitems = marray->nitems + marray->nalloc;
		new_nfree = marray->nfree + marray->nalloc;
		if (fixed) {
			if (idx >= new_nitems) {
				/* Round new nitems to BIT_WORD_NBITS. */
				new_nitems = (idx + 1 + BIT_WORD_NBITS - 1) & ~(BIT_WORD_NBITS - 1);
				new_nfree = marray->nfree + (new_nitems - marray->nitems);
			}
		} else
			idx = marray->nitems;

		/* Realloc bitmap and mark extended bitwords as unused. */
		size1 = BITMAP_SIZE(new_nitems);
		if ( (bitword = mem_realloc(marray->bitmap, size1, m_marray)) == NULL) {
			logmsgx("marray_alloc(%s): mem_realloc for bitmap failed: %s",
			    marray->name, my_strerror(errno));
			error = -1;
			goto out;
		}
		size2 = BITMAP_SIZE(marray->nitems);
		if (fixed) {
			/* Simply update hint, since bitword can have new address. */
			marray->bitmap_hint = (BITMAP_TYPE *)(bitword +
			    (size_t)(marray->bitmap_hint - marray->bitmap));
		} else {
			/* No free items, so point hint to just allocated bitwords. */
			marray->bitmap_hint = (BITMAP_TYPE *)((char *)bitword + size2);
		}
		marray->bitmap_size = size1;
		memset((char *)bitword + size2, 0, size1 - size2);
		marray->bitmap = bitword;

		/*
		 * Realloc array, if we can't realloc it, then keep
		 * bitmap unchanged, everything is Ok since marray->nfree
		 * hasn't been changed yet and this is also correct for
		 * already updated bitmap_hint.
		 */
		size1 = new_nitems * marray->isize;
		if ( (arr = mem_realloc(*marray->arr, size1, m_marray)) == NULL) {
			logmsgx("marray_alloc(%s): mem_realloc for array failed",
			    marray->name);
			error = -1;
			goto out;
		}

		*marray->arr = arr;
		marray->arr_size = size1;
		marray->nitems = new_nitems;
		marray->nfree = new_nfree;

		bitword = BIT_WORD(marray->bitmap, idx);
		bitmask = BIT_MASK(idx);
	}

	BIT_SET(bitword, bitmask);

#ifdef WITH_MEMFUNC_DEBUG
	memset((char *)*marray->arr + idx * marray->isize, FILL_ALLOC, marray->isize);
#endif

	marray->nfree--;

	*idxp = idx;

out:
	marray->reqs++;

	UNLOCK_MARRAY(marray);

	return error;
}

/*
 * Return item with the given index to the marray, check if it is
 * possible to reduce size of marray.
 */
void
marray_free(ipa_marray *marray, u_int idx)
{
	BITMAP_TYPE *bitword, bitmask;

	if (marray == NULL)
		memfunc_panic("marray_free: NULL marray");

	LOCK_MARRAY(marray);

	if (idx >= marray->nitems)
		memfunc_panic("marray_free(%s): index %u is out of range (0..%u)",
		    marray->name, idx, marray->nitems - 1);

	bitword = BIT_WORD(marray->bitmap, idx);
	bitmask = BIT_MASK(idx);

	if (!BIT_TEST(bitword, bitmask))
		memfunc_panic("marray_free(%s): item with index %u was not allocated",
		    marray->name, idx);

	BIT_CLEAR(bitword, bitmask);

#ifdef WITH_MEMFUNC_DEBUG
	memset((char *)(*marray->arr) + idx * marray->isize, FILL_FREE, marray->isize);
#endif

	marray->nfree++;

	if (BIT_SLOT(idx) == BIT_SLOT(marray->nitems - 1) &&
	    *bitword == 0 && marray->nfree >= marray->nalloc) {
		u_int	new_nitems, new_nfree;
		size_t	size;
		void	*arr;

		/*
		 * Last bitword and it is free and there are enough
		 * unused items -- try to decrease array.
		 */
		if (marray->nfree == marray->nitems)
			/* Easy case, all items are free. */
			new_nitems = new_nfree = marray->nalloc;
		else {
			new_nitems = marray->nitems;
			new_nfree = marray->nfree;
			for (; *bitword == 0; --bitword)
				if (new_nfree - BIT_WORD_NBITS >= marray->nalloc) {
					/* Has enough unused items. */
					new_nitems -= BIT_WORD_NBITS;
					new_nfree -= BIT_WORD_NBITS;
				} else
					break;
		}
		if (new_nitems < marray->nitems) {
			/*
			 * Number of items was decreased,
			 * so reallocate array and bitmap.
			 */
			size = new_nitems * marray->isize;
			if ( (arr = mem_realloc(*marray->arr, size, m_marray)) == NULL)
				logmsgx("marray_free(%s): mem_realloc for array failed",
				    marray->name);
			else {
				*marray->arr = arr;
				marray->nitems = new_nitems;
				marray->nfree = new_nfree;

				marray->arr_size = size;

				/*
				 * If can't reallocate bitmap, then it
				 * will be bigger than we need.
				 */
				size = BITMAP_SIZE(new_nitems);
				if ( (bitword = mem_realloc(marray->bitmap, size, m_marray)) == NULL)
					logmsgx("marray_free(%s): mem_realloc for bitmap failed",
					    marray->name);
				else {
					marray->bitmap_hint = (BITMAP_TYPE *)(bitword +
					    (size_t)(marray->bitmap_hint - marray->bitmap));
					marray->bitmap = bitword;
					marray->bitmap_size = size;
				}
			}
		}
	} else {
		if (marray->bitmap_hint > bitword)
			marray->bitmap_hint = bitword;
	}

	UNLOCK_MARRAY(marray);
}

/*
 * Reduce array if it uses more memory than needed.
 */
void
marray_minimize(ipa_marray *marray)
{
	u_int	new_nitems, new_nfree;
	void	*arr;
	size_t	size;
	BITMAP_TYPE *bitword;

	if (marray == NULL)
		memfunc_panic("marray_minimize: NULL marray");

	LOCK_MARRAY(marray);

	if (marray->nitems != marray->nfree) {
		/*
		 * There are at least one used item in marray.
		 * If marray is free why not to deinit it?
		 */
		new_nitems = marray->nitems;
		new_nfree = marray->nfree;
		for (bitword = marray->bitmap + BIT_SLOT(marray->nitems - 1);
		    *bitword == 0; --bitword) {
			new_nitems -= BIT_WORD_NBITS;
			new_nfree -= BIT_WORD_NBITS;
		}

		size = new_nitems * marray->isize;
		if ( (arr = mem_realloc(*marray->arr, size, m_marray)) == NULL)
			logmsgx("marray_minimize(%s): mem_realloc for array failed",
			    marray->name);
		else {
			*marray->arr = arr;
			marray->nitems = new_nitems;
			marray->nfree = new_nfree;

			marray->arr_size = size;

			/*
			 * If can't realloc bitmap, then it will be bigger
			 * than we need.
			 */
			size = BITMAP_SIZE(new_nitems);
			if ( (bitword = mem_realloc(marray->bitmap, size, m_marray)) == NULL)
				logmsgx("marray_minimize(%s): mem_realloc for bitmap failed",
				    marray->name);
			else {
				marray->bitmap = marray->bitmap_hint = bitword;
				marray->bitmap_size = size;
			}
		}
	}

	UNLOCK_MARRAY(marray);
}

/*
 * Return 0 if item with the given index is not used,
 * otherwise return non-zero.
 */
int
marray_check_index(ipa_marray *marray, u_int idx)
{
	int ret;

	if (marray == NULL)
		memfunc_panic("marray_check_index: NULL marray");

	LOCK_MARRAY(marray);

	ret = idx >= marray->nitems ?
	    0 : BIT_TEST(BIT_WORD(marray->bitmap, idx), BIT_MASK(idx));

	UNLOCK_MARRAY(marray);

	return ret;
}

/*
 * Return number of allocated items in marray.
 */
u_int
marray_nused(ipa_marray *marray)
{
	u_int ret;

	if (marray == NULL)
		memfunc_panic("marray_nused: NULL marray");

	LOCK_MARRAY(marray);

	ret = marray->nitems - marray->nfree;

	UNLOCK_MARRAY(marray);

	return ret;
}

void
memfunc_pre_init(void)
{
#if defined(WITH_PTHREAD) && (defined(_SC_PAGE_SIZE) || defined(_SC_PAGESIZE))
	char strerrbuf[STRERRBUF_SIZE];
#endif

#if defined(_SC_PAGE_SIZE)
	errno = 0;
	if ( (pagesize = sysconf(_SC_PAGE_SIZE)) < 0) {
		logmsgx("memfunc_pre_init: sysconf(_SC_PAGE_SIZE): %s",
		    my_strerror(errno));
		pagesize = PAGESIZE_DEFAULT;
	}
#elif defined(_SC_PAGESIZE)
	errno = 0;
	if ( (pagesize = sysconf(_SC_PAGESIZE)) < 0) {
		logmsgx("memfunc_pre_init: sysconf(_SC_PAGESIZE): %s",
		    my_strerror(errno));
		pagesize = PAGESIZE_DEFAULT;
	}
#elif defined(PAGE_SIZE)
	pagesize = PAGE_SIZE
#elif defined(PAGESIZE)
	pagesize = PAGESIZE;
#else
	pagesize = PAGESIZE_DEFAULT;
#endif
}

/*
 * Init mzone_mzone and marray_mzone zones.
 */
int
memfunc_init(void)
{
	static int first_call = 1;

	switch (memfunc_inited) {
	case 1:
		memfunc_panic("memfunc_init: memfunc was not completely inited previous time");
		/* NOTREACHED */
	case 2:
		return 0;
	}

	memfunc_inited = 1;

#ifdef WITH_PTHREAD
	if (first_call)
		if (memfunc_mutexattr_init() < 0) {
			logmsgx("memfunc_init: cannot init default mutex attributes");
			return -1;
		}
#endif

	STAILQ_INIT(&mem_type_list);
	TAILQ_INIT(&mzone_list);
	TAILQ_INIT(&marray_list);

	if (mem_type_init_internal(&m_mzone, MTYPE_NAME(mzone),
	    "Memory used by all mzones", MEMFUNC_INTERNAL_FLAGS) < 0) {
		logmsgx("memfunc_init: mem_type_init_internal(%s) failed",
		    MTYPE_NAME(mzone));
		return -1;
	}
	m_mzone.local = 1;

	if (mzone_init_internal(&mzone_mzone, MZONE_NAME(mzone), "Mzone structures",
	    MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE, sizeof(ipa_mzone), MZONE_NSIZE,
	    MZONE_NALLOC) < 0) {
		logmsgx("memfunc_init: mzone_init_internal(%s) failed",
		    MZONE_NAME(mzone));
		return -1;
	}
	if ( (mem_type_mzone = mzone_init(MZONE_NAME(mem_type), "Memory types structures",
	    MEMFUNC_INTERNAL_FLAGS, sizeof(ipa_mem_type), MEM_TYPE_NSIZE,
	    MEM_TYPE_NALLOC)) == NULL) {
		logmsgx("memfunc_init: mzone_init(%s) failed",
		    MZONE_NAME(mem_type));
		return -1;
	}

	if ( (marray_mzone = mzone_init(MZONE_NAME(marray), "Marray structures",
	    MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE, sizeof(ipa_marray), MARRAY_NSIZE,
	    MARRAY_NALLOC)) == NULL) {
		logmsgx("memfunc_init: mzone_init(%s) failed",
		    MZONE_NAME(marray));
		return -1;
	}

	if ( (m_marray = mem_type_new_local(MTYPE_NAME(marray),
	    "Memory used by all marrays", MEMFUNC_INTERNAL_FLAGS|MEMFLAG_OPTIMIZE)) == NULL) {
		logmsgx("memfunc_init: mem_type_new_local(%s) failed",
		    MTYPE_NAME(marray));
		return -1;
	}

	memfunc_inited = 2;

	first_call = 0;

	return 0;
}

static void
mem_types_deinit(int foreign, int local)
{
	ipa_mem_type *mem_type;

	LOCK_MEM_TYPE_LIST();
	STAILQ_FOREACH(mem_type, &mem_type_list, link)
		if (mem_type->local == local) {
			if (mem_type->size != 0 && !foreign) {
				logmsgx("mem_types_deinit(%d): memory leak: mem_type %s \"%s\" has not freed %lu bytes",
				    local, mem_type->name, mem_type->desc, (u_long)mem_type->size);
				mem_size_cnt += mem_type->size;
			}
			mem_type_deinit_internal(mem_type);
		}
	UNLOCK_MEM_TYPE_LIST();
}

/*
 * First step: deinit marrays and mzones (except mem_type_mzone and
 * mzone_mzone) and check nonlocal mem_types, we need to run this step
 * before unloading modules, since we need to access mzones', marrays'
 * and mem_types' names, which are hold in modules' address spaces.
 */
void
memfunc_deinit_1(int foreign)
{
	ipa_mzone *mzone;
	ipa_marray *marray;

	switch (memfunc_inited) {
	case 0:
		return;
	case 1:
		memfunc_panic("memfunc_deinit: memfunc was not completely inited, cannot deinit it");
	}
	memfunc_inited = 0;

	while ( (marray = TAILQ_FIRST(&marray_list)) != NULL) {
		if (!foreign)
			logmsgx("memfunc_deinit_1: memory leak: marray %s \"%s\" was not deinited (%u used items)",
			    marray->name, marray->desc, marray->nitems - marray->nfree);
		marray_deinit(marray);
	}

	mzone_deinit(marray_mzone);

	mem_size_cnt = 0;

	mem_types_deinit(foreign, 0);

	TAILQ_REMOVE(&mzone_list, mem_type_mzone, link);
	while ( (mzone = TAILQ_FIRST(&mzone_list)) != NULL) {
		if (!foreign)
			logmsgx("memfunc_deinit_1: memory leak, mzone %s \"%s\" was not deinited (%u used items)",
			    mzone->name, mzone->desc, mzone->nused);
		mzone_deinit(mzone);
	}
	TAILQ_INSERT_HEAD(&mzone_list, mem_type_mzone, link);
}

/*
 * Second step: check local mem_types
 */
void
memfunc_deinit_2(int foreign)
{
	mem_types_deinit(foreign, 1);
	mzone_deinit(mem_type_mzone);

	mzone_deinit_internal(&mzone_mzone);

	if (m_mzone.size != 0 && !foreign) {
		logmsgx("memfunc_deinit_2: memory leak: mem_type %s \"%s\" has not freed %lu bytes",
		    m_mzone.name, m_mzone.desc, (u_long)m_mzone.size);
		mem_size_cnt += m_mzone.size;
	}

	mem_type_deinit_internal(&m_mzone);

	if (mem_size_cnt != 0 && !foreign) {
		logmsgx("memfunc_deinit_2: memory leak: %lu bytes were not freed after reconfiguration",
		    (u_long)mem_size_cnt);
		mem_size_cnt_all += mem_size_cnt;
	}

	if (!foreign) {
		if (mem_size_cnt_all != 0)
			logmsgx("memfunc_deinit_2: memory leak: %lu bytes were not freed after start",
			    (u_long)mem_size_cnt_all);
	} else
		mem_size_cnt_all = 0;
}

/*
 * This function is called only from modules.  Note, that this is not
 * a deep-free, so some memory will be not deallocated.
 */
static void
memfunc_deinit(int foreign)
{
	memfunc_deinit_1(foreign);
	memfunc_deinit_2(foreign);
}

#ifdef WITH_MEMFUNC_STAT

#include "ipactl.h"

extern void	*m_ctl;

int
memfunc_get_stat(void **bufp, size_t *buf_sizep)
{
	int	flag;
	u_int	nmem_type, nmzones, nmarrays;
	char	*buf;
	size_t	buf_size, size_cnt;
	ipa_mzone *mzone;
	ipa_marray *marray;
	ipa_mem_type *mem_type;
	struct ctl_cmd_answer_memory *answer_memory;
	struct ctl_cmd_answer_mzone *answer_mzone;
	struct ctl_cmd_answer_marray *answer_marray;
	struct ctl_cmd_answer_mem_type *answer_mem_type;

	buf_size = sizeof *answer_memory;
	if ( (buf = mem_malloc(sizeof *answer_memory, m_ctl)) == NULL) {
		logmsgx("memfunc_get_stat: mem_malloc failed");
		return -1;
	}

	/*
	 * Get stat for mem_types.
	 */
	nmem_type = 0;
	size_cnt = 0;
	LOCK_MEM_TYPE_LIST();
	STAILQ_INSERT_HEAD(&mem_type_list, &m_mzone, link);
	STAILQ_FOREACH(mem_type, &mem_type_list, link) {
		if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_mem_type, m_ctl)) == NULL) {
			STAILQ_REMOVE_HEAD(&mem_type_list, link);
			UNLOCK_MEM_TYPE_LIST();
			logmsgx("memfunc_get_stat: mem_realloc failed");
			goto failed;
		}
		answer_mem_type = (struct ctl_cmd_answer_mem_type *)(buf + buf_size);
		buf_size += sizeof *answer_mem_type;
		LOCK_MEM_TYPE(mem_type);
		strncpy(answer_mem_type->name, mem_type->name, sizeof(answer_mem_type->name) - 1);
		answer_mem_type->name[sizeof(answer_mem_type->name) - 1] = '\0';
		strncpy(answer_mem_type->desc, mem_type->desc, sizeof(answer_mem_type->desc) - 1);
		answer_mem_type->desc[sizeof(answer_mem_type->desc) - 1] = '\0';
		size_cnt += answer_mem_type->size = mem_type->size;
		answer_mem_type->reqs = mem_type->reqs;
		UNLOCK_MEM_TYPE(mem_type);
		++nmem_type;
	}
	STAILQ_REMOVE_HEAD(&mem_type_list, link);
	UNLOCK_MEM_TYPE_LIST();

	/*
	 * Get stat for mzones.
	 */
	nmzones = 0;
	LOCK_MZONE_LIST();
	TAILQ_INSERT_HEAD(&mzone_list, &mzone_mzone, link);
	flag = 0;
	TAILQ_FOREACH(mzone, &mzone_list, link) {
		if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_mzone, m_ctl)) == NULL) {
			logmsgx("memfunc_get_stat: mem_realloc failed");
			TAILQ_REMOVE(&mzone_list, &mzone_mzone, link);
			UNLOCK_MZONE_LIST();
			goto failed;
		}
		answer_mzone = (struct ctl_cmd_answer_mzone *)(buf + buf_size);
		buf_size += sizeof *answer_mzone;
		if (flag)
			LOCK_MZONE(mzone);
		strncpy(answer_mzone->name, mzone->name, sizeof(answer_mzone->name) - 1);
		answer_mzone->name[sizeof(answer_mzone->name) - 1] = '\0';
		strncpy(answer_mzone->desc, mzone->desc, sizeof(answer_mzone->desc) - 1);
		answer_mzone->desc[sizeof(answer_mzone->desc) - 1] = '\0';
		answer_mzone->isize = mzone->isize;
		answer_mzone->nused = mzone->nused;
		answer_mzone->nfree = mzone->nfree;
		answer_mzone->pools_size = mzone->pools_size;
		answer_mzone->reqs = mzone->reqs;
		if (flag)
			UNLOCK_MZONE(mzone);
		else
			flag = 1;
		++nmzones;
	}
	TAILQ_REMOVE(&mzone_list, &mzone_mzone, link);
	UNLOCK_MZONE_LIST();

	/*
	 * Get stat for marrays.
	 */
	nmarrays = 0;
	LOCK_MARRAY_LIST();
	TAILQ_FOREACH(marray, &marray_list, link) {
		if ( (buf = mem_realloc(buf, buf_size + sizeof *answer_marray, m_ctl)) == NULL) {
			logmsgx("memfunc_get_stat: mem_realloc failed");
			UNLOCK_MARRAY_LIST();
			goto failed;
		}
		answer_marray = (struct ctl_cmd_answer_marray *)(buf + buf_size);
		buf_size += sizeof *answer_marray;
		LOCK_MARRAY(marray);
		strncpy(answer_marray->name, marray->name, sizeof(answer_marray->name) - 1);
		answer_marray->name[sizeof(answer_marray->name) - 1] = '\0';
		strncpy(answer_marray->desc, marray->desc, sizeof(answer_marray->desc) - 1);
		answer_marray->desc[sizeof(answer_marray->desc) - 1] = '\0';
		answer_marray->isize = marray->isize;
		answer_marray->nused = marray->nitems - marray->nfree;
		answer_marray->nfree = marray->nfree;
		answer_marray->arr_size = marray->arr_size;
		answer_marray->bitmap_size = marray->bitmap_size;
		answer_marray->reqs = marray->reqs;
		UNLOCK_MARRAY(marray);
		++nmarrays;
	}
	UNLOCK_MARRAY_LIST();

	answer_memory = (struct ctl_cmd_answer_memory *)buf;
	answer_memory->size_cnt = size_cnt + mem_size_cnt_all;
	answer_memory->nmem_type = nmem_type;
	answer_memory->nmzones = nmzones;
	answer_memory->nmarrays = nmarrays;

	*bufp = buf;
	*buf_sizep = buf_size;

	return 0;

failed:
	mem_free(buf, m_ctl);
	return -1;
}
#endif /* WITH_MEMFUNC_STAT */

#ifdef MEMFUNC_STANDALONE

static void
vlogmsgx(const char *format, va_list ap)
{
	fflush(stdout);
	fprintf(stderr, "ERROR: ");
	vfprintf(stderr, format, ap);
	fprintf(stderr, "\n");
}

static void
show_bitmap(const char *msg, const BITMAP_TYPE *bitmap, size_t size)
{
	u_int	bit;
	const BITMAP_TYPE *bitword;

	if (msg != NULL)
		printf("%s\n", msg);

	for (bitword = bitmap; bitword < bitmap + size; ++bitword) {
		for (bit = 1 << (BIT_WORD_NBITS - 1); bit > 0; bit >>= 1)
			if (*bitword & bit)
				printf("1");
			else
				printf("0");
		printf(" ");
	}
	printf("\n");
}

static void
show_marray_bitmap(const char *msg, const struct marray *marray)
{
	show_bitmap(msg, marray->bitmap,
	    BITMAP_SIZE(marray->nitems) / sizeof(BITMAP_TYPE));
}

int
main(void)
{
	mvlogmsgx = vlogmsgx;

	if (memfunc_init() < 0)
		return 1;

	memfunc_deinit(0);

	return 0;
}

#endif /* MEMFUNC_STANDALONE */

/*
 * This structure is exported to modules.
 */
ipa_memfunc memfunc = {
	IPA_MEMFUNC_API_VERSION,/* api_ver		*/
	memfunc_init,		/* memfunc_init		*/
	memfunc_deinit,		/* memfunc_deinit	*/
	NULL,			/* m_parser		*/
	mem_type_new,		/* mem_type_new		*/
	mem_malloc,		/* mem_malloc		*/
	mem_calloc,		/* mem_calloc		*/
	mem_realloc,		/* mem_realloc		*/
	mem_strdup,		/* mem_strdup		*/
	mem_vasprintf,		/* mem_vasprintf	*/
	mem_free,		/* mem_free		*/
	marray_init,		/* marray_init		*/
	marray_deinit,		/* marray_deinit	*/
	marray_alloc,		/* marray_alloc		*/
	marray_free,		/* marray_free		*/
	marray_minimize,	/* marray_minimize	*/
	marray_check_index,	/* marray_check_index	*/
	marray_nused,		/* marray_nused		*/
	mzone_init,		/* mzone_init		*/
	mzone_deinit,		/* mzone_deinit		*/
	mzone_alloc,		/* mzone_alloc		*/
	mzone_free,		/* mzone_free		*/
	mzone_nused		/* mzone_nused		*/
};


syntax highlighted by Code2HTML, v. 0.9.1