Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 89 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,13 +384,24 @@ enum TypeSer<S: 'static> {
len: usize,
elem: SerTypeInfo<S>,
},
// Slice {
// elem: SerTypeInfo<S>,
// },
/// A reference `&T`, with the pointee's size and vtable.
/// A dynamically-sized slice `[T]`, with element size and vtable.
/// The slice length is recovered at runtime from `size_of_val`.
Slice {
elem: SerTypeInfo<S>,
},
/// The dynamically-sized string slice `str`.
/// The byte length is recovered at runtime from `size_of_val`.
Str,
/// A reference `&T` to a sized pointee, with the pointee's size and vtable.
Reference {
referent: SerTypeInfo<S>,
},
/// A reference `&[T]` to a dynamically-sized slice.
SliceReference {
elem: SerTypeInfo<S>,
},
/// A reference `&str` to a dynamically-sized string.
StrReference,
/// A type whose structure is not directly supported by reflection
/// (e.g. enums, unions). Falls back to `todo!()` at runtime.
Other,
Expand Down Expand Up @@ -513,46 +524,55 @@ impl<S: Serializer + 'static> TypeSer<S> {
}
TypeKind::Array(array) => {
let ty = array.element_ty;
let type_info = ty.info();
let elem = SerTypeInfo {
name: "", // todo: get type name
size: type_info.size.unwrap(),
size: ty.size().unwrap(),
vtable: get_reflect_vtable::<S>(ty),
};
TypeSer::Array {
len: array.len,
elem,
}
}
// TypeKind::Slice(slice) => {
// let ty = slice.element_ty;
// let type_info = ty.info();
// let elem = SerTypeInfo {
// name: "", // todo: get type name
// size: type_info.size.unwrap(),
// vtable: get_reflect_vtable::<S>(ty),
// };
// TypeSer::Slice { elem }
// }
TypeKind::Reference(reference) => {
let Some(size) = reference.pointee.info().size else {
// Unsized types behind references are not supported here, since `try_as_dyn` currently only works for sized types.
// When `try_as_dyn` is extended to support `?Sized` types, we can remove this check and handle unsized types behind references as well.
return TypeSer::Other;
};
let ty = reference.pointee;
let referent = SerTypeInfo {
TypeKind::Slice(slice) => {
let ty = slice.element_ty;
let elem = SerTypeInfo {
name: "", // todo: get type name
size,
size: ty.size().unwrap(),
vtable: get_reflect_vtable::<S>(ty),
};
TypeSer::Reference { referent }
TypeSer::Slice { elem }
}
TypeKind::Reference(reference) => {
let ty = reference.pointee;
match ty.size() {
Some(size) => {
let referent = SerTypeInfo {
name: "", // todo: get type name
size,
vtable: get_reflect_vtable::<S>(ty),
};
TypeSer::Reference { referent }
}
None => match ty.info().kind {
TypeKind::Slice(slice) => {
let elem_ty = slice.element_ty;
let elem = SerTypeInfo {
name: "", // todo: get type name
size: elem_ty.size().unwrap(),
vtable: get_reflect_vtable::<S>(elem_ty),
};
TypeSer::SliceReference { elem }
}
TypeKind::Str(_) => TypeSer::StrReference,
_ => TypeSer::Other,
},
}
}
TypeKind::Str(_) => TypeSer::Str,
TypeKind::Bool(_) | TypeKind::Char(_) | TypeKind::Int(_) | TypeKind::Float(_) => {
TypeSer::Primitive(type_info.kind)
}
TypeKind::Bool(_)
| TypeKind::Char(_)
| TypeKind::Int(_)
| TypeKind::Float(_)
| TypeKind::Str(_) => TypeSer::Primitive(type_info.kind),
_ => TypeSer::Other,
}
}
Expand All @@ -563,7 +583,7 @@ impl<S: Serializer + 'static> TypeSer<S> {
}
}

impl<T: 'static /* can't add `+ ?Sized` now` */, S: Serializer + 'static> Ser<S> for T {
impl<T: 'static + ?Sized, S: Serializer + 'static> Ser<S> for T {
fn serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
if let Some(specialized) = std::any::try_as_dyn::<_, dyn SpecializedSer<S>>(self) {
specialized.specialized_serialize(serializer)
Expand Down Expand Up @@ -602,14 +622,48 @@ impl<T: 'static /* can't add `+ ?Sized` now` */, S: Serializer + 'static> Ser<S>
}
seq.end()
},
// Slice is handled by the impl for [T] in specialized_impls.rs, so we can assume it's never returned by TypeSer::of
// When T try_as_dyn can be used for ?Sized types, we can remove this assumption and handle slices here as well.
// TypeSer::Slice { elem: _ } => unreachable!(),
TypeSer::Slice { elem } => unsafe {
let total_bytes = std::mem::size_of_val::<T>(self);
let len = total_bytes / elem.size;
let data_ptr = self as *const T as *const u8;
let mut seq = serializer.serialize_seq(Some(len))?;
for i in 0..len {
let elem_ptr = data_ptr.add(i * elem.size);
let elem_value = elem.to_dyn(&*elem_ptr.cast::<()>());
seq.serialize_element(elem_value)?;
}
seq.end()
},
TypeSer::Str => unsafe {
let data_ptr = self as *const T as *const u8;
let len = std::mem::size_of_val::<T>(self);
let bytes = std::slice::from_raw_parts(data_ptr, len);
serializer.serialize_str(std::str::from_utf8_unchecked(bytes))
},
TypeSer::Reference { referent } => unsafe {
let pointee_ptr = *(self as *const T as *const *const u8);
let pointee = referent.to_dyn(&*pointee_ptr.cast::<()>());
pointee.serialize(serializer)
},
TypeSer::SliceReference { elem } => unsafe {
// `T` is statically `&[U]` here, so reading 16 bytes at
// `self` yields the slice's `(data, len)` fat pointer.
let parts = self as *const T as *const (*const u8, usize);
let (data_ptr, len) = *parts;
let mut seq = serializer.serialize_seq(Some(len))?;
for i in 0..len {
let elem_ptr = data_ptr.add(i * elem.size);
let elem_value = elem.to_dyn(&*elem_ptr.cast::<()>());
seq.serialize_element(elem_value)?;
}
seq.end()
},
TypeSer::StrReference => unsafe {
let parts = self as *const T as *const (*const u8, usize);
let (data_ptr, len) = *parts;
let bytes = std::slice::from_raw_parts(data_ptr, len);
serializer.serialize_str(std::str::from_utf8_unchecked(bytes))
},
TypeSer::Other => todo!("{} other!", std::any::type_name::<T>()),
}
}
Expand Down Expand Up @@ -671,9 +725,6 @@ fn serialize_primitive<T: 'static + ?Sized, S: Serializer>(
_ => unreachable!(),
}
},
TypeKind::Str(_str) => {
unreachable!() // str should be handled by SpecializedSerInner
}
_ => unreachable!(),
}
}
46 changes: 12 additions & 34 deletions src/specialized_impls.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,27 @@
//! Specialized [`Ser`] implementations for standard library types.
//! Specialized [`SpecializedSerInner`] implementations for standard library types.
//!
//! These implementations handle types that cannot be serialized through the
//! generic reflection-based blanket impl, either because they are unsized
//! (e.g. `[T]`, `str`) or because they require custom serialization logic
//! (e.g. `Option<T>`, `String`, `Vec<T>`, smart pointers, collections).
//! These implementations handle types that require custom serialization logic
//! (e.g. `Option<T>`, `String`, `Vec<T>`, smart pointers, collections), or
//! unsized types whose default reflection-based serialization is not the
//! desired output (e.g. `OsStr`, `Path`).
//!
//! The implementations are split into two categories:
//!
//! - **Direct `Ser` impls**: for unsized types (`[T]`, `str`, `OsStr`, `Path`)
//! that cannot go through the `T: 'static` blanket impl.
//! - **`SpecializedSerInner` impls**: for sized types (`Option<T>`, `&str`,
//! `String`, `Vec<T>`, `Box<T>`, collections, etc.) that override the blanket
//! impl via `try_as_dyn` dispatch.
//! All specializations are dispatched through `try_as_dyn` in the blanket
//! `Ser` impl, so they work for both sized and unsized types without requiring
//! the `min_specialization` feature.

use super::*;
use std::{ffi::OsStr, ops::Deref, path::Path};

/// Serializes a slice `[T]` as a sequence.
impl<T: Ser<S>, S: Serializer + 'static> Ser<S> for [T] {
fn serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for elem in self {
seq.serialize_element(elem)?;
}
seq.end()
}
}

/// Serializes a `str` slice as a string.
impl<S: Serializer> Ser<S> for str {
fn serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self)
}
}

/// Serializes an `OsStr` as a string (with lossy UTF-8 conversion).
impl<S: Serializer> Ser<S> for OsStr {
fn serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
impl<S: Serializer> SpecializedSerInner<S> for OsStr {
fn specialized_serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.to_string_lossy().as_ref())
}
}

/// Serializes a `Path` as a string (with lossy UTF-8 conversion).
impl<S: Serializer> Ser<S> for Path {
fn serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
impl<S: Serializer> SpecializedSerInner<S> for Path {
fn specialized_serialize(&self, serializer: &mut S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_os_str().to_string_lossy().as_ref())
}
}
Expand Down