From 47174de7d07b7bc6caf4a58c63fa0fbcb7eec68c Mon Sep 17 00:00:00 2001 From: Ian Ker-Seymer Date: Fri, 27 May 2022 16:40:49 -0400 Subject: [PATCH 1/2] Allow for static, freeable, String for fast handoff to Ruby --- src/r_string.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/r_string.rs b/src/r_string.rs index 1ee79c97..46eddcc6 100644 --- a/src/r_string.rs +++ b/src/r_string.rs @@ -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}, @@ -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, }; @@ -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: 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); + std::mem::drop(self); + } + /// Implementation detail of [`r_string`]. #[doc(hidden)] #[inline] From 4dcacddbb061fd1f1247982f0bd549527ae0b2c6 Mon Sep 17 00:00:00 2001 From: Ian Ker-Seymer Date: Fri, 27 May 2022 16:49:14 -0400 Subject: [PATCH 2/2] Add comment --- src/r_string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_string.rs b/src/r_string.rs index 46eddcc6..2830d92d 100644 --- a/src/r_string.rs +++ b/src/r_string.rs @@ -151,7 +151,7 @@ impl RString { /// ``` 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); + flags.bitand_assign(ruby_fl_type::RUBY_FL_USER18 as u64); // STR_NOFREE std::mem::drop(self); }