mirror of
https://github.com/zigzap/zap.git
synced 2025-10-21 15:44:10 +00:00
652 lines
20 KiB
C
652 lines
20 KiB
C
#ifndef H_FIOBJECT_H
|
|
/*
|
|
Copyright: Boaz Segev, 2017-2019
|
|
License: MIT
|
|
*/
|
|
|
|
/**
|
|
This facil.io core library provides wrappers around complex and (or) dynamic
|
|
types, abstracting some complexity and making dynamic type related tasks easier.
|
|
*/
|
|
#define H_FIOBJECT_H
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <limits.h>
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <fio_siphash.h>
|
|
|
|
#include <fio.h>
|
|
|
|
#if !defined(__GNUC__) && !defined(__clang__) && !defined(FIO_GNUC_BYPASS)
|
|
#define __attribute__(...)
|
|
#define __has_include(...) 0
|
|
#define __has_builtin(...) 0
|
|
#define FIO_GNUC_BYPASS 1
|
|
#elif !defined(__clang__) && !defined(__has_builtin)
|
|
#define __has_builtin(...) 0
|
|
#define FIO_GNUC_BYPASS 1
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* *****************************************************************************
|
|
Core Types
|
|
***************************************************************************** */
|
|
|
|
typedef enum __attribute__((packed)) {
|
|
FIOBJ_T_NUMBER = 0x01,
|
|
FIOBJ_T_NULL = 0x06,
|
|
FIOBJ_T_TRUE = 0x16,
|
|
FIOBJ_T_FALSE = 0x26,
|
|
FIOBJ_T_FLOAT,
|
|
FIOBJ_T_STRING,
|
|
FIOBJ_T_ARRAY,
|
|
FIOBJ_T_HASH,
|
|
FIOBJ_T_DATA,
|
|
FIOBJ_T_UNKNOWN
|
|
} fiobj_type_enum;
|
|
|
|
typedef uintptr_t FIOBJ;
|
|
|
|
/** a Macro retriving an object's type. Use FIOBJ_TYPE_IS(x) for testing. */
|
|
#define FIOBJ_TYPE(obj) fiobj_type((obj))
|
|
#define FIOBJ_TYPE_IS(obj, type) fiobj_type_is((obj), (type))
|
|
#define FIOBJ_IS_NULL(obj) (!obj || obj == (FIOBJ)FIOBJ_T_NULL)
|
|
#define FIOBJ_INVALID 0
|
|
|
|
#ifndef FIO_STR_INFO_TYPE
|
|
/** A String information type, reports information about a C string. */
|
|
typedef struct fio_str_info_s {
|
|
size_t capa; /* Buffer capacity, if the string is writable. */
|
|
size_t len; /* String length. */
|
|
char *data; /* String's first byte. */
|
|
} fio_str_info_s;
|
|
#define FIO_STR_INFO_TYPE
|
|
#endif
|
|
|
|
/* *****************************************************************************
|
|
Primitives
|
|
***************************************************************************** */
|
|
|
|
#define FIO_INLINE static inline __attribute__((unused))
|
|
|
|
FIO_INLINE FIOBJ fiobj_null(void) { return (FIOBJ)FIOBJ_T_NULL; }
|
|
FIO_INLINE FIOBJ fiobj_true(void) { return (FIOBJ)FIOBJ_T_TRUE; }
|
|
FIO_INLINE FIOBJ fiobj_false(void) { return (FIOBJ)FIOBJ_T_FALSE; }
|
|
|
|
/* *****************************************************************************
|
|
Generic Object API
|
|
***************************************************************************** */
|
|
|
|
/** Returns a C string naming the objects dynamic type. */
|
|
FIO_INLINE const char *fiobj_type_name(const FIOBJ obj);
|
|
|
|
/**
|
|
* Heuristic copy with a preference for copy reference(!) to minimize
|
|
* allocations.
|
|
*
|
|
* Always returns the value passed along.
|
|
*/
|
|
FIO_INLINE FIOBJ fiobj_dup(FIOBJ);
|
|
|
|
/**
|
|
* Frees the object and any of it's "children".
|
|
*
|
|
* This function affects nested objects, meaning that when an Array or
|
|
* a Hash object is passed along, it's children (nested objects) are
|
|
* also freed.
|
|
*/
|
|
FIO_INLINE void fiobj_free(FIOBJ);
|
|
|
|
/**
|
|
* Tests if an object evaluates as TRUE.
|
|
*
|
|
* This is object type specific. For example, empty strings might evaluate as
|
|
* FALSE, even though they aren't a boolean type.
|
|
*/
|
|
FIO_INLINE int fiobj_is_true(const FIOBJ);
|
|
|
|
/**
|
|
* Returns an Object's numerical value.
|
|
*
|
|
* If a String is passed to the function, it will be parsed assuming base 10
|
|
* numerical data.
|
|
*
|
|
* Hashes and Arrays return their object count.
|
|
*
|
|
* IO objects return the length of their data.
|
|
*
|
|
* A type error results in 0.
|
|
*/
|
|
FIO_INLINE intptr_t fiobj_obj2num(const FIOBJ obj);
|
|
|
|
/**
|
|
* Returns a Float's value.
|
|
*
|
|
* If a String is passed to the function, they will benparsed assuming base 10
|
|
* numerical data.
|
|
*
|
|
* A type error results in 0.
|
|
*/
|
|
FIO_INLINE double fiobj_obj2float(const FIOBJ obj);
|
|
|
|
/**
|
|
* Returns a C String (NUL terminated) using the `fio_str_info_s` data type.
|
|
*
|
|
* The Sting in binary safe and might contain NUL bytes in the middle as well as
|
|
* a terminating NUL.
|
|
*
|
|
* If a a Number or a Float are passed to the function, they
|
|
* will be parsed as a *temporary*, thread-safe, String.
|
|
*
|
|
* Numbers will be represented in base 10 numerical data.
|
|
*
|
|
* A type error results in NULL (i.e. object isn't a String).
|
|
*/
|
|
FIO_INLINE fio_str_info_s fiobj_obj2cstr(const FIOBJ obj);
|
|
|
|
/**
|
|
* Calculates an Objects's SipHash value for possible use as a HashMap key.
|
|
*
|
|
* The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In
|
|
* other words, Hash Objects and Arrays can NOT be used for Hash keys.
|
|
*/
|
|
FIO_INLINE uint64_t fiobj_obj2hash(const FIOBJ o);
|
|
|
|
/**
|
|
* Single layer iteration using a callback for each nested fio object.
|
|
*
|
|
* Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
|
|
* processed. The container itself (the Array or the Hash) is **not** processed
|
|
* (unlike `fiobj_each2`).
|
|
*
|
|
* The callback task function must accept an object and an opaque user pointer.
|
|
*
|
|
* Hash objects pass along only the value object. The keys can be accessed using
|
|
* the `fiobj_hash_key_in_loop` function.
|
|
*
|
|
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
|
*
|
|
* Returns the "stop" position, i.e., the number of items processed + the
|
|
* starting point.
|
|
*/
|
|
FIO_INLINE size_t fiobj_each1(FIOBJ, size_t start_at,
|
|
int (*task)(FIOBJ obj, void *arg), void *arg);
|
|
|
|
/**
|
|
* Deep iteration using a callback for each fio object, including the parent.
|
|
*
|
|
* Accepts any `FIOBJ ` type.
|
|
*
|
|
* Collections (Arrays, Hashes) are deeply probed and shouldn't be edited
|
|
* during an `fiobj_each2` call (or weird things may happen).
|
|
*
|
|
* The callback task function must accept an object and an opaque user pointer.
|
|
*
|
|
* Hash objects keys are available using the `fiobj_hash_key_in_loop` function.
|
|
*
|
|
* Notice that when passing collections to the function, the collection itself
|
|
* is sent to the callback followed by it's children (if any). This is true also
|
|
* for nested collections (a nested Hash will be sent first, followed by the
|
|
* nested Hash's children and then followed by the rest of it's siblings.
|
|
*
|
|
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
|
*/
|
|
size_t fiobj_each2(FIOBJ, int (*task)(FIOBJ obj, void *arg), void *arg);
|
|
|
|
/**
|
|
* Deeply compare two objects. No hashing or recursive function calls are
|
|
* involved.
|
|
*
|
|
* Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
|
|
*
|
|
* Hash objects are order sensitive. To be equal, Hash keys must match in order.
|
|
*
|
|
* Returns 1 if true and 0 if false.
|
|
*/
|
|
FIO_INLINE int fiobj_iseq(const FIOBJ obj1, const FIOBJ obj2);
|
|
|
|
/* *****************************************************************************
|
|
Object Type Identification
|
|
***************************************************************************** */
|
|
|
|
#define FIOBJECT_NUMBER_FLAG 1
|
|
|
|
#if UINTPTR_MAX < 0xFFFFFFFFFFFFFFFF
|
|
#define FIOBJECT_PRIMITIVE_FLAG 2
|
|
#define FIOBJECT_STRING_FLAG 0
|
|
#define FIOBJECT_HASH_FLAG 0
|
|
#define FIOBJECT_TYPE_MASK (~(uintptr_t)3)
|
|
#else
|
|
#define FIOBJECT_PRIMITIVE_FLAG 6
|
|
#define FIOBJECT_STRING_FLAG 2
|
|
#define FIOBJECT_HASH_FLAG 4
|
|
#define FIOBJECT_TYPE_MASK (~(uintptr_t)7)
|
|
#endif
|
|
|
|
#define FIOBJ_NUMBER_SIGN_MASK ((~((uintptr_t)0)) >> 1)
|
|
#define FIOBJ_NUMBER_SIGN_BIT (~FIOBJ_NUMBER_SIGN_MASK)
|
|
#define FIOBJ_NUMBER_SIGN_EXCLUDE_BIT (FIOBJ_NUMBER_SIGN_BIT >> 1)
|
|
|
|
#define FIOBJ_IS_ALLOCATED(o) \
|
|
((o) && ((o)&FIOBJECT_NUMBER_FLAG) == 0 && \
|
|
((o)&FIOBJECT_PRIMITIVE_FLAG) != FIOBJECT_PRIMITIVE_FLAG)
|
|
#define FIOBJ2PTR(o) ((void *)((o)&FIOBJECT_TYPE_MASK))
|
|
|
|
FIO_INLINE fiobj_type_enum fiobj_type(FIOBJ o) {
|
|
if (!o)
|
|
return FIOBJ_T_NULL;
|
|
if (o & FIOBJECT_NUMBER_FLAG)
|
|
return FIOBJ_T_NUMBER;
|
|
if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
|
|
return (fiobj_type_enum)o;
|
|
if (FIOBJECT_STRING_FLAG &&
|
|
(o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_STRING_FLAG)
|
|
return FIOBJ_T_STRING;
|
|
if (FIOBJECT_HASH_FLAG && (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_HASH_FLAG)
|
|
return FIOBJ_T_HASH;
|
|
return ((fiobj_type_enum *)FIOBJ2PTR(o))[0];
|
|
}
|
|
|
|
/**
|
|
* This is faster than getting the type, since the switch statement is
|
|
* optimized away (it's calculated during compile time).
|
|
*/
|
|
FIO_INLINE size_t fiobj_type_is(FIOBJ o, fiobj_type_enum type) {
|
|
switch (type) {
|
|
case FIOBJ_T_NUMBER:
|
|
return (o & FIOBJECT_NUMBER_FLAG) ||
|
|
((fiobj_type_enum *)o)[0] == FIOBJ_T_NUMBER;
|
|
case FIOBJ_T_NULL:
|
|
return !o || o == fiobj_null();
|
|
case FIOBJ_T_TRUE:
|
|
return o == fiobj_true();
|
|
case FIOBJ_T_FALSE:
|
|
return o == fiobj_false();
|
|
case FIOBJ_T_STRING:
|
|
return (FIOBJECT_STRING_FLAG && (o & FIOBJECT_NUMBER_FLAG) == 0 &&
|
|
(o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_STRING_FLAG) ||
|
|
(FIOBJECT_STRING_FLAG == 0 && FIOBJ_IS_ALLOCATED(o) &&
|
|
((fiobj_type_enum *)FIOBJ2PTR(o))[0] == FIOBJ_T_STRING);
|
|
case FIOBJ_T_HASH:
|
|
if (FIOBJECT_HASH_FLAG) {
|
|
return ((o & FIOBJECT_NUMBER_FLAG) == 0 &&
|
|
(o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_HASH_FLAG);
|
|
}
|
|
/* fallthrough */
|
|
case FIOBJ_T_FLOAT:
|
|
case FIOBJ_T_ARRAY:
|
|
case FIOBJ_T_DATA:
|
|
case FIOBJ_T_UNKNOWN:
|
|
return FIOBJ_IS_ALLOCATED(o) &&
|
|
((fiobj_type_enum *)FIOBJ2PTR(o))[0] == type;
|
|
}
|
|
return FIOBJ_IS_ALLOCATED(o) && ((fiobj_type_enum *)FIOBJ2PTR(o))[0] == type;
|
|
}
|
|
|
|
/* *****************************************************************************
|
|
Object Header
|
|
***************************************************************************** */
|
|
|
|
typedef struct {
|
|
/* a String allowing logging type data. */
|
|
const char *class_name;
|
|
/* deallocate root object's memory, perform task for each nested object. */
|
|
void (*const dealloc)(FIOBJ, void (*task)(FIOBJ, void *), void *);
|
|
/* return the number of normal nested object */
|
|
uintptr_t (*const count)(const FIOBJ);
|
|
/* tests the object for truthfulness. */
|
|
size_t (*const is_true)(const FIOBJ);
|
|
/* tests if two objects are equal. */
|
|
size_t (*const is_eq)(const FIOBJ, const FIOBJ);
|
|
/* iterates through the normal nested objects (ignore deep nesting) */
|
|
size_t (*const each)(FIOBJ, size_t start_at, int (*task)(FIOBJ, void *),
|
|
void *);
|
|
/* object value as String */
|
|
fio_str_info_s (*const to_str)(const FIOBJ);
|
|
/* object value as Integer */
|
|
intptr_t (*const to_i)(const FIOBJ);
|
|
/* object value as Float */
|
|
double (*const to_f)(const FIOBJ);
|
|
} fiobj_object_vtable_s;
|
|
|
|
typedef struct {
|
|
/* must be first */
|
|
fiobj_type_enum type;
|
|
/* reference counter */
|
|
uint32_t ref;
|
|
} fiobj_object_header_s;
|
|
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_NUMBER;
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_FLOAT;
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_STRING;
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_ARRAY;
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_HASH;
|
|
extern const fiobj_object_vtable_s FIOBJECT_VTABLE_DATA;
|
|
|
|
#define FIOBJECT2VTBL(o) fiobj_type_vtable(o)
|
|
#define FIOBJECT2HEAD(o) (((fiobj_object_header_s *)FIOBJ2PTR((o))))
|
|
|
|
FIO_INLINE const fiobj_object_vtable_s *fiobj_type_vtable(FIOBJ o) {
|
|
switch (FIOBJ_TYPE(o)) {
|
|
case FIOBJ_T_NUMBER:
|
|
return &FIOBJECT_VTABLE_NUMBER;
|
|
case FIOBJ_T_FLOAT:
|
|
return &FIOBJECT_VTABLE_FLOAT;
|
|
case FIOBJ_T_STRING:
|
|
return &FIOBJECT_VTABLE_STRING;
|
|
case FIOBJ_T_ARRAY:
|
|
return &FIOBJECT_VTABLE_ARRAY;
|
|
case FIOBJ_T_HASH:
|
|
return &FIOBJECT_VTABLE_HASH;
|
|
case FIOBJ_T_DATA:
|
|
return &FIOBJECT_VTABLE_DATA;
|
|
case FIOBJ_T_NULL:
|
|
case FIOBJ_T_TRUE:
|
|
case FIOBJ_T_FALSE:
|
|
case FIOBJ_T_UNKNOWN:
|
|
return NULL;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* *****************************************************************************
|
|
Atomic reference counting
|
|
***************************************************************************** */
|
|
|
|
/* C11 Atomics are defined? */
|
|
#if defined(__ATOMIC_RELAXED)
|
|
/** An atomic addition operation */
|
|
#define fiobj_ref_inc(o) \
|
|
__atomic_add_fetch(&FIOBJECT2HEAD(o)->ref, 1, __ATOMIC_SEQ_CST)
|
|
/** An atomic subtraction operation */
|
|
#define fiobj_ref_dec(o) \
|
|
__atomic_sub_fetch(&FIOBJECT2HEAD(o)->ref, 1, __ATOMIC_SEQ_CST)
|
|
|
|
/* Select the correct compiler builtin method. */
|
|
#elif defined(__has_builtin) && !FIO_GNUC_BYPASS
|
|
|
|
#if __has_builtin(__sync_fetch_and_or)
|
|
/** An atomic addition operation */
|
|
#define fiobj_ref_inc(o) __sync_add_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
|
|
/** An atomic subtraction operation */
|
|
#define fiobj_ref_dec(o) __sync_sub_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
|
|
|
|
#else
|
|
#error missing required atomic options.
|
|
#endif /* defined(__has_builtin) */
|
|
|
|
#elif __GNUC__ > 3
|
|
/** An atomic addition operation */
|
|
#define fiobj_ref_inc(o) __sync_add_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
|
|
/** An atomic subtraction operation */
|
|
#define fiobj_ref_dec(o) __sync_sub_and_fetch(&FIOBJECT2HEAD(o)->ref, 1)
|
|
|
|
#else
|
|
#error missing required atomic options.
|
|
#endif
|
|
|
|
#define OBJREF_ADD(o) fiobj_ref_inc(o)
|
|
#define OBJREF_REM(o) fiobj_ref_dec(o)
|
|
|
|
/* *****************************************************************************
|
|
Inlined Functions
|
|
***************************************************************************** */
|
|
|
|
/** Returns a C string naming the objects dynamic type. */
|
|
FIO_INLINE const char *fiobj_type_name(const FIOBJ o) {
|
|
if (o & FIOBJECT_NUMBER_FLAG)
|
|
return "Number";
|
|
if (FIOBJ_IS_ALLOCATED(o))
|
|
return FIOBJECT2VTBL(o)->class_name;
|
|
if (!o)
|
|
return "NULL";
|
|
return "Primitive";
|
|
}
|
|
|
|
/** used internally to free objects with nested objects. */
|
|
void fiobj_free_complex_object(FIOBJ o);
|
|
|
|
/**
|
|
* Copy by reference(!) - increases an object's (and any nested object's)
|
|
* reference count.
|
|
*
|
|
* Always returns the value passed along.
|
|
*/
|
|
FIO_INLINE FIOBJ fiobj_dup(FIOBJ o) {
|
|
if (FIOBJ_IS_ALLOCATED(o))
|
|
OBJREF_ADD(o);
|
|
return o;
|
|
}
|
|
|
|
/**
|
|
* Decreases an object's reference count, releasing memory and
|
|
* resources.
|
|
*
|
|
* This function affects nested objects, meaning that when an Array or
|
|
* a Hash object is passed along, it's children (nested objects) are
|
|
* also freed.
|
|
*
|
|
* Returns the number of existing references or zero if memory was released.
|
|
*/
|
|
FIO_INLINE void fiobj_free(FIOBJ o) {
|
|
if (!FIOBJ_IS_ALLOCATED(o))
|
|
return;
|
|
if (fiobj_ref_dec(o))
|
|
return;
|
|
if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
|
|
fiobj_free_complex_object(o);
|
|
else
|
|
FIOBJECT2VTBL(o)->dealloc(o, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* Tests if an object evaluates as TRUE.
|
|
*
|
|
* This is object type specific. For example, empty strings might evaluate as
|
|
* FALSE, even though they aren't a boolean type.
|
|
*/
|
|
FIO_INLINE int fiobj_is_true(const FIOBJ o) {
|
|
if (o & FIOBJECT_NUMBER_FLAG)
|
|
return ((uintptr_t)o >> 1) != 0;
|
|
if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
|
|
return o == FIOBJ_T_TRUE;
|
|
return (int)(FIOBJECT2VTBL(o)->is_true(o));
|
|
}
|
|
|
|
/**
|
|
* Returns an object's numerical value.
|
|
*
|
|
* If a String or Symbol are passed to the function, they will be
|
|
* parsed assuming base 10 numerical data.
|
|
*
|
|
* Hashes and Arrays return their object count.
|
|
*
|
|
* IO and File objects return their underlying file descriptor.
|
|
*
|
|
* A type error results in 0.
|
|
*/
|
|
FIO_INLINE intptr_t fiobj_obj2num(const FIOBJ o) {
|
|
if (o & FIOBJECT_NUMBER_FLAG) {
|
|
const uintptr_t sign =
|
|
(o & FIOBJ_NUMBER_SIGN_BIT)
|
|
? (FIOBJ_NUMBER_SIGN_BIT | FIOBJ_NUMBER_SIGN_EXCLUDE_BIT)
|
|
: 0;
|
|
return (intptr_t)(((o & FIOBJ_NUMBER_SIGN_MASK) >> 1) | sign);
|
|
}
|
|
if (!o || !FIOBJ_IS_ALLOCATED(o))
|
|
return o == FIOBJ_T_TRUE;
|
|
return FIOBJECT2VTBL(o)->to_i(o);
|
|
}
|
|
|
|
/** Converts a number to a temporary, thread safe, C string object */
|
|
fio_str_info_s fio_ltocstr(long);
|
|
|
|
/** Converts a float to a temporary, thread safe, C string object */
|
|
fio_str_info_s fio_ftocstr(double);
|
|
|
|
/**
|
|
* Returns a C String (NUL terminated) using the `fio_str_info_s` data type.
|
|
*
|
|
* The Sting in binary safe and might contain NUL bytes in the middle as well as
|
|
* a terminating NUL.
|
|
*
|
|
* If a a Number or a Float are passed to the function, they
|
|
* will be parsed as a *temporary*, thread-safe, String.
|
|
*
|
|
* Numbers will be represented in base 10 numerical data.
|
|
*
|
|
* A type error results in NULL (i.e. object isn't a String).
|
|
*/
|
|
FIO_INLINE fio_str_info_s fiobj_obj2cstr(const FIOBJ o) {
|
|
if (!o) {
|
|
fio_str_info_s ret = {0, 4, (char *)"null"};
|
|
return ret;
|
|
}
|
|
if (o & FIOBJECT_NUMBER_FLAG)
|
|
return fio_ltocstr(((intptr_t)o) >> 1);
|
|
if ((o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG) {
|
|
switch ((fiobj_type_enum)o) {
|
|
case FIOBJ_T_NULL: {
|
|
fio_str_info_s ret = {0, 4, (char *)"null"};
|
|
return ret;
|
|
}
|
|
case FIOBJ_T_FALSE: {
|
|
fio_str_info_s ret = {0, 5, (char *)"false"};
|
|
return ret;
|
|
}
|
|
case FIOBJ_T_TRUE: {
|
|
fio_str_info_s ret = {0, 4, (char *)"true"};
|
|
return ret;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return FIOBJECT2VTBL(o)->to_str(o);
|
|
}
|
|
|
|
/* referenced here */
|
|
uint64_t fiobj_str_hash(FIOBJ o);
|
|
/**
|
|
* Calculates an Objects's SipHash value for possible use as a HashMap key.
|
|
*
|
|
* The Object MUST answer to the fiobj_obj2cstr, or the result is unusable. In
|
|
* other words, Hash Objects and Arrays can NOT be used for Hash keys.
|
|
*/
|
|
FIO_INLINE uint64_t fiobj_obj2hash(const FIOBJ o) {
|
|
if (FIOBJ_TYPE_IS(o, FIOBJ_T_STRING))
|
|
return fiobj_str_hash(o);
|
|
if (!FIOBJ_IS_ALLOCATED(o))
|
|
return (uint64_t)o;
|
|
fio_str_info_s s = fiobj_obj2cstr(o);
|
|
return FIO_HASH_FN(s.data, s.len, &fiobj_each2, &fiobj_free_complex_object);
|
|
}
|
|
|
|
FIO_INLINE uint64_t fiobj_hash_string(const void *data, size_t len) {
|
|
return FIO_HASH_FN(data, len, &fiobj_each2, &fiobj_free_complex_object);
|
|
}
|
|
|
|
/**
|
|
* Returns a Float's value.
|
|
*
|
|
* If a String or Symbol are passed to the function, they will be
|
|
* parsed assuming base 10 numerical data.
|
|
*
|
|
* Hashes and Arrays return their object count.
|
|
*
|
|
* IO and File objects return their underlying file descriptor.
|
|
*
|
|
* A type error results in 0.
|
|
*/
|
|
FIO_INLINE double fiobj_obj2float(const FIOBJ o) {
|
|
if (o & FIOBJECT_NUMBER_FLAG)
|
|
return (double)(fiobj_obj2num(o));
|
|
if (!o || (o & FIOBJECT_PRIMITIVE_FLAG) == FIOBJECT_PRIMITIVE_FLAG)
|
|
return (double)(o == FIOBJ_T_TRUE);
|
|
return FIOBJECT2VTBL(o)->to_f(o);
|
|
}
|
|
|
|
/** used internally for complext nested tests (Array / Hash types) */
|
|
int fiobj_iseq____internal_complex__(FIOBJ o, FIOBJ o2);
|
|
/**
|
|
* Deeply compare two objects. No hashing or recursive function calls are
|
|
* involved.
|
|
*
|
|
* Uses a similar algorithm to `fiobj_each2`, except adjusted to two objects.
|
|
*
|
|
* Hash order will be tested when comapring Hashes.
|
|
*
|
|
* KNOWN ISSUES:
|
|
*
|
|
* * Temporarily broken for collections (Arrays / Hashes).
|
|
*
|
|
* * Hash order will be tested as well as the Hash content, which means that
|
|
* equal Hashes might be considered unequal if their order doesn't match.
|
|
*
|
|
* Returns 1 if true and 0 if false.
|
|
*/
|
|
FIO_INLINE int fiobj_iseq(const FIOBJ o, const FIOBJ o2) {
|
|
if (o == o2)
|
|
return 1;
|
|
if (!o || !o2)
|
|
return 0; /* they should have compared equal before. */
|
|
if (!FIOBJ_IS_ALLOCATED(o) || !FIOBJ_IS_ALLOCATED(o2))
|
|
return 0; /* they should have compared equal before. */
|
|
if (FIOBJECT2HEAD(o)->type != FIOBJECT2HEAD(o2)->type)
|
|
return 0; /* non-type equality is a barriar to equality. */
|
|
if (!FIOBJECT2VTBL(o)->is_eq(o, o2))
|
|
return 0;
|
|
if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
|
|
return fiobj_iseq____internal_complex__((FIOBJ)o, (FIOBJ)o2);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Single layer iteration using a callback for each nested fio object.
|
|
*
|
|
* Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
|
|
* processed. The container itself (the Array or the Hash) is **not** processed
|
|
* (unlike `fiobj_each2`).
|
|
*
|
|
* The callback task function must accept an object and an opaque user pointer.
|
|
*
|
|
* Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
|
|
* references for both the key and the object. Keys shouldn't be altered once
|
|
* placed as a key (or the Hash will break). Collections (Arrays / Hashes) can't
|
|
* be used as keeys.
|
|
*
|
|
* If the callback returns -1, the loop is broken. Any other value is ignored.
|
|
*
|
|
* Returns the "stop" position, i.e., the number of items processed + the
|
|
* starting point.
|
|
*/
|
|
FIO_INLINE size_t fiobj_each1(FIOBJ o, size_t start_at,
|
|
int (*task)(FIOBJ obj, void *arg), void *arg) {
|
|
if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each)
|
|
return FIOBJECT2VTBL(o)->each(o, start_at, task, arg);
|
|
return 0;
|
|
}
|
|
|
|
#if DEBUG
|
|
void fiobj_test_core(void);
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
} /* closing brace for extern "C" */
|
|
#endif
|
|
#endif
|