/* $Id$ */ /* Copyright (c) 2012-2021 Pierre Pronchery */ /* 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 #include #include #include #include #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); }