diff --git a/src/lib.rs b/src/lib.rs index d8d263d..49507ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -384,13 +384,24 @@ enum TypeSer { len: usize, elem: SerTypeInfo, }, - // Slice { - // elem: SerTypeInfo, - // }, - /// 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, + }, + /// 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, }, + /// A reference `&[T]` to a dynamically-sized slice. + SliceReference { + elem: SerTypeInfo, + }, + /// 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, @@ -513,10 +524,9 @@ impl TypeSer { } 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::(ty), }; TypeSer::Array { @@ -524,35 +534,45 @@ impl TypeSer { 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::(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::(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::(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::(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, } } @@ -563,7 +583,7 @@ impl TypeSer { } } -impl Ser for T { +impl Ser for T { fn serialize(&self, serializer: &mut S) -> Result { if let Some(specialized) = std::any::try_as_dyn::<_, dyn SpecializedSer>(self) { specialized.specialized_serialize(serializer) @@ -602,14 +622,48 @@ impl Ser } 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::(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::(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::()), } } @@ -671,9 +725,6 @@ fn serialize_primitive( _ => unreachable!(), } }, - TypeKind::Str(_str) => { - unreachable!() // str should be handled by SpecializedSerInner - } _ => unreachable!(), } } diff --git a/src/specialized_impls.rs b/src/specialized_impls.rs index 5975f6e..62d5548 100644 --- a/src/specialized_impls.rs +++ b/src/specialized_impls.rs @@ -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`, `String`, `Vec`, smart pointers, collections). +//! These implementations handle types that require custom serialization logic +//! (e.g. `Option`, `String`, `Vec`, 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`, `&str`, -//! `String`, `Vec`, `Box`, 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, S: Serializer + 'static> Ser for [T] { - fn serialize(&self, serializer: &mut S) -> Result { - 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 Ser for str { - fn serialize(&self, serializer: &mut S) -> Result { - serializer.serialize_str(self) - } -} - /// Serializes an `OsStr` as a string (with lossy UTF-8 conversion). -impl Ser for OsStr { - fn serialize(&self, serializer: &mut S) -> Result { +impl SpecializedSerInner for OsStr { + fn specialized_serialize(&self, serializer: &mut S) -> Result { serializer.serialize_str(self.to_string_lossy().as_ref()) } } /// Serializes a `Path` as a string (with lossy UTF-8 conversion). -impl Ser for Path { - fn serialize(&self, serializer: &mut S) -> Result { +impl SpecializedSerInner for Path { + fn specialized_serialize(&self, serializer: &mut S) -> Result { serializer.serialize_str(self.as_os_str().to_string_lossy().as_ref()) } }