1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Definitions for Ruby's special constants.
//!
//! Makes it easier to reference important Ruby constants, without havign to dig
//! around in bindgen's output.

#![allow(non_upper_case_globals)]
#![allow(non_snake_case)]

use std::convert::TryInto;

use crate::{
    ruby_value_type::{self, RUBY_T_MASK},
    Qfalse, Qnil, Qtrue, Qundef, RBasic, FIXNUM_P, FLONUM_P, SPECIAL_CONST_P, STATIC_SYM_P, VALUE,
};

pub use ruby_value_type::*;

/// Queries the type of the object.
///
/// @param[in]  obj  Object in question.
/// @pre        `obj` must not be a special constant.
/// @return     The type of `obj`.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
#[inline(always)]
pub unsafe fn RB_BUILTIN_TYPE(obj: VALUE) -> ruby_value_type {
    debug_assert!(!SPECIAL_CONST_P(obj));

    let rbasic = obj as *const RBasic;

    let ret = (*rbasic).flags & RUBY_T_MASK as VALUE;
    let ret: u32 = ret.try_into().unwrap();

    std::mem::transmute::<_, ruby_value_type>(ret)
}

/// Queries if the object is an instance of ::rb_cInteger.
///
/// @param[in]  obj    Object in question.
/// @retval     true   It is.
/// @retval     false  It isn't.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
#[inline(always)]
pub unsafe fn RB_INTEGER_TYPE_P(obj: VALUE) -> bool {
    if FIXNUM_P(obj) {
        true
    } else if SPECIAL_CONST_P(obj) {
        false
    } else {
        RB_BUILTIN_TYPE(obj) == RUBY_T_BIGNUM
    }
}

/// Queries if the object is a dynamic symbol.
///
/// @param[in]  obj    Object in question.
/// @retval     true   It is.
/// @retval     false  It isn't.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
#[inline(always)]
pub unsafe fn RB_DYNAMIC_SYM_P(obj: VALUE) -> bool {
    if SPECIAL_CONST_P(obj) {
        false
    } else {
        RB_BUILTIN_TYPE(obj) == RUBY_T_SYMBOL
    }
}

/// Queries if the object is an instance of ::rb_cSymbol.
///
/// @param[in]  obj    Object in question.
/// @retval     true   It is.
/// @retval     false  It isn't.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
#[inline(always)]
pub unsafe fn RB_SYMBOL_P(obj: VALUE) -> bool {
    STATIC_SYM_P(obj) || RB_DYNAMIC_SYM_P(obj)
}

/// Identical to RB_BUILTIN_TYPE(), except it can also accept special constants.
///
/// @param[in]  obj  Object in question.
/// @return     The type of `obj`.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
#[inline(always)]
pub unsafe fn RB_TYPE_P<T: Into<VALUE>>(value: T) -> ruby_value_type {
    let obj = value.into();

    if !SPECIAL_CONST_P(obj) {
        RB_BUILTIN_TYPE(obj)
    } else if obj == Qfalse as VALUE {
        RUBY_T_FALSE
    } else if obj == Qnil as VALUE {
        RUBY_T_NIL
    } else if obj == Qtrue as VALUE {
        RUBY_T_TRUE
    } else if obj == Qundef as VALUE {
        RUBY_T_UNDEF
    } else if FIXNUM_P(obj) {
        RUBY_T_FIXNUM
    } else if STATIC_SYM_P(obj) {
        RUBY_T_SYMBOL
    } else {
        debug_assert!(FLONUM_P(obj));
        RUBY_T_FLOAT
    }
}

/// Queries if the object is an instance of ::rb_cFloat.
///
/// @param[in]  obj    Object in question.
/// @retval     true   It is.
/// @retval     false  It isn't.
///
/// # Safety
/// This function is unsafe because it could dereference a raw pointer when
/// attemping to access the underlying [`RBasic`] struct.
pub unsafe fn RB_FLOAT_TYPE_P(obj: VALUE) -> bool {
    if FLONUM_P(obj) {
        true
    } else if SPECIAL_CONST_P(obj) {
        false
    } else {
        RB_BUILTIN_TYPE(obj) == RUBY_T_FLOAT
    }
}