use serde::de;
use std::hash::{Hash, Hasher};
pub type BoxError = Box<dyn std::error::Error>;
#[derive(Debug, Copy, Clone)]
pub struct StrByAddr<'a>(pub &'a str);
impl<'a> Hash for StrByAddr<'a> {
#[inline(always)]
fn hash<H: Hasher>(&self, state: &mut H) {
std::ptr::hash(self.0, state);
}
}
impl<'a> PartialEq for StrByAddr<'a> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
std::ptr::addr_eq(self.0, other.0)
}
}
impl<'a> Eq for StrByAddr<'a> {}
pub trait MapAccessSerdeExt<'de>: de::MapAccess<'de> {
#[inline]
fn deserialize_once_into_option<T>(
&mut self,
slot: &mut Option<T>,
field_name: &'static str,
) -> Result<(), Self::Error>
where
T: serde::Deserialize<'de>,
{
if slot.is_some() {
return Err(de::Error::duplicate_field(field_name));
}
*slot = self.next_value::<Option<T>>()?;
Ok(())
}
}
impl<'de, A> MapAccessSerdeExt<'de> for A where A: de::MapAccess<'de> {}
pub fn vec_into_ntex_bytes(vec: Vec<u8>) -> ntex::util::Bytes {
ntex::util::Bytes::from_ext(VecStorage(vec))
}
struct VecStorage(Vec<u8>);
const VEC_STORAGE_VTABLE: ntex_bytes::StorageVTable = ntex_bytes::StorageVTable::new(
|addr, _| unsafe { (*addr.cast::<Vec<u8>>()).as_ptr() },
|_, len| len,
|addr, len| unsafe {
std::sync::Arc::increment_strong_count(addr.cast::<Vec<u8>>());
Some((addr, len))
},
|addr, _| unsafe { std::sync::Arc::decrement_strong_count(addr.cast::<Vec<u8>>()) },
);
impl ntex_bytes::StorageExt for VecStorage {
fn create(self) -> (*const u8, usize, &'static ntex_bytes::StorageVTable) {
let len = self.0.len();
let addr = std::sync::Arc::into_raw(std::sync::Arc::new(self.0));
(addr.cast::<u8>(), len, &VEC_STORAGE_VTABLE)
}
}
#[cfg(test)]
mod tests {
use super::vec_into_ntex_bytes;
#[test]
fn vec_into_ntex_bytes_keeps_the_data_through_clones_and_slices() {
let data: Vec<u8> = (0..200u8).collect();
let ptr = data.as_ptr();
let bytes = vec_into_ntex_bytes(data.clone());
assert_eq!(&bytes[..], &data[..]);
let clone = bytes.clone();
assert_eq!(clone.as_ptr(), bytes.as_ptr(), "a clone shares the buffer");
drop(bytes);
assert_eq!(&clone[..], &data[..], "the buffer outlives the original");
assert_eq!(&clone.slice(10..20)[..], &data[10..20]);
assert_eq!(&clone[..], &data[..]);
let moved = vec_into_ntex_bytes(data);
assert_eq!(moved.as_ptr(), ptr, "the Vec's buffer is used as is");
}
#[test]
fn vec_into_ntex_bytes_copies_the_rest_after_a_partial_write() {
let data: Vec<u8> = (0..=255u8).cycle().take(64 * 1024).collect();
let mut body = vec_into_ntex_bytes(data.clone());
let ptr = body.as_ptr();
body.advance_to(1000);
assert_eq!(&body[..], &data[1000..]);
assert_ne!(
body.as_ptr(),
ptr.wrapping_add(1000),
"the rest is a copy, not our buffer"
);
let copy_ptr = body.as_ptr();
body.advance_to(1000);
assert_eq!(&body[..], &data[2000..]);
assert_eq!(
body.as_ptr(),
copy_ptr.wrapping_add(1000),
"copied only once"
);
}
#[test]
fn vec_into_ntex_bytes_uses_the_length_not_the_capacity() {
let mut data = Vec::with_capacity(4096);
data.extend_from_slice(b"{\"data\":null}");
let bytes = vec_into_ntex_bytes(data);
assert_eq!(&bytes[..], b"{\"data\":null}");
assert_eq!(bytes.len(), 13);
let empty = vec_into_ntex_bytes(Vec::new());
assert!(empty.is_empty());
assert_eq!(&empty.clone()[..], b"");
}
#[test]
fn vec_into_ntex_bytes_clones_can_be_dropped_on_other_threads() {
let bytes = vec_into_ntex_bytes(vec![7u8; 1024]);
let clones: Vec<_> = (0..4).map(|_| bytes.clone()).collect();
drop(bytes);
let handles: Vec<_> = clones
.into_iter()
.map(|b| std::thread::spawn(move || assert!(b.iter().all(|&x| x == 7))))
.collect();
for h in handles {
h.join().unwrap();
}
}
}