Skip to content

Commit

Permalink
Manual RStruct bindings and implemented rstruct_len for all versions.
Browse files Browse the repository at this point in the history
  • Loading branch information
goyox86 committed Dec 19, 2024
1 parent a393240 commit dbfc3dd
Show file tree
Hide file tree
Showing 8 changed files with 417 additions and 24 deletions.
26 changes: 23 additions & 3 deletions crates/rb-sys-tests/src/stable_api_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::ffi::{CStr, CString};

use rb_sys::{StableApiDefinition, VALUE};
use rb_sys_test_helpers::rstring as gen_rstring;

Expand Down Expand Up @@ -659,10 +657,32 @@ parity_test!(
);

parity_test!(
name: test_rb_rstruct_len,
name: test_rb_rstruct_len_embedded,
func: rstruct_len,
data_factory: {
ruby_eval!("Person = Struct.new(:name, :age); Person.new('Matz', 59)")
},
expected: 2
);

parity_test!(
name: test_rb_rstruct_len_heap,
func: rstruct_len,
data_factory: {
ruby_eval!("
field_names = (0..80).map { |i| \"field#{i}\" };
LargeStruct = Struct.new(*field_names.map(&:to_sym)) do
def initialize(*)
super
# Initialize all nil fields with 0 for demonstration
members.each do |field|
self[field] = 0 if self[field].nil?
end
end
end;
LargeStruct.new
")
},
expected: 81
);
60 changes: 59 additions & 1 deletion crates/rb-sys/src/stable_api/ruby_2_6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,39 @@ use crate::ruby_rarray_flags::*;
use crate::ruby_rstring_flags::*;
use crate::{
internal::{RArray, RString},
value_type, VALUE,
ruby_fl_type, value_type, VALUE,
};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
};

const RSTRUCT_EMBED_LEN_MASK: VALUE =
(ruby_fl_type::RUBY_FL_USER2 as VALUE) | (ruby_fl_type::RUBY_FL_USER1 as VALUE);
const RSTRUCT_EMBED_LEN_SHIFT: VALUE = (ruby_fl_type::RUBY_FL_USHIFT as VALUE) + 1;
const RSTRUCT_EMBED_LEN_MAX: usize = 3;

#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct HeapStructData {
pub(crate) len: std::ffi::c_long,
pub(crate) ptr: *const VALUE,
}

#[repr(C)]
pub(crate) union RStructUnion {
pub(crate) heap: HeapStructData,
pub(crate) ary: [VALUE; RSTRUCT_EMBED_LEN_MAX],
}

#[repr(C)]
pub struct RStruct {
pub(crate) basic: crate::RBasic,
pub(crate) as_: RStructUnion,
}

#[cfg(not(ruby_eq_2_6))]
compile_error!("This file should only be included in Ruby 2.6 builds");

Expand Down Expand Up @@ -276,4 +301,37 @@ impl StableApiDefinition for Definition {

unsafe { crate::rb_thread_wait_for(time) }
}

#[inline]
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { crate::rb_struct_define(name.as_ptr(), members) }
}

#[inline]
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
crate::rb_struct_getmember(st, idx as _)
}

#[inline]
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
crate::rb_struct_aset(st, idx as _, value);
}

#[inline]
unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
let rbasic = st as *const crate::RBasic;
if ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) != 0 {
let mut ret = ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) as c_long;
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
ret
} else {
let rstruct = st as *const RStruct;
(*rstruct).as_.heap.len
}
}
}
60 changes: 59 additions & 1 deletion crates/rb-sys/src/stable_api/ruby_2_7.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
use super::StableApiDefinition;
use crate::{
internal::{RArray, RString},
value_type, VALUE,
ruby_fl_type, value_type, VALUE,
};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
};

const RSTRUCT_EMBED_LEN_MASK: VALUE =
(ruby_fl_type::RUBY_FL_USER2 as VALUE) | (ruby_fl_type::RUBY_FL_USER1 as VALUE);
const RSTRUCT_EMBED_LEN_SHIFT: VALUE = (ruby_fl_type::RUBY_FL_USHIFT as VALUE) + 1;
const RSTRUCT_EMBED_LEN_MAX: usize = 3;

#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct HeapStructData {
pub(crate) len: std::ffi::c_long,
pub(crate) ptr: *const VALUE,
}

#[repr(C)]
pub(crate) union RStructUnion {
pub(crate) heap: HeapStructData,
pub(crate) ary: [VALUE; RSTRUCT_EMBED_LEN_MAX],
}

#[repr(C)]
pub struct RStruct {
pub(crate) basic: crate::RBasic,
pub(crate) as_: RStructUnion,
}

#[cfg(not(ruby_eq_2_7))]
compile_error!("This file should only be included in Ruby 2.7 builds");

Expand Down Expand Up @@ -276,4 +301,37 @@ impl StableApiDefinition for Definition {

unsafe { crate::rb_thread_wait_for(time) }
}

#[inline]
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { crate::rb_struct_define(name.as_ptr(), members) }
}

#[inline]
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
crate::rb_struct_getmember(st, idx as _)
}

#[inline]
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
crate::rb_struct_aset(st, idx as _, value);
}

#[inline]
unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
let rbasic = st as *const crate::RBasic;
if ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) != 0 {
let mut ret = ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) as c_long;
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
ret
} else {
let rstruct = st as *const RStruct;
(*rstruct).as_.heap.len
}
}
}
60 changes: 59 additions & 1 deletion crates/rb-sys/src/stable_api/ruby_3_0.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
use super::StableApiDefinition;
use crate::{
internal::{RArray, RString},
value_type, VALUE,
ruby_fl_type, ruby_fl_ushift, value_type, VALUE,
};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
};

const RSTRUCT_EMBED_LEN_MASK: VALUE =
(ruby_fl_type::RUBY_FL_USER2 as VALUE) | (ruby_fl_type::RUBY_FL_USER1 as VALUE);
const RSTRUCT_EMBED_LEN_SHIFT: VALUE = (ruby_fl_ushift::RUBY_FL_USHIFT as VALUE) + 1;
const RSTRUCT_EMBED_LEN_MAX: usize = 3;

#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct HeapStructData {
pub(crate) len: std::ffi::c_long,
pub(crate) ptr: *const VALUE,
}

#[repr(C)]
pub(crate) union RStructUnion {
pub(crate) heap: HeapStructData,
pub(crate) ary: [VALUE; RSTRUCT_EMBED_LEN_MAX],
}

#[repr(C)]
pub struct RStruct {
pub(crate) basic: crate::RBasic,
pub(crate) as_: RStructUnion,
}

#[cfg(not(ruby_eq_3_0))]
compile_error!("This file should only be included in Ruby 3.0 builds");

Expand Down Expand Up @@ -284,4 +309,37 @@ impl StableApiDefinition for Definition {

unsafe { crate::rb_thread_wait_for(time) }
}

#[inline]
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { crate::rb_struct_define(name.as_ptr(), members) }
}

#[inline]
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
crate::rb_struct_getmember(st, idx as _)
}

#[inline]
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
crate::rb_struct_aset(st, idx as _, value);
}

#[inline]
unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
let rbasic = st as *const crate::RBasic;
if ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) != 0 {
let mut ret = ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) as c_long;
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
ret
} else {
let rstruct = st as *const RStruct;
(*rstruct).as_.heap.len
}
}
}
59 changes: 58 additions & 1 deletion crates/rb-sys/src/stable_api/ruby_3_1.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
use super::StableApiDefinition;
use crate::{
internal::{RArray, RString},
value_type, VALUE,
ruby_fl_type, ruby_fl_ushift, value_type, VALUE,
};
use std::{
ffi::{c_int, CStr},
os::raw::{c_char, c_long},
ptr::NonNull,
time::Duration,
};

const RSTRUCT_EMBED_LEN_MASK: VALUE =
(ruby_fl_type::RUBY_FL_USER2 as VALUE) | (ruby_fl_type::RUBY_FL_USER1 as VALUE);
const RSTRUCT_EMBED_LEN_SHIFT: VALUE = (ruby_fl_ushift::RUBY_FL_USHIFT as VALUE) + 1;
const RSTRUCT_EMBED_LEN_MAX: usize = 3;

#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) struct HeapStructData {
pub(crate) len: std::ffi::c_long,
pub(crate) ptr: *const VALUE,
}

#[repr(C)]
pub(crate) union RStructUnion {
pub(crate) heap: HeapStructData,
pub(crate) ary: [VALUE; RSTRUCT_EMBED_LEN_MAX],
}

#[repr(C)]
pub struct RStruct {
pub(crate) basic: crate::RBasic,
pub(crate) as_: RStructUnion,
}
#[cfg(not(ruby_eq_3_1))]
compile_error!("This file should only be included in Ruby 3.1 builds");

Expand Down Expand Up @@ -277,4 +301,37 @@ impl StableApiDefinition for Definition {

unsafe { crate::rb_thread_wait_for(time) }
}

#[inline]
fn rstruct_define(&self, name: &CStr, members: &[&CStr]) -> VALUE {
let mut members: Vec<*const c_char> = members
.iter()
.map(|m| m.as_ptr() as *const c_char)
.collect();
members.push(std::ptr::null());
unsafe { crate::rb_struct_define(name.as_ptr(), members) }
}

#[inline]
unsafe fn rstruct_get(&self, st: VALUE, idx: c_int) -> VALUE {
crate::rb_struct_getmember(st, idx as _)
}

#[inline]
unsafe fn rstruct_set(&self, st: VALUE, idx: c_int, value: VALUE) {
crate::rb_struct_aset(st, idx as _, value);
}

#[inline]
unsafe fn rstruct_len(&self, st: VALUE) -> c_long {
let rbasic = st as *const crate::RBasic;
if ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) != 0 {
let mut ret = ((*rbasic).flags & RSTRUCT_EMBED_LEN_MASK as VALUE) as c_long;
ret >>= RSTRUCT_EMBED_LEN_SHIFT;
ret
} else {
let rstruct = st as *const RStruct;
(*rstruct).as_.heap.len
}
}
}
Loading

0 comments on commit dbfc3dd

Please sign in to comment.