use serde::ser::{self, Serialize};
#[derive(Debug, thiserror::Error)]
#[error("logical host storage estimate is unavailable")]
pub(crate) struct StorageError;
impl ser::Error for StorageError {
fn custom<T: std::fmt::Display>(_: T) -> Self {
Self
}
}
pub(crate) fn heap_bytes<T: Serialize + ?Sized>(value: &T) -> Option<u64> {
value.serialize(Storage { inline: false }).ok()
}
const COMPOSITE_INLINE: u64 = 1024;
const _: () = {
use eredu_core::{capture::*, discovery::*, intervention::*};
assert!(std::mem::size_of::<CaptureSelection>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<CaptureSlice>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<ObservationPoint>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<TensorAxis>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<SymbolicDimension>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<InterventionOperation>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<InterventionPoint>() <= COMPOSITE_INLINE as usize);
assert!(std::mem::size_of::<InterventionAction>() <= COMPOSITE_INLINE as usize);
};
struct Storage {
inline: bool,
}
impl Storage {
fn bytes(self, dynamic: usize, inline: u64) -> Result<u64, StorageError> {
u64::try_from(dynamic)
.map_err(|_| StorageError)?
.checked_add(if self.inline { inline } else { 0 })
.ok_or(StorageError)
}
}
struct Fields {
bytes: u64,
elements: bool,
}
impl Fields {
fn add<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), StorageError> {
self.bytes = self
.bytes
.checked_add(value.serialize(Storage {
inline: self.elements,
})?)
.ok_or(StorageError)?;
Ok(())
}
}
macro_rules! scalar {
($($method:ident($ty:ty)),*) => { $(fn $method(self, _: $ty) -> Result<u64, StorageError> { self.bytes(0, std::mem::size_of::<$ty>() as u64) })* };
}
impl ser::Serializer for Storage {
type Ok = u64;
type Error = StorageError;
type SerializeSeq = Fields;
type SerializeTuple = Fields;
type SerializeTupleStruct = Fields;
type SerializeTupleVariant = Fields;
type SerializeMap = Fields;
type SerializeStruct = Fields;
type SerializeStructVariant = Fields;
scalar!(
serialize_bool(bool),
serialize_i8(i8),
serialize_i16(i16),
serialize_i32(i32),
serialize_i64(i64),
serialize_i128(i128),
serialize_u8(u8),
serialize_u16(u16),
serialize_u32(u32),
serialize_u64(u64),
serialize_u128(u128),
serialize_f32(f32),
serialize_f64(f64),
serialize_char(char)
);
fn serialize_str(self, value: &str) -> Result<u64, StorageError> {
self.bytes(value.len(), std::mem::size_of::<String>() as u64)
}
fn serialize_bytes(self, value: &[u8]) -> Result<u64, StorageError> {
self.bytes(value.len(), std::mem::size_of::<Vec<u8>>() as u64)
}
fn serialize_none(self) -> Result<u64, StorageError> {
self.bytes(0, COMPOSITE_INLINE)
}
fn serialize_some<T: Serialize + ?Sized>(self, value: &T) -> Result<u64, StorageError> {
let tag = if self.inline { 16 } else { 0 };
value.serialize(self)?.checked_add(tag).ok_or(StorageError)
}
fn serialize_unit(self) -> Result<u64, StorageError> {
Ok(0)
}
fn serialize_unit_struct(self, _: &'static str) -> Result<u64, StorageError> {
self.bytes(0, COMPOSITE_INLINE)
}
fn serialize_unit_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
) -> Result<u64, StorageError> {
self.bytes(0, COMPOSITE_INLINE)
}
fn serialize_newtype_struct<T: Serialize + ?Sized>(
self,
_: &'static str,
value: &T,
) -> Result<u64, StorageError> {
self.bytes(0, COMPOSITE_INLINE)?
.checked_add(value.serialize(Storage { inline: false })?)
.ok_or(StorageError)
}
fn serialize_newtype_variant<T: Serialize + ?Sized>(
self,
_: &'static str,
_: u32,
_: &'static str,
value: &T,
) -> Result<u64, StorageError> {
self.bytes(0, COMPOSITE_INLINE)?
.checked_add(value.serialize(Storage { inline: false })?)
.ok_or(StorageError)
}
fn serialize_seq(self, _: Option<usize>) -> Result<Fields, StorageError> {
Ok(Fields {
bytes: self.bytes(0, std::mem::size_of::<Vec<()>>() as u64)?,
elements: true,
})
}
fn serialize_tuple(self, _: usize) -> Result<Fields, StorageError> {
Ok(Fields {
bytes: 0,
elements: self.inline,
})
}
fn serialize_tuple_struct(self, _: &'static str, _: usize) -> Result<Fields, StorageError> {
self.serialize_tuple(0)
}
fn serialize_tuple_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Fields, StorageError> {
self.serialize_tuple(0)
}
fn serialize_map(self, _: Option<usize>) -> Result<Fields, StorageError> {
self.serialize_seq(None)
}
fn serialize_struct(self, _: &'static str, _: usize) -> Result<Fields, StorageError> {
Ok(Fields {
bytes: self.bytes(0, COMPOSITE_INLINE)?,
elements: false,
})
}
fn serialize_struct_variant(
self,
_: &'static str,
_: u32,
_: &'static str,
_: usize,
) -> Result<Fields, StorageError> {
self.serialize_struct("variant", 0)
}
fn collect_str<T: std::fmt::Display + ?Sized>(self, value: &T) -> Result<u64, StorageError> {
struct Count(u64);
impl std::fmt::Write for Count {
fn write_str(&mut self, text: &str) -> std::fmt::Result {
self.0 = self
.0
.checked_add(u64::try_from(text.len()).map_err(|_| std::fmt::Error)?)
.ok_or(std::fmt::Error)?;
Ok(())
}
}
use std::fmt::Write;
let mut count = Count(0);
write!(&mut count, "{value}").map_err(|_| StorageError)?;
count
.0
.checked_add(self.bytes(0, std::mem::size_of::<String>() as u64)?)
.ok_or(StorageError)
}
}
macro_rules! sequence {
($trait:ident, $method:ident) => {
impl ser::$trait for Fields {
type Ok = u64;
type Error = StorageError;
fn $method<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), StorageError> {
self.add(value)
}
fn end(self) -> Result<u64, StorageError> {
Ok(self.bytes)
}
}
};
}
sequence!(SerializeSeq, serialize_element);
sequence!(SerializeTuple, serialize_element);
sequence!(SerializeTupleStruct, serialize_field);
sequence!(SerializeTupleVariant, serialize_field);
impl ser::SerializeMap for Fields {
type Ok = u64;
type Error = StorageError;
fn serialize_key<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), StorageError> {
self.add(value)
}
fn serialize_value<T: Serialize + ?Sized>(&mut self, value: &T) -> Result<(), StorageError> {
self.add(value)
}
fn end(self) -> Result<u64, StorageError> {
Ok(self.bytes)
}
}
macro_rules! structure {
($trait:ident) => {
impl ser::$trait for Fields {
type Ok = u64;
type Error = StorageError;
fn serialize_field<T: Serialize + ?Sized>(
&mut self,
_: &'static str,
value: &T,
) -> Result<(), StorageError> {
self.add(value)
}
fn end(self) -> Result<u64, StorageError> {
Ok(self.bytes)
}
}
};
}
structure!(SerializeStruct);
structure!(SerializeStructVariant);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_nested_payloads_charge_inline_entries_and_dynamic_contents() {
#[derive(serde::Serialize)]
struct Payload {
values: Vec<String>,
indices: Vec<Vec<u64>>,
}
let value = Payload {
values: vec!["small".into(), "x".repeat(1024)],
indices: vec![vec![1, 2, 3]],
};
let expected =
2 * std::mem::size_of::<String>() + 1029 + std::mem::size_of::<Vec<u64>>() + 24;
assert_eq!(heap_bytes(&value), Some(expected as u64));
}
}