Skip to content

Commit

Permalink
Add symbol::rb_intern! macro to memoize symbols (#318)
Browse files Browse the repository at this point in the history
  • Loading branch information
ianks authored Jan 18, 2024
1 parent dae4cfe commit 157d0c9
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 0 deletions.
3 changes: 3 additions & 0 deletions crates/rb-sys-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ mod memory_test;

#[cfg(test)]
mod stable_api_test;

#[cfg(test)]
mod symbol_test;
45 changes: 45 additions & 0 deletions crates/rb-sys-tests/src/symbol_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use std::slice;

use rb_sys::{
rb_funcall, rb_id2sym, rb_intern, rb_utf8_str_new, RSTRING_LEN, RSTRING_PTR, STATIC_SYM_P,
};
use rb_sys_test_helpers::ruby_test;

#[ruby_test]
fn test_creates_a_usable_id() {
let method_name = unsafe { rb_intern!("reverse") };

let mystring = unsafe { rb_utf8_str_new("jerrbear".as_ptr() as *mut _, 8) };
let ret = unsafe { rb_funcall(mystring, method_name, 0) };
let ptr = unsafe { RSTRING_PTR(ret) as *const u8 };
let len = unsafe { RSTRING_LEN(ret) } as _;
let result = unsafe { slice::from_raw_parts(ptr, len) };

assert_eq!(result, b"raebrrej");
}

#[ruby_test]
fn test_has_repeatable_results() {
let method_name1 = unsafe { rb_intern!("reverse") };
let method_name2 = unsafe { rb_intern!("reverse") };

assert_ne!(method_name1, 0);
assert_ne!(method_name2, 0);
assert_eq!(method_name1, method_name2);
}

#[ruby_test]
fn test_non_usascii() {
let method_name1 = unsafe { rb_intern!("🙈") };
let method_name2 = unsafe { rb_intern!("🙈") };

assert_ne!(method_name1, 0);
assert_ne!(method_name2, 0);
assert_eq!(method_name1, method_name2);

let sym1 = unsafe { rb_id2sym(method_name1) };
let sym2 = unsafe { rb_id2sym(method_name2) };

assert!(STATIC_SYM_P(sym1));
assert!(STATIC_SYM_P(sym2));
}
1 change: 1 addition & 0 deletions crates/rb-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod memory;
pub mod special_consts;
#[cfg(feature = "stable-api")]
pub mod stable_api;
pub mod symbol;
pub mod tracking_allocator;
pub mod value_type;

Expand Down
31 changes: 31 additions & 0 deletions crates/rb-sys/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// Finds or creates a symbol for the given static string. This macro will
/// memoize the ID to avoid repeated calls to libruby. You should prefer this
/// macro over [`rb_intern3`] when the string is known at compile time.
///
/// # Safety
///
/// This macro is safe under two conditions:
/// - Ruby VM is initialized and that thus safe to call into libruby
/// - The first call to this macro will be done inside of a managed Ruby thread (i.e. not a native thread)
///
/// # Example
///
/// ```no_run
/// use rb_sys::{symbol::rb_intern, rb_funcall, rb_utf8_str_new};
///
/// unsafe {
/// let reverse_id = rb_intern!("reverse");
/// let msg = rb_utf8_str_new("nice one".as_ptr() as *mut _, 4);
/// rb_funcall(msg, reverse_id, 0);
/// }
/// ```
#[macro_export]
macro_rules! rb_intern {
($s:literal) => {{
static mut ID: $crate::ID = 0;
if ID == 0 {
ID = $crate::rb_intern3($s.as_ptr() as _, $s.len() as _, $crate::rb_utf8_encoding());
}
ID
}};
}

0 comments on commit 157d0c9

Please sign in to comment.