use curl_sys::{CURL_BLOB_NOCOPY, curl_blob};
use std::{cell::UnsafeCell, fmt, ptr, sync::Arc};
#[derive(Clone)]
pub(crate) struct Blob {
arc: Arc<Inner<dyn AsRef<[u8]> + Send + Sync + 'static>>,
}
struct Inner<T: ?Sized> {
blob: UnsafeCell<curl_blob>,
object: T,
}
impl Blob {
pub(crate) fn new<B: AsRef<[u8]> + Send + Sync + 'static>(bytes: B) -> Self {
let arc = Arc::new(Inner {
blob: UnsafeCell::new(curl_blob {
data: ptr::null_mut(),
len: 0,
flags: CURL_BLOB_NOCOPY,
}),
object: bytes,
});
let slice = arc.object.as_ref();
unsafe {
arc.blob.get().write(curl_blob {
data: slice.as_ptr().cast_mut().cast(),
len: slice.len(),
flags: CURL_BLOB_NOCOPY,
});
}
Self { arc }
}
pub(crate) fn len(&self) -> usize {
self.as_raw_ref().len
}
pub(crate) fn as_raw_ptr(&self) -> *const curl_blob {
self.arc.blob.get()
}
fn as_raw_ref(&self) -> &curl_blob {
unsafe { &*self.as_raw_ptr() }
}
}
impl fmt::Debug for Blob {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Blob").field("len", &self.len()).finish()
}
}
unsafe impl<T: Send + ?Sized> Send for Inner<T> {}
unsafe impl<T: Sync + ?Sized> Sync for Inner<T> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn blob_len_matches_input() {
let data = vec![1, 2, 3, 4, 5];
let blob = Blob::new(data);
assert_eq!(blob.len(), 5);
}
#[test]
fn blob_has_expected_flags() {
let data = vec![1, 2, 3, 4, 5];
let blob = Blob::new(data);
assert_eq!(blob.as_raw_ref().flags, CURL_BLOB_NOCOPY);
}
#[test]
fn blob_from_stack_array() {
let data = [1, 2, 3];
let blob = Blob::new(data);
assert_eq!(blob.len(), 3);
}
#[test]
fn blob_clone_preserves_data_and_len() {
let data = "hello world";
let len = data.len();
let blob1 = Blob::new(data);
let blob2 = blob1.clone();
assert_eq!(blob1.len(), len);
assert_eq!(blob2.len(), len);
let raw1 = blob1.as_raw_ref();
let raw2 = blob2.as_raw_ref();
assert!(!raw1.data.is_null());
assert_eq!(raw1.data, raw2.data);
assert_eq!(raw1.len, raw2.len);
assert_eq!(raw1.flags, raw2.flags);
}
}