use std::alloc::{GlobalAlloc, Layout, System};
use std::fmt;
use std::marker::PhantomData;
use std::net::Ipv4Addr;
use std::sync::atomic::{AtomicU64, Ordering};
use serde::de::{
Deserialize, DeserializeOwned, Deserializer, Error, IgnoredAny, MapAccess, Visitor,
};
use ktav::de::from_value;
use ktav::value::{ObjectMap, Scalar, Value};
const LONG_KEY: &str = "a_very_long_map_key_name_beyond_inline_capacity";
struct ExtractKey<T> {
key: T,
}
impl<'de, T: Deserialize<'de>> Deserialize<'de> for ExtractKey<T> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct KeyVisitor<T>(PhantomData<T>);
impl<'de, T: Deserialize<'de>> Visitor<'de> for KeyVisitor<T> {
type Value = ExtractKey<T>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an object with a key to extract")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let key = map
.next_key::<T>()?
.ok_or_else(|| Error::custom("expected at least one entry"))?;
map.next_value::<IgnoredAny>()?;
while map.next_key::<IgnoredAny>()?.is_some() {
map.next_value::<IgnoredAny>()?;
}
Ok(ExtractKey { key })
}
}
deserializer.deserialize_map(KeyVisitor(PhantomData))
}
}
struct KeyLen(usize);
impl<'de> Deserialize<'de> for KeyLen {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
struct LenVisitor;
impl<'de> Visitor<'de> for LenVisitor {
type Value = KeyLen;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a string key")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(KeyLen(v.len()))
}
}
deserializer.deserialize_str(LenVisitor)
}
}
struct CountingAlloc;
static ALLOCS: AtomicU64 = AtomicU64::new(0);
#[global_allocator]
static GLOBAL: CountingAlloc = CountingAlloc;
unsafe impl GlobalAlloc for CountingAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOCS.fetch_add(1, Ordering::Relaxed);
System.alloc(layout)
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
ALLOCS.fetch_add(1, Ordering::Relaxed);
System.alloc_zeroed(layout)
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
ALLOCS.fetch_add(1, Ordering::Relaxed);
System.realloc(ptr, layout, new_size)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}
fn counted<T>(f: impl FnOnce() -> T) -> (u64, T) {
let before = ALLOCS.load(Ordering::Relaxed);
let out = f();
(ALLOCS.load(Ordering::Relaxed) - before, out)
}
fn one_entry_object(key: Scalar) -> Value {
let mut obj = ObjectMap::default();
obj.insert(key, Value::Null);
Value::Object(obj)
}
fn scalar_control<T: DeserializeOwned>(text: &str) -> (u64, T) {
counted(|| from_value::<T>(Value::String(text.into())).unwrap())
}
fn object_key_ptr(value: &Value) -> usize {
match value {
Value::Object(obj) => obj.keys().next().unwrap().as_str().as_ptr() as usize,
other => panic!("expected object, got {other:?}"),
}
}
#[test]
fn key_read_allocation_matrix() {
let _ = from_value::<ExtractKey<Scalar>>(one_entry_object("port".into())).unwrap();
let obj = one_entry_object("port".into());
let (n, got) = counted(|| from_value::<ExtractKey<Scalar>>(obj).unwrap());
assert_eq!(n, 0, "Scalar short key allocated {n} times");
assert_eq!(got.key.as_str(), "port");
let (c, _) = scalar_control::<Scalar>("port");
assert_eq!(c, 0, "scalar control allocated {c} times");
let obj = one_entry_object("port".into());
let (n, got) = counted(|| from_value::<ExtractKey<Option<Scalar>>>(obj).unwrap());
assert_eq!(n, 0, "Option<Scalar> short key allocated {n} times");
assert_eq!(got.key.as_deref(), Some("port"));
let obj = one_entry_object("127.0.0.1".into());
let (n, got) = counted(|| from_value::<ExtractKey<Ipv4Addr>>(obj).unwrap());
assert_eq!(n, 0, "Ipv4Addr short key allocated {n} times");
assert_eq!(got.key, Ipv4Addr::new(127, 0, 0, 1));
let (c, _) = scalar_control::<Ipv4Addr>("127.0.0.1");
assert_eq!(c, 0, "Ipv4Addr control allocated {c} times");
let obj = one_entry_object("port".into());
let (n, got) = counted(|| from_value::<ExtractKey<KeyLen>>(obj).unwrap());
assert_eq!(n, 0, "slice-only visitor short key allocated {n} times");
assert_eq!(got.key.0, 4);
let (c, _) = scalar_control::<KeyLen>("port");
assert_eq!(c, 0, "slice-only visitor control allocated {c} times");
let obj = one_entry_object("port".into());
let (n, got) = counted(|| from_value::<ExtractKey<String>>(obj).unwrap());
assert_eq!(n, 1, "String short key allocated {n} times");
assert_eq!(got.key, "port");
let (c, _) = scalar_control::<String>("port");
assert_eq!(c, 1, "String control allocated {c} times");
let obj = one_entry_object("42".into());
let (n, got) = counted(|| from_value::<ExtractKey<u32>>(obj).unwrap());
assert_eq!(n, 0, "u32 short key allocated {n} times");
assert_eq!(got.key, 42);
let source = one_entry_object(LONG_KEY.into());
let key_ptr = object_key_ptr(&source);
let (n, got) = counted(|| from_value::<ExtractKey<String>>(source).unwrap());
assert_eq!(n, 0, "String long key allocated {n} times");
assert_eq!(
got.key.as_ptr() as usize,
key_ptr,
"String long key buffer must be moved, not copied"
);
assert_eq!(got.key.len(), 47);
let source = one_entry_object(LONG_KEY.into());
let key_ptr = object_key_ptr(&source);
let (n, got) = counted(|| from_value::<ExtractKey<Scalar>>(source).unwrap());
assert_eq!(n, 0, "Scalar long key allocated {n} times");
assert_eq!(
got.key.as_ptr() as usize,
key_ptr,
"Scalar long key buffer must be moved, not copied"
);
assert_eq!(got.key.len(), 47);
}