| use super::*; |
| |
| unsafe extern "C" { |
| pub fn proto2_rust_cpp_new_string(src: PtrAndLen) -> CppStdString; |
| pub fn proto2_rust_cpp_delete_string(src: CppStdString); |
| pub fn proto2_rust_cpp_string_to_view(src: CppStdString) -> PtrAndLen; |
| } |
| |
| /// Kernel-specific owned `string` and `bytes` field type. |
| #[derive(Debug)] |
| #[doc(hidden)] |
| pub struct InnerProtoString { |
| owned_ptr: CppStdString, |
| } |
| |
| impl Drop for InnerProtoString { |
| fn drop(&mut self) { |
| // SAFETY: `self.owned_ptr` points to a valid std::string object. |
| unsafe { |
| proto2_rust_cpp_delete_string(self.owned_ptr); |
| } |
| } |
| } |
| |
| impl InnerProtoString { |
| pub(crate) fn as_bytes(&self) -> &[u8] { |
| // SAFETY: `self.owned_ptr` points to a valid std::string object. |
| unsafe { proto2_rust_cpp_string_to_view(self.owned_ptr).as_ref() } |
| } |
| |
| pub fn into_raw(self) -> CppStdString { |
| let s = ManuallyDrop::new(self); |
| s.owned_ptr |
| } |
| |
| /// # Safety |
| /// - `src` points to a valid CppStdString. |
| pub unsafe fn from_raw(src: CppStdString) -> InnerProtoString { |
| InnerProtoString { owned_ptr: src } |
| } |
| } |
| |
| impl From<&[u8]> for InnerProtoString { |
| fn from(val: &[u8]) -> Self { |
| // SAFETY: `val` is valid byte slice. |
| let owned_ptr: CppStdString = unsafe { proto2_rust_cpp_new_string(val.into()) }; |
| InnerProtoString { owned_ptr } |
| } |
| } |
| |
| /// Represents an ABI-stable version of `NonNull<[u8]>`/`string_view` (a |
| /// borrowed slice of bytes) for FFI use only. |
| /// |
| /// Has semantics similar to `std::string_view` in C++ and `&[u8]` in Rust, |
| /// but is not ABI-compatible with either. |
| /// |
| /// If `len` is 0, then `ptr` can be null or dangling. C++ considers a dangling |
| /// 0-len `std::string_view` to be invalid, and Rust considers a `&[u8]` with a |
| /// null data pointer to be invalid. |
| #[repr(C)] |
| #[derive(Copy, Clone)] |
| #[doc(hidden)] |
| pub struct PtrAndLen { |
| /// Pointer to the first byte. |
| /// Borrows the memory. |
| pub ptr: *const u8, |
| |
| /// Length of the `[u8]` pointed to by `ptr`. |
| pub len: usize, |
| } |
| |
| impl PtrAndLen { |
| /// Unsafely dereference this slice. |
| /// |
| /// # Safety |
| /// - `self.ptr` must be dereferenceable and immutable for `self.len` bytes |
| /// for the lifetime `'a`. It can be null or dangling if `self.len == 0`. |
| pub unsafe fn as_ref<'a>(self) -> &'a [u8] { |
| if self.ptr.is_null() { |
| assert_eq!(self.len, 0, "Non-empty slice with null data pointer"); |
| &[] |
| } else { |
| // SAFETY: |
| // - `ptr` is non-null |
| // - `ptr` is valid for `len` bytes as promised by the caller. |
| unsafe { slice::from_raw_parts(self.ptr, self.len) } |
| } |
| } |
| } |
| |
| impl From<&[u8]> for PtrAndLen { |
| fn from(slice: &[u8]) -> Self { |
| Self { ptr: slice.as_ptr(), len: slice.len() } |
| } |
| } |
| |
| impl From<&ProtoStr> for PtrAndLen { |
| fn from(s: &ProtoStr) -> Self { |
| let bytes = s.as_bytes(); |
| Self { ptr: bytes.as_ptr(), len: bytes.len() } |
| } |
| } |
| |
| /// Serialized Protobuf wire format data. It's typically produced by |
| /// `<Message>.serialize()`. |
| /// |
| /// This struct is ABI-compatible with the equivalent struct on the C++ side. It |
| /// owns (and drops) its data. |
| #[repr(C)] |
| #[doc(hidden)] |
| pub struct SerializedData { |
| /// Owns the memory. |
| data: NonNull<u8>, |
| len: usize, |
| } |
| |
| impl SerializedData { |
| pub fn new() -> Self { |
| Self { data: NonNull::dangling(), len: 0 } |
| } |
| |
| /// Constructs owned serialized data from raw components. |
| /// |
| /// # Safety |
| /// - `data` must be readable for `len` bytes. |
| /// - `data` must be an owned pointer and valid until deallocated. |
| /// - `data` must have been allocated by the Rust global allocator with a |
| /// size of `len` and align of 1. |
| pub unsafe fn from_raw_parts(data: NonNull<u8>, len: usize) -> Self { |
| Self { data, len } |
| } |
| |
| /// Gets a raw slice pointer. |
| pub fn as_ptr(&self) -> *const [u8] { |
| ptr::slice_from_raw_parts(self.data.as_ptr(), self.len) |
| } |
| |
| /// Gets a mutable raw slice pointer. |
| fn as_mut_ptr(&mut self) -> *mut [u8] { |
| ptr::slice_from_raw_parts_mut(self.data.as_ptr(), self.len) |
| } |
| |
| /// Converts into a Vec<u8>. |
| pub fn into_vec(self) -> Vec<u8> { |
| // We need to prevent self from being dropped, because we are going to transfer |
| // ownership of self.data to the Vec<u8>. |
| let s = ManuallyDrop::new(self); |
| |
| unsafe { |
| // SAFETY: |
| // - `data` was allocated by the Rust global allocator. |
| // - `data` was allocated with an alignment of 1 for u8. |
| // - The allocated size was `len`. |
| // - The length and capacity are equal. |
| // - All `len` bytes are initialized. |
| // - The capacity (`len` in this case) is the size the pointer was allocated |
| // with. |
| // - The allocated size is no more than isize::MAX, because the protobuf |
| // serializer will refuse to serialize a message if the output would exceed |
| // 2^31 - 1 bytes. |
| Vec::<u8>::from_raw_parts(s.data.as_ptr(), s.len, s.len) |
| } |
| } |
| } |
| |
| impl Deref for SerializedData { |
| type Target = [u8]; |
| fn deref(&self) -> &Self::Target { |
| // SAFETY: `data` is valid for `len` bytes until deallocated as promised by |
| // `from_raw_parts`. |
| unsafe { &*self.as_ptr() } |
| } |
| } |
| |
| impl Drop for SerializedData { |
| fn drop(&mut self) { |
| // SAFETY: `data` was allocated by the Rust global allocator with a |
| // size of `len` and align of 1 as promised by `from_raw_parts`. |
| unsafe { drop(Box::from_raw(self.as_mut_ptr())) } |
| } |
| } |
| |
| impl fmt::Debug for SerializedData { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| fmt::Debug::fmt(self.deref(), f) |
| } |
| } |
| |
| /// A type to transfer an owned Rust string across the FFI boundary: |
| /// * This struct is ABI-compatible with the equivalent C struct. |
| /// * It owns its data but does not drop it. Immediately turn it into a |
| /// `String` by calling `.into()` on it. |
| /// * `.data` points to a valid UTF-8 string that has been allocated with the |
| /// Rust allocator and is 1-byte aligned. |
| /// * `.data` contains exactly `.len` bytes. |
| /// * The empty string is represented as `.data.is_null() == true`. |
| #[repr(C)] |
| #[doc(hidden)] |
| pub struct RustStringRawParts { |
| data: *const u8, |
| len: usize, |
| } |
| |
| impl From<RustStringRawParts> for String { |
| fn from(value: RustStringRawParts) -> Self { |
| if value.data.is_null() { |
| // Handle the case where the string is empty. |
| return String::new(); |
| } |
| // SAFETY: |
| // - `value.data` contains valid UTF-8 bytes as promised by |
| // `RustStringRawParts`. |
| // - `value.data` has been allocated with the Rust allocator and is 1-byte |
| // aligned as promised by `RustStringRawParts`. |
| // - `value.data` contains and is allocated for exactly `value.len` bytes. |
| unsafe { String::from_raw_parts(value.data as *mut u8, value.len, value.len) } |
| } |
| } |
| |
| unsafe extern "C" { |
| fn proto2_rust_utf8_debug_string(raw: RawMessage) -> RustStringRawParts; |
| } |
| |
| pub fn debug_string(raw: RawMessage, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| // SAFETY: |
| // - `raw` is a valid protobuf message. |
| let dbg_str: String = unsafe { proto2_rust_utf8_debug_string(raw) }.into(); |
| write!(f, "{dbg_str}") |
| } |
| |
| pub fn str_to_ptrlen<'msg>(val: impl Into<&'msg ProtoStr>) -> PtrAndLen { |
| val.into().as_bytes().into() |
| } |
| |
| // Warning: this function is unsound on its own! `val.as_ref()` must be safe to |
| // call. |
| pub fn ptrlen_to_str<'msg>(val: PtrAndLen) -> &'msg ProtoStr { |
| ProtoStr::from_utf8_unchecked(unsafe { val.as_ref() }) |
| } |
| |
| pub fn protostr_into_cppstdstring(val: ProtoString) -> CppStdString { |
| val.into_inner(Private).into_raw() |
| } |
| |
| pub fn protobytes_into_cppstdstring(val: ProtoBytes) -> CppStdString { |
| val.into_inner(Private).into_raw() |
| } |
| |
| // Warning: this function is unsound on its own! `val.as_ref()` must be safe to |
| // call. |
| pub fn ptrlen_to_bytes<'msg>(val: PtrAndLen) -> &'msg [u8] { |
| unsafe { val.as_ref() } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use googletest::prelude::*; |
| |
| // We need to allocate the byte array so SerializedData can own it and |
| // deallocate it in its drop. This function makes it easier to do so for our |
| // tests. |
| fn allocate_byte_array(content: &'static [u8]) -> (*mut u8, usize) { |
| let content: &mut [u8] = Box::leak(content.into()); |
| (content.as_mut_ptr(), content.len()) |
| } |
| |
| #[gtest] |
| fn test_serialized_data_roundtrip() { |
| let (ptr, len) = allocate_byte_array(b"Hello world"); |
| let serialized_data = SerializedData { data: NonNull::new(ptr).unwrap(), len }; |
| assert_that!(&*serialized_data, eq(b"Hello world")); |
| } |
| |
| #[gtest] |
| fn test_empty_string() { |
| let empty_str: String = RustStringRawParts { data: std::ptr::null(), len: 0 }.into(); |
| assert_that!(empty_str, eq("")); |
| } |
| } |