use std::fmt;
use std::ops::Deref;
use allocative::Allocative;
use allocative::Visitor;
use dupe::Dupe;
use serde::Deserialize;
use serde::Serialize;
use crate::PagableDeserialize;
use crate::PagableDeserializer;
use crate::PagableSerialize;
use crate::PagableSerializer;
pub struct StaticValueEntry<T: 'static + ?Sized> {
pub value: &'static T,
pub index: std::cell::Cell<u32>,
}
unsafe impl<T: Sync + ?Sized> Sync for StaticValueEntry<T> {}
impl<T: PartialEq + ?Sized> PartialEq for StaticValueEntry<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl<T: Eq + ?Sized> Eq for StaticValueEntry<T> {}
impl<T: std::hash::Hash + ?Sized> std::hash::Hash for StaticValueEntry<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
pub struct StaticValue<T: 'static + ?Sized>(pub &'static StaticValueEntry<T>);
impl<T: ?Sized> Clone for StaticValue<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for StaticValue<T> {}
impl<T: ?Sized> Dupe for StaticValue<T> {}
impl<T: PartialEq + ?Sized> PartialEq for StaticValue<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: Eq + ?Sized> Eq for StaticValue<T> {}
impl<T: std::hash::Hash + ?Sized> std::hash::Hash for StaticValue<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T: ?Sized> StaticValue<T> {
pub const fn value(self) -> &'static T {
self.0.value
}
}
impl<T: ?Sized> Deref for StaticValue<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.value
}
}
impl<T: fmt::Debug + ?Sized> fmt::Debug for StaticValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.0.value, f)
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for StaticValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0.value, f)
}
}
impl<T: ?Sized> Allocative for StaticValue<T> {
fn visit<'a, 'b: 'a>(&self, visitor: &'a mut Visitor<'b>) {
let _ = visitor;
}
}
pub struct StaticValueRegistry<T: 'static + ?Sized> {
entries: Vec<&'static StaticValueEntry<T>>,
}
impl<T: Ord + ?Sized> StaticValueRegistry<T> {
pub fn from_entries<'a>(iter: impl Iterator<Item = &'a &'static StaticValueEntry<T>>) -> Self {
let mut entries: Vec<&'static StaticValueEntry<T>> = iter.copied().collect();
entries.sort_by_key(|e| e.value);
for (i, entry) in entries.iter().enumerate() {
entry.index.set(i as u32);
}
StaticValueRegistry { entries }
}
}
pub trait HasStaticValueRegistry: Ord + 'static {
fn registry() -> &'static StaticValueRegistry<Self>;
fn ensure_registry_initialized() {
let _ = Self::registry();
}
}
impl<T: HasStaticValueRegistry + ?Sized> PagableSerialize for StaticValue<T> {
fn pagable_serialize(&self, serializer: &mut dyn PagableSerializer) -> crate::Result<()> {
T::ensure_registry_initialized();
let index = self.0.index.get();
if index == u32::MAX {
panic!("Unregistered static value. Register it with: pagable::static_value!(...)",);
}
Ok(index.serialize(serializer.serde())?)
}
}
impl<'de, T: HasStaticValueRegistry + ?Sized> PagableDeserialize<'de> for StaticValue<T> {
fn pagable_deserialize<De: PagableDeserializer<'de> + ?Sized>(
deserializer: &mut De,
) -> crate::Result<Self> {
T::ensure_registry_initialized();
let index = u32::deserialize(deserializer.serde())?;
let registry = T::registry();
match registry.entries.get(index as usize) {
Some(&entry) => Ok(StaticValue(entry)),
None => panic!(
"Invalid static value index: {}. Registry has {} entries.",
index,
registry.entries.len()
),
}
}
}
pub type StaticStr = StaticValue<str>;
crate::declare_static_value_type!(str, StaticStrEntry);
impl StaticStr {
pub const fn as_str(self) -> &'static str {
self.0.value
}
}
#[macro_export]
macro_rules! static_str {
($vis:vis $name:ident = $val:expr) => {
$crate::static_value!($vis $name: str = $val, $crate::static_value::StaticStrEntry);
};
}
pub type StaticBytes = StaticValue<[u8]>;
crate::declare_static_value_type!([u8], StaticBytesEntry);
impl StaticBytes {
pub const fn as_bytes(self) -> &'static [u8] {
self.0.value
}
}
#[macro_export]
macro_rules! static_bytes {
($vis:vis $name:ident = $val:expr) => {
$crate::static_value!($vis $name: [u8] = $val, $crate::static_value::StaticBytesEntry);
};
}
#[macro_export]
macro_rules! static_value {
($vis:vis $name:ident : $ty:ty = $val:expr, $wrapper:path) => {
$vis static $name: $crate::StaticValue<$ty> = {
static ENTRY: $crate::static_value::StaticValueEntry<$ty> = $crate::static_value::StaticValueEntry {
value: $val,
index: $crate::__internal::Cell::new(u32::MAX),
};
$crate::__internal::inventory::submit! { $wrapper(&ENTRY) }
$crate::StaticValue(&ENTRY)
};
};
}
#[macro_export]
macro_rules! declare_static_value_type {
($ty:ty, $wrapper:ident) => {
#[doc(hidden)]
pub struct $wrapper(pub &'static $crate::static_value::StaticValueEntry<$ty>);
$crate::__internal::inventory::collect!($wrapper);
impl $crate::static_value::HasStaticValueRegistry for $ty {
fn registry() -> &'static $crate::static_value::StaticValueRegistry<$ty> {
static REGISTRY: $crate::__internal::once_cell::sync::Lazy<
$crate::static_value::StaticValueRegistry<$ty>,
> = $crate::__internal::once_cell::sync::Lazy::new(|| {
$crate::static_value::StaticValueRegistry::from_entries(
$crate::__internal::inventory::iter::<$wrapper>
.into_iter()
.map(|w| &w.0),
)
});
®ISTRY
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::TestingDeserializer;
use crate::testing::TestingSerializer;
use crate::traits::PagableDeserialize;
use crate::traits::PagableSerialize;
crate::static_str!(STRING_ALPHA = "alpha");
crate::static_str!(STRING_BETA = "beta");
crate::static_str!(STRING_GAMMA = "gamma");
crate::static_str!(STRING_DELTA = "delta");
#[test]
fn test_str_indices_are_unique_and_sorted_alphabetically() {
<str as HasStaticValueRegistry>::ensure_registry_initialized();
let strings = [STRING_ALPHA, STRING_BETA, STRING_DELTA, STRING_GAMMA];
let indices: Vec<_> = strings.iter().map(|s| s.0.index.get()).collect();
for (i, &idx) in indices.iter().enumerate() {
assert_ne!(idx, u32::MAX);
if i > 0 {
assert!(
indices[i - 1] < idx,
"indices should be strictly increasing"
);
}
}
}
#[test]
fn test_str_serialize_multiple() -> crate::Result<()> {
let input = [STRING_GAMMA, STRING_ALPHA, STRING_DELTA, STRING_BETA];
let mut serializer = TestingSerializer::new();
for s in &input {
s.pagable_serialize(&mut serializer)?;
}
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
for expected in &input {
let restored = StaticStr::pagable_deserialize(&mut deserializer)?;
assert_eq!(expected.as_str(), restored.as_str());
}
Ok(())
}
crate::static_bytes!(BYTES_HELLO = b"hello");
crate::static_bytes!(BYTES_WORLD = b"world");
crate::static_bytes!(BYTES_EMPTY = b"");
#[test]
fn test_bytes_roundtrip() -> crate::Result<()> {
let input = [BYTES_HELLO, BYTES_WORLD, BYTES_EMPTY];
let mut serializer = TestingSerializer::new();
for b in &input {
b.pagable_serialize(&mut serializer)?;
}
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
for expected in &input {
let restored = StaticBytes::pagable_deserialize(&mut deserializer)?;
assert_eq!(expected.as_bytes(), restored.as_bytes());
}
Ok(())
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct WrappedType(u32);
crate::declare_static_value_type!(WrappedType, WrappedTypeEntry);
static VAL: WrappedType = WrappedType(42);
crate::static_value!(STATIC_VAL: WrappedType = &VAL, WrappedTypeEntry);
#[test]
fn test_wrapper_roundtrip() -> crate::Result<()> {
let mut serializer = TestingSerializer::new();
STATIC_VAL.pagable_serialize(&mut serializer)?;
let bytes = serializer.finish();
let mut deserializer = TestingDeserializer::new(&bytes);
let restored = StaticValue::<WrappedType>::pagable_deserialize(&mut deserializer)?;
assert_eq!(restored.value().0, 42);
Ok(())
}
}