libSystem/src/variable.c

556 lines
12 KiB
C

/* $Id$ */
/* Copyright (c) 2012 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS System libSystem */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include "System.h"
/* Variable */
/* private */
/* types */
struct _Variable
{
VariableType type;
union
{
int8_t int8;
uint8_t uint8;
int16_t int16;
uint16_t uint16;
int32_t int32;
uint32_t uint32;
int64_t int64;
uint64_t uint64;
Buffer * buffer;
String * string;
} u;
};
/* public */
/* variable_new */
Variable * variable_new(VariableType type, void * value)
{
Variable * variable;
int8_t * i8;
uint8_t * u8;
int16_t * i16;
uint16_t * u16;
int32_t * i32;
uint32_t * u32;
int64_t * i64;
uint64_t * u64;
Buffer * b;
char const * s;
if((variable = object_new(sizeof(*variable))) == NULL)
return NULL;
memset(&variable->u, 0, sizeof(variable->u));
if(value == NULL)
variable->type = VT_NULL;
else
switch((variable->type = type))
{
case VT_NULL:
break;
case VT_INT8:
i8 = value;
variable->u.int8 = *i8;
break;
case VT_UINT8:
u8 = value;
variable->u.uint8 = *u8;
break;
case VT_INT16:
i16 = value;
variable->u.int16 = *i16;
break;
case VT_UINT16:
u16 = value;
variable->u.uint16 = *u16;
break;
case VT_INT32:
i32 = value;
variable->u.int32 = *i32;
break;
case VT_UINT32:
u32 = value;
variable->u.uint32 = *u32;
break;
case VT_INT64:
i64 = value;
variable->u.int64 = *i64;
break;
case VT_UINT64:
u64 = value;
variable->u.uint64 = *u64;
break;
case VT_BUFFER:
if((b = buffer_new_copy(value)) == NULL)
{
object_delete(variable);
return NULL;
}
variable->u.buffer = b;
break;
case VT_STRING:
s = value;
if((variable->u.string = string_new(s)) == NULL)
{
object_delete(variable);
return NULL;
}
break;
}
return variable;
}
/* variable_new_copy */
Variable * variable_new_copy(Variable * variable)
{
return variable_new(variable->type, &variable->u);
}
/* variable_new_deserialize */
Variable * variable_new_deserialize(size_t * size, char const * data)
{
Variable * variable;
unsigned char u;
size_t s;
/* obtain the type from the data */
if(*size < sizeof(u))
{
*size = 1;
return NULL;
}
u = data[0];
s = *size - sizeof(u);
/* deserialize according to the type */
if((variable = variable_new_deserialize_type(u, &s, &data[sizeof(u)]))
== NULL)
{
*size = s + sizeof(u);
return NULL;
}
return variable;
}
/* variable_new_deserialize_type */
Variable * variable_new_deserialize_type(VariableType type, size_t * size,
char const * data)
{
size_t s;
int16_t i16;
int32_t i32;
uint32_t u32;
int64_t i64;
void * p = (char *)data;
/* estimate the size required */
switch(type)
{
case VT_NULL:
s = 0;
break;
case VT_INT8:
case VT_UINT8:
s = sizeof(int8_t);
break;
case VT_INT16:
case VT_UINT16:
s = sizeof(int16_t);
p = (char *)&i16;
break;
case VT_INT32:
case VT_UINT32:
s = sizeof(int32_t);
p = (char *)&i32;
break;
case VT_INT64:
case VT_UINT64:
s = sizeof(int64_t);
p = (char *)&i64;
break;
case VT_BUFFER:
s = sizeof(uint32_t);
if(*size < s)
break;
memcpy(&u32, data, s);
u32 = ntohl(u32);
s += u32;
break;
case VT_STRING:
for(s = 0; s < *size;)
if(data[s++] != '\0')
continue;
else if((p = malloc(s)) == NULL)
{
error_set_code(1, "%s",
strerror(errno));
return NULL;
}
else
break;
break;
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;
if(p != data)
memcpy(p, data, s);
/* convert the data if necessary */
switch(type)
{
case VT_NULL:
case VT_INT8:
case VT_UINT8:
break;
case VT_INT16:
case VT_UINT16:
i16 = ntohs(i16);
break;
case VT_INT32:
case VT_UINT32:
i32 = ntohl(i32);
break;
case VT_STRING:
break;
case VT_INT64:
case VT_UINT64:
case VT_BUFFER:
if((p = buffer_new(s - sizeof(u32), &data[sizeof(u32)]))
== NULL)
return NULL;
break;
default:
error_set_code(1, "Unable to deserialize type %u",
type);
return NULL;
}
return variable_new(type, p);
}
/* variable_delete */
void variable_delete(Variable * variable)
{
switch(variable->type)
{
case VT_NULL:
case VT_INT8:
case VT_UINT8:
case VT_INT16:
case VT_UINT16:
case VT_INT32:
case VT_UINT32:
case VT_INT64:
case VT_UINT64:
break;
case VT_BUFFER:
buffer_delete(variable->u.buffer);
break;
case VT_STRING:
string_delete(variable->u.string);
break;
}
object_delete(variable);
}
/* variable_get_as */
int variable_get_as(Variable * variable, VariableType type, void * result)
{
size_t size = 0;
void * p = NULL;
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
switch(type)
{
case VT_NULL:
if(variable->type == VT_NULL)
return 0;
break;
case VT_INT8:
size = sizeof(i8);
if(variable->type == VT_INT8)
{
p = &variable->u.int8;
break;
}
if(variable->type == VT_UINT8
&& variable->u.uint8 < (1 << 7))
i8 = variable->u.uint8;
else if(variable->type == VT_INT16
&& variable->u.int16 >= -128
&& variable->u.int16 < (1 << 7))
i8 = variable->u.int16;
else if(variable->type == VT_UINT16
&& variable->u.uint16 < (1 << 7))
i8 = variable->u.uint16;
else if(variable->type == VT_INT32
&& variable->u.int32 >= -128
&& variable->u.int32 < (1 << 7))
i8 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32 < (1 << 7))
i8 = variable->u.uint32;
else
/* FIXME implement more conversions */
break;
p = &i8;
break;
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 < (1 << 8))
u8 = variable->u.int16;
else if(variable->type == VT_UINT16
&& variable->u.uint16 < (1 << 8))
u8 = variable->u.uint16;
else if(variable->type == VT_INT32
&& variable->u.int32 >= 0
&& variable->u.int32 < (1 << 8))
u8 = variable->u.int32;
else if(variable->type == VT_UINT32
&& variable->u.uint32
< (1 << 8))
u8 = variable->u.uint32;
else
/* FIXME implement more conversions */
break;
p = &u8;
break;
case VT_INT16:
size = sizeof(i16);
if(variable->type == VT_INT16)
{
p = &variable->u.int16;
break;
}
if(variable->type == VT_UINT16
&& variable->u.uint16 < (1 << 15))
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
/* FIXME implement more conversions */
break;
p = &i16;
break;
case VT_UINT16:
size = sizeof(u16);
if(variable->type == VT_UINT16)
{
p = &variable->u.uint16;
break;
}
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
/* FIXME implement more conversions */
break;
p = &u16;
break;
case VT_INT32:
size = sizeof(i32);
if(variable->type == VT_INT32)
{
p = &variable->u.int32;
break;
}
if(variable->type == VT_UINT32
&& variable->u.uint32
< (uint32_t)1 << 31)
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
/* FIXME implement more conversions */
break;
p = &i32;
break;
case VT_UINT32:
size = sizeof(u32);
if(variable->type == VT_UINT32)
{
p = &variable->u.uint32;
break;
}
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
/* FIXME implement more conversions */
break;
size = sizeof(u32);
p = &u32;
break;
case VT_INT64:
case VT_UINT64:
/* FIXME consider signedness */
if(variable->type == VT_INT64
|| variable->type == VT_UINT64)
{
size = sizeof(variable->u.uint64);
p = &variable->u.uint64;
break;
}
break;
case VT_BUFFER:
case VT_STRING:
/* FIXME implement */
break;
}
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);
}
/* useful */
/* variable_serialize */
int variable_serialize(Variable * variable, Buffer * buffer, int type)
{
size_t size = 0;
size_t offset;
void * p;
unsigned char u;
uint32_t u32;
/* FIXME set everything in network endian */
switch(variable->type)
{
case VT_NULL:
p = NULL;
break;
case VT_INT8:
case VT_UINT8:
size = sizeof(variable->u.int8);
p = &variable->u.int8;
break;
case VT_INT16:
case VT_UINT16:
size = sizeof(variable->u.int16);
p = &variable->u.int16;
break;
case VT_INT32:
case VT_UINT32:
size = sizeof(variable->u.int32);
p = &variable->u.int32;
break;
case VT_INT64:
case VT_UINT64:
size = sizeof(variable->u.int64);
p = &variable->u.int64;
break;
case VT_BUFFER:
size = sizeof(variable->u.int32)
+ buffer_get_size(variable->u.buffer);
u32 = buffer_get_size(variable->u.buffer);
u32 = htonl(u32);
p = buffer_get_data(variable->u.buffer);
break;
case VT_STRING:
size = string_get_length(variable->u.string);
p = variable->u.string;
break;
}
if(size == 0 && variable->type != VT_NULL)
return -error_set_code(1, "Unable to serialize type %u", type);
if(type)
{
/* prefix with the type */
u = variable->type;
if(buffer_set(buffer, sizeof(u), (char *)&u) != 0)
return -1;
offset = sizeof(u);
if(variable->type == VT_BUFFER)
{
if(buffer_set_data(buffer, offset, (char *)&u32,
sizeof(u32)) != 0)
return -1;
offset += sizeof(u32);
}
return buffer_set_data(buffer, offset, p, size);
}
if(variable->type == VT_BUFFER)
{
if(buffer_set(buffer, sizeof(u32), (char *)&u32) != 0)
return -1;
return buffer_set_data(buffer, sizeof(u32), p, size);
}
return buffer_set(buffer, size, p);
}