diff options
Diffstat (limited to 'third_party/rust/ffi-support/src/string.rs')
-rw-r--r-- | third_party/rust/ffi-support/src/string.rs | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/third_party/rust/ffi-support/src/string.rs b/third_party/rust/ffi-support/src/string.rs new file mode 100644 index 0000000000..b1311669f5 --- /dev/null +++ b/third_party/rust/ffi-support/src/string.rs @@ -0,0 +1,162 @@ +/* Copyright 2018-2019 Mozilla Foundation + * + * Licensed under the Apache License (Version 2.0), or the MIT license, + * (the "Licenses") at your option. You may not use this file except in + * compliance with one of the Licenses. You may obtain copies of the + * Licenses at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * http://opensource.org/licenses/MIT + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licenses is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licenses for the specific language governing permissions and + * limitations under the Licenses. */ + +use crate::FfiStr; +use std::ffi::CString; +use std::os::raw::c_char; +use std::ptr; + +/// Convert a rust string into a NUL-terminated utf-8 string suitable for passing to C, or to things +/// ABI-compatible with C. +/// +/// Important: This string must eventually be freed. You may either do that using the +/// [`destroy_c_string`] method (or, if you must, by dropping the underlying [`std::ffi::CString`] +/// after recovering it via [`std::ffi::CString::from_raw`]). +/// +/// It's common to want to allow the consumer (e.g. on the "C" side of the FFI) to be allowed to +/// free this memory, and the macro [`define_string_destructor!`] may be used to do so. +/// +/// ## Panics +/// +/// This function may panic if the argument has an interior null byte. This is fairly rare, but +/// is possible in theory. +#[inline] +pub fn rust_string_to_c(rust_string: impl Into<String>) -> *mut c_char { + CString::new(rust_string.into()) + .expect("Error: Rust string contained an interior null byte.") + .into_raw() +} + +/// Variant of [`rust_string_to_c`] which takes an Option, and returns null for None. +#[inline] +pub fn opt_rust_string_to_c(opt_rust_string: Option<impl Into<String>>) -> *mut c_char { + if let Some(s) = opt_rust_string { + rust_string_to_c(s) + } else { + ptr::null_mut() + } +} + +/// Free the memory of a string created by [`rust_string_to_c`] on the rust heap. If `c_string` is +/// null, this is a no-op. +/// +/// See the [`define_string_destructor!`] macro which may be used for exposing this function over +/// the FFI. +/// +/// ## Safety +/// +/// This is inherently unsafe, since we're deallocating memory. Be sure +/// +/// - Nobody can use the memory after it's deallocated. +/// - The memory was actually allocated on this heap (and it's not a string from the other side of +/// the FFI which was allocated on e.g. the C heap). +/// - If multiple separate rust libraries are in use (for example, as DLLs) in a single program, +/// you must also make sure that the rust library that allocated the memory is also the one +/// that frees it. +/// +/// See documentation for [`define_string_destructor!`], which gives a more complete overview of the +/// potential issues. +#[inline] +pub unsafe fn destroy_c_string(cstring: *mut c_char) { + // we're not guaranteed to be in a place where we can complain about this beyond logging, + // and there's an obvious way to handle it. + if !cstring.is_null() { + drop(CString::from_raw(cstring)) + } +} + +/// Convert a null-terminated C string to a rust `str`. This does not take ownership of the string, +/// and you should be careful about the lifetime of the resulting string. Note that strings +/// containing invalid UTF-8 are replaced with the empty string (for many cases, you will want to +/// use [`rust_string_from_c`] instead, which will do a lossy conversion). +/// +/// If you actually need an owned rust `String`, you're encouraged to use [`rust_string_from_c`], +/// which, as mentioned, also behaves better in the face of invalid UTF-8. +/// +/// ## Safety +/// +/// This is unsafe because we read from a raw pointer, which may or may not be valid. +/// +/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's +/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which +/// can result in a enormous number of problems. +/// +/// ## Panics +/// +/// Panics if it's argument is null, see [`opt_rust_str_from_c`] for a variant that returns None in +/// this case instead. +/// +/// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else +/// that uses [`std::panic::catch_unwind`]), as it is UB to panic across the FFI boundary. +#[inline] +#[deprecated(since = "0.3.0", note = "Please use FfiStr::as_str instead")] +pub unsafe fn rust_str_from_c<'a>(c_string: *const c_char) -> &'a str { + FfiStr::from_raw(c_string).as_str() +} + +/// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting. +/// +/// ## Safety +/// +/// This is unsafe because we read from a raw pointer, which may or may not be valid. +/// +/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's +/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which +/// can result in a enormous number of problems. +#[inline] +#[deprecated(since = "0.3.0", note = "Please use FfiStr::as_opt_str instead")] +pub unsafe fn opt_rust_str_from_c<'a>(c_string: *const c_char) -> Option<&'a str> { + FfiStr::from_raw(c_string).as_opt_str() +} + +/// Convert a null-terminated C into an owned rust string, replacing invalid UTF-8 with the +/// unicode replacement character. +/// +/// ## Safety +/// +/// This is unsafe because we dereference a raw pointer, which may or may not be valid. +/// +/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's +/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which +/// can result in a enormous number of problems. +/// +/// ## Panics +/// +/// Panics if it's argument is null. See also [`opt_rust_string_from_c`], which returns None +/// instead. +/// +/// Note: This means it's forbidden to call this outside of a `call_with_result` (or something else +/// that uses `std::panic::catch_unwind`), as it is UB to panic across the FFI boundary. +#[inline] +#[deprecated(since = "0.3.0", note = "Please use FfiStr::into_string instead")] +pub unsafe fn rust_string_from_c(c_string: *const c_char) -> String { + FfiStr::from_raw(c_string).into_string() +} + +/// Same as `rust_string_from_c`, but returns None if `c_string` is null instead of asserting. +/// +/// ## Safety +/// +/// This is unsafe because we dereference a raw pointer, which may or may not be valid. +/// +/// We also assume `c_string` is a null terminated string, and have no way of knowing if that's +/// actually true. If it's not, we'll read arbitrary memory from the heap until we see a '\0', which +/// can result in a enormous number of problems. +#[inline] +#[deprecated(since = "0.3.0", note = "Please use FfiStr::into_opt_string instead")] +pub unsafe fn opt_rust_string_from_c(c_string: *const c_char) -> Option<String> { + FfiStr::from_raw(c_string).into_opt_string() +} |