Files

351 lines
12 KiB
C

/*
Copyright (c) 2006-2020 Chung, Hyung-Hwan. 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 AUTHOR "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 AUTHOR 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.
*/
#ifndef _HAWK_VAL_PRV_H_
#define _HAWK_VAL_PRV_H_
#define HAWK_VAL_CHUNK_SIZE 100
/* this has been commented out and isn't necessary as the implemenation is
* context(rtx)-centric. I'll leave it here for experiment for those who like
* to turn it on.
#define HAWK_USE_ATOMIC_REFCNT */
typedef struct hawk_val_chunk_t hawk_val_chunk_t;
typedef struct hawk_val_ichunk_t hawk_val_ichunk_t;
typedef struct hawk_val_rchunk_t hawk_val_rchunk_t;
struct hawk_val_chunk_t
{
hawk_val_chunk_t* next;
};
struct hawk_val_ichunk_t
{
hawk_val_chunk_t* next;
/* make sure that it has the same fields as
hawk_val_chunk_t up to this point */
hawk_val_int_t slot[HAWK_VAL_CHUNK_SIZE];
};
struct hawk_val_rchunk_t
{
hawk_val_chunk_t* next;
/* make sure that it has the same fields as
hawk_val_chunk_t up to this point */
hawk_val_flt_t slot[HAWK_VAL_CHUNK_SIZE];
};
/*
* if shared objects link a static library, statically defined objects
* in the static library will be instatiated in the multiple shared objects.
*
* so equality check with a value pointer doesn't work
* if the code crosses the library boundaries. instead, i decided to
* add a field to indicate if a value is static.
*
#define HAWK_IS_STATICVAL(val) ((val) == HAWK_NULL || (val) == hawk_val_nil || (val) == hawk_val_zls || (val) == hawk_val_zlbs)
*/
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE_ALWAYS int HAWK_IS_STATICVAL (const hawk_val_t* val) { return val->v_static; }
#else
#define HAWK_IS_STATICVAL(val) (((hawk_val_t*)(val))->v_static)
#endif
/* hawk_val_t pointer encoding assumes the pointer is an even number.
* i shift an integer within a certain range and set bit 0 to 1 to
* encode it in a pointer. (vtr = value pointer).
*
* is this a safe assumption? do i have to use memalign or write my own
* aligned malloc()? */
#define HAWK_VTR_NUM_TYPE_BITS_LO 2 /* last 2 bits */
#define HAWK_VTR_MASK_TYPE_BITS_LO 3 /* 11 - all 1's in the last 2 bits */
#define HAWK_VTR_NUM_TYPE_BITS_LOHI 4
#define HAWK_VTR_MASK_TYPE_BITS_LOHI 15 /* 1111 */
/* @nil, @true, @false are kind of fixed.
* as for the bool type, there are only two fixed instances @true and @false.
* encoding it in a pointer is unlikely to benefit much. only a type that
* can represents many different values are encoded in a pointer */
#define HAWK_VTR_TYPE_BITS_POINTER 0 /* 00 */
#define HAWK_VTR_TYPE_BITS_INT 1 /* 01 */
#define HAWK_VTR_TYPE_BITS_CHAR 2 /* 10 */
#define HAWK_VTR_TYPE_BITS_QEXT 3 /* 11 extended */
#define HAWK_VTR_TYPE_BITS_BCHR 3 /* 0011 */
#define HAWK_VTR_TYPE_BITS_RESERVED0 7 /* 0111 */
#define HAWK_VTR_TYPE_BITS_RESERVED1 11 /* 1011 */
#define HAWK_VTR_TYPE_BITS_RESERVED2 15 /* 1111 */
#define HAWK_VTR_SIGN_BIT ((hawk_uintptr_t)1 << (HAWK_SIZEOF_UINTPTR_T * 8 - 1))
/* shrink the bit range by 1 more bit to ease sign-bit handling.
* i want abs(max) == abs(min).
* i don't want abs(max) + 1 == abs(min). e.g min: -32768, max: 32767
*/
#define HAWK_INT_MAX ((hawk_int_t)((~(hawk_uintptr_t)0) >> (HAWK_VTR_NUM_TYPE_BITS_LO + 1)))
#define HAWK_INT_MIN (-HAWK_INT_MAX)
#define HAWK_IN_INT_RANGE(i) ((i) >= HAWK_INT_MIN && (i) <= HAWK_INT_MAX)
#define HAWK_VTR_TYPE_BITS_LO(p) (((hawk_uintptr_t)(p)) & HAWK_VTR_MASK_TYPE_BITS_LO)
#define HAWK_VTR_TYPE_BITS_LOHI(p) (((hawk_uintptr_t)(p)) & HAWK_VTR_MASK_TYPE_BITS_LOHI)
//#define HAWK_VTR_TYPE_BITS(p) (((hawk_uintptr_t)(p)) & HAWK_VTR_MASK_TYPE_BITS)
#define HAWK_VTR_TYPE_BITS(p) (HAWK_VTR_TYPE_BITS_LO(p) == HAWK_VTR_TYPE_BITS_QEXT? HAWK_VTR_TYPE_BITS_LOHI(p): HAWK_VTR_TYPE_BITS_LO(p))
#define HAWK_VTR_IS_POINTER(p) (HAWK_VTR_TYPE_BITS(p) == HAWK_VTR_TYPE_BITS_POINTER)
#define HAWK_VTR_IS_INT(p) (HAWK_VTR_TYPE_BITS(p) == HAWK_VTR_TYPE_BITS_INT)
#define HAWK_VTR_IS_CHAR(p) (HAWK_VTR_TYPE_BITS(p) == HAWK_VTR_TYPE_BITS_CHAR)
#define HAWK_VTR_IS_BCHR(p) (HAWK_VTR_TYPE_BITS(p) == HAWK_VTR_TYPE_BITS_BCHR)
#define HAWK_INT_TO_VTR_POSITIVE(i) \
(((hawk_uintptr_t)(i) << HAWK_VTR_NUM_TYPE_BITS_LO) | HAWK_VTR_TYPE_BITS_INT)
#define HAWK_INT_TO_VTR_NEGATIVE(i) \
((((hawk_uintptr_t)-(i)) << HAWK_VTR_NUM_TYPE_BITS_LO) | HAWK_VTR_TYPE_BITS_INT | HAWK_VTR_SIGN_BIT)
#define HAWK_INT_TO_VTR(i) \
((hawk_val_t*)(((i) < 0)? HAWK_INT_TO_VTR_NEGATIVE(i): HAWK_INT_TO_VTR_POSITIVE(i)))
#define HAWK_CHAR_TO_VTR(i) ((hawk_val_t*)(((hawk_uintptr_t)(i) << HAWK_VTR_NUM_TYPE_BITS_LO) | HAWK_VTR_TYPE_BITS_CHAR))
#define HAWK_BCHR_TO_VTR(i) ((hawk_val_t*)(((hawk_uintptr_t)(i) << HAWK_VTR_NUM_TYPE_BITS_LOHI) | HAWK_VTR_TYPE_BITS_BCHR))
#define HAWK_VTR_ZERO ((hawk_val_t*)HAWK_INT_TO_VTR_POSITIVE(0))
#define HAWK_VTR_ONE ((hawk_val_t*)HAWK_INT_TO_VTR_POSITIVE(1))
#define HAWK_VTR_NEGONE ((hawk_val_t*)HAWK_INT_TO_VTR_NEGATIVE(-1))
/* sizeof(hawk_intptr_t) may not be the same as sizeof(hawk_int_t).
* so step-by-step type conversions are needed.
* e.g) pointer to uintptr_t, uintptr_t to intptr_t, intptr_t to hawk_int_t */
#define HAWK_VTR_TO_INT_POSITIVE(p) \
((hawk_intptr_t)((hawk_uintptr_t)(p) >> HAWK_VTR_NUM_TYPE_BITS_LO))
#define HAWK_VTR_TO_INT_NEGATIVE(p) \
(-(hawk_intptr_t)(((hawk_uintptr_t)(p) & ~HAWK_VTR_SIGN_BIT) >> HAWK_VTR_NUM_TYPE_BITS_LO))
#define HAWK_VTR_TO_INT(p) \
(((hawk_uintptr_t)(p) & HAWK_VTR_SIGN_BIT)? HAWK_VTR_TO_INT_NEGATIVE(p): HAWK_VTR_TO_INT_POSITIVE(p))
#define HAWK_VTR_TO_CHAR(p) ((hawk_ooch_t)(hawk_oochu_t)((hawk_uintptr_t)(p) >> HAWK_VTR_NUM_TYPE_BITS_LO))
#define HAWK_VTR_TO_BCHR(p) ((hawk_bch_t)(hawk_bchu_t)((hawk_uintptr_t)(p) >> HAWK_VTR_NUM_TYPE_BITS_LOHI))
#define HAWK_GET_VAL_TYPE(p) (HAWK_VTR_IS_INT(p)? HAWK_VAL_INT: \
HAWK_VTR_IS_CHAR(p)? HAWK_VAL_CHAR: \
HAWK_VTR_IS_BCHR(p)? HAWK_VAL_BCHR: (p)->v_type)
#define HAWK_RTX_GETVALTYPE(rtx, p) HAWK_GET_VAL_TYPE(p)
#define HAWK_RTX_GETINTFROMVAL(rtx, p) ((HAWK_VTR_IS_INT(p)? (hawk_int_t)HAWK_VTR_TO_INT(p): ((hawk_val_int_t*)(p))->i_val))
#define HAWK_RTX_GETCHARFROMVAL(rtx, p) (HAWK_VTR_TO_CHAR(p))
#define HAWK_RTX_GETBCHRFROMVAL(rtx, p) (HAWK_VTR_TO_BCHR(p))
#define HAWK_VAL_ZERO HAWK_VTR_ZERO
#define HAWK_VAL_ONE HAWK_VTR_ONE
#define HAWK_VAL_NEGONE HAWK_VTR_NEGONE
#define HAWK_RTX_FREEVAL_CACHE (1 << 0)
#define HAWK_RTX_FREEVAL_GC_PRESERVE (1 << 1)
#if defined(__cplusplus)
extern "C" {
#endif
/* represents a nil value */
extern hawk_val_t* hawk_val_nil;
/* represents a true boolean value */
extern hawk_val_t* hawk_val_true;
/* represents a false boolean value */
extern hawk_val_t* hawk_val_false;
/* represents an empty string */
extern hawk_val_t* hawk_val_zls;
/* represents an empty byte string */
extern hawk_val_t* hawk_val_zlbs;
hawk_val_t* hawk_rtx_makeintval_full (
hawk_rtx_t* rtx,
hawk_int_t v
);
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE_ALWAYS hawk_val_t* hawk_rtx_makeintval_inline (hawk_rtx_t* rtx, hawk_int_t v)
{
return HAWK_IN_INT_RANGE(v)? HAWK_INT_TO_VTR(v): hawk_rtx_makeintval_full(rtx, v);
}
#else
#define hawk_rtx_makeintval_inline(rtx, v) (HAWK_IN_INT_RANGE(v)? HAWK_INT_TO_VTR(v): hawk_rtx_makeintval_full(rtx, v))
#endif
void hawk_rtx_freeval (
hawk_rtx_t* rtx,
hawk_val_t* val,
int flags
);
void hawk_rtx_freevalchunk (
hawk_rtx_t* rtx,
hawk_val_chunk_t* chunk
);
#if defined(HAWK_HAVE_INLINE)
/* the nocheck versions are defined in case you already know the value
* is a pointer and not a static value */
static HAWK_INLINE_ALWAYS void hawk_rtx_refupval_nocheck_inline (hawk_rtx_t* rtx, hawk_val_t* val)
{
#if defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_ADD)
HAWK_ATOMIC_FETCH_ADD(&val->v_refs, 1, HAWK_ATOMIC_RELAXED);
#else
val->v_refs++;
#endif
}
/* regular refup with pointer check */
static HAWK_INLINE_ALWAYS void hawk_rtx_refupval_inline (hawk_rtx_t* rtx, hawk_val_t* val)
{
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val))
{
#if defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_ADD)
HAWK_ATOMIC_FETCH_ADD(&val->v_refs, 1, HAWK_ATOMIC_RELAXED);
#else
val->v_refs++;
#endif
}
}
#elif defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_ADD)
#define hawk_rtx_refupval_nocheck_inline(rtx, val) do { \
HAWK_ATOMIC_FETCH_ADD(&(val)->v_refs, 1, HAWK_ATOMIC_RELAXED); \
} while(0)
#define hawk_rtx_refupval_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) { \
HAWK_ATOMIC_FETCH_ADD(&(val)->v_refs, 1, HAWK_ATOMIC_RELAXED); \
} \
} while(0)
#else
#define hawk_rtx_refupval_nocheck_inline(rtx, val) do { (val)->v_refs++; } while(0)
#define hawk_rtx_refupval_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) (val)->v_refs++; \
} while(0)
#endif
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE_ALWAYS void hawk_rtx_refdownval_inline (hawk_rtx_t* rtx, hawk_val_t* val)
{
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val))
{
HAWK_ASSERT(val->v_refs > 0);
#if defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_SUB)
if (HAWK_ATOMIC_FETCH_SUB(&val->v_refs, 1, HAWK_ATOMIC_RELAXED) == 1)
hawk_rtx_freeval(rtx, val, HAWK_RTX_FREEVAL_CACHE);
#else
val->v_refs--;
if (val->v_refs <= 0) hawk_rtx_freeval(rtx, val, HAWK_RTX_FREEVAL_CACHE);
#endif
}
}
#elif defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_SUB)
#define hawk_rtx_refdownval_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) \
{ \
HAWK_ASSERT(((hawk_val_t*)(val))->v_refs > 0); \
if (HAWK_ATOMIC_FETCH_SUB(&((hawk_val_t*)(val))->v_refs, 1, HAWK_ATOMIC_RELAXED) == 1) \
hawk_rtx_freeval(rtx, val, HAWK_RTX_FREEVAL_CACHE); \
} \
} while(0)
#else
#define hawk_rtx_refdownval_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) \
{ \
HAWK_ASSERT(((hawk_val_t*)(val))->v_refs > 0); \
((hawk_val_t*)(val))->v_refs--; \
if (((hawk_val_t*)(val))->v_refs <= 0) hawk_rtx_freeval(rtx, val, HAWK_RTX_FREEVAL_CACHE); \
} \
} while(0)
#endif
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE_ALWAYS void hawk_rtx_refdownval_nofree_inline (hawk_rtx_t* rtx, hawk_val_t* val)
{
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val))
{
HAWK_ASSERT(val->v_refs > 0);
#if defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_SUB)
HAWK_ATOMIC_FETCH_SUB(&val->v_refs, 1, HAWK_ATOMIC_RELAXED);
#else
val->v_refs--;
#endif
}
}
#elif defined(HAWK_USE_ATOMIC_REFCNT) && defined(HAWK_ATOMIC_FETCH_SUB)
#define hawk_rtx_refdownval_nofree_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) \
{ \
HAWK_ASSERT(((hawk_val_t*)(val))->v_refs > 0); \
HAWK_ATOMIC_FETCH_SUB(&((hawk_val_t*)(val))->v_refs, 1, HAWK_ATOMIC_RELAXED);
} \
} while(0)
#else
#define hawk_rtx_refdownval_nofree_inline(rtx, val) do { \
if (HAWK_VTR_IS_POINTER(val) && !HAWK_IS_STATICVAL(val)) \
{ \
HAWK_ASSERT(((hawk_val_t*)(val))->v_refs > 0); \
((hawk_val_t*)(val))->v_refs--; \
} \
} while(0)
#endif
#if defined(HAWK_HAVE_INLINE)
static HAWK_INLINE int hawk_rtx_valtoint_inline (hawk_rtx_t* rtx, const hawk_val_t* v, hawk_int_t* l)
{
int n;
hawk_flt_t r;
n = hawk_rtx_valtonum(rtx, v, l, &r);
if (n == 1)
{
*l = (hawk_int_t)r;
n = 0;
}
return n;
}
#else
/* i can't easily inlinde this function without using compiler extentions or changing the return value to a parameter */
#define hawk_rtx_valtoint_inline(rtx, v, l) hawk_rtx_valtoint(rtx, v, l)
#endif
#if defined(__cplusplus)
}
#endif
#endif