File:  [ELWIX - Embedded LightWeight unIX -] / libelwix / src / vars.c
Revision 1.13: download - view: text, annotated - select for diffs - revision graph
Mon Oct 24 00:10:22 2022 UTC (2 years ago) by misho
Branches: MAIN
CVS tags: elwix6_6, elwix6_5, elwix6_4, elwix6_3, elwix6_2, elwix6_1, elwix5_9, elwix5_8, elwix5_7, elwix5_12, elwix5_11, elwix5_10, HEAD, ELWIX6_5, ELWIX6_4, ELWIX6_2, ELWIX6_1, ELWIX6_0, ELWIX5_9, ELWIX5_8, ELWIX5_7, ELWIX5_6, ELWIX5_11, ELWIX5_10
version 5.6

/*************************************************************************
* (C) 2011 AITNET ltd - Sofia/Bulgaria - <misho@aitnet.org>
*  by Michael Pounov <misho@elwix.org>
*
* $Author: misho $
* $Id: vars.c,v 1.13 2022/10/24 00:10:22 misho Exp $
*
**************************************************************************
The ELWIX and AITNET software is distributed under the following
terms:

All of the documentation and software included in the ELWIX and AITNET
Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>

Copyright 2004 - 2022
	by Michael Pounov <misho@elwix.org>.  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.
3. All advertising materials mentioning features or use of this software
   must display the following acknowledgement:
This product includes software developed by Michael Pounov <misho@elwix.org>
ELWIX - Embedded LightWeight unIX and its contributors.
4. Neither the name of AITNET nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY AITNET 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 REGENTS 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 "global.h"


static inline int
vars2buffer(u_char * __restrict buf, int buflen, int be, array_t * __restrict vars)
{
	int Limit = 0;
	register int i;
	ait_val_t *v, *val;
	u_char *dat;

	assert(buf);
	assert(vars);
	if (!buf || !vars)
		return -1;
	if (!buflen || !array_Size(vars))
		return 0;
	be = !!be;

	Limit = sizeof(ait_val_t) * array_Size(vars);
	if (Limit > buflen) {
		elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d needed min %d", 
				buflen, Limit);
		return -1;
	} else {
		memset(buf, 0, buflen);

		v = (ait_val_t*) buf;
		dat = buf + Limit;
	}

	/* marshaling */
	for (i = 0; i < array_Size(vars); i++) {
		val = array(vars, i, ait_val_t*);

		v[i].val_type = val->val_type;
		AIT_IN(&v[i]) = 1;
		AIT_BE(&v[i]) = be;
		AIT_LE(&v[i]) = !be;
		if (AIT_BE(&v[i])) {
			AIT_KEY(&v[i]) = htobe16(AIT_KEY(val));
			AIT_LEN(&v[i]) = htobe32(AIT_LEN(val));
		}
		if (AIT_LE(&v[i])) {
			AIT_KEY(&v[i]) = htole16(AIT_KEY(val));
			AIT_LEN(&v[i]) = htole32(AIT_LEN(val));
		}

		switch (AIT_TYPE(val)) {
			case blob:
			case f32:
			case f64:
			case i8:
			case i16:
			case i32:
			case i64:
			case u8:
			case u16:
			case u32:
			case u64:
				if (AIT_BE(&v[i]))
					v[i].val.net = htobe64(val->val.net);
				if (AIT_LE(&v[i]))
					v[i].val.net = htole64(val->val.net);
				break;
			case data:
				if (AIT_LEN(val) > buflen - Limit) {
					elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d "
							"needed min %d", buflen, Limit + AIT_LEN(val));
					return -1;
				} else
					Limit += AIT_LEN(val);

				memcpy(dat, val->val_data, AIT_LEN(val));
				/* Debug:: data offset in packet, not matter for anything! */
				v[i].val.net = dat - buf;
				dat += AIT_LEN(val);
				break;
			case buffer:
			case string:
			case ptr:
				if (AIT_LEN(val) > buflen - Limit) {
					elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d "
							"needed min %d", buflen, Limit + AIT_LEN(val));
					return -1;
				} else
					Limit += AIT_LEN(val);

				memcpy(dat, val->val.buffer, AIT_LEN(val));
				/* Debug:: data offset in packet, not matter for anything! */
				v[i].val.net = dat - buf;
				dat += AIT_LEN(val);
				break;
			default:
				elwix_SetErr(EINVAL, "Unsupported variable type=%d at element #%d", 
						AIT_TYPE(val), i);
				return -1;
		}
	}

	return Limit;
}

static inline array_t *
buffer2vars(u_char * __restrict buf, int buflen, int vnum, int zcpy)
{
	array_t *vars;
	int Limit = 0;
	register int i;
	ait_val_t *v, *val;
	u_char *dat;

	assert(buf);
	if (!buf || !buflen || !vnum)
		return NULL;

	Limit = sizeof(ait_val_t) * vnum;
	if (Limit > buflen) {
		elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d needed min %d", 
				buflen, Limit);
		return NULL;
	} else {
		if (!(vars = array_Init(vnum)))
			return NULL;

		v = (ait_val_t*) buf;
		dat = buf + Limit;
	}

	/* de-marshaling */
	for (i = 0; i < array_Size(vars); i++) {
		if (!zcpy) {
			val = e_malloc(sizeof(ait_val_t));
			if (!val) {
				if (!zcpy)
					array_Free(vars);
				array_Destroy(&vars);
				return NULL;
			}
			AIT_IN(val) = 0;
		} else {
			val = v + i;
			AIT_IN(val) = 1;
		}
		array_Set(vars, i, val);

		val->val_type = v[i].val_type;
		AIT_BE(val) = AIT_BE(&v[i]);
		AIT_LE(val) = AIT_LE(&v[i]);
		if (AIT_BE(val)) {
			AIT_LEN(val) = be32toh(AIT_LEN(&v[i]));
			AIT_KEY(val) = be16toh(AIT_KEY(&v[i]));
		}
		if (AIT_LE(val)) {
			AIT_LEN(val) = le32toh(AIT_LEN(&v[i]));
			AIT_KEY(val) = le16toh(AIT_KEY(&v[i]));
		}

		switch (AIT_TYPE(val)) {
			case blob:
			case f32:
			case f64:
			case i8:
			case i16:
			case i32:
			case i64:
			case u8:
			case u16:
			case u32:
			case u64:
				if (AIT_BE(val))
					val->val.net = be64toh(v[i].val.net);
				if (AIT_LE(val))
					val->val.net = le64toh(v[i].val.net);
				break;
			case data:
			case ptr:
				/* WARNING:: remap data and ptr type to buffer! */
				val->val_type = buffer;
			case buffer:
			case string:
				if (!zcpy) {
					val->val.buffer = e_malloc(AIT_LEN(val));
					if (!val->val.buffer) {
						array_Free(vars);
						array_Destroy(&vars);
						return NULL;
					} else
						memcpy(val->val.buffer, dat, AIT_LEN(val));
				} else
					val->val.buffer = dat;
				dat += AIT_LEN(val);
				break;
			default:
				elwix_SetErr(EINVAL, "Unsupported variable type=%d at element #%d", 
						AIT_TYPE(val), i);
				if (!zcpy)
					array_Free(vars);
				array_Destroy(&vars);
				return NULL;
		}
	}

	return vars;
}

/*
 * ait_var2tlv() - Marshaling data from variable to TLV buffer
 *
 * @buf = Buffer, If =NULL then we return only needed buffer size
 * @buflen = Size of buffer
 * @var = Variable
 * return: -1 error, 0 nothing done or >0 size of marshaled data
 */
int
ait_var2tlv(u_char * __restrict buf, int buflen, ait_val_t * __restrict v)
{
	int Limit = 0;
	u_char *dat;

	assert(v);
	if (!v)
		return -1;
	if (AIT_TYPE(v) == empty)
		return 0;

	/* calculate amount of data into buffer */
	Limit = 5 + AIT_LEN(v);
	/* check only needed buffer size */
	if (!buf)
		return Limit;
	else
		dat = buf;

	if (Limit > buflen) {
		elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d needed min %d", 
				buflen, Limit);
		return -1;
	} else
		memset(buf, 0, buflen);

	/* marshaling */
	*dat++ = AIT_TYPE(v); 

	*((uint32_t*) dat) = htonl(AIT_LEN(v));
	dat += sizeof(uint32_t);

	switch (AIT_TYPE(v)) {
		case i8:
			*((int8_t*) dat) = AIT_GET_I8(v);
			break;
		case u8:
			*((uint8_t*) dat) = AIT_GET_U8(v);
			break;
		case i16:
			*((int16_t*) dat) = AIT_GET_I16(v);
			break;
		case u16:
			*((uint16_t*) dat) = AIT_GET_U16(v);
			break;
		case i32:
			*((int32_t*) dat) = AIT_GET_I32(v);
			break;
		case blob:
		case u32:
			*((uint32_t*) dat) = AIT_GET_U32(v);
			break;
		case i64:
			*((int64_t*) dat) = AIT_GET_I64(v);
			break;
		case u64:
			*((uint64_t*) dat) = AIT_GET_U64(v);
			break;
		case f32:
			*((float*) dat) = AIT_GET_F32(v);
			break;
		case f64:
			*((double*) dat) = AIT_GET_F64(v);
			break;
		case data:
			memcpy(dat, AIT_GET_DATA(v), AIT_LEN(v));
			break;
		case buffer:
			memcpy(dat, AIT_GET_BUF(v), AIT_LEN(v));
			break;
		case string:
			memcpy(dat, AIT_GET_STR(v), AIT_LEN(v));
			break;
		case ptr:
			memcpy(dat, AIT_GET_PTR(v), AIT_LEN(v));
			break;
		default:
			elwix_SetErr(EINVAL, "Unsupported variable type=%d", AIT_TYPE(v));
			return -1;
	}

	return Limit;
}

/*
 * ait_vars2tlv() - Marshaling data from array with variables to TLV buffer
 *
 * @buf = Buffer, If =NULL then we return only needed buffer size
 * @buflen = Size of buffer
 * @vars = Variable array
 * return: -1 error, 0 nothing done or >0 size of marshaled data
 */
int
ait_vars2tlv(u_char * __restrict buf, int buflen, array_t * __restrict vars)
{
	int Limit = 0;
	register int i;
	ait_val_t *val;
	u_char *dat;

	assert(vars);
	if (!vars)
		return -1;
	if (!array_Size(vars))
		return 0;

	/* calculate amount of data into buffer */
	for (i = 0, Limit = 0; i < array_Size(vars); 
			Limit += 5 + AIT_LEN(array(vars, i, ait_val_t*)), i++);
	/* check only needed buffer size */
	if (!buf)
		return Limit;

	if (Limit > buflen) {
		elwix_SetErr(EMSGSIZE, "Short buffer buflen=%d needed min %d", 
				buflen, Limit);
		return -1;
	} else
		memset(buf, 0, buflen);

	/* marshaling */
	for (i = 0, dat = buf; i < array_Size(vars) && dat < (buf + Limit); i++) {
		val = array(vars, i, ait_val_t*);

		*dat++ = AIT_TYPE(val); 

		*((uint32_t*) dat) = htonl(AIT_LEN(val));
		dat += sizeof(uint32_t);

		switch (AIT_TYPE(val)) {
			case i8:
				*((int8_t*) dat) = AIT_GET_I8(val);
				break;
			case u8:
				*((uint8_t*) dat) = AIT_GET_U8(val);
				break;
			case i16:
				*((int16_t*) dat) = AIT_GET_I16(val);
				break;
			case u16:
				*((uint16_t*) dat) = AIT_GET_U16(val);
				break;
			case i32:
				*((int32_t*) dat) = AIT_GET_I32(val);
				break;
			case blob:
			case u32:
				*((uint32_t*) dat) = AIT_GET_U32(val);
				break;
			case i64:
				*((int64_t*) dat) = AIT_GET_I64(val);
				break;
			case u64:
				*((uint64_t*) dat) = AIT_GET_U64(val);
				break;
			case f32:
				*((float*) dat) = AIT_GET_F32(val);
				break;
			case f64:
				*((double*) dat) = AIT_GET_F64(val);
				break;
			case data:
				memcpy(dat, AIT_GET_DATA(val), AIT_LEN(val));
				break;
			case buffer:
				memcpy(dat, AIT_GET_BUF(val), AIT_LEN(val));
				break;
			case string:
				memcpy(dat, AIT_GET_STR(val), AIT_LEN(val));
				break;
			case ptr:
				memcpy(dat, AIT_GET_PTR(val), AIT_LEN(val));
				break;
			default:
				elwix_SetErr(EINVAL, "Unsupported variable type=%d at element #%d", 
						AIT_TYPE(val), i);
				return -1;
		}
		dat += AIT_LEN(val);
	}

	return Limit;
}

/*
 * ait_tlv2var() - De-marshaling data from TLV buffer to variable
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * @next_tlv = Next TLV position, if it is !=NULL 
 * return: =NULL error, !=NULL allocated variable array, after use must free with ait_freeVar()
 */
ait_val_t *
ait_tlv2var(u_char * __restrict buf, int buflen, off_t *next_tlv)
{
	ait_val_t *val;
	u_char *dat;

	assert(buf);
	if (!buf || !buflen)
		return NULL;

	if (!(val = ait_allocVar()))
		return NULL;
	else
		dat = buf;

	/* de-marshaling */
	if (*dat != empty) {
		val->val_type = *dat++;
		AIT_LEN(val) = ntohl(*((uint32_t*) dat));
		dat += sizeof(uint32_t);
		
		switch (AIT_TYPE(val)) {
			case f32:
				AIT_SET_F32(val, *((float*) dat));
				break;
			case f64:
				AIT_SET_F64(val, *((double*) dat));
				break;
			case i8:
				AIT_SET_I8(val, *((int8_t*) dat));
				break;
			case i16:
				AIT_SET_I16(val, *((int16_t*) dat));
				break;
			case i32:
				AIT_SET_I32(val, *((int32_t*) dat));
				break;
			case i64:
				AIT_SET_I64(val, *((int64_t*) dat));
				break;
			case u8:
				AIT_SET_U8(val, *((uint8_t*) dat));
				break;
			case u16:
				AIT_SET_U16(val, *((uint16_t*) dat));
				break;
			case blob:
			case u32:
				AIT_SET_U32(val, *((uint32_t*) dat));
				break;
			case u64:
				AIT_SET_U64(val, *((uint64_t*) dat));
				break;
			case data:
				AIT_SET_DATA(val, dat, AIT_LEN(val));
				break;
			case ptr:
			case buffer:
				AIT_SET_BUF(val, dat, AIT_LEN(val));
				break;
			case string:
				AIT_SET_STR(val, (char*) dat);
				break;
			default:
				elwix_SetErr(EINVAL, "Unsupported variable type=%d", AIT_TYPE(val));
				ait_freeVar(&val);
				return NULL;
		}
		dat += AIT_LEN(val);
	}

	if (next_tlv)
		*next_tlv = dat - buf;

	return val;
}

/*
 * ait_tlv2vars() - De-marshaling data from TLV buffer to array with variables
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * return: =NULL error, !=NULL allocated variable array, after use must free with ait_freeVars()
 */
array_t *
ait_tlv2vars(u_char * __restrict buf, int buflen)
{
	array_t *vars;
	register int i;
	ait_val_t *val;
	u_char *dat;

	assert(buf);
	if (!buf || !buflen)
		return NULL;

	if (!(vars = ait_allocVars(1)))
		return NULL;

	/* de-marshaling */
	for (i = 0, dat = buf; *dat != empty && dat < (buf + buflen); i++) {
		val = ait_getVars(&vars, i);
		if (!val) {
			ait_freeVars(&vars);
			return NULL;
		}

		val->val_type = *dat++;
		AIT_LEN(val) = ntohl(*((uint32_t*) dat));
		dat += sizeof(uint32_t);
		
		switch (AIT_TYPE(val)) {
			case f32:
				AIT_SET_F32(val, *((float*) dat));
				break;
			case f64:
				AIT_SET_F64(val, *((double*) dat));
				break;
			case i8:
				AIT_SET_I8(val, *((int8_t*) dat));
				break;
			case i16:
				AIT_SET_I16(val, *((int16_t*) dat));
				break;
			case i32:
				AIT_SET_I32(val, *((int32_t*) dat));
				break;
			case i64:
				AIT_SET_I64(val, *((int64_t*) dat));
				break;
			case u8:
				AIT_SET_U8(val, *((uint8_t*) dat));
				break;
			case u16:
				AIT_SET_U16(val, *((uint16_t*) dat));
				break;
			case blob:
			case u32:
				AIT_SET_U32(val, *((uint32_t*) dat));
				break;
			case u64:
				AIT_SET_U64(val, *((uint64_t*) dat));
				break;
			case data:
				AIT_SET_DATA(val, dat, AIT_LEN(val));
				break;
			case ptr:
			case buffer:
				AIT_SET_BUF(val, dat, AIT_LEN(val));
				break;
			case string:
				AIT_SET_STR(val, (char*) dat);
				break;
			default:
				elwix_SetErr(EINVAL, "Unsupported variable type=%d at element #%d", 
						AIT_TYPE(val), i);
				ait_freeVars(&vars);
				return NULL;
		}
		dat += AIT_LEN(val);
	}

	return vars;
}

/* buffer marshaling with swapping bytes to network order */

/*
 * ait_vars2buffer() - Marshaling data from array with variables to buffer
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * @vars = Variable array
 * return: -1 error, 0 nothing done or >0 size of marshaled data
 */
int
ait_vars2buffer(u_char * __restrict buf, int buflen, array_t * __restrict vars)
{
	return vars2buffer(buf, buflen, 42, vars);
}

/*
 * ait_buffer2vars() - De-marshaling data from buffer to array with variables
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * @vnum = Number of variables into buffer
 * @zcpy = Zero-copy for variables, if !=0 don't use array_Free() for free variables and 
 		*DON'T MODIFY OR DESTROY BUFFER*. =0 call array_Free() before array_Destroy()
 * return: =NULL error, !=NULL allocated variable array, after use must free with array_Destroy()
 */
array_t *
ait_buffer2vars(u_char * __restrict buf, int buflen, int vnum, int zcpy)
{
	return buffer2vars(buf, buflen, vnum, zcpy);
}

/* buffer marshaling without swapping bytes to network order */

/*
 * ait_vars2map() - Marshaling data from array with variables to memory map
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * @vars = Variable array
 * return: -1 error, 0 nothing done or >0 size of marshaled data
 */
int
ait_vars2map(u_char *buf, int buflen, array_t *vars)
{
	return vars2buffer(buf, buflen, 0, vars);
}

/*
 * ait_map2vars() - De-marshaling data from memory map to array with variables
 *
 * @buf = Buffer
 * @buflen = Size of buffer
 * @vnum = Number of variables into buffer
 * @zcpy = Zero-copy for variables, if !=0 don't use array_Free() for free variables and 
 		*DON'T MODIFY OR DESTROY BUFFER*. =0 call array_Free() before array_Destroy()
 * return: =NULL error, !=NULL allocated variable array, after use must free with array_Destroy()
 */
array_t *
ait_map2vars(u_char *buf, int buflen, int vnum, int zcpy)
{
	return buffer2vars(buf, buflen, vnum, zcpy);
}

/*
 * ait_array2vars() - Build array with variables from Null Terminated String Array
 *
 * @args = Null-terminated array with strings
 * @dn = Convert numbers from strings to numbers into variables
 * return: =NULL error, !=NULL allocated variable array, after use must free with ait_freeVars()
 */
array_t *
ait_array2vars(const char **args, int dn)
{
	array_t *vars;
	ait_val_t *val;
	register int i;
	long n;
	double d;
	char *str;

	if (!args)
		return NULL;

	vars = ait_allocVars(0);
	if (!vars)
		return NULL;

	for (i = 0; *args; i++, args++) {
		val = ait_getVars(&vars, i);
		if (!val) {
			ait_freeVars(&vars);
			return NULL;
		}

		if (dn) {
			n = strtol(*args, &str, 0);
			if (!str || !*str) {
				AIT_SET_I64(val, (int64_t) n);
				continue;
			}
			d = strtod(*args, &str);
			if (!str || !*str) {
				AIT_SET_F64(val, d);
				continue;
			}
			AIT_SET_STR(val, *args);
		} else
			AIT_SET_STR(val, *args);
	}

	return vars;
}

/* variables array */

/*
 * ait_allocVars() - Allocate ait_val_t array
 *
 * @varnum = Number of variables
 * return: =NULL error or !=NULL allocated array
 */
array_t *
ait_allocVars(int varnum)
{
	array_t *arr;
	register int i;
	ait_val_t *v;

	if (!(arr = array_Init(varnum)))
		return NULL;

	for (i = 0; i < array_Size(arr); i++) {
		if (!(v = ait_allocVar())) {
			ait_freeVars(&arr);
			return NULL;
		} else
			array_Set(arr, i, v);
	}

	return arr;
}

/*
 * ait_getVars() - Get ait_val_t element from array and if not exists allocate it
 *
 * @vars = Variable array
 * @n = index of variable into array
 * return: NULL error or !=NULL ait_val_t element
 */
ait_val_t *
ait_getVars(array_t ** __restrict vars, int n)
{
	register int i;
	ait_val_t *v;

	if (!vars)
		return NULL;

	if (!*vars) {
		if (!(*vars = ait_allocVars(n + 1)))
			return NULL;
	} else if (n >= (i = array_Size(*vars))) {
		if (array_Grow(*vars, n + 1, 0))
			return NULL;
		for (; i < array_Size(*vars); i++)
			if (!array_Get(*vars, i)) {
				if (!(v = ait_allocVar()))
					return NULL;
				else
					array_Set(*vars, i, v);
			}
	}

	return array(*vars, n, ait_val_t*);
}

/*
 * ait_clrVars() - Clear ait_val_t elements from array
 *
 * @vars = Variable array
 * return: -1 error or size of array
 */
int
ait_clrVars(array_t * __restrict vars)
{
	register int i;
	ait_val_t *v;

	if (!vars)
		return -1;

	for (i = 0; i < array_Size(vars); i++)
		if ((v = array(vars, i, ait_val_t*)))
			AIT_FREE_VAL(v);

	return array_Size(vars);
}

/*
 * ait_freeVars() - Free ait_val_t array
 *
 * @vars = Variable array
 * return: none
 */
void
ait_freeVars(array_t ** __restrict vars)
{
	register int i;
	ait_val_t *v;

	if (!vars || !*vars)
		return;

	for (i = 0; i < array_Size(*vars); i++)
		if ((v = array(*vars, i, ait_val_t*))) {
			/* free memory if isn't zero copy */
			if (!AIT_IN(v)) {
				AIT_FREE_VAL(v);
				if ((*vars)->arr_data[i])
					e_free((*vars)->arr_data[i]);
			} else
				AIT_FREE_VAL(v);
			(*vars)->arr_data[i] = NULL;
		}
	(*vars)->arr_last = -1;

	array_Destroy(vars);
}

/*
 * ait_resideVars() - Calculate footprint of resided variables into array
 *
 * @vars = Variable array
 * return: bytes for whole array
 */
size_t
ait_resideVars(array_t * __restrict vars)
{
	size_t ret = 0;
	register int i;

	if (vars) {
		ret = array_Size(vars) * sizeof(ait_val_t);
		for (i = 0; i < array_Size(vars); i++)
			switch (AIT_TYPE(array(vars, i, ait_val_t*))) {
				case buffer:
				case string:
				case data:
				case ptr:
					ret += AIT_LEN(array(vars, i, ait_val_t*));
					break;
				default:
					break;
			}
	}

	return ret;
}


/*
 * ait_allocVar() - Allocate memory for variable
 *
 * return: NULL error or new variable, after use free variable with ait_freeVar()
 */
ait_val_t *
ait_allocVar(void)
{
	ait_val_t *v = NULL;

	v = e_malloc(sizeof(ait_val_t));
	if (!v)
		return NULL;
	else
		memset(v, 0, sizeof(ait_val_t));
	v->val_type = empty;

	return v;
}

/*
 * ait_freeVar() - Free allocated memory for variable
 *
 * @val = Variable
 * return: none
 */
void
ait_freeVar(ait_val_t ** __restrict val)
{
	if (val && *val) {
		AIT_FREE_VAL(*val);
		e_free(*val);
		*val = NULL;
	}
}

/*
 * ait_makeVar() - Allocate memory and fill variable
 *
 * @type = type of variable
 * @... = arg1 is value of variable
 * @... = arg2 is length of variabla. Not required for numbers and strings!
 * return: NULL error or new variable, after use free variable with io_freeVar()
 */
ait_val_t *
ait_makeVar(ait_type_t type, ...)
{
	ait_val_t *v = NULL;
	va_list lst;
	void *p = NULL;
	uint32_t len = 0;
	uint64_t n = 0LL;

	v = ait_allocVar();
	if (!v)
		return NULL;

	va_start(lst, type);
	switch (type) {
		case empty:
			v->val_type = (uint8_t) empty;
			break;
		case ptr:
			p = va_arg(lst, void*);
			len = va_arg(lst, uint32_t);
			AIT_SET_PTR(v, p, len);
			break;
		case data:
			p = va_arg(lst, void*);
			len = va_arg(lst, uint32_t);
			AIT_SET_DATA(v, p, len);
			break;
		case buffer:
			p = va_arg(lst, void*);
			len = va_arg(lst, uint32_t);
			AIT_SET_BUF(v, p, len);
			break;
		case string:
			p = va_arg(lst, char*);
			AIT_SET_STR(v, (char*) p);
			break;
		case blob:
			n = va_arg(lst, uint32_t);
			len = va_arg(lst, uint32_t);
			AIT_SET_BLOB(v, n, len);
			break;
		case f32:
			AIT_SET_F32(v, (float) va_arg(lst, double));
			break;
		case f64:
			AIT_SET_F64(v, va_arg(lst, double));
			break;
		case u8:
			AIT_SET_U8(v, (uint8_t) va_arg(lst, int));
			break;
		case u16:
			AIT_SET_U16(v, (uint16_t) va_arg(lst, int));
			break;
		case u32:
			AIT_SET_U32(v, va_arg(lst, uint32_t));
			break;
		case u64:
			AIT_SET_U64(v, va_arg(lst, uint64_t));
			break;
		case i8:
			AIT_SET_I8(v, (int8_t) va_arg(lst, int));
			break;
		case i16:
			AIT_SET_I16(v, (int16_t) va_arg(lst, int));
			break;
		case i32:
			AIT_SET_I32(v, va_arg(lst, int32_t));
			break;
		case i64:
			AIT_SET_I64(v, va_arg(lst, int64_t));
			break;
	}
	va_end(lst);

	return v;
}

static int
_cmp_arr_key_asc(const void *a, const void *b)
{
	return AIT_KEY(*(ait_val_t**) a) - AIT_KEY(*(ait_val_t**) b);
}

static int
_cmp_arr_key_desc(const void *a, const void *b)
{
	return AIT_KEY(*(ait_val_t**) b) - AIT_KEY(*(ait_val_t**) a);
}

static int
_cmp_arr_val_asc(const void *a, const void *b)
{
	return AIT_RAW(*(ait_val_t**) a) - AIT_RAW(*(ait_val_t**) b);
}

static int
_cmp_arr_val_desc(const void *a, const void *b)
{
	return AIT_RAW(*(ait_val_t**) b) - AIT_RAW(*(ait_val_t**) a);
}

/*
 * ait_sortVarsByVal() - Sorting array with variables by value
 *
 * @vars = Variable array
 * @order = Sort order. If =0 ascend or !=0 descend
 * @cmp = Custom compare function for sorting. If =NULL compare by value
 * return: none
 */
void
ait_sortVarsByVal(array_t * __restrict vars, int order,  int (*cmp)(const void*, const void*))
{
	if (!vars)
		return;

	if (cmp)
		qsort(vars->arr_data, vars->arr_num, sizeof(uintptr_t), cmp);
	else if (order)
		qsort(vars->arr_data, vars->arr_num, sizeof(uintptr_t), _cmp_arr_val_desc);
	else
		qsort(vars->arr_data, vars->arr_num, sizeof(uintptr_t), _cmp_arr_val_asc);
}

/*
 * ait_sortVarsByKey() - Sorting array with variables by key
 *
 * @vars = Variable array
 * @order = Sort order. If =0 ascend or !=0 descend
 * return: none
 */
void
ait_sortVarsByKey(array_t * __restrict vars, int order)
{
	if (!vars)
		return;

	if (order)
		qsort(vars->arr_data, vars->arr_num, sizeof(uintptr_t), _cmp_arr_key_desc);
	else
		qsort(vars->arr_data, vars->arr_num, sizeof(uintptr_t), _cmp_arr_key_asc);
}

/*
 * ait_findKeyVars() - Find variable by key from array
 *
 * @vars = Variables
 * @key = Search key
 * return: NULL error or not found, !=NULL valid element
 */
ait_val_t *
ait_findKeyVars(array_t * __restrict vars, u_short key)
{
	array_t *tmp;
	ait_val_t **vv, *v = NULL;
	register int i;
	const u_char *p;

	if (!vars)
		return NULL;

	if (array_Copy(&tmp, vars) == -1)
		return NULL;
	else
		qsort(tmp->arr_data, tmp->arr_num, sizeof(uintptr_t), _cmp_arr_key_asc);

	/* binary search */
	for (p = (const u_char*) tmp->arr_data, i = array_Size(tmp); i; i >>= 1) {
		vv = (ait_val_t**) (p + (i >> 1) * sizeof(uintptr_t));
		if (!(key - AIT_KEY(*vv))) {	/* found! */
			v = *vv;
			break;
		}
		if ((key - AIT_KEY(*vv)) > 0) {	/* move right key > current */
			p = (const u_char*) vv + sizeof(uintptr_t);
			i--;
		}				/* else move left */
	}

	array_Destroy(&tmp);
	return v;
}

/*
 * ait_hashVar() - Generate hash key for variable from string or value
 *
 * @v = variable
 * @key = key string for hash, if =NULL hash will built from variable
 * return: hash key
 */
u_short
ait_hashVar(ait_val_t * __restrict v, const char * __restrict key)
{
	void *p;
	u_short cksum;
	int l;

	if (!v)
		return 0;

	if (key) {
		p = (void*) key;
		l = (strlen(key) + 1) / 2;
	} else {
		switch (AIT_TYPE(v)) {
			case empty:
				AIT_KEY(v) = 0;
				return 0;
			case string:
			case buffer:
				p = AIT_ADDR(v);
				l = AIT_LEN(v) / 2;
				break;
			case data:
				p = v->val_data;
				l = AIT_LEN(v) / 2;
				break;
			default:
				p = &AIT_RAW(v);
				l = sizeof AIT_RAW(v) / 2;
				break;
		}
	}

	cksum = crcFletcher16((u_short*) p, l);

	if (AIT_BE(v))
		AIT_KEY(v) = htobe16(cksum);
	else if (AIT_LE(v))
		AIT_KEY(v) = htole16(cksum);
	else
		AIT_KEY(v) = cksum;

	return AIT_KEY(v);
}

/*
 * ait_hashKeyVars() - Generate hash keys for variables
 *
 * @vars = Variables
 * return -1 error or 0 ok
 */
int
ait_hashKeyVars(array_t * __restrict vars)
{
	register int i;

	if (!vars)
		return -1;

	for (i = 0; i < array_Size(vars); i++)
		ait_hashVar(array(vars, i, ait_val_t*), NULL);

	return 0;
}

/*
 * ait_findKeyHash() - Find variable by hash string from array
 *
 * @vars = Variables
 * @key = Search string
 * return: NULL error or not found, !=NULL valid element
 */
ait_val_t *
ait_findKeyHash(array_t * __restrict vars, const char * __restrict key)
{
	u_short k = 0;

	if (!vars || !key)
		return NULL;

	k = crcFletcher16((u_short*) key, (strlen(key) + 1) / 2);
	return ait_findKeyVars(vars, k);
}

/*
 * ait_sprintfVar() - Builtin string variable from formatted input
 *
 * @v = variable
 * @fmt = format string
 * @... = argument(s)
 * return: -1 error or >0 copied bytes to variable
 */
int
ait_sprintfVar(ait_val_t * __restrict v, const char *fmt, ...)
{
	int ret = 0;
	va_list lst;
	char str[65536] = { [0 ... 65535] = 0 };

	if (!v || !fmt)
		return -1;

	va_start(lst, fmt);
	ret = vsnprintf(str, sizeof str - 1, fmt, lst);
	va_end(lst);

	if (ret > -1) {
		AIT_FREE_VAL(v);
		AIT_SET_STR(v, str);
	} else
		LOGERR;

	return ret;
}

/*
 * ait_setlikeVar() - Set variable like ...
 *
 * @v = variable
 * @t = type of data
 * @l = length of data
 * @... = data
 * return: -1 error or 0 ok
 */
int
ait_setlikeVar(ait_val_t * __restrict v, ait_type_t t, u_int l, ...)
{
	va_list lst;

	if (!v)
		return -1;

	AIT_FREE_VAL(v);
	AIT_INIT_VAL2(v, t);
	AIT_LEN(v) = l;
	AIT_IN(v) = 1;

	va_start(lst, l);
	switch (AIT_TYPE(v)) {
		case ptr:
		case buffer:
		case string:
			AIT_ADDR(v) = va_arg(lst, void*);
			break;
		default:
			AIT_RAW(v) = va_arg(lst, uint64_t);
			break;
	}
	va_end(lst);

	return 0;
}

/*
 * ait_getlikeVar() - Get variable like ...
 *
 * @v = variable
 * return: return raw data
 */
uint64_t
ait_getlikeVar(ait_val_t * __restrict v)
{
	if (!v)
		return (uintptr_t) -1;

	return AIT_RAW(v);
}

/*
 * ait_cmpVar() - Compare two variables
 *
 * @a = 1st variable
 * @b = 2nd variable
 * return: 0 is equal or !=0 is different
 */
int
ait_cmpVar(ait_val_t * __restrict a, ait_val_t * __restrict b)
{
	intptr_t ret;

	if (!(ret = (a - b)))
		return ret;
	if ((ret = AIT_TYPE(a) - AIT_TYPE(b)))
		return ret;
	if ((ret = AIT_LEN(a) - AIT_LEN(b)))
		return ret;

	switch (AIT_TYPE(a)) {
		case buffer:
			ret = memcmp(AIT_GET_BUF(a), AIT_GET_BUF(b), AIT_LEN(a));
			break;
		case string:
			ret = strncmp(AIT_GET_STR(a), AIT_GET_STR(b), AIT_LEN(a));
			break;
		case data:
			ret = memcmp(AIT_GET_DATA(a), AIT_GET_DATA(b), AIT_LEN(a));
			break;
		case ptr:
			ret = AIT_ADDR(a) - AIT_ADDR(b);
			break;
		default:
			ret = AIT_RAW(a) - AIT_RAW(b);
			break;
	}

	return (int) ret;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>