/*
 * Similar core function to LGPL licensed talloc from Samba
 */

#ifndef _HIERALLOC_H_
#define _HIERALLOC_H_

#include <stdio.h>
#include <stdarg.h>

// allow __LINE__ to be stringified
#ifndef __location__
#define __HIERALLOC_STRING_0__(s)   #s
#define __HIERALLOC_STRING_1__(s)   __HIERALLOC_STRING_0__(s)
#define __HIERALLOC_STRING_2__      __HIERALLOC_STRING_1__(__LINE__)
#define __location__                __FILE__ ":" __HIERALLOC_STRING_2__
#endif

#define hieralloc(ctx, type) (type *)hieralloc_allocate(ctx, sizeof(type), #type)
#define hieralloc_size(ctx, size) hieralloc_allocate(ctx, size, "sz:"__location__)
#define hieralloc_new(ctx) hieralloc_allocate(ctx, 0, "nw:" __location__)
#define hieralloc_zero(ctx, type) (type *)_hieralloc_zero(ctx, sizeof(type), "zr:"#type)
#define hieralloc_zero_size(ctx, size) _hieralloc_zero(ctx, size, "zrsz:"__location__)
#define hieralloc_array(ctx, type, count) (type *)hieralloc_allocate(ctx, sizeof(type) * count, "ar:"#type)
#define hieralloc_realloc(ctx, p, type, count) (type *)hieralloc_reallocate(ctx, p, sizeof(type) * count, "re:"#type)

#ifdef __cplusplus
extern "C" {
#endif
// allocate memory and attach to parent context and siblings
void * hieralloc_allocate(const void * context, unsigned size, const char * name);

// (re)allocate memory and attach to parent context and siblings
void * hieralloc_reallocate(const void * context, void * ptr, unsigned size, const char * name);

// calls destructor if set, and frees children.
// if destructor returns -1, then do nothing and return -1.
int hieralloc_free(void * ptr);

// creates 0 allocation to be used as parent context
void * hieralloc_init(const char * name);

// returns global context
void * hieralloc_autofree_context();

// sets destructor to be called before freeing; dctor return -1 aborts free
void hieralloc_set_destructor(const void * ptr, int (* destructor)(void *));

// gets parent context of allocated memory
void * hieralloc_parent(const void * ptr);

// moves allocation to new parent context; maintain children but update siblings
// returns ptr on success
void * hieralloc_steal(const void * new_ctx, const void * ptr);

// not implemented from talloc_reference
void * hieralloc_reference(const void * ref_ctx, const void * ptr);

// not implemented from talloc_unlink
int hieralloc_unlink(const void * ctx, void * ptr);

// allocate and zero memory
void * _hieralloc_zero(const void * ctx, unsigned size, const char * name);

// allocate and copy 
char * hieralloc_strdup(const void * ctx, const char * str);

// allocate and copy
char * hieralloc_strndup(const void * ctx, const char * str, unsigned len);

// reallocate and append
char * hieralloc_strdup_append(char * str, const char * append);

// reallocate and append
char * hieralloc_strndup_append(char * str, const char * append, unsigned len);

// allocate and vsprintf
char * hieralloc_vasprintf(const void * ctx, const char * fmt, va_list va);

// allocate and sprintf
char * hieralloc_asprintf(const void * ctx, const char * fmt, ...);

// reallocate and append vsprintf
char * hieralloc_vasprintf_append(char * str, const char * fmt, va_list va);

// reallocate and append sprintf
char * hieralloc_asprintf_append(char * str, const char * fmt, ...);

// report self and child allocations
void hieralloc_report(const void * ptr, FILE * file);

void hieralloc_report_brief(const void * ptr, FILE * file);

void hieralloc_report_lineage(const void * ptr, FILE * file, int tab);

int hieralloc_find(const void * top, const void * ptr, FILE * file, int tab);

#ifdef __cplusplus
}
#endif

#endif