ffi_string/
lib.rs

1//! # FFI String
2//! 
3//! This is a small crate for moving strings across `extern "C"`, giving FFI-safe version of String and &str
4//! 
5
6
7
8#![feature(str_from_raw_parts)]
9
10#![warn(missing_docs)]
11
12
13
14use std::{fmt::{self, Display, Formatter}, ops::Deref};
15
16
17
18/// FFI version of &str
19/// 
20/// <br>
21/// 
22/// Features:
23/// - `fn new(&str) -> Self`
24/// - `fn as_str(&self) -> &str`
25/// - `impl Deref<Target = str>`
26/// - `impl Copy, Clone`
27/// - `impl Display, Debug`
28/// - `impl From<&str>, AsRef<str>`
29/// - `impl Into<String>`
30/// 
31/// <br>
32#[derive(Copy, Clone)]
33#[repr(C)]
34pub struct FFIStr<'a> {
35	ptr: &'a u8,
36	len: u32,
37}
38
39impl<'a> FFIStr<'a> {
40	/// Creates a new FFIStr from a string slice, copying only pointers
41	pub fn new(from: &'a str) -> Self {
42		unsafe {
43			Self {
44				ptr: &*from.as_ptr(),
45				len: from.len() as u32,
46			}
47		}
48	}
49	/// Creates a string slice, copying only pointers
50	/// 
51	/// Also, the function `to_string()` (implementation of fmt::Display) creates a new String, copying the underlying data
52	pub fn as_str(&self) -> &'a str {
53		unsafe {
54			core::str::from_raw_parts(self.ptr, self.len as usize)
55		}
56	}
57}
58
59impl Deref for FFIStr<'_> {
60	type Target = str;
61	fn deref(&self) -> &Self::Target {
62		self.as_str()
63	}
64}
65
66impl fmt::Debug for FFIStr<'_> {
67	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
68		write!(f, "\"{}\"", self.as_str())
69	}
70}
71
72impl Display for FFIStr<'_> {
73	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
74		write!(f, "{}", self.as_str())
75	}
76}
77
78impl<'a> From<&'a str> for FFIStr<'a> {
79	fn from(value: &'a str) -> Self {
80		Self::new(value)
81	}
82}
83
84impl<'a> AsRef<str> for FFIStr<'a> {
85	fn as_ref(&self) -> &str {
86		self.as_str()
87	}
88}
89
90impl Into<String> for FFIStr<'_> {
91	fn into(self) -> String {
92		self.to_string()
93	}
94}
95
96/// Adds `to_ffi_str()` and `to_ffi_string()` to &str
97pub trait StrToFFI {
98	/// Convenience function for converting from &'a str to FFIStr<'a>, copying only pointers
99	fn to_ffi_str<'a>(&'a self) -> FFIStr<'a>;
100	/// Convenience function for converting from &str to FFIString, copying underlying data
101	fn to_ffi_string(&self) -> FFIString;
102}
103
104impl StrToFFI for str {
105	fn to_ffi_str<'a>(&'a self) -> FFIStr<'a> {
106		FFIStr::new(self)
107	}
108	fn to_ffi_string(&self) -> FFIString {
109		FFIString::new(self)
110	}
111}
112
113
114
115/// FFI version of String
116/// 
117/// <br>
118/// 
119/// Features:
120/// - `fn new(&str) -> Self`
121/// - `fn as_str(&self) -> &str`
122/// - `impl Deref<Target = str>`
123/// - `impl Clone`
124/// - `impl Display, Debug`
125/// - `impl From<String>, Into<String>`
126/// - `impl AsRef<str>`
127/// - Correctly drops underlying data
128/// 
129/// <br>
130#[repr(C)]
131pub struct FFIString {
132	ptr: *mut u8,
133	len: u32,
134	cap: u32,
135}
136
137impl FFIString {
138	/// Creates a new FFIString from a String, copying only pointers (if you pass String) or all underlying data (for anything else)
139	pub fn new(from: impl Into<String>) -> Self {
140		let mut from = from.into();
141		let output = Self {
142			ptr: from.as_mut_ptr(),
143			len: from.len() as u32,
144			cap: from.capacity() as u32,
145		};
146		std::mem::forget(from);
147		output
148	}
149	/// Creates a new String, copying only pointers
150	/// 
151	/// Also, the function `to_string()` (implementation of fmt::Display) creates a new String, copying the underlying data
152	pub fn into_string(self) -> String {
153		unsafe {
154			let output = String::from_raw_parts(self.ptr, self.len as usize, self.cap as usize);
155			std::mem::forget(self);
156			output
157		}
158	}
159	/// Creates a string slice, copying only pointers
160	pub fn as_str(&self) -> &str {
161		unsafe {
162			core::str::from_raw_parts(self.ptr, self.len as usize)
163		}
164	}
165}
166
167impl Drop for FFIString {
168	fn drop(&mut self) {
169		unsafe {
170			String::from_raw_parts(self.ptr, self.len as usize, self.cap as usize);
171		}
172	}
173}
174
175impl Deref for FFIString {
176	type Target = str;
177	fn deref(&self) -> &Self::Target {
178		self.as_str()
179	}
180}
181
182impl fmt::Debug for FFIString {
183	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
184		write!(f, "\"{}\"", self.as_str())
185	}
186}
187
188impl Display for FFIString {
189	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
190		write!(f, "{}", self.as_str())
191	}
192}
193
194impl From<String> for FFIString {
195	fn from(value: String) -> Self {
196		Self::new(value)
197	}
198}
199
200impl AsRef<str> for FFIString {
201	fn as_ref(&self) -> &str {
202		self.as_str()
203	}
204}
205
206impl Into<String> for FFIString {
207	fn into(self) -> String {
208		self.to_string()
209	}
210}
211
212impl Clone for FFIString {
213	fn clone(&self) -> Self {
214		Self::new(self.to_string())
215	}
216}
217
218/// Adds `into_ffi_string()` to String
219/// 
220/// This does not add `to_ffi_str()` or `to_ffi_string()` because those are already available with String's `Deref<Target = str>`
221pub trait StringToFFI {
222	/// Convenience function for converting from String to FFIString, copying only pointers
223	fn into_ffi_string(self) -> FFIString;
224}
225
226impl StringToFFI for String {
227	fn into_ffi_string(self) -> FFIString {
228		FFIString::new(self)
229	}
230}