use std::ffi::CString;
use std::os::raw::c_char;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct BincodeBuffer {
pub data: *mut u8,
pub len: usize,
}
impl BincodeBuffer {
#[must_use]
pub const fn empty() -> Self {
Self {
data: std::ptr::null_mut(),
len: 0,
}
}
#[must_use]
pub fn from_vec(
bytes: Vec<u8>
) -> Self {
let len = bytes.len();
let data = Box::into_raw(
bytes.into_boxed_slice(),
)
.cast::<u8>();
Self {
data,
len,
}
}
#[must_use]
pub const fn is_null(
&self
) -> bool {
self.data.is_null()
|| self.len == 0
}
#[must_use]
pub const unsafe fn as_slice(
&self
) -> &[u8] {
unsafe {
if self.is_null() {
&[]
} else {
std::slice::from_raw_parts(
self.data,
self.len,
)
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn rssn_free_string(
s: *mut c_char
) {
if !s.is_null() {
unsafe {
let _ =
CString::from_raw(s);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn rssn_free_bincode_buffer(
buffer: BincodeBuffer
) {
if !buffer.is_null() {
unsafe {
let _ = Box::from_raw(
std::ptr::slice_from_raw_parts_mut(buffer.data, buffer.len),
);
}
}
}
#[must_use]
pub fn to_c_string(
s: String
) -> *mut c_char {
match CString::new(s) {
| Ok(c_str) => c_str.into_raw(),
| Err(_) => {
std::ptr::null_mut()
},
}
}
pub fn to_json_string<
T: serde::Serialize,
>(
value: &T
) -> *mut c_char {
match serde_json::to_string(value) {
| Ok(json) => to_c_string(json),
| Err(_) => {
std::ptr::null_mut()
},
}
}
#[must_use]
pub fn from_json_string<
T: serde::de::DeserializeOwned,
>(
json: *const c_char
) -> Option<T> {
if json.is_null() {
return None;
}
unsafe {
let c_str =
std::ffi::CStr::from_ptr(
json,
);
c_str
.to_str()
.ok()
.and_then(|s| {
serde_json::from_str(s)
.ok()
})
}
}
pub fn to_bincode_buffer<
T: serde::Serialize,
>(
value: &T
) -> BincodeBuffer {
match bincode_next::serde::encode_to_vec(
value,
bincode_next::config::standard(),
) {
| Ok(bytes) => BincodeBuffer::from_vec(bytes),
| Err(_) => BincodeBuffer::empty(),
}
}
#[must_use]
pub fn from_bincode_buffer<
T: serde::de::DeserializeOwned,
>(
buffer: &BincodeBuffer
) -> Option<T> {
if buffer.is_null() {
return None;
}
unsafe {
let slice = buffer.as_slice();
bincode_next::serde::decode_from_slice(
slice,
bincode_next::config::standard(),
)
.ok()
.map(|(v, _)| v)
}
}
#[must_use]
pub unsafe fn c_str_to_str<'a>(
s: *const c_char
) -> Option<&'a str> {
unsafe {
if s.is_null() {
None
} else {
std::ffi::CStr::from_ptr(s)
.to_str()
.ok()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bincode_buffer_empty() {
let buffer =
BincodeBuffer::empty();
assert!(buffer.is_null());
}
#[test]
fn test_bincode_buffer_from_vec() {
let vec = vec![1, 2, 3, 4];
let buffer =
BincodeBuffer::from_vec(
vec,
);
assert!(!buffer.is_null());
assert_eq!(buffer.len, 4);
unsafe {
assert_eq!(
buffer.as_slice(),
&[1, 2, 3, 4]
);
}
rssn_free_bincode_buffer(
buffer,
);
}
#[test]
fn test_to_c_string() {
let s =
"Hello, World!".to_string();
let c_str = to_c_string(s);
assert!(!c_str.is_null());
rssn_free_string(c_str);
}
}