use crate::rubysys::{
constant::{
FL_USER_1, FL_USER_3, FL_USER_4, FL_USER_5, FL_USER_6, FL_USER_7, FL_USER_8, FL_USER_9,
FL_USHIFT,
},
libc::size_t,
types::{c_int, c_long, InternalValue, RBasic, Value},
};
use std::mem;
#[cfg_attr(rutie_dllimport, link(name = "rutie_ruby"))]
extern "C" {
pub fn rb_ary_concat(array: Value, other_array: Value) -> Value;
pub fn rb_ary_dup(array: Value) -> Value;
pub fn rb_ary_freeze(array: Value) -> Value;
pub fn rb_ary_aref(argc: c_int, argv: *const Value, array: Value) -> Value;
pub fn rb_ary_assoc(array: Value, key: Value) -> Value;
pub fn rb_ary_clear(array: Value) -> Value;
pub fn rb_ary_cmp(array: Value, other: Value) -> Value;
pub fn rb_ary_delete(array: Value, item: Value) -> Value;
pub fn rb_ary_delete_at(array: Value, position: c_long) -> Value;
pub fn rb_ary_includes(array: Value, item: Value) -> Value;
pub fn rb_ary_plus(array: Value, other: Value) -> Value;
pub fn rb_ary_rassoc(array: Value, value: Value) -> Value;
pub fn rb_ary_replace(copy: Value, original: Value) -> Value;
pub fn rb_ary_resize(array: Value, len: c_long) -> Value;
pub fn rb_ary_rotate(array: Value, count: c_long) -> Value;
pub fn rb_ary_subseq(array: Value, begin: c_long, len: c_long) -> Value;
pub fn rb_ary_to_ary(object: Value) -> Value;
pub fn rb_check_array_type(object: Value) -> Value;
pub fn rb_ary_entry(array: Value, offset: c_long) -> Value;
pub fn rb_ary_join(array: Value, separator: Value) -> Value;
pub fn rb_ary_new() -> Value;
pub fn rb_ary_new_from_values(count: c_long, elements: *const Value) -> Value;
pub fn rb_ary_new_capa(capacity: c_long) -> Value;
pub fn rb_ary_pop(array: Value) -> Value;
pub fn rb_ary_push(array: Value, item: Value) -> Value;
pub fn rb_ary_reverse(array: Value) -> Value;
pub fn rb_ary_shift(array: Value) -> Value;
pub fn rb_ary_sort_bang(array: Value) -> Value;
pub fn rb_ary_sort(array: Value) -> Value;
pub fn rb_ary_store(array: Value, index: c_long, item: Value);
pub fn rb_ary_to_s(array: Value) -> Value;
pub fn rb_ary_unshift(array: Value, item: Value) -> Value;
#[cfg(ruby_gte_3_2)]
pub fn rb_ary_hidden_new(capacity: c_long) -> Value;
}
#[derive(Debug, PartialEq)]
#[repr(C)]
enum RArrayEmbed {
Flag = FL_USER_1,
#[cfg(not(ruby_gte_3_2))]
LenMask = FL_USER_4 | FL_USER_3,
#[cfg(ruby_gte_3_2)]
LenMask = FL_USER_9 | FL_USER_8 | FL_USER_7 | FL_USER_6 | FL_USER_5 | FL_USER_4 | FL_USER_3,
LenShift = FL_USHIFT + 3,
}
#[repr(C)]
struct RArrayAs {
heap: RArrayHeap,
}
#[repr(C)]
struct RArrayHeap {
len: c_long,
value: InternalValue,
ptr: InternalValue,
}
#[repr(C)]
struct RArray {
basic: RBasic,
as_: RArrayAs,
}
pub unsafe fn rb_ary_len(value: Value) -> c_long {
let rarray: *const RArray = value.value as *const RArray;
let flags = (*rarray).basic.flags;
if flags & (RArrayEmbed::Flag as size_t) == 0 {
(*rarray).as_.heap.len
} else {
((flags as i64 >> RArrayEmbed::LenShift as i64)
& (RArrayEmbed::LenMask as i64 >> RArrayEmbed::LenShift as i64)) as c_long
}
}
#[cfg(test)]
mod tests {
use super::rb_ary_len;
use crate::{Array, Fixnum, Object, VM};
#[cfg(ruby_gte_3_2)]
#[test]
fn test_ary_hidden_new() {
use super::{rb_ary_hidden_new, rb_ary_push, RBasic};
crate::on_ruby_thread(|| unsafe {
let array = rb_ary_hidden_new(4);
assert_eq!((*(array.value as *const RBasic)).klass, 0);
assert_eq!(rb_ary_len(array), 0);
rb_ary_push(array, Fixnum::new(7).value());
assert_eq!(rb_ary_len(array), 1);
});
}
#[test]
fn test_direct_rarray_len() {
crate::on_ruby_thread(|| {
let arrays = VM::eval(
"(0..200).flat_map do |n|
a = Array.new(n) { |i| i }
grown = []; n.times { |i| grown << i }
[a, a.dup, grown, ([nil] + a)[1..], a.first(n), a.frozen? ? a : a.dup.freeze]
end",
)
.unwrap();
let arrays = Array::from(arrays.value());
for i in 0..arrays.length() {
let array = arrays.at(i as i64);
let length = unsafe { array.send("length", &[]) };
let expected = length.try_convert_to::<Fixnum>().unwrap().to_i64();
assert_eq!(
unsafe { rb_ary_len(array.value()) } as i64,
expected,
"array #{}",
i
);
}
});
}
}