Skip to content

RFC: Allow for static, freeable, String for fast handoff to Ruby #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion src/r_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
fmt, io,
iter::Iterator,
mem::transmute,
ops::BitAndAssign,
ops::Deref,
os::raw::{c_char, c_long},
path::{Path, PathBuf},
Expand All @@ -15,7 +16,7 @@ use std::{
use crate::ruby_sys::{
self, rb_enc_str_coderange, rb_enc_str_new, rb_str_buf_append, rb_str_buf_new, rb_str_cat,
rb_str_conv_enc, rb_str_new, rb_str_new_frozen, rb_str_new_shared, rb_str_strlen,
rb_str_to_str, rb_utf8_str_new, rb_utf8_str_new_static, ruby_coderange_type,
rb_str_to_str, rb_utf8_str_new, rb_utf8_str_new_static, ruby_coderange_type, ruby_fl_type,
ruby_rstring_flags, ruby_value_type, VALUE,
};

Expand Down Expand Up @@ -103,6 +104,57 @@ impl RString {
}
}

/// Create a new Ruby string and transfers ownership completely to Ruby
///
/// The benefit of using this method is it will avoid any `memcpy` of the string in Ruby, at
/// the cost of losing ownership of the Rust string. Use this method when you want to cheaply
/// pass a String to Ruby.
///
/// The encoding of the Ruby string will be UTF-8.
///
/// # Examples
///
/// ```
/// use magnus::{eval, RString};
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let my_string = String::from("Hello, world!");
/// let val = RString::new_static(my_string);
/// let res: bool = eval!(r#"val == "Hello, world!""#, val).unwrap();
/// assert!(res);
/// ```
pub fn new_static<S: Into<String>>(s: S) -> Self {
let s = s.into();
let ptr = s.as_ptr();
let len = s.len();

std::mem::forget(s);

unsafe { Self::new_lit(ptr as _, len as _) }
}

/// Marks the string as freeable by Ruby
///
/// This method allows the underlying string to be freeable by Ruby (i.e. for a static Ruby
/// string). After calling this function, it is no longer safe to interact with this RString in
/// Rust. As such, `mark_freeable` consumes ownership so it can no longer be used.
///
/// ```
/// use magnus::RString;
/// # let _cleanup = unsafe { magnus::embed::init() };
///
/// let my_string = String::from("Free me!");
/// let s = RString::new_static(my_string);
/// s.mark_freeable();
///
/// # can no longer access `my_string`
/// ```
pub fn mark_freeable(self) {
let mut flags = unsafe { self.r_basic_unchecked().as_ref().flags };
flags.bitand_assign(ruby_fl_type::RUBY_FL_USER18 as u64); // STR_NOFREE
std::mem::drop(self);
}

/// Implementation detail of [`r_string`].
#[doc(hidden)]
#[inline]
Expand Down