/*
Copyright (c) 2015-2019 Roger Light <roger@atchoo.org>

All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
and Eclipse Distribution License v1.0 which accompany this distribution.
 
The Eclipse Public License is available at
   http://www.eclipse.org/legal/epl-v10.html
and the Eclipse Distribution License is available at
  http://www.eclipse.org/org/documents/edl-v10.php.
 
Contributors:
   Roger Light - initial implementation and documentation.
*/
#ifndef UHPA_H
#define UHPA_H

/* "Union of heap pointer and array"
 *
 * This set of macros is intended to provide a simple means of treating a
 * variable as a heap pointer or a fixed size array, depending on the size of
 * the data being stored. It was borne out of the situation where the majority
 * of calls to malloc for a struct member were ending up less than 8 bytes -
 * i.e. the size of the 64-bit pointer. By using uhpa a great number of calls
 * to malloc could be avoided. The downsize to this method is that you must
 * always know the length of data that you are dealing with.
 *
 * The data structure you provide must look something like:
 *
 * typedef union {
 *		void *ptr;
 *		char array[MAX_ARRAY_SIZE];
 *	} uhpa_u
 *
 * This code really only makes sense if the data types you want to store are
 * smaller than a pointer - the most obvious choice being bytes. All of these
 * functions assume that the array is made up of single bytes.
 *
 * You can change the type of ptr to match your needs, with the above caveat
 * about array. So make ptr a char*, uint8_t*, unsigned char*, ...
 *
 * It should be possible to modify the code to work with arrays that have
 * element sizes bigger than a byte.
 *
 * Define MAX_ARRAY_SIZE to be as large as you want, depending on the size of
 * your commonly used data. Define MAX_ARRAY_SIZE to be sizeof(void *) if you
 * do not want to "waste" any memory per item - with the tradeoff that calls to
 * malloc will be more frequent because MAX_ARRAY_SIZE will only be 4 or 8
 * bytes, depending on your architecture.
 *
 * =============================================================================
 * Basic Functions
 * =============================================================================
 *
 * Note that if you are using strings, set size to be strlen(s)+1, so that the
 * null terminator is included, or use the _STR functions below.
 *
 * UHPA_ALLOC(u, size)
 *		Call to allocate memory to a uhpa variable if required.
 *
 *		u : the uhpa data type that will have memory allocated or not,
 *		depending on "size".
 *		size : the length of the data to be stored, in bytes.
 *
 *		returns :	1 if memory was allocated successfully
 *					0 if memory was not able to be allocated
 *					-1 if no memory needed to be allocated
 * 
 * UHPA_ACCESS(u, size)
 *		Call to access (for read or write) a uhpa variable that has already had
 *		UHPA_ALLOC() called on it.
 *
 *		u : the uhpa data type that has already had memory allocated.
 *		size : the length of the stored data, in bytes.
 *
 *		returns : an appropriate pointer/array address
 *
 * UHPA_FREE(u, size)
 *		Call to free memory associated with a uhpa variable. This is safe to
 *		call with a data structure that does not have heap allocated memory.
 *
 *		u : the uhpa data type that has already had memory allocated.
 *		size : the length of the stored data, in bytes.
 *		
 * UHPA_MOVE(dest, src, src_size)
 *		Call to move memory stored in one uhpa variable to another. If the data
 *		is stored with heap allocated memory, then dest.ptr is set to src.ptr.
 *		If the data is stored as an array, memmove is used to copy data from
 *		src to dest. In both cases the data stored in src is invalidated by
 *		setting src.ptr to NULL and calling memset(src.array, 0, src_size)
 *		respectively.
 *
 * =============================================================================
 * String Functions
 * =============================================================================
 *
 * Convenience functions when working with strings. These are identical to the
 * non-string versions, except that they increase the value of "size" by 1, to
 * take into account the need for storing the 0 termination character.
 *
 * UHPA_ALLOC_STR(u, size)
 * UHPA_ACCESS_STR(u, size)
 * UHPA_FREE_STR(u, size)
 * UHPA_MOVE_STR(dest, src, size)
 *		
 * =============================================================================
 * Forcing use of malloc
 * =============================================================================
 *
 * If you wish to force the use of malloc without removing the UHPA macros from
 * your code (i.e. the macros will never use the array part of the union) then
 * #define UHPA_FORCE_MALLOC before including this file.
 *
 * =============================================================================
 * Memory Functions
 * =============================================================================
 *
 * If you wish to use your own memory functions for alloc/free, #define both
 * uhpa_malloc and uhpa_free to your own functions.
 */


#ifndef uhpa_malloc
#	define uhpa_malloc(size) malloc(size)
#endif

#ifndef uhpa_free
#	define uhpa_free(ptr) free(ptr)
#endif

#define UHPA_ALLOC_CHK(u, size) \
	((size) > sizeof((u).array)? \
		(((u).ptr = uhpa_malloc((size)))?1:0) \
		:-1)

#define UHPA_ACCESS_CHK(u, size) ((size) > sizeof((u).array)?(u).ptr:(u).array)

#define UHPA_FREE_CHK(u, size) \
	if((size) > sizeof((u).array) && (u).ptr){ \
		uhpa_free((u).ptr); \
		(u).ptr = NULL; \
	} 

#define UHPA_MOVE_CHK(dest, src, src_size) \
	if((src_size) > sizeof((src).array) && (src).ptr){ \
		(dest).ptr = (src).ptr; \
		(src).ptr = NULL; \
	}else{ \
		memmove((dest).array, (src).array, (src_size)); \
		memset((src).array, 0, (src_size)); \
	}

#ifdef UHPA_FORCE_MALLOC
#  define UHPA_ALLOC(u, size) ((u).ptr = uhpa_malloc(size))
#  define UHPA_ACCESS(u, size) (u).ptr
#  define UHPA_FREE(u, size) uhpa_free((u).ptr); (u).ptr = NULL;
#  define UHPA_MOVE(dest, src, src_size) {(dest).ptr = (src).ptr; (src).ptr = NULL;}
#else
#  define UHPA_ALLOC(u, size) UHPA_ALLOC_CHK(u, size)
#  define UHPA_ACCESS(u, size) UHPA_ACCESS_CHK(u, size)
#  define UHPA_FREE(u, size) UHPA_FREE_CHK(u, size)
#  define UHPA_MOVE(dest, src, src_size) UHPA_MOVE_CHK(dest, src, src_size)
#endif

#define UHPA_ALLOC_STR(u, size) UHPA_ALLOC((u), (size)+1)
#define UHPA_ACCESS_STR(u, size) ((char *)UHPA_ACCESS((u), (size)+1))
#define UHPA_FREE_STR(u, size) UHPA_FREE((u), (size)+1)
#define UHPA_MOVE_STR(dest, src, src_size) UHPA_MOVE((dest), (src), (src_size)+1)

#endif
