Files
fx/fx/value.c
T

711 lines
21 KiB
C

#include <fx/bool.h>
#include <fx/comparable.h>
#include <fx/convertible.h>
#include <fx/float.h>
#include <fx/int.h>
#include <fx/operable.h>
#include <fx/reflection/type.h>
#include <fx/string.h>
#include <fx/value-type.h>
#include <fx/value.h>
#include <fx/wstr.h>
void fx_value_init(fx_value *v, fx_type_id type)
{
}
void fx_value_copy(fx_value *dst, const fx_value *src)
{
memcpy(dst, src, sizeof *dst);
if (dst->v_type && !fx_type_is_value_type(dst->v_type)) {
fx_object_ref(dst->v_object);
}
}
fx_value fx_value_copy_return(const fx_value src)
{
fx_value result;
fx_value_copy(&result, &src);
return result;
}
fx_value fx_value_ref_copy_return(const fx_value *src)
{
if (!src) {
return FX_VALUE_EMPTY;
}
fx_value result;
fx_value_copy(&result, src);
return result;
}
void fx_value_copy_array(fx_value *dst, const fx_value *src, size_t count)
{
for (size_t i = 0; i < count; i++) {
fx_value_copy(dst + i, src + i);
}
}
void fx_value_unset(fx_value *v)
{
if (!v->v_type) {
return;
}
if (!fx_type_is_value_type(v->v_type)) {
fx_object_unref(v->v_object);
}
memset(v, 0x0, sizeof *v);
}
void fx_value_unset_array(fx_value *v, size_t count)
{
for (size_t i = 0; i < count; i++) {
fx_value_unset(v + i);
}
}
fx_status fx_value_to_string(
const fx_value *v,
fx_stream *out,
const char *format)
{
if (!v->v_type) {
return FX_SUCCESS;
}
void *c = fx_class_get(v->v_type);
if (!c) {
return FX_ERR_NOT_SUPPORTED;
}
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
if (object && object->to_string) {
return object->to_string(v, out, format);
}
const fx_type *ty = fx_type_get_by_id(v->v_type);
if (ty) {
fx_stream_write_fmt(out, NULL, "%s", fx_type_get_name(ty));
return FX_SUCCESS;
}
return FX_ERR_NOT_SUPPORTED;
}
fx_status fx_value_hash(const fx_value *v, uint64_t *out_hash)
{
void *c = fx_class_get(v->v_type);
if (!c) {
return FX_ERR_NOT_SUPPORTED;
}
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
if (!object || !object->hash) {
return FX_ERR_NOT_SUPPORTED;
}
return object->hash(v, out_hash);
}
int fx_value_compare(const fx_value *left, const fx_value *right)
{
if (!left->v_type && !right->v_type) {
return 0;
}
if (!left->v_type || !right->v_type) {
return -1;
}
void *c = fx_class_get(left->v_type);
if (!c) {
return FX_ERR_NOT_SUPPORTED;
}
fx_comparable_class *comparable
= fx_class_get_interface(c, FX_TYPE_COMPARABLE);
if (!comparable || !comparable->c_compare) {
return FX_ERR_NOT_SUPPORTED;
}
return comparable->c_compare(left, right);
}
bool fx_value_is_set(const fx_value *v)
{
return v->v_type != NULL;
}
bool fx_value_is_type(const fx_value *v, fx_type_id type)
{
if (fx_type_id_compare(v->v_type, type) == 0) {
return true;
}
if (!fx_type_is_value_type(v->v_type)) {
return fx_object_is_type(v->v_object, type);
}
return false;
}
static fx_status value_change_string(fx_value *in, fx_value *out)
{
if (!in->v_type) {
return FX_SUCCESS;
}
void *c = fx_class_get(in->v_type);
if (!c) {
return FX_ERR_NOT_SUPPORTED;
}
fx_object_class *object = fx_class_get_interface(c, FX_TYPE_OBJECT);
if (!object || !object->to_string) {
return FX_ERR_NOT_SUPPORTED;
}
fx_stringstream *strm = fx_stringstream_create();
if (!strm) {
return FX_ERR_NO_MEMORY;
}
fx_status status = object->to_string(in, strm, NULL);
if (!FX_OK(status)) {
fx_stringstream_unref(strm);
return status;
}
fx_string *result
= fx_string_create_from_cstr(fx_stringstream_get_cstr(strm));
fx_stringstream_unref(strm);
if (!result) {
return FX_ERR_NO_MEMORY;
}
*out = FX_VALUE_OBJECT(result);
return FX_SUCCESS;
}
fx_status fx_value_change_type(
fx_value *in,
fx_value *out,
fx_type_id convert_to)
{
if (fx_value_is_type(in, convert_to)) {
fx_value_copy(out, in);
return FX_SUCCESS;
}
if (fx_type_id_compare(convert_to, FX_TYPE_STRING) == 0) {
return value_change_string(in, out);
}
void *c = fx_class_get(in->v_type);
if (!c) {
return FX_ERR_NOT_SUPPORTED;
}
fx_convertible_class *convertible
= fx_class_get_interface(c, FX_TYPE_CONVERTIBLE);
if (!convertible) {
return FX_ERR_NOT_SUPPORTED;
}
if (!fx_type_is_value_type(convert_to)) {
if (convertible->c_to_object) {
return convertible->c_to_object(in, out, convert_to);
}
return FX_ERR_NOT_SUPPORTED;
}
unsigned int value_type = __fx_type_get_value_type(convert_to);
fx_status status = FX_SUCCESS;
switch (value_type) {
case __FX_VALUE_TYPE_BOOL:
status = convertible->c_to_bool
? convertible->c_to_bool(in, &out->v_bool)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_I16:
status = convertible->c_to_i16
? convertible->c_to_i16(in, &out->v_i16)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_U16:
status = convertible->c_to_u16
? convertible->c_to_u16(in, &out->v_u16)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_I32:
status = convertible->c_to_i32
? convertible->c_to_i32(in, &out->v_i32)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_U32:
status = convertible->c_to_u32
? convertible->c_to_u32(in, &out->v_u32)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_I64:
status = convertible->c_to_i64
? convertible->c_to_i64(in, &out->v_i64)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_U64:
status = convertible->c_to_u64
? convertible->c_to_u64(in, &out->v_u64)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_IPTR:
status = convertible->c_to_iptr
? convertible->c_to_iptr(in, &out->v_iptr)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_UPTR:
status = convertible->c_to_uptr
? convertible->c_to_uptr(in, &out->v_uptr)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_SBYTE:
status = convertible->c_to_sbyte
? convertible->c_to_sbyte(in, &out->v_sbyte)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_BYTE:
status = convertible->c_to_byte
? convertible->c_to_byte(in, &out->v_byte)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_SHORT:
status = convertible->c_to_short
? convertible->c_to_short(in, &out->v_short)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_USHORT:
status = convertible->c_to_ushort
? convertible->c_to_ushort(in, &out->v_ushort)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_INT:
status = convertible->c_to_int
? convertible->c_to_int(in, &out->v_int)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_UINT:
status = convertible->c_to_uint
? convertible->c_to_uint(in, &out->v_uint)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_LONG:
status = convertible->c_to_long
? convertible->c_to_long(in, &out->v_long)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_ULONG:
status = convertible->c_to_ulong
? convertible->c_to_ulong(in, &out->v_ulong)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_LONGLONG:
status = convertible->c_to_longlong
? convertible->c_to_longlong(
in,
&out->v_longlong)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_ULONGLONG:
status = convertible->c_to_ulonglong
? convertible->c_to_ulonglong(
in,
&out->v_ulonglong)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_SIZE:
status = convertible->c_to_size
? convertible->c_to_size(in, &out->v_size)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_FLOAT:
status = convertible->c_to_float
? convertible->c_to_float(in, &out->v_float)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_DOUBLE:
status = convertible->c_to_double
? convertible->c_to_double(in, &out->v_double)
: FX_ERR_NOT_SUPPORTED;
break;
case __FX_VALUE_TYPE_CSTR:
status = convertible->c_to_cstr
? convertible->c_to_cstr(in, &out->v_cstr)
: FX_ERR_NOT_SUPPORTED;
break;
default:
return FX_ERR_NOT_SUPPORTED;
}
if (FX_OK(status)) {
out->v_type = convert_to;
}
return status;
}
#define VALUE_GETTER(c_type_name, fx_type_name, type_id) \
fx_status fx_value_get_##fx_type_name( \
const fx_value *v, \
c_type_name *out) \
{ \
if (fx_type_id_compare(v->v_type, type_id) == 0) { \
*out = v->v_##fx_type_name; \
return FX_SUCCESS; \
} \
\
void *c = fx_class_get(v->v_type); \
if (!c) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
fx_convertible_class *convertible \
= fx_class_get_interface(c, FX_TYPE_CONVERTIBLE); \
if (!convertible || !convertible->c_to_##fx_type_name) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
c_type_name result; \
fx_status status \
= convertible->c_to_##fx_type_name(v, &result); \
if (!FX_OK(status)) { \
return status; \
} \
\
*out = result; \
return FX_SUCCESS; \
}
#define VALUE_SETTER(c_type_name, fx_type_name, type_id) \
fx_status fx_value_set_##fx_type_name(fx_value *v, c_type_name value) \
{ \
fx_value_unset(v); \
v->v_type = type_id; \
v->v_##fx_type_name = value; \
return FX_SUCCESS; \
}
VALUE_GETTER(bool, bool, FX_TYPE_BOOL)
VALUE_GETTER(i16, i16, FX_TYPE_I16)
VALUE_GETTER(u16, u16, FX_TYPE_U16)
VALUE_GETTER(i32, i32, FX_TYPE_I32)
VALUE_GETTER(u32, u32, FX_TYPE_U32)
VALUE_GETTER(i64, i64, FX_TYPE_I64)
VALUE_GETTER(u64, u64, FX_TYPE_U64)
VALUE_GETTER(iptr, iptr, FX_TYPE_IPTR)
VALUE_GETTER(uptr, uptr, FX_TYPE_UPTR)
VALUE_GETTER(sbyte, sbyte, FX_TYPE_SBYTE)
VALUE_GETTER(byte, byte, FX_TYPE_BYTE)
VALUE_GETTER(short, short, FX_TYPE_SHORT)
VALUE_GETTER(unsigned short, ushort, FX_TYPE_USHORT)
VALUE_GETTER(int, int, FX_TYPE_INT)
VALUE_GETTER(unsigned int, uint, FX_TYPE_UINT)
VALUE_GETTER(long, long, FX_TYPE_LONG)
VALUE_GETTER(unsigned long, ulong, FX_TYPE_ULONG)
VALUE_GETTER(long long, longlong, FX_TYPE_LONGLONG)
VALUE_GETTER(unsigned long long, ulonglong, FX_TYPE_ULONGLONG)
VALUE_GETTER(size_t, size, FX_TYPE_SIZE)
VALUE_GETTER(float, float, FX_TYPE_FLOAT)
VALUE_GETTER(double, double, FX_TYPE_DOUBLE)
VALUE_GETTER(fx_wchar, wchar, FX_TYPE_WCHAR)
VALUE_GETTER(const fx_wchar *, wstr, FX_TYPE_WSTR)
VALUE_SETTER(bool, bool, FX_TYPE_BOOL)
VALUE_SETTER(i16, i16, FX_TYPE_I16)
VALUE_SETTER(u16, u16, FX_TYPE_U16)
VALUE_SETTER(i32, i32, FX_TYPE_I32)
VALUE_SETTER(u32, u32, FX_TYPE_U32)
VALUE_SETTER(i64, i64, FX_TYPE_I64)
VALUE_SETTER(u64, u64, FX_TYPE_U64)
VALUE_SETTER(iptr, iptr, FX_TYPE_IPTR)
VALUE_SETTER(uptr, uptr, FX_TYPE_UPTR)
VALUE_SETTER(sbyte, sbyte, FX_TYPE_SBYTE)
VALUE_SETTER(byte, byte, FX_TYPE_BYTE)
VALUE_SETTER(short, short, FX_TYPE_SHORT)
VALUE_SETTER(unsigned short, ushort, FX_TYPE_USHORT)
VALUE_SETTER(int, int, FX_TYPE_INT)
VALUE_SETTER(unsigned int, uint, FX_TYPE_UINT)
VALUE_SETTER(long, long, FX_TYPE_LONG)
VALUE_SETTER(unsigned long, ulong, FX_TYPE_ULONG)
VALUE_SETTER(long long, longlong, FX_TYPE_LONGLONG)
VALUE_SETTER(unsigned long long, ulonglong, FX_TYPE_ULONGLONG)
VALUE_SETTER(size_t, size, FX_TYPE_SIZE)
VALUE_SETTER(float, float, FX_TYPE_FLOAT)
VALUE_SETTER(double, double, FX_TYPE_DOUBLE)
VALUE_SETTER(fx_wchar, wchar, FX_TYPE_WCHAR)
fx_status fx_value_get_cstr(const fx_value *v, const char **out)
{
if (fx_type_id_compare(v->v_type, FX_TYPE_CSTR) == 0) {
*out = v->v_cstr;
return FX_SUCCESS;
}
if (fx_type_id_compare(v->v_type, FX_TYPE_STRING) == 0) {
*out = fx_string_get_cstr(v->v_object);
return FX_SUCCESS;
}
*out = NULL;
return FX_ERR_NOT_SUPPORTED;
}
fx_status fx_value_get_pointer(const fx_value *v, void **out)
{
if (fx_type_id_compare(v->v_type, FX_TYPE_POINTER) == 0) {
*out = v->v_pointer;
return FX_SUCCESS;
}
*out = NULL;
return FX_ERR_NOT_SUPPORTED;
}
fx_status fx_value_get_object(const fx_value *v, fx_object **out)
{
if (!v || !v->v_type) {
*out = NULL;
return FX_SUCCESS;
}
if (!fx_type_is_value_type(v->v_type)) {
*out = v->v_object;
return FX_SUCCESS;
}
*out = NULL;
return FX_ERR_NOT_SUPPORTED;
}
static bool is_integral_type(unsigned int type)
{
switch (type) {
case __FX_VALUE_TYPE_BOOL:
case __FX_VALUE_TYPE_I16:
case __FX_VALUE_TYPE_U16:
case __FX_VALUE_TYPE_I32:
case __FX_VALUE_TYPE_U32:
case __FX_VALUE_TYPE_I64:
case __FX_VALUE_TYPE_U64:
case __FX_VALUE_TYPE_IPTR:
case __FX_VALUE_TYPE_UPTR:
case __FX_VALUE_TYPE_SBYTE:
case __FX_VALUE_TYPE_BYTE:
case __FX_VALUE_TYPE_SHORT:
case __FX_VALUE_TYPE_USHORT:
case __FX_VALUE_TYPE_INT:
case __FX_VALUE_TYPE_UINT:
case __FX_VALUE_TYPE_LONG:
case __FX_VALUE_TYPE_ULONG:
case __FX_VALUE_TYPE_LONGLONG:
case __FX_VALUE_TYPE_ULONGLONG:
case __FX_VALUE_TYPE_SIZE:
return true;
default:
return false;
}
}
static bool is_signed_type(unsigned int type)
{
switch (type) {
case __FX_VALUE_TYPE_I16:
case __FX_VALUE_TYPE_I32:
case __FX_VALUE_TYPE_I64:
case __FX_VALUE_TYPE_IPTR:
case __FX_VALUE_TYPE_SBYTE:
case __FX_VALUE_TYPE_SHORT:
case __FX_VALUE_TYPE_INT:
case __FX_VALUE_TYPE_LONG:
case __FX_VALUE_TYPE_LONGLONG:
return true;
default:
return false;
}
}
static bool is_float_type(unsigned int type)
{
switch (type) {
case __FX_VALUE_TYPE_FLOAT:
case __FX_VALUE_TYPE_DOUBLE:
return true;
default:
return false;
}
}
static bool is_negative(unsigned int type, const fx_value *v)
{
switch (type) {
case __FX_VALUE_TYPE_I16:
return v->v_i16 < 0;
case __FX_VALUE_TYPE_I32:
return v->v_i32 < 0;
case __FX_VALUE_TYPE_I64:
return v->v_i64 < 0;
case __FX_VALUE_TYPE_IPTR:
return v->v_iptr < 0;
case __FX_VALUE_TYPE_SBYTE:
return v->v_sbyte < 0;
case __FX_VALUE_TYPE_SHORT:
return v->v_short < 0;
case __FX_VALUE_TYPE_INT:
return v->v_int < 0;
case __FX_VALUE_TYPE_LONG:
return v->v_long < 0;
case __FX_VALUE_TYPE_LONGLONG:
return v->v_longlong < 0;
default:
return false;
}
}
FX_API fx_type_id fx_value_get_common_type(const fx_value *a, const fx_value *b)
{
unsigned int a_type = __fx_type_get_value_type(a->v_type);
unsigned int b_type = __fx_type_get_value_type(b->v_type);
if (fx_type_id_compare(a->v_type, FX_TYPE_STRING) == 0
&& b_type == __FX_VALUE_TYPE_CSTR) {
return FX_TYPE_STRING;
}
if (fx_type_id_compare(b->v_type, FX_TYPE_STRING) == 0
&& a_type == __FX_VALUE_TYPE_CSTR) {
return FX_TYPE_STRING;
}
if (!fx_type_is_value_type(a->v_type)
|| !fx_type_is_value_type(b->v_type)) {
if (fx_type_id_compare(a->v_type, b->v_type) == 0) {
return a->v_type;
}
return NULL;
}
if (a_type == b_type) {
return a->v_type;
}
if (is_float_type(a_type) && is_float_type(b_type)) {
return a_type == b_type ? a->v_type : FX_TYPE_DOUBLE;
}
if (!is_integral_type(a_type) || !is_integral_type(b_type)) {
if (a_type == __FX_VALUE_TYPE_CSTR) {
return FX_TYPE_STRING;
}
return a->v_type;
}
size_t a_size = __fx_value_type_get_size(a_type);
size_t b_size = __fx_value_type_get_size(b_type);
bool a_signed = is_signed_type(a_type);
bool b_signed = is_signed_type(b_type);
if ((!a_signed && !b_signed) || (a_signed && b_signed)) {
return a_size > b_size ? a->v_type : b->v_type;
}
bool a_negative = is_negative(a_type, a);
bool b_negative = is_negative(b_type, b);
if ((!a_negative && !b_negative) || (a_negative && b_negative)) {
return a_size > b_size ? a->v_type : b->v_type;
}
/* TODO if value is too large for i64, return double instead */
return FX_TYPE_I64;
}
#define BINARY_OP(name) \
fx_status fx_value_##name( \
const fx_value *left, \
const fx_value *right, \
fx_value *out) \
{ \
if (fx_type_id_compare(left->v_type, right->v_type) != 0) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
void *c = fx_class_get(left->v_type); \
if (!c) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
fx_operable_class *operable \
= fx_class_get_interface(c, FX_TYPE_OPERABLE); \
if (!operable || !operable->op_##name) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
return operable->op_##name(left, right, out); \
}
#define PREFIX_OP(name) \
fx_status fx_value_##name(fx_value *value) \
{ \
void *c = fx_class_get(value->v_type); \
if (!c) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
fx_operable_class *operable \
= fx_class_get_interface(c, FX_TYPE_OPERABLE); \
if (!operable || !operable->op_##name) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
return operable->op_##name(value); \
}
#define IN_PLACE_OP(name) \
fx_status fx_value_##name(fx_value *left, const fx_value *right) \
{ \
if (fx_type_id_compare(left->v_type, right->v_type) != 0) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
void *c = fx_class_get(left->v_type); \
if (!c) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
fx_operable_class *operable \
= fx_class_get_interface(c, FX_TYPE_OPERABLE); \
if (!operable || !operable->op_##name) { \
return FX_ERR_NOT_SUPPORTED; \
} \
\
return operable->op_##name(left, right); \
}
BINARY_OP(add)
BINARY_OP(subtract)
BINARY_OP(multiply)
BINARY_OP(divide)
PREFIX_OP(increment)
PREFIX_OP(decrement)
IN_PLACE_OP(add_in_place)
IN_PLACE_OP(subtract_in_place)
IN_PLACE_OP(multiply_in_place)
IN_PLACE_OP(divide_in_place)