Skip to main content

rb_sys/
macros.rs

1//! Implementation of Ruby macros.
2//!
3//! Since macros are rely on the C preprocessor, or defined as `inline` C
4//! functions, they are not available when linking libruby. In order to use the
5//! libruby macros from Rust, `rb-sys` implements them using the following
6//! strategies:
7//!
8//! 1. For stable versions of Ruby, the macros are implemented as Rust functions
9//! 2. For ruby-head, the macros are implemented as C functions that are linked
10//!    into the crate.
11
12#![allow(rustdoc::broken_intra_doc_links)]
13#![allow(non_upper_case_globals)]
14#![allow(non_snake_case)]
15
16use crate::rb_data_type_t;
17use crate::ruby_value_type;
18use crate::stable_api::get_default as api;
19use crate::StableApiDefinition;
20use crate::VALUE;
21use std::ffi::c_void;
22use std::os::raw::{c_char, c_long};
23
24/// Emulates Ruby's "if" statement.
25///
26/// - @param[in]  obj    An arbitrary ruby object.
27/// - @retval     false  `obj` is either ::RUBY_Qfalse or ::RUBY_Qnil.
28/// - @retval     true   Anything else.
29///
30/// ```
31/// use rb_sys::special_consts::*;
32///
33/// assert!(!TEST(Qfalse));
34/// assert!(!TEST(Qnil));
35/// assert!(TEST(Qtrue));
36/// ```
37#[inline(always)]
38pub fn TEST(obj: VALUE) -> bool {
39    api().rb_test(obj)
40}
41
42/// Checks if the given object is nil.
43///
44/// - @param[in]  obj    An arbitrary ruby object.
45/// - @retval     true   `obj` is ::RUBY_Qnil.
46/// - @retval     false  Anything else.
47///
48/// ### Example
49///
50/// ```
51/// use rb_sys::special_consts::*;
52///
53/// assert!(NIL_P(Qnil));
54/// assert!(!NIL_P(Qtrue));
55/// ```
56#[inline(always)]
57pub fn NIL_P(obj: VALUE) -> bool {
58    api().nil_p(obj)
59}
60
61/// Checks if the given object is a so-called Fixnum.
62///
63/// - @param[in]  obj    An arbitrary ruby object.
64/// - @retval     true   `obj` is a Fixnum.
65/// - @retval     false  Anything else.
66/// - @note       Fixnum was  a thing  in the  20th century, but  it is  rather an implementation detail today.
67#[inline(always)]
68pub fn FIXNUM_P(obj: VALUE) -> bool {
69    api().fixnum_p(obj)
70}
71
72/// Checks if the given object is a static symbol.
73///
74/// - @param[in]  obj    An arbitrary ruby object.
75/// - @retval     true   `obj` is a static symbol
76/// - @retval     false  Anything else.
77/// - @see        RB_DYNAMIC_SYM_P()
78/// - @see        RB_SYMBOL_P()
79/// - @note       These days  there are static  and dynamic symbols, just  like we once had Fixnum/Bignum back in the old days.
80#[inline(always)]
81pub fn STATIC_SYM_P(obj: VALUE) -> bool {
82    api().static_sym_p(obj)
83}
84
85/// Get the backend storage of a Ruby array.
86///
87/// ### Safety
88///
89/// This function is unsafe because it dereferences a raw pointer and returns
90/// raw pointers to Ruby memory. The caller must ensure that the pointer stays live
91/// for the duration of usage the the underlying array (by either GC marking or
92/// keeping the RArray on the stack).
93///
94/// - @param[in]  a  An object of ::RArray.
95/// - @return     Its backend storage.
96#[inline(always)]
97pub unsafe fn RARRAY_CONST_PTR(obj: VALUE) -> *const VALUE {
98    api().rarray_const_ptr(obj)
99}
100
101/// Get the length of a Ruby array.
102///
103/// ### Safety
104///
105/// This function is unsafe because it dereferences a raw pointer in order to
106/// access internal Ruby memory.
107///
108/// - @param[in]  a  An object of ::RArray.
109/// - @return     Its length.
110#[inline(always)]
111pub unsafe fn RARRAY_LEN(obj: VALUE) -> c_long {
112    api().rarray_len(obj)
113}
114
115/// Read array element at index (akin to `RARRAY_AREF`).
116///
117/// ### Safety
118///
119/// This function is unsafe because it dereferences a raw pointer in order to
120/// access internal Ruby memory.
121///
122/// - @param[in]  obj  An object of ::RArray.
123/// - @param[in]  idx  Index within the array (must be within bounds: 0..RARRAY_LEN(obj)).
124/// - @return     The element at the given index.
125#[inline(always)]
126pub unsafe fn RARRAY_AREF(obj: VALUE, idx: isize) -> VALUE {
127    api().rarray_aref(obj.into(), idx)
128}
129
130/// Write array element at index (akin to `RARRAY_ASET`).
131///
132/// This function includes the GC write barrier for correctness.
133///
134/// ### Safety
135///
136/// This function is unsafe because it dereferences a raw pointer in order to
137/// access internal Ruby memory.
138///
139/// - @param[in]  obj  An object of ::RArray.
140/// - @param[in]  idx  Index within the array (must be within bounds: 0..RARRAY_LEN(obj)).
141/// - @param[in]  val  The value to set at the given index.
142#[inline(always)]
143pub unsafe fn RARRAY_ASET(obj: VALUE, idx: isize, val: VALUE) {
144    api().rarray_aset(obj.into(), idx, val)
145}
146
147/// Get the length of a Ruby string.
148///
149/// ### Safety
150///
151/// This function is unsafe because it dereferences a raw pointer in order to
152/// access internal Ruby memory.
153///
154/// - @param[in]  a  An object of ::RString.
155/// - @return     Its length.
156#[inline(always)]
157pub unsafe fn RSTRING_LEN(obj: VALUE) -> c_long {
158    api().rstring_len(obj)
159}
160
161/// Get the backend storage of a Ruby string.
162///
163/// ### Safety
164///
165/// This function is unsafe because it dereferences a raw pointer and returns
166/// raw pointers to Ruby memory. The caller must ensure that the pointer stays live
167/// for the duration of usage the the underlying array (by either GC marking or
168/// keeping the RArray on the stack).
169///
170/// - @param[in]  a  An object of ::RString.
171/// - @return     Its backend storage
172#[inline(always)]
173pub unsafe fn RSTRING_PTR(obj: VALUE) -> *const c_char {
174    api().rstring_ptr(obj)
175}
176
177/// Checks if the given object is a so-called Flonum.
178///
179/// @param[in]  obj    An arbitrary ruby object.
180/// @retval     true   `obj` is a Flonum.
181/// @retval     false  Anything else.
182/// @see        RB_FLOAT_TYPE_P()
183/// @note       These days there are Flonums and non-Flonum floats, just like we
184///             once had Fixnum/Bignum back in the old days.
185#[inline(always)]
186pub fn FLONUM_P(obj: VALUE) -> bool {
187    api().flonum_p(obj)
188}
189
190/// Checks if  the given  object is  an immediate  i.e. an  object which  has no
191/// corresponding storage inside of the object space.
192///
193/// @param[in]  obj    An arbitrary ruby object.
194/// @retval     true   `obj` is a Flonum.
195/// @retval     false  Anything else.
196/// @see        RB_FLOAT_TYPE_P()
197/// @note       The concept of "immediate" is purely C specific.
198#[inline(always)]
199pub fn IMMEDIATE_P(obj: VALUE) -> bool {
200    api().immediate_p(obj)
201}
202
203/// Checks if the given object is of enum ::ruby_special_consts.
204///
205/// @param[in]  obj    An arbitrary ruby object.
206/// @retval     true   `obj` is a special constant.
207/// @retval     false  Anything else.
208///
209/// ### Example
210///
211/// ```
212/// use rb_sys::special_consts::*;
213///
214/// assert!(SPECIAL_CONST_P(Qnil));
215/// assert!(SPECIAL_CONST_P(Qtrue));
216/// assert!(SPECIAL_CONST_P(Qfalse));
217/// ```
218#[inline(always)]
219pub fn SPECIAL_CONST_P(obj: VALUE) -> bool {
220    api().special_const_p(obj)
221}
222
223/// Queries the type of the object.
224///
225/// @param[in]  obj  Object in question.
226/// @pre        `obj` must not be a special constant.
227/// @return     The type of `obj`.
228///
229/// # Safety
230/// This function is unsafe because it could dereference a raw pointer when
231/// attemping to access the underlying [`RBasic`] struct.
232#[inline(always)]
233pub unsafe fn RB_BUILTIN_TYPE(obj: VALUE) -> ruby_value_type {
234    api().builtin_type(obj)
235}
236
237/// Queries if the object is an instance of ::rb_cInteger.
238///
239/// @param[in]  obj    Object in question.
240/// @retval     true   It is.
241/// @retval     false  It isn't.
242///
243/// # Safety
244/// This function is unsafe because it could dereference a raw pointer when
245/// attemping to access the underlying [`RBasic`] struct.
246#[inline(always)]
247pub unsafe fn RB_INTEGER_TYPE_P(obj: VALUE) -> bool {
248    api().integer_type_p(obj)
249}
250
251/// Queries if the object is a dynamic symbol.
252///
253/// @param[in]  obj    Object in question.
254/// @retval     true   It is.
255/// @retval     false  It isn't.
256///
257/// # Safety
258/// This function is unsafe because it could dereference a raw pointer when
259/// attemping to access the underlying [`RBasic`] struct.
260#[inline(always)]
261pub unsafe fn RB_DYNAMIC_SYM_P(obj: VALUE) -> bool {
262    api().dynamic_sym_p(obj)
263}
264
265/// Queries if the object is an instance of ::rb_cSymbol.
266///
267/// @param[in]  obj    Object in question.
268/// @retval     true   It is.
269/// @retval     false  It isn't.
270///
271/// # Safety
272/// This function is unsafe because it could dereference a raw pointer when
273/// attemping to access the underlying [`RBasic`] struct.
274#[inline(always)]
275pub unsafe fn RB_SYMBOL_P(obj: VALUE) -> bool {
276    api().symbol_p(obj)
277}
278
279/// Identical to RB_BUILTIN_TYPE(), except it can also accept special constants.
280///
281/// @param[in]  obj  Object in question.
282/// @return     The type of `obj`.
283///
284/// # Safety
285/// This function is unsafe because it could dereference a raw pointer when
286/// attemping to access the underlying [`RBasic`] struct.
287#[inline(always)]
288pub unsafe fn RB_TYPE(value: VALUE) -> ruby_value_type {
289    api().rb_type(value)
290}
291
292/// Queries if the given object is of given type.
293///
294/// @param[in]  obj    An object.
295/// @param[in]  t      A type.
296/// @retval     true   `obj` is of type `t`.
297/// @retval     false  Otherwise.
298///
299/// # Safety
300/// This function is unsafe because it could dereference a raw pointer when
301/// attemping to access the underlying [`RBasic`] struct.
302#[inline(always)]
303#[cfg(ruby_engine = "mri")] // truffleruby provides its own implementation
304pub unsafe fn RB_TYPE_P(obj: VALUE, ty: ruby_value_type) -> bool {
305    api().type_p(obj, ty)
306}
307
308/// Queries if the object is an instance of ::rb_cFloat.
309///
310/// @param[in]  obj    Object in question.
311/// @retval     true   It is.
312/// @retval     false  It isn't.
313///
314/// # Safety
315/// This function is unsafe because it could dereference a raw pointer when
316/// attemping to access the underlying [`RBasic`] struct.
317#[inline(always)]
318pub unsafe fn RB_FLOAT_TYPE_P(obj: VALUE) -> bool {
319    api().float_type_p(obj)
320}
321
322/// Checks if the given object is an RTypedData.
323///
324/// @param[in]  obj    Object in question.
325/// @retval     true   It is an RTypedData.
326/// @retval     false  It isn't an RTypedData.
327///
328/// # Safety
329/// This function is unsafe because it could dereference a raw pointer when
330/// accessing the underlying data structure.
331#[inline(always)]
332pub unsafe fn RTYPEDDATA_P(obj: VALUE) -> bool {
333    api().rtypeddata_p(obj)
334}
335
336/// Gets the data type information from an RTypedData object.
337///
338/// @param[in]  obj    An RTypedData object.
339/// @return     Pointer to the rb_data_type_t structure for this object.
340///
341/// # Safety
342/// This function is unsafe because it dereferences a raw pointer to get
343/// access to the underlying data type. The caller must ensure the object
344/// is a valid RTypedData.
345#[inline(always)]
346pub unsafe fn RTYPEDDATA_TYPE(obj: VALUE) -> *const rb_data_type_t {
347    api().rtypeddata_type(obj)
348}
349
350/// Gets the data pointer from an RTypedData object.
351///
352/// @param[in]  obj    An RTypedData object.
353/// @return     Pointer to the wrapped C struct.
354///
355/// # Safety
356/// This function is unsafe because it dereferences a raw pointer to get
357/// access to the underlying data. The caller must ensure the object
358/// is a valid RTypedData.
359#[inline(always)]
360pub unsafe fn RTYPEDDATA_GET_DATA(obj: VALUE) -> *mut c_void {
361    api().rtypeddata_get_data(obj)
362}
363
364/// Checks if the bignum is positive.
365///
366/// @param[in]  b      An object of RBignum.
367/// @retval     false  `b` is less than zero.
368/// @retval     true   Otherwise.
369///
370/// # Safety
371/// This function is unsafe because it could dereference a raw pointer when
372/// accessing the underlying bignum structure.
373#[inline(always)]
374pub unsafe fn RBIGNUM_POSITIVE_P(b: VALUE) -> bool {
375    api().bignum_positive_p(b)
376}
377
378/// Checks if the bignum is negative.
379///
380/// @param[in]  b      An object of RBignum.
381/// @retval     true   `b` is less than zero.
382/// @retval     false  Otherwise.
383///
384/// # Safety
385/// This function is unsafe because it could dereference a raw pointer when
386/// accessing the underlying bignum structure.
387#[inline(always)]
388pub unsafe fn RBIGNUM_NEGATIVE_P(b: VALUE) -> bool {
389    api().bignum_negative_p(b)
390}
391
392/// Convert ID to Symbol (akin to `ID2SYM` or `RB_ID2SYM`).
393///
394/// Converts an internal ID to its corresponding Symbol VALUE.
395/// This is a safe operation - just bit manipulation for static symbols.
396///
397/// @param[in]  id     An ID value.
398/// @return     The Symbol VALUE corresponding to the ID.
399#[inline(always)]
400pub fn ID2SYM(id: crate::ID) -> VALUE {
401    api().id2sym(id)
402}
403
404/// Alias for ID2SYM for compatibility with Ruby naming conventions.
405#[inline(always)]
406pub fn RB_ID2SYM(id: crate::ID) -> VALUE {
407    api().id2sym(id)
408}
409
410/// Convert Symbol to ID (akin to `SYM2ID` or `RB_SYM2ID`).
411///
412/// Converts a Symbol VALUE to its internal ID representation.
413///
414/// @param[in]  obj    A Symbol VALUE.
415/// @return     The ID corresponding to the Symbol.
416///
417/// # Safety
418/// - `obj` must be a valid Symbol VALUE
419/// - For dynamic symbols, this may access the heap
420#[inline(always)]
421pub unsafe fn SYM2ID(obj: VALUE) -> crate::ID {
422    api().sym2id(obj)
423}
424
425/// Alias for SYM2ID for compatibility with Ruby naming conventions.
426///
427/// # Safety
428/// - `obj` must be a valid Symbol VALUE
429/// - For dynamic symbols, this may access the heap
430#[inline(always)]
431pub unsafe fn RB_SYM2ID(obj: VALUE) -> crate::ID {
432    api().sym2id(obj)
433}
434
435/// Convert Fixnum to long (akin to `FIX2LONG`).
436///
437/// Extracts the integer value from a Fixnum VALUE by performing an arithmetic right shift.
438///
439/// # Safety
440/// - `obj` must be a valid Fixnum VALUE (checked with FIXNUM_P)
441/// - Behavior is undefined if called on non-Fixnum values
442#[inline(always)]
443pub unsafe fn FIX2LONG(obj: VALUE) -> std::os::raw::c_long {
444    api().fix2long(obj)
445}
446
447/// Convert Fixnum to unsigned long (akin to `FIX2ULONG`).
448///
449/// Extracts the unsigned integer value from a Fixnum VALUE.
450///
451/// # Safety
452/// - `obj` must be a valid positive Fixnum VALUE
453/// - Behavior is undefined for negative fixnums
454#[inline(always)]
455pub unsafe fn FIX2ULONG(obj: VALUE) -> std::os::raw::c_ulong {
456    api().fix2ulong(obj)
457}
458
459/// Convert long to Fixnum (akin to `LONG2FIX`).
460///
461/// Creates a Fixnum VALUE from a long integer.
462///
463/// # Safety
464/// - `val` must be within the valid Fixnum range (use FIXABLE to check)
465/// - Behavior is undefined if value is out of range
466#[inline(always)]
467pub unsafe fn LONG2FIX(val: std::os::raw::c_long) -> VALUE {
468    api().long2fix(val)
469}
470
471/// Check if long value can be represented as Fixnum (akin to `FIXABLE`).
472///
473/// Returns true if the value fits within the Fixnum range.
474#[inline(always)]
475pub fn FIXABLE(val: std::os::raw::c_long) -> bool {
476    api().fixable(val)
477}
478
479/// Check if unsigned long value can be represented as positive Fixnum (akin to `POSFIXABLE`).
480///
481/// Returns true if the unsigned value fits within the positive Fixnum range.
482#[inline(always)]
483pub fn POSFIXABLE(val: std::os::raw::c_ulong) -> bool {
484    api().posfixable(val)
485}
486
487/// Convert Ruby Integer to long (akin to `NUM2LONG`).
488///
489/// Converts any Ruby Integer (Fixnum or Bignum) to a C long.
490/// May raise a RangeError exception if the value is out of range.
491///
492/// # Safety
493/// - `obj` must be a valid Integer VALUE
494/// - May call into Ruby runtime and potentially raise exceptions
495/// - May trigger garbage collection
496#[inline(always)]
497pub unsafe fn NUM2LONG(obj: VALUE) -> std::os::raw::c_long {
498    api().num2long(obj)
499}
500
501/// Convert Ruby Integer to unsigned long (akin to `NUM2ULONG`).
502///
503/// Converts any Ruby Integer (Fixnum or Bignum) to a C unsigned long.
504/// May raise a RangeError exception if the value is out of range or negative.
505///
506/// # Safety
507/// - `obj` must be a valid Integer VALUE
508/// - May call into Ruby runtime and potentially raise exceptions
509/// - May trigger garbage collection
510#[inline(always)]
511pub unsafe fn NUM2ULONG(obj: VALUE) -> std::os::raw::c_ulong {
512    api().num2ulong(obj)
513}
514
515/// Convert long to Ruby Integer (akin to `LONG2NUM`).
516///
517/// Creates a Ruby Integer (Fixnum or Bignum) from a C long.
518/// Uses Fixnum if possible, otherwise allocates a Bignum.
519#[inline(always)]
520pub fn LONG2NUM(val: std::os::raw::c_long) -> VALUE {
521    api().long2num(val)
522}
523
524/// Convert unsigned long to Ruby Integer (akin to `ULONG2NUM`).
525///
526/// Creates a Ruby Integer (Fixnum or Bignum) from a C unsigned long.
527/// Uses Fixnum if possible, otherwise allocates a Bignum.
528#[inline(always)]
529pub fn ULONG2NUM(val: std::os::raw::c_ulong) -> VALUE {
530    api().ulong2num(val)
531}
532
533/// Execute GC write barrier when storing a reference (akin to `RB_OBJ_WRITE`).
534///
535/// Writes `young` into `*slot` and notifies the GC so generational/incremental
536/// collection stays correct. Without this, the GC may collect objects that are
537/// still referenced.
538///
539/// @param[in]  old    The object being modified (must be heap-allocated).
540/// @param[in]  slot   Pointer to the VALUE slot within `old` being written to.
541/// @param[in]  young  The VALUE being stored.
542/// @return     `old` — matches CRuby's `RB_OBJ_WRITE` semantics.
543///
544/// # Safety
545/// - `old` must be a valid heap-allocated Ruby object
546/// - `slot` must be a valid pointer to a VALUE within `old`
547/// - `young` must be a valid VALUE
548#[inline(always)]
549pub unsafe fn RB_OBJ_WRITE(old: VALUE, slot: *mut VALUE, young: VALUE) -> VALUE {
550    api().rb_obj_write(old, slot, young)
551}
552
553/// Declare a write barrier without actually writing (akin to `RB_OBJ_WRITTEN`).
554///
555/// Use this when you've already written a reference but need to inform the GC.
556/// This is useful when the write happens through a different mechanism but
557/// the GC still needs to be notified.
558///
559/// @param[in]  old    The object being modified (must be heap-allocated).
560/// @param[in]  oldv   The previous value (can be any VALUE).
561/// @param[in]  young  The VALUE that was written.
562/// @return     `old` — matches CRuby's `RB_OBJ_WRITTEN` semantics.
563///
564/// # Safety
565/// - `old` must be a valid heap-allocated Ruby object
566/// - `oldv` is the previous value (can be any VALUE)
567/// - `young` must be a valid VALUE that was written
568#[inline(always)]
569pub unsafe fn RB_OBJ_WRITTEN(old: VALUE, oldv: VALUE, young: VALUE) -> VALUE {
570    api().rb_obj_written(old, oldv, young)
571}
572
573/// Check if an object can have flags (akin to `RB_FL_ABLE`).
574///
575/// Returns false for immediate values (nil, true, false, Fixnum, Symbol, Flonum).
576/// Returns true for heap-allocated objects that can have flags set.
577///
578/// @param[in]  obj    An object to check.
579/// @retval     true   The object can have flags.
580/// @retval     false  The object is an immediate value.
581#[inline(always)]
582pub fn FL_ABLE(obj: VALUE) -> bool {
583    api().fl_able(obj)
584}
585
586/// Get pointer to end of string contents (akin to `RSTRING_END`).
587///
588/// # Safety
589/// - `obj` must be a valid Ruby String object
590#[inline(always)]
591pub unsafe fn RSTRING_END(obj: VALUE) -> *const c_char {
592    api().rstring_end(obj)
593}
594
595/// Get data pointer from RData/TypedData object (akin to `DATA_PTR`).
596///
597/// # Safety
598/// - `obj` must be a valid RData or TypedData object
599#[inline(always)]
600pub unsafe fn DATA_PTR(obj: VALUE) -> *mut c_void {
601    api().rdata_ptr(obj)
602}
603
604/// Freeze an object (akin to `RB_OBJ_FREEZE`).
605///
606/// # Safety
607/// - `obj` must be a valid heap-allocated Ruby object
608#[inline(always)]
609pub unsafe fn RB_OBJ_FREEZE(obj: VALUE) {
610    api().rb_obj_freeze(obj)
611}
612
613/// Check if object is promoted to old GC generation (akin to `RB_OBJ_PROMOTED`).
614///
615/// # Safety
616/// - `obj` must be a valid VALUE
617#[inline(always)]
618pub unsafe fn RB_OBJ_PROMOTED(obj: VALUE) -> bool {
619    api().rb_obj_promoted(obj)
620}
621
622/// Raw version assuming FL_ABLE (akin to `RB_OBJ_PROMOTED_RAW`).
623///
624/// # Safety
625/// - `obj` must be a valid heap-allocated Ruby object (FL_ABLE must be true)
626#[inline(always)]
627pub unsafe fn RB_OBJ_PROMOTED_RAW(obj: VALUE) -> bool {
628    api().rb_obj_promoted_raw(obj)
629}
630
631/// Convert Ruby numeric to C double (akin to `NUM2DBL`).
632///
633/// Works for Float (including Flonum), Fixnum, and other numeric types.
634///
635/// @param[in]  obj    A Ruby numeric object.
636/// @return     C double representation.
637///
638/// # Safety
639/// This function is unsafe because it may dereference a raw pointer to access
640/// underlying Ruby data. The caller must ensure the VALUE is a valid Ruby numeric.
641#[inline(always)]
642pub unsafe fn NUM2DBL(obj: VALUE) -> std::os::raw::c_double {
643    api().num2dbl(obj)
644}
645
646/// Convert C double to Ruby Float VALUE (akin to `DBL2NUM`).
647///
648/// May return a Flonum (tagged pointer) for small values on 64-bit platforms,
649/// or a heap-allocated Float object.
650///
651/// @param[in]  val    A C double value.
652/// @return     A Ruby Float VALUE.
653#[inline(always)]
654pub fn DBL2NUM(val: std::os::raw::c_double) -> VALUE {
655    api().dbl2num(val)
656}
657
658/// Get hash size (akin to `RHASH_SIZE`).
659///
660/// Returns the number of entries in the hash.
661///
662/// @param[in]  obj    A Ruby Hash object.
663/// @return     Number of entries.
664///
665/// # Safety
666/// This function is unsafe because it dereferences a raw pointer to access
667/// underlying Ruby data. The caller must ensure the VALUE is a valid Hash.
668#[inline(always)]
669pub unsafe fn RHASH_SIZE(obj: VALUE) -> usize {
670    api().rhash_size(obj)
671}
672
673/// Check if hash is empty (akin to `RHASH_EMPTY_P`).
674///
675/// @param[in]  obj    A Ruby Hash object.
676/// @retval     true   The hash has no entries.
677/// @retval     false  The hash has at least one entry.
678///
679/// # Safety
680/// This function is unsafe because it dereferences a raw pointer to access
681/// underlying Ruby data. The caller must ensure the VALUE is a valid Hash.
682#[inline(always)]
683pub unsafe fn RHASH_EMPTY_P(obj: VALUE) -> bool {
684    api().rhash_empty_p(obj)
685}
686
687/// Get encoding index from object (akin to `ENCODING_GET`).
688///
689/// Returns the encoding index stored in the object's flags.
690///
691/// @param[in]  obj    A Ruby object with encoding (String, Regexp, Symbol).
692/// @return     Encoding index.
693///
694/// # Safety
695/// This function is unsafe because it dereferences a raw pointer to access
696/// the RBasic flags. The caller must ensure the VALUE is a valid object
697/// with encoding support.
698#[inline(always)]
699pub unsafe fn ENCODING_GET(obj: VALUE) -> std::os::raw::c_int {
700    api().encoding_get(obj)
701}