libSystem
/* $Id$ */
/* Copyright (c) 2012-2021 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libSystem */
/* 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 COPYRIGHT HOLDERS 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 COPYRIGHT
* HOLDER 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 <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "System/error.h"
#include "System/mutator.h"
#include "System/object.h"
#include "System/variable.h"
/* Variable */
/* private */
/* types */
struct _Variable
{
VariableType type;
union
{
bool b;
int8_t int8;
uint8_t uint8;
int16_t int16;
uint16_t uint16;
int32_t int32;
uint32_t uint32;
int64_t int64;
uint64_t uint64;
float f;
double d;
Buffer * buffer;
String * string;
struct {
VariableType type;
Array * array;
} array;
struct {
VariableClass _class;
String * name;
Mutator * members;
} compound;
void * pointer;
} u;
};
typedef struct _VariableCompoundCallbackData
{
size_t * size;
char * result;
size_t pos;
int ret;
} VariableCompoundCallbackData;
/* constants */
static const size_t _variable_sizes[VT_COUNT] = { 0, 1,
sizeof(int8_t), sizeof(uint8_t), sizeof(int16_t), sizeof(uint16_t),
sizeof(int32_t), sizeof(uint32_t), sizeof(int64_t), sizeof(uint64_t),
sizeof(float), sizeof(double), sizeof(uint32_t), 0, 0, 0,
sizeof(void *) };
/* prototypes */
static uint16_t _bswap16(uint16_t u);
static uint32_t _bswap32(uint32_t u);
static uint64_t _bswap64(uint64_t u);
static void _variable_destroy(Variable * variable);
static void _variable_destroy_compound(Variable * variable);
static void _variable_destroy_compound_members(Mutator * members);
/* public */
/* variable_new */
Variable * variable_new(VariableType type, ...)
{
Variable * variable;
va_list ap;
va_start(ap, type);
variable = variable_newv(type, ap);
va_end(ap);
return variable;
}
/* variable_newv */
Variable * variable_newv(VariableType type, va_list ap)
{
Variable * variable;
if((variable = (Variable *)object_new(sizeof(*variable))) == NULL)
return NULL;
variable->type = VT_NULL;
if(variable_set_typev(variable, type, ap) != 0)
{
object_delete(variable);
return NULL;
}
return variable;
}
/* variable_new_array */
Variable * variable_new_array(VariableType type, size_t size, ...)
{
Variable * variable;
va_list ap;
va_start(ap, size);
variable = variable_new_arrayv(type, size, ap);
va_end(ap);
return variable;
}
/* variable_new_arrayv */
Variable * variable_new_arrayv(VariableType type, size_t size, va_list ap)
{
Variable * variable;
size_t i;
void * p;
if((variable = variable_new(VT_ARRAY, type, size)) == NULL)
return NULL;
for(i = 0; i < size; i++)
{
p = va_arg(ap, void *);
if(array_set(variable->u.array.array, i, p) != 0)
{
variable_delete(variable);
return NULL;
}
}
return variable;
}
/* variable_new_compound */
Variable * variable_new_compound(String const * name, ...)
{
Variable * variable;
va_list ap;
va_start(ap, name);
variable = variable_new_compoundv(name, ap);
va_end(ap);
return variable;
}
/* variable_new_compoundv */
Variable * variable_new_compoundv(String const * name, va_list ap)
{
Variable * variable;
VariableType type;
Mutator * m;
Variable * v;
if((variable = variable_new(VT_COMPOUND, name)) == NULL)
return NULL;
m = variable->u.compound.members;
for(;;)
{
type = (VariableType)va_arg(ap, unsigned int);
if(type == VT_NULL)
return variable;
name = va_arg(ap, String const *);
if(name == NULL)
break;
if((v = (Variable *)mutator_get(m, name)) != NULL)
variable_delete(v);
if((v = variable_newv(type, ap)) == NULL
|| mutator_set(m, name, v) != 0)
{
variable_delete(v);
break;
}
}
variable_delete(variable);
return NULL;
}
/* variable_new_compound_variables */
Variable * variable_new_compound_variables(String const * name, size_t members,
String const ** names, Variable const ** variables)
{
Variable * variable;
Mutator * m;
size_t i;
Variable * v;
if((variable = variable_new(VT_COMPOUND, name)) == NULL)
return NULL;
m = variable->u.compound.members;
for(i = 0; i < members; i++)
{
if(names[i] == NULL)
{
error_set_code(-EINVAL, "%s", strerror(EINVAL));
break;
}
if((v = (Variable *)mutator_get(m, names[i])) != NULL)
{
mutator_set(m, names[i], NULL);
variable_delete(v);
}
if((v = variable_new_copy(variables[i])) == NULL
|| mutator_set(m, names[i], v) != 0)
break;
}
if(i != members)
{
variable_delete(variable);
return NULL;
}
return variable;
}
/* variable_new_copy */
static Variable * _new_copy_array(Variable const * from);
static Variable * _new_copy_compound(Variable const * from);
static void _new_copy_compound_foreach(Mutator const * mutator,
String const * key, void * value, void * data);
Variable * variable_new_copy(Variable const * from)
{
switch(from->type)
{
case VT_NULL:
return variable_new(from->type);
case VT_BOOL:
return variable_new(from->type, from->u.b);
case VT_INT8:
return variable_new(from->type, from->u.int8);
case VT_UINT8:
return variable_new(from->type, from->u.uint8);
case VT_INT16:
return variable_new(from->type, from->u.int16);
case VT_UINT16:
return variable_new(from->type, from->u.uint16);
case VT_INT32:
return variable_new(from->type, from->u.int32);
case VT_UINT32:
return variable_new(from->type, from->u.uint32);
case VT_INT64:
return variable_new(from->type, from->u.int64);
case VT_UINT64:
return variable_new(from->type, from->u.uint64);
case VT_FLOAT:
return variable_new(from->type, from->u.f);
case VT_DOUBLE:
return variable_new(from->type, from->u.d);
case VT_STRING:
return variable_new(from->type, from->u.string);
case VT_BUFFER:
return variable_new(from->type, from->u.buffer);
case VT_ARRAY:
return _new_copy_array(from);
case VT_COMPOUND:
return _new_copy_compound(from);
case VT_POINTER:
return variable_new(from->type, from->u.pointer);
}
error_set_code(1, "%s%u%s", "Unable to copy this type of variable (",
from->type, ")");
return NULL;
}
static Variable * _new_copy_array(Variable const * from)
{
Variable * variable;
size_t size;
size = array_get_size(from->u.array.array);
if((variable = variable_new_array(from->u.array.type, size)) == NULL)
return NULL;
if(array_copy(variable->u.array.array, from->u.array.array) != 0)
{
variable_delete(variable);
return NULL;
}
return variable;
}
static Variable * _new_copy_compound(Variable const * from)
{
Variable * variable;
Mutator * m;
if((variable = variable_new_compound(from->u.compound.name, VT_NULL))
== NULL)
return NULL;
m = variable->u.compound.members;
mutator_foreach(from->u.compound.members, _new_copy_compound_foreach,
&m);
if(m == NULL)
{
variable_delete(variable);
return NULL;
}
return variable;
}
static void _new_copy_compound_foreach(Mutator const * mutator,
String const * key, void * value, void * data)
{
Mutator ** m = (Mutator **)data;
Variable const * from = (Variable const *)value;
Variable * v;
(void) mutator;
if(*m == NULL)
return;
if((v = (Variable *)mutator_get(*m, key)) != NULL)
{
mutator_set(*m, key, NULL);
variable_delete(v);
}
if((v = variable_new_copy(from)) == NULL
|| mutator_set(*m, key, v) != 0)
{
variable_delete(v);
*m = NULL;
}
}
/* variable_new_deserialize */
Variable * variable_new_deserialize(size_t * size, char const * data)
{
Variable * variable;
uint8_t u8;
VariableType type;
size_t s;
/* check the arguments */
if(size == NULL || *size < sizeof(u8) || data == NULL)
{
error_set_code(-EINVAL, "%s", strerror(EINVAL));
return NULL;
}
/* obtain the type from the data */
u8 = data[0];
type = (VariableType)u8;
s = *size - sizeof(u8);
/* deserialize according to the type */
variable = variable_new_deserialize_type(type, &s, &data[sizeof(u8)]);
*size = s + sizeof(u8);
return variable;
}
/* variable_new_deserialize_buffer */
Variable * variable_new_deserialize_buffer(size_t * size, Buffer const * buffer)
{
BufferData const * data;
data = buffer_get_data(buffer);
return variable_new_deserialize(size, data);
}
/* variable_new_deserialize_type */
Variable * variable_new_deserialize_type(VariableType type, size_t * size,
char const * data)
{
Variable * v;
size_t s;
uint32_t u32;
uint64_t u64;
float f;
double d;
Buffer * b;
int res;
unsigned char const * udata = (unsigned char const *)data;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u, %lu, %p)\n", __func__, type, *size,
(void *)data);
#endif
/* estimate the size required */
s = (type < sizeof(_variable_sizes) / sizeof(*_variable_sizes))
? _variable_sizes[type] : 0;
switch(type)
{
case VT_NULL:
case VT_BOOL:
case VT_INT8:
case VT_UINT8:
case VT_INT16:
case VT_UINT16:
case VT_INT32:
case VT_UINT32:
case VT_INT64:
case VT_UINT64:
case VT_POINTER:
break;
case VT_BUFFER:
if(*size < s)
break;
memcpy(&u32, data, s);
u32 = _bswap32(u32);
s += u32;
break;
case VT_FLOAT:
case VT_DOUBLE:
case VT_STRING:
for(s = 1; s <= *size && data[s - 1] != '\0'; s++);
if(s < *size)
break;
/* fallthrough */
default:
error_set_code(1, "Unable to deserialize type %u",
type);
return NULL;
}
if(*size < s)
{
*size = s;
error_set_code(1, "More data needed to deserialize type %u",
type);
return NULL;
}
*size = s;
/* convert the data if necessary */
switch(type)
{
case VT_NULL:
v = variable_new(type);
break;
case VT_BOOL:
if((data[0] & 0xfe) != 0x0)
{
error_set_code(1, "Invalid boolean value");
return NULL;
}
v = variable_new(type, data[0]);
break;
case VT_INT8:
case VT_UINT8:
v = variable_new(type, data[0]);
break;
case VT_INT16:
case VT_UINT16:
v = variable_new(type, (uint32_t)udata[0] << 8
| udata[1]);
break;
case VT_INT32:
case VT_UINT32:
v = variable_new(type, (uint32_t)udata[0] << 24
| (uint32_t)udata[1] << 16
| (uint32_t)udata[2] << 8
| (uint32_t)udata[3]);
break;
case VT_FLOAT:
res = sscanf(data, "%e", &f);
if(res != 1)
{
error_set_code(1, "Invalid float value");
return NULL;
}
v = variable_new(type, f);
break;
case VT_DOUBLE:
res = sscanf(data, "%le", &d);
if(res != 1)
{
error_set_code(1, "Invalid double value");
return NULL;
}
v = variable_new(type, d);
break;
case VT_STRING:
v = variable_new(type, data);
break;
case VT_INT64:
case VT_UINT64:
u64 = (uint64_t)udata[0] << 56
| (uint64_t)udata[1] << 48
| (uint64_t)udata[2] << 40
| (uint64_t)udata[3] << 32
| (uint64_t)udata[4] << 24
| (uint64_t)udata[5] << 16
| (uint64_t)udata[6] << 8
| (uint64_t)udata[7];
v = variable_new(type, u64);
break;
case VT_BUFFER:
/* XXX avoid copying the buffer */
if((b = buffer_new(s - sizeof(u32), &data[sizeof(u32)]))
== NULL)
return NULL;
v = variable_new(type, b);
buffer_delete(b);
break;
default:
error_set_code(1, "Unable to deserialize type %u",
type);
return NULL;
}
return v;
}
/* variable_delete */
void variable_delete(Variable * variable)
{
if(variable == NULL)
return;
_variable_destroy(variable);
object_delete(variable);
}
/* variable_get_as */
static VariableError _get_as_compound(Variable const * variable, void * result,
size_t * size);
static void _get_as_compound_callback(Mutator const * mutator,
String const * key, void * value, void * data);
static VariableError _get_as_convert(Variable const * variable,
VariableType type, void * result);
static VariableError _get_as_convert_string(Variable const * variable,
String ** result);
static int _get_as_pointer(size_t * size);
VariableError variable_get_as(Variable const * variable, VariableType type,
void * result, size_t * size)
{
int ret;
void const * p;
size_t sz;
Buffer ** b;
String ** s;
Array ** a;
if(variable->type != type)
return _get_as_convert(variable, type, result);
switch(type)
{
case VT_NULL:
return 0;
case VT_BOOL:
case VT_INT8:
case VT_UINT8:
case VT_INT16:
case VT_UINT16:
case VT_INT32:
case VT_UINT32:
case VT_INT64:
case VT_UINT64:
case VT_FLOAT:
case VT_DOUBLE:
case VT_POINTER:
sz = (type < sizeof(_variable_sizes)
/ sizeof(*_variable_sizes))
? _variable_sizes[type] : 0;
if(size != NULL)
{
if(sz > *size)
return error_set_code(-ENOSPC, "%s",
strerror(ENOSPC));
*size = sz;
}
p = variable_get_pointer(variable);
memcpy(result, p, sz);
return 0;
case VT_BUFFER:
if((ret = _get_as_pointer(size)) != 0)
return ret;
b = (Buffer **)result;
return (*b = buffer_new_copy(variable->u.buffer))
!= NULL ? 0 : -1;
case VT_STRING:
if((ret = _get_as_pointer(size)) != 0)
return ret;
s = (String **)result;
return (*s = string_new(variable->u.string)) != NULL
? 0 : -1;
case VT_ARRAY:
if((ret = _get_as_pointer(size)) != 0)
return ret;
a = (Array **)result;
return (*a = array_new_copy(variable->u.array.array))
!= NULL ? 0 : -1;
case VT_COMPOUND:
return _get_as_compound(variable, result, size);
}
return error_set_code(-ENOTSUP, "%s", strerror(ENOTSUP));
}
static VariableError _get_as_compound(Variable const * variable, void * result,
size_t * size)
{
VariableCompoundCallbackData data;
if(size == NULL)
return error_set_code(-EINVAL, "%s", strerror(EINVAL));
data.size = size;
data.pos = 0;
data.result = result;
data.ret = 0;
/* XXX there is no warranty on the right order */
mutator_foreach(variable->u.compound.members,
_get_as_compound_callback, &data);
return data.ret;
}
static void _get_as_compound_callback(Mutator const * mutator,
String const * key, void * value, void * data)
{
VariableCompoundCallbackData * vccd = data;
Variable const * from = value;
VariableType type;
size_t size;
int res;
(void) mutator;
(void) key;
/* XXX assumes alignment on byte boundary */
type = variable_get_type(from);
size = *(vccd->size);
size = (size >= vccd->pos) ? size - vccd->pos : 0;
if((res = variable_get_as(from, type, &vccd->result[vccd->pos], &size))
!= 0)
vccd->ret = res;
vccd->pos += size;
}
static VariableError _get_as_convert(Variable const * variable,
VariableType type, void * result)
{
size_t size = 0;
void const * p = NULL;
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
float * fp;
double * dp;
switch(type)
{
case VT_NULL:
case VT_POINTER:
break;
case VT_INT8:
size = sizeof(i8);
if(variable->type == VT_UINT8
&& variable->u.uint8 <= 0x7f)
i8 = variable->u.uint8;
else if(variable->type == VT_INT16
&& variable->u.int16 >= (-0x7f-1)
&& variable->u.int16 <= 0x7f)
i8 = variable->u.int16;
else if(variable->type == VT_UINT16
&& variable->u.uint16 <= 0x7f)
i8 = variable->u.uint16;
else if(variable->type == VT_INT32
&& variable->u.int32 >= (-0x7f-1)
&& variable->u.int32 <= 0x7f)
i8 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32 <= 0x7f)
i8 = variable->u.uint32;
else if(variable->type == VT_INT64
&& variable->u.int64 >= (-0x7f-1)
&& variable->u.int64 <= 0x7f)
i8 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0x7f)
i8 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
p = &i8;
break;
case VT_BOOL:
case VT_UINT8:
size = sizeof(u8);
if(variable->type == VT_UINT8)
{
p = &variable->u.uint8;
break;
}
if(variable->type == VT_INT8
&& variable->u.int8 >= 0)
u8 = variable->u.int8;
else if(variable->type == VT_INT16
&& variable->u.int16 >= 0
&& variable->u.int16 <= 0xff)
u8 = variable->u.int16;
else if(variable->type == VT_UINT16
&& variable->u.uint16 <= 0xff)
u8 = variable->u.uint16;
else if(variable->type == VT_INT32
&& variable->u.int32 >= 0
&& variable->u.int32 <= 0xff)
u8 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32 <= 0xff)
u8 = variable->u.uint32;
else if(variable->type == VT_INT64
&& variable->u.int64 >= 0
&& variable->u.int64 <= 0xff)
u8 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0xff)
u8 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
p = &u8;
break;
case VT_INT16:
size = sizeof(i16);
if(variable->type == VT_UINT16
&& variable->u.uint16 <= 0x7fff)
i16 = variable->u.uint16;
else if(variable->type == VT_INT8)
i16 = variable->u.int8;
else if(variable->type == VT_UINT8)
i16 = variable->u.uint8;
else if(variable->type == VT_INT32
&& variable->u.int32 >= (-0x7fff-1)
&& variable->u.int32 <= 0x7fff)
i16 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32 <= 0x7fff)
i16 = variable->u.uint32;
else if(variable->type == VT_INT64
&& variable->u.int64 >= (-0x7fff-1)
&& variable->u.int64 <= 0x7fff)
i16 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0x7fff)
i16 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
p = &i16;
break;
case VT_UINT16:
size = sizeof(u16);
if(variable->type == VT_INT16 && variable->u.int16 >= 0)
u16 = variable->u.int16;
else if(variable->type == VT_INT8
&& variable->u.int8 >= 0)
u16 = variable->u.int8;
else if(variable->type == VT_UINT8)
u16 = variable->u.uint8;
else if(variable->type == VT_INT32
&& variable->u.int32 >= 0
&& variable->u.int32 <= 0xffff)
u16 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32 <= 0xffff)
u16 = variable->u.uint32;
else if(variable->type == VT_INT64
&& variable->u.int64 >= 0
&& variable->u.int64 <= 0xffff)
u16 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0xffff)
u16 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
p = &u16;
break;
case VT_INT32:
size = sizeof(i32);
if(variable->type == VT_UINT32
&& variable->u.uint32 <= 0x7fffffff)
i32 = variable->u.uint32;
else if(variable->type == VT_INT8)
i32 = variable->u.int8;
else if(variable->type == VT_UINT8)
i32 = variable->u.uint8;
else if(variable->type == VT_INT16)
i32 = variable->u.int16;
else if(variable->type == VT_UINT16)
i32 = variable->u.uint16;
else if(variable->type == VT_INT64
&& variable->u.int64 >= (-0x7fffffff-1)
&& variable->u.int64 <= 0x7fffffff)
i32 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0x7fffffff)
i32 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
p = &i32;
break;
case VT_UINT32:
size = sizeof(u32);
if(variable->type == VT_INT32
&& variable->u.int32 >= 0)
u32 = variable->u.int32;
else if(variable->type == VT_INT8
&& variable->u.int8 >= 0)
u32 = variable->u.int8;
else if(variable->type == VT_UINT8)
u32 = variable->u.uint8;
else if(variable->type == VT_INT16
&& variable->u.int16 >= 0)
u32 = variable->u.int16;
else if(variable->type == VT_UINT16)
u32 = variable->u.uint16;
else if(variable->type == VT_INT64
&& variable->u.int64 >= 0
&& variable->u.int64 <= 0xffffffff)
u32 = variable->u.int64;
else if(variable->type == VT_UINT64
&& variable->u.uint64 <= 0xffffffff)
u32 = variable->u.uint64;
else
/* TODO implement more conversions */
break;
size = sizeof(u32);
p = &u32;
break;
case VT_INT64:
size = sizeof(i64);
if(variable->type == VT_UINT64
&& variable->u.int64 <= 0x7fffffffffffffff)
i64 = variable->u.int64;
else
/* TODO implement more conversions */
break;
p = &i64;
break;
case VT_UINT64:
size = sizeof(u64);
if(variable->type == VT_INT64
&& variable->u.int64 >= 0)
u64 = variable->u.int64;
else
/* TODO implement more conversions */
break;
p = &u64;
break;
case VT_FLOAT:
fp = (float *)result;
if(variable->type == VT_DOUBLE)
*fp = variable->u.d;
else
/* TODO implement more conversions */
break;
return 0;
case VT_DOUBLE:
dp = (double *)result;
if(variable->type == VT_FLOAT)
*dp = variable->u.f;
else
/* TODO implement more conversions */
break;
return 0;
case VT_BUFFER:
/* TODO serialize the variable */
break;
case VT_STRING:
return _get_as_convert_string(variable,
(String **)result);
}
if(size != 0 && p != NULL)
{
memcpy(result, p, size);
return 0;
}
return -error_set_code(1, "Unable to convert from type %u to %u",
variable->type, type);
}
static VariableError _get_as_convert_string(Variable const * variable,
String ** result)
{
Variable const * v = variable;
BufferData const * bufdata;
size_t cnt;
size_t i;
switch(v->type)
{
case VT_NULL:
*result = NULL;
return 0;
case VT_BOOL:
return ((*result = string_new(v->u.b
? "true" : "false"))
!= NULL) ? 0 : -1;
case VT_INT8:
return ((*result = string_new_format("%" PRId8,
v->u.int8)) != NULL)
? 0 : -1;
case VT_UINT8:
return ((*result = string_new_format("%" PRIu8,
v->u.uint8)) != NULL)
? 0 : -1;
case VT_INT16:
return ((*result = string_new_format("%" PRId16,
v->u.int16)) != NULL)
? 0 : -1;
case VT_UINT16:
return ((*result = string_new_format("%" PRIu16,
v->u.uint16)) != NULL)
? 0 : -1;
case VT_INT32:
return ((*result = string_new_format("%" PRId32,
v->u.int32)) != NULL)
? 0 : -1;
case VT_UINT32:
return ((*result = string_new_format("%" PRIu32,
v->u.uint32)) != NULL)
? 0 : -1;
case VT_INT64:
return ((*result = string_new_format("%" PRId64,
v->u.int64)) != NULL)
? 0 : -1;
case VT_UINT64:
return ((*result = string_new_format("%" PRIu64,
v->u.uint64)) != NULL)
? 0 : -1;
case VT_FLOAT:
return ((*result = string_new_format("%f", v->u.f))
!= NULL) ? 0 : -1;
case VT_DOUBLE:
return ((*result = string_new_format("%f", v->u.d))
!= NULL) ? 0 : -1;
case VT_STRING:
/* cannot happen */
case VT_COMPOUND:
/* no possible conversion */
break;
case VT_BUFFER:
if((*result = string_new("")) == NULL)
return -1;
bufdata = buffer_get_data(v->u.buffer);
cnt = buffer_get_size(v->u.buffer);
for(i = 0; i < cnt; i++)
if(string_append_format(result, "\\x%02hhx",
bufdata[i]) != 0)
{
string_delete(*result);
return -1;
}
return 0;
#if 0
case VT_ARRAY:
/* FIXME implement */
break;
#endif
case VT_POINTER:
return ((*result = string_new_format("%p",
v->u.pointer)) != NULL)
? 0 : -1;
}
return -error_set_code(1, "Unable to convert from type %u to %u",
variable->type, VT_STRING);
}
static int _get_as_pointer(size_t * size)
{
size_t sz;
sz = _variable_sizes[VT_POINTER];
if(size != NULL)
{
if(sz > *size)
return error_set_code(-ENOSPC, "%s", strerror(ENOSPC));
*size = sz;
}
return 0;
}
/* variable_get_pointer */
void const * variable_get_pointer(Variable const * variable)
{
switch(variable->type)
{
case VT_NULL:
return NULL;
case VT_BOOL:
return &variable->u.b;
case VT_INT8:
return &variable->u.int8;
case VT_UINT8:
return &variable->u.uint8;
case VT_INT16:
return &variable->u.int16;
case VT_UINT16:
return &variable->u.uint16;
case VT_INT32:
return &variable->u.int32;
case VT_UINT32:
return &variable->u.uint32;
case VT_INT64:
return &variable->u.int64;
case VT_UINT64:
return &variable->u.uint64;
case VT_FLOAT:
return &variable->u.f;
case VT_DOUBLE:
return &variable->u.d;
case VT_BUFFER:
return variable->u.buffer;
case VT_STRING:
/* XXX because of String operations */
return &variable->u.string;
case VT_ARRAY:
return variable->u.array.array;
case VT_COMPOUND:
return NULL;
case VT_POINTER:
return &variable->u.pointer;
}
return NULL;
}
/* variable_get_type */
VariableType variable_get_type(Variable const * variable)
{
return variable->type;
}
/* variable_is_array */
bool variable_is_array(Variable const * variable)
{
return variable_is_type(variable, VT_ARRAY);
}
/* variable_is_class */
bool variable_is_class(Variable const * variable, VariableClass _class)
{
if(!variable_is_type(variable, VT_COMPOUND)
|| variable->u.compound.name == NULL)
return false;
return (variable->u.compound._class == _class) ? true : false;
}
/* variable_is_compound */
bool variable_is_compound(Variable const * variable)
{
return (variable_is_type(variable, VT_COMPOUND)
&& variable->u.compound.name == NULL)
? true : false;
}
/* variable_is_instance */
bool variable_is_instance(Variable const * variable, String const * name)
{
if(!variable_is_type(variable, VT_COMPOUND)
|| variable->u.compound.name == NULL)
return false;
return (string_compare(variable->u.compound.name, name) == 0)
? true : false;
}
/* variable_is_scalar */
bool variable_is_scalar(Variable const * variable)
{
VariableType type;
type = variable_get_type(variable);
if(type >= VT_BOOL && type <= VT_DOUBLE)
return true;
if(type == VT_STRING || type == VT_POINTER)
return true;
return false;
}
/* variable_is_type */
bool variable_is_type(Variable const * variable, VariableType type)
{
return (variable_get_type(variable) == type) ? true : false;
}
/* variable_set */
VariableError variable_set(Variable * variable, ...)
{
VariableError ret;
va_list ap;
va_start(ap, variable);
ret = variable_setv(variable, ap);
va_end(ap);
return ret;
}
/* variable_setv */
VariableError variable_setv(Variable * variable, va_list ap)
{
return variable_set_typev(variable, variable->type, ap);
}
/* variable_set_type */
VariableError variable_set_type(Variable * variable, VariableType type, ...)
{
VariableError ret;
va_list ap;
va_start(ap, type);
ret = variable_set_typev(variable, type, ap);
va_end(ap);
return ret;
}
/* variable_set_typev */
VariableError variable_set_typev(Variable * variable, VariableType type,
va_list ap)
{
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
double d;
Buffer * b;
String * s;
Mutator * m;
switch(type)
{
case VT_NULL:
_variable_destroy(variable);
break;
case VT_BOOL:
_variable_destroy(variable);
u32 = va_arg(ap, uint32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%s)\n", __func__,
u32 ? "true" : "false");
#endif
variable->u.b = u32 ? true : false;
break;
case VT_INT8:
_variable_destroy(variable);
i32 = va_arg(ap, int32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d)\n", __func__, i32);
#endif
variable->u.int8 = i32;
break;
case VT_UINT8:
_variable_destroy(variable);
u32 = va_arg(ap, uint32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, u32);
#endif
variable->u.uint8 = u32;
break;
case VT_INT16:
_variable_destroy(variable);
i32 = va_arg(ap, int32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d)\n", __func__, i32);
#endif
variable->u.int16 = i32;
break;
case VT_UINT16:
_variable_destroy(variable);
u32 = va_arg(ap, uint32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, u32);
#endif
variable->u.uint16 = u32;
break;
case VT_INT32:
_variable_destroy(variable);
i32 = va_arg(ap, int32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%d)\n", __func__, i32);
#endif
variable->u.int32 = i32;
break;
case VT_UINT32:
_variable_destroy(variable);
u32 = va_arg(ap, uint32_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, u32);
#endif
variable->u.uint32 = u32;
break;
case VT_INT64:
_variable_destroy(variable);
i64 = va_arg(ap, int64_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%ld)\n", __func__, i64);
#endif
variable->u.int64 = i64;
break;
case VT_UINT64:
_variable_destroy(variable);
u64 = va_arg(ap, uint64_t);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%lu)\n", __func__, u64);
#endif
variable->u.uint64 = u64;
break;
case VT_FLOAT:
_variable_destroy(variable);
d = va_arg(ap, double);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%f)\n", __func__, d);
#endif
variable->u.f = d;
break;
case VT_DOUBLE:
_variable_destroy(variable);
d = va_arg(ap, double);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%lf)\n", __func__, d);
#endif
variable->u.d = d;
break;
case VT_BUFFER:
b = va_arg(ap, Buffer *);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%p)\n", __func__, (void *)b);
#endif
b = (b != NULL)
? buffer_new_copy(b) : buffer_new(0, NULL);
if(b == NULL)
return -1;
_variable_destroy(variable);
variable->u.buffer = b;
break;
case VT_STRING:
s = va_arg(ap, String *);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, s);
#endif
if((s = string_new(s)) == NULL)
return -1;
_variable_destroy(variable);
variable->u.string = s;
break;
case VT_COMPOUND:
s = va_arg(ap, String *);
if(s != NULL && (s = string_new(s)) == NULL)
return -1;
if((m = mutator_new()) == NULL)
{
string_delete(s);
return -1;
}
_variable_destroy(variable);
variable->u.compound.name = s;
variable->u.compound.members = m;
break;
case VT_POINTER:
_variable_destroy(variable);
variable->u.pointer = va_arg(ap, void *);
break;
default:
return error_set_code(-ENOSYS, "%s", strerror(ENOSYS));
}
variable->type = type;
return 0;
}
/* useful */
/* variable_copy */
static VariableError _copy_compound(Variable * variable, Variable const * from);
static void _copy_compound_foreach(Mutator const * mutator, String const * key,
void * value, void * data);
VariableError variable_copy(Variable * variable, Variable const * from)
{
Buffer * b;
String * s;
Array * a;
switch(from->type)
{
case VT_NULL:
case VT_BOOL:
case VT_INT8:
case VT_UINT8:
case VT_INT16:
case VT_UINT16:
case VT_INT32:
case VT_UINT32:
case VT_INT64:
case VT_UINT64:
case VT_FLOAT:
case VT_DOUBLE:
case VT_POINTER:
_variable_destroy(variable);
memcpy(&variable->u, &from->u, sizeof(from->u));
break;
case VT_BUFFER:
if((b = buffer_new_copy(from->u.buffer)) == NULL)
return -1;
_variable_destroy(variable);
variable->u.buffer = b;
break;
case VT_STRING:
if((s = string_new(from->u.string)) == NULL)
return -1;
_variable_destroy(variable);
variable->u.string = s;
break;
case VT_ARRAY:
if((a = array_new_copy(from->u.array.array)) == NULL)
return -1;
_variable_destroy(variable);
variable->u.array.type = from->u.array.type;
variable->u.array.array = a;
break;
case VT_COMPOUND:
return _copy_compound(variable, from);
default:
/* TODO implement */
return error_set_code(-ENOSYS, "%s", strerror(ENOSYS));
}
variable->type = from->type;
return 0;
}
static VariableError _copy_compound(Variable * variable, Variable const * from)
{
String * s;
Mutator * m;
Mutator * n;
if(from->u.compound.name == NULL)
s = NULL;
else if((s = string_new(from->u.compound.name)) == NULL)
return -1;
if((n = m = mutator_new()) == NULL)
{
string_delete(s);
return -1;
}
mutator_foreach(from->u.compound.members, _copy_compound_foreach, &n);
if(n == NULL)
{
_variable_destroy_compound_members(m);
string_delete(s);
return -1;
}
_variable_destroy(variable);
variable->u.compound.name = s;
variable->u.compound.members = m;
return 0;
}
static void _copy_compound_foreach(Mutator const * mutator, String const * key,
void * value, void * data)
{
Mutator ** m = (Mutator **)data;
Variable * v = (Variable *)value;
(void) mutator;
if(*m == NULL)
return;
if(v == NULL)
return;
if((v = variable_new_copy(v)) == NULL
|| mutator_set(*m, key, v) != 0)
{
variable_delete(v);
*m = NULL;
}
}
/* variable_serialize */
VariableError variable_serialize(Variable * variable, Buffer * buffer,
bool prefix)
{
size_t size = 0;
size_t offset;
void * p;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
int64_t i64;
uint32_t u32;
uint64_t u64;
char buf[16];
Array const * array;
size_t i;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(%u)\n", __func__, variable->type);
#endif
switch(variable->type)
{
case VT_NULL:
p = NULL;
break;
case VT_BOOL:
case VT_INT8:
case VT_UINT8:
size = sizeof(variable->u.int8);
p = &variable->u.int8;
break;
case VT_INT16:
size = sizeof(i16);
i16 = _bswap16(variable->u.int16);
p = &i16;
break;
case VT_UINT16:
size = sizeof(u16);
u16 = _bswap16(variable->u.uint16);
p = &u16;
break;
case VT_INT32:
size = sizeof(i32);
i32 = _bswap32(variable->u.int32);
p = &i32;
break;
case VT_UINT32:
size = sizeof(u32);
u32 = _bswap32(variable->u.uint32);
p = &u32;
break;
case VT_INT64:
size = sizeof(i64);
i64 = _bswap64(variable->u.int64);
p = &i64;
break;
case VT_UINT64:
size = sizeof(u64);
u64 = _bswap64(variable->u.uint64);
p = &u64;
break;
case VT_FLOAT:
size = snprintf(buf, sizeof(buf), "%.e", variable->u.f);
p = buf;
break;
case VT_DOUBLE:
size = snprintf(buf, sizeof(buf), "%.e", variable->u.d);
p = buf;
break;
case VT_BUFFER:
size = buffer_get_size(variable->u.buffer);
u32 = buffer_get_size(variable->u.buffer);
u32 = _bswap32(u32);
p = (void *)buffer_get_data(variable->u.buffer);
break;
case VT_STRING:
size = string_get_length(variable->u.string) + 1;
p = variable->u.string;
break;
case VT_ARRAY:
array = variable->u.array.array;
size = sizeof(u32);
u32 = array_count(array);
u32 = _bswap32(u32);
p = &u32;
break;
case VT_POINTER:
size = sizeof(u64);
u64 = (uint64_t)variable->u.pointer;
u64 = _bswap64(u64);
p = &u64;
break;
}
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %lu\n", __func__, size);
#endif
if(size == 0 && variable->type != VT_NULL)
return -error_set_code(1, "Unable to serialize type %u",
variable->type);
offset = 0;
if(prefix)
{
/* prefix with the type */
u8 = variable->type;
if(buffer_set(buffer, sizeof(u8), (char const *)&u8) != 0)
return -1;
offset = sizeof(u8);
if(variable->type == VT_BUFFER)
{
if(buffer_set_data(buffer, offset, (char const *)&u32,
sizeof(u32)) != 0)
return -1;
offset += sizeof(u32);
}
else if(variable->type != VT_ARRAY)
return buffer_set_data(buffer, offset, (char const *)p,
size);
}
if(variable->type == VT_BUFFER)
{
if(buffer_set(buffer, sizeof(u32), (char const *)&u32) != 0)
return -1;
return buffer_set_data(buffer, sizeof(u32), (char const *)p,
size);
}
else if(variable->type == VT_ARRAY)
{
array = variable->u.array.array;
u8 = variable->u.array.type;
if(buffer_set_data(buffer, offset, (char const *)&u8,
sizeof(u8)) != 0)
return -1;
offset += sizeof(u8);
if(buffer_set_data(buffer, offset, p, size) != 0)
return -1;
offset += sizeof(u32);
size = array_get_size(array);
for(i = 0; i < array_count(array); i++)
if(buffer_set_data(buffer, offset, array_get(array, i),
size) != 0)
return -1;
else
offset += size;
return 0;
}
return buffer_set(buffer, size, (char const *)p);
}
/* private */
/* bswap16 */
static uint16_t _bswap16(uint16_t u)
{
return ((u & 0xff) << 8) | ((u & 0xff00) >> 8);
}
/* bswap32 */
static uint32_t _bswap32(uint32_t u)
{
return ((u & 0xff) << 24) | ((u & 0xff00) << 8)
| ((u & 0xff0000) >> 8) | ((u & 0xff000000) >> 24);
}
/* bswap64 */
static uint64_t _bswap64(uint64_t u)
{
return ((u & 0xff) << 56) | ((u & 0xff00) << 40)
| ((u & 0xff0000) << 24) | ((u & 0xff000000) << 8)
| ((u & 0xff00000000) >> 8) | ((u & 0xff0000000000) >> 24)
| ((u & 0xff000000000000) >> 40)
| ((u & 0xff00000000000000) >> 56);
}
/* variable_destroy */
static void _variable_destroy(Variable * variable)
{
switch(variable->type)
{
case VT_NULL:
case VT_BOOL:
case VT_INT8:
case VT_UINT8:
case VT_INT16:
case VT_UINT16:
case VT_INT32:
case VT_UINT32:
case VT_INT64:
case VT_UINT64:
case VT_FLOAT:
case VT_DOUBLE:
case VT_POINTER:
break;
case VT_BUFFER:
buffer_delete(variable->u.buffer);
break;
case VT_STRING:
string_delete(variable->u.string);
break;
case VT_ARRAY:
array_delete(variable->u.array.array);
break;
case VT_COMPOUND:
_variable_destroy_compound(variable);
break;
}
}
/* variable_destroy_compound */
static void _variable_destroy_compound(Variable * variable)
{
string_delete(variable->u.compound.name);
_variable_destroy_compound_members(variable->u.compound.members);
}
/* variable_destroy_compound_members */
static void _destroy_compound_members_foreach(Mutator const * mutator,
String const * key, void * value, void * data);
static void _variable_destroy_compound_members(Mutator * members)
{
mutator_foreach(members, _destroy_compound_members_foreach, NULL);
mutator_delete(members);
}
static void _destroy_compound_members_foreach(Mutator const * mutator,
String const * key, void * value, void * data)
{
Variable * v = (Variable *)value;
(void) mutator;
(void) key;
(void) data;
variable_delete(v);
}