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;
}
#[derive(Debug)]
#[doc(hidden)]
pub struct InnerProtoString {
owned_ptr: CppStdString,
}
impl Drop for InnerProtoString {
fn drop(&mut self) {
unsafe {
proto2_rust_cpp_delete_string(self.owned_ptr);
}
}
}
impl InnerProtoString {
pub(crate) fn as_bytes(&self) -> &[u8] {
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
}
pub unsafe fn from_raw(src: CppStdString) -> InnerProtoString {
InnerProtoString { owned_ptr: src }
}
}
impl From<&[u8]> for InnerProtoString {
fn from(val: &[u8]) -> Self {
let owned_ptr: CppStdString = unsafe { proto2_rust_cpp_new_string(val.into()) };
InnerProtoString { owned_ptr }
}
}
#[repr(C)]
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct PtrAndLen {
pub ptr: *const u8,
pub len: usize,
}
impl PtrAndLen {
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 {
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() }
}
}
#[repr(C)]
#[doc(hidden)]
pub struct SerializedData {
data: NonNull<u8>,
len: usize,
}
impl SerializedData {
pub fn new() -> Self {
Self { data: NonNull::dangling(), len: 0 }
}
pub unsafe fn from_raw_parts(data: NonNull<u8>, len: usize) -> Self {
Self { data, len }
}
pub fn as_ptr(&self) -> *const [u8] {
ptr::slice_from_raw_parts(self.data.as_ptr(), self.len)
}
fn as_mut_ptr(&mut self) -> *mut [u8] {
ptr::slice_from_raw_parts_mut(self.data.as_ptr(), self.len)
}
pub fn into_vec(self) -> Vec<u8> {
let s = ManuallyDrop::new(self);
unsafe {
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 {
unsafe { &*self.as_ptr() }
}
}
impl Drop for SerializedData {
fn drop(&mut self) {
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)
}
}
#[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() {
return String::new();
}
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 {
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()
}
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()
}
pub fn ptrlen_to_bytes<'msg>(val: PtrAndLen) -> &'msg [u8] {
unsafe { val.as_ref() }
}
#[cfg(test)]
mod tests {
use super::*;
use googletest::prelude::*;
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(""));
}
}