blob: b1082f0c37d50590fd9f60563194bbac8c999ce1 [file] [log] [blame] [raw]
/*
Copyright: Boaz Segev, 2017-2018
License: MIT
*/
#include <fiobj_numbers.h>
#include <fiobject.h>
#include <fio.h>
#include <assert.h>
#include <errno.h>
#include <math.h>
/* *****************************************************************************
Numbers Type
***************************************************************************** */
typedef struct {
fiobj_object_header_s head;
intptr_t i;
} fiobj_num_s;
typedef struct {
fiobj_object_header_s head;
double f;
} fiobj_float_s;
#define obj2num(o) ((fiobj_num_s *)FIOBJ2PTR(o))
#define obj2float(o) ((fiobj_float_s *)FIOBJ2PTR(o))
/* *****************************************************************************
Numbers VTable
***************************************************************************** */
static __thread char num_buffer[512];
static intptr_t fio_i2i(const FIOBJ o) { return obj2num(o)->i; }
static intptr_t fio_f2i(const FIOBJ o) {
return (intptr_t)floorl(obj2float(o)->f);
}
static double fio_i2f(const FIOBJ o) { return (double)obj2num(o)->i; }
static double fio_f2f(const FIOBJ o) { return obj2float(o)->f; }
static size_t fio_itrue(const FIOBJ o) { return (obj2num(o)->i != 0); }
static size_t fio_ftrue(const FIOBJ o) { return (obj2float(o)->f != 0); }
static fio_str_info_s fio_i2str(const FIOBJ o) {
return (fio_str_info_s){
.data = num_buffer,
.len = fio_ltoa(num_buffer, obj2num(o)->i, 10),
};
}
static fio_str_info_s fio_f2str(const FIOBJ o) {
if (isnan(obj2float(o)->f))
return (fio_str_info_s){.data = (char *)"NaN", .len = 3};
else if (isinf(obj2float(o)->f)) {
if (obj2float(o)->f > 0)
return (fio_str_info_s){.data = (char *)"Infinity", .len = 8};
else
return (fio_str_info_s){.data = (char *)"-Infinity", .len = 9};
}
return (fio_str_info_s){
.data = num_buffer,
.len = fio_ftoa(num_buffer, obj2float(o)->f, 10),
};
}
static size_t fiobj_i_is_eq(const FIOBJ self, const FIOBJ other) {
return obj2num(self)->i == obj2num(other)->i;
}
static size_t fiobj_f_is_eq(const FIOBJ self, const FIOBJ other) {
return obj2float(self)->f == obj2float(other)->f;
}
void fiobject___simple_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg);
uintptr_t fiobject___noop_count(FIOBJ o);
const fiobj_object_vtable_s FIOBJECT_VTABLE_NUMBER = {
.class_name = "Number",
.to_i = fio_i2i,
.to_f = fio_i2f,
.to_str = fio_i2str,
.is_true = fio_itrue,
.is_eq = fiobj_i_is_eq,
.count = fiobject___noop_count,
.dealloc = fiobject___simple_dealloc,
};
const fiobj_object_vtable_s FIOBJECT_VTABLE_FLOAT = {
.class_name = "Float",
.to_i = fio_f2i,
.to_f = fio_f2f,
.is_true = fio_ftrue,
.to_str = fio_f2str,
.is_eq = fiobj_f_is_eq,
.count = fiobject___noop_count,
.dealloc = fiobject___simple_dealloc,
};
/* *****************************************************************************
Number API
***************************************************************************** */
/** Creates a Number object. Remember to use `fiobj_free`. */
FIOBJ fiobj_num_new_bignum(intptr_t num) {
fiobj_num_s *o = fio_malloc(sizeof(*o));
if (!o) {
perror("ERROR: fiobj number couldn't allocate memory");
exit(errno);
}
*o = (fiobj_num_s){
.head =
{
.type = FIOBJ_T_NUMBER,
.ref = 1,
},
.i = num,
};
return (FIOBJ)o;
}
/** Mutates a Big Number object's value. Effects every object's reference! */
// void fiobj_num_set(FIOBJ target, intptr_t num) {
// assert(FIOBJ_TYPE_IS(target, FIOBJ_T_NUMBER) &&
// FIOBJ_IS_ALLOCATED(target)); obj2num(target)->i = num;
// }
/** Creates a temporary Number object. This ignores `fiobj_free`. */
FIOBJ fiobj_num_tmp(intptr_t num) {
static __thread fiobj_num_s ret;
ret = (fiobj_num_s){
.head = {.type = FIOBJ_T_NUMBER, .ref = ((~(uint32_t)0) >> 4)},
.i = num,
};
return (FIOBJ)&ret;
}
/* *****************************************************************************
Float API
***************************************************************************** */
/** Creates a Float object. Remember to use `fiobj_free`. */
FIOBJ fiobj_float_new(double num) {
fiobj_float_s *o = fio_malloc(sizeof(*o));
if (!o) {
perror("ERROR: fiobj float couldn't allocate memory");
exit(errno);
}
*o = (fiobj_float_s){
.head =
{
.type = FIOBJ_T_FLOAT,
.ref = 1,
},
.f = num,
};
return (FIOBJ)o;
}
/** Mutates a Float object's value. Effects every object's reference! */
void fiobj_float_set(FIOBJ obj, double num) {
assert(FIOBJ_TYPE_IS(obj, FIOBJ_T_FLOAT));
obj2float(obj)->f = num;
}
/** Creates a temporary Number object. This ignores `fiobj_free`. */
FIOBJ fiobj_float_tmp(double num) {
static __thread fiobj_float_s ret;
ret = (fiobj_float_s){
.head =
{
.type = FIOBJ_T_FLOAT,
.ref = ((~(uint32_t)0) >> 4),
},
.f = num,
};
return (FIOBJ)&ret;
}
/* *****************************************************************************
Numbers to Strings - Buffered
***************************************************************************** */
static __thread char num_buffer[512];
fio_str_info_s fio_ltocstr(long i) {
return (fio_str_info_s){.data = num_buffer,
.len = fio_ltoa(num_buffer, i, 10)};
}
fio_str_info_s fio_ftocstr(double f) {
return (fio_str_info_s){.data = num_buffer,
.len = fio_ftoa(num_buffer, f, 10)};
}
/* *****************************************************************************
Tests
***************************************************************************** */
#if DEBUG
void fiobj_test_numbers(void) {
#define NUMTEST_ASSERT(cond, ...) \
if (!(cond)) { \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "Testing failed.\n"); \
exit(-1); \
}
FIOBJ i = fiobj_num_new(8);
fprintf(stderr, "=== Testing Numbers\n");
fprintf(stderr, "* FIOBJ_NUMBER_SIGN_MASK == %p\n",
(void *)FIOBJ_NUMBER_SIGN_MASK);
fprintf(stderr, "* FIOBJ_NUMBER_SIGN_BIT == %p\n",
(void *)FIOBJ_NUMBER_SIGN_BIT);
fprintf(stderr, "* FIOBJ_NUMBER_SIGN_EXCLUDE_BIT == %p\n",
(void *)FIOBJ_NUMBER_SIGN_EXCLUDE_BIT);
NUMTEST_ASSERT(FIOBJ_TYPE_IS(i, FIOBJ_T_NUMBER),
"* FIOBJ_TYPE_IS failed to return true.");
NUMTEST_ASSERT((FIOBJ_TYPE(i) == FIOBJ_T_NUMBER),
"* FIOBJ_TYPE failed to return type.");
NUMTEST_ASSERT(!FIOBJ_TYPE_IS(i, FIOBJ_T_NULL),
"* FIOBJ_TYPE_IS failed to return false.");
NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG),
"* Number 8 was dynamically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2num(i) == 8), "* Number 8 was not returned! %p\n",
(void *)i);
fiobj_free(i);
i = fiobj_num_new(-1);
NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG),
"* Number -1 was dynamically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2num(i) == -1), "* Number -1 was not returned! %p\n",
(void *)i);
fiobj_free(i);
i = fiobj_num_new(INTPTR_MAX);
NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
"* INTPTR_MAX was statically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2num(i) == INTPTR_MAX),
"* INTPTR_MAX was not returned! %p\n", (void *)i);
NUMTEST_ASSERT(
FIOBJ_TYPE_IS(i, FIOBJ_T_NUMBER),
"* FIOBJ_TYPE_IS failed to return true for dynamic allocation.");
NUMTEST_ASSERT((FIOBJ_TYPE(i) == FIOBJ_T_NUMBER),
"* FIOBJ_TYPE failed to return type for dynamic allocation.");
fiobj_free(i);
i = fiobj_num_new(INTPTR_MIN);
NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
"* INTPTR_MIN was statically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2num(i) == INTPTR_MIN),
"* INTPTR_MIN was not returned! %p\n", (void *)i);
fiobj_free(i);
fprintf(stderr, "* passed.\n");
fprintf(stderr, "=== Testing Floats\n");
i = fiobj_float_new(1.0);
NUMTEST_ASSERT(((i & FIOBJECT_NUMBER_FLAG) == 0),
"* float 1 was statically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2float(i) == 1.0),
"* Float 1.0 was not returned! %p\n", (void *)i);
fiobj_free(i);
i = fiobj_float_new(-1.0);
NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
"* Float -1 was statically allocated?! %p\n", (void *)i);
NUMTEST_ASSERT((fiobj_obj2float(i) == -1.0),
"* Float -1 was not returned! %p\n", (void *)i);
fiobj_free(i);
fprintf(stderr, "* passed.\n");
}
#endif