Skip to content

Commit

Permalink
attr vtable
Browse files Browse the repository at this point in the history
  • Loading branch information
scottlamb committed Dec 17, 2021
1 parent 511f53b commit cd706e5
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 82 deletions.
74 changes: 30 additions & 44 deletions static-xml-derive/src/deserialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,57 +91,45 @@ fn finalize_visitor_fields(struct_: &ElementStruct) -> Vec<TokenStream> {
struct_.fields.iter().map(|field| {
let span = field.inner.span();
let ty = &field.inner.ty;
let default = field.default.then(|| {
quote_spanned! {span=>
<#ty as ::std::default::Default>::default
}
});
let default = match field.default {
true => quote_spanned! {span=> Some(<#ty as ::std::default::Default>::default) },
false => quote! { None }
};
match field.mode {
ElementFieldMode::Element { sorted_elements_pos: p } => {
let field_present = format_ident!("{}_present", field.ident);
let field_mut = field_mut(field);
let d = if let Some(d) = default {
quote! { Some(#d) }
} else {
quote! { None }
};
quote_spanned! {span=>
// SAFETY: #field_mut and #field_present are valid/consistent.
unsafe {
::static_xml::de::DeserializeElementField::finalize(
::static_xml::de::ElementField::finalize(
&mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>),
&mut self.#field_present,
&ELEMENTS[#p],
#d,
#default,
)?;
}
}
}
ElementFieldMode::Attribute { sorted_attributes_pos: p } => {
let field_present = format_ident!("{}_present", field.ident);
let field_mut = field_mut(field);
let value = match default {
Some(d) => quote! { #d() },
None => quote_spanned! {span=>
<#ty as ::static_xml::de::DeserializeAttrField>::missing(&ATTRIBUTES[#p])?
}
};
quote_spanned! {span=>
if !self.#field_present {
// SAFETY: #field_mut is a valid pointer to uninitialized memory.
unsafe { ::std::ptr::write(#field_mut /*as *mut #ty*/, #value) };
// SAFETY: #field_mut and #field_present are valid/consistent.
unsafe {
::static_xml::de::AttrField::finalize(
&mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>),
&mut self.#field_present,
&ATTRIBUTES[#p],
#default,
)?;
}
}
}
ElementFieldMode::Flatten => {
let field_visitor = format_ident!("{}_visitor", field.ident);
let d = if let Some(d) = default {
quote! { Some(#d) }
} else {
quote! { None }
};
quote_spanned! {span=>
<#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #d)?;
<#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #default)?;
}
}
ElementFieldMode::Text => todo!(),
Expand Down Expand Up @@ -172,21 +160,19 @@ fn attribute_match_branches(struct_: &ElementStruct) -> Vec<TokenStream> {
.enumerate()
.map(|(i, &p)| {
let field = &struct_.fields[p];
let field_present = format_ident!("{}_present", field.ident);
let ty = &field.inner.ty;
let span = field.inner.span();
let field_present = format_ident!("{}_present", &field.ident);
let field_mut = field_mut(field);
quote! {
Some(#i) if self.#field_present => {
Err(::static_xml::de::VisitorError::duplicate_attribute(name))
}
Some(#i) => {
// SAFETY: #field_mut is a valid pointer to uninitialized memory.
unsafe {
::std::ptr::write(
#field_mut,
::static_xml::de::DeserializeAttrField::init(value)?,
);
}
self.#field_present = true;
quote_spanned! {span=>
// SAFETY: #field_mut and #field_present are consistent/valid.
Some(#i) => unsafe {
::static_xml::de::AttrField::attribute(
&mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>),
&mut self.#field_present,
name,
value,
)?;
Ok(None)
}
}
Expand All @@ -208,7 +194,7 @@ fn element_match_branches(struct_: &ElementStruct) -> Vec<TokenStream> {
quote_spanned! {span=>
// SAFETY: #field_mut and #field_present are consistent/valid.
Some(#i) => unsafe {
::static_xml::de::DeserializeElementField::element(
::static_xml::de::ElementField::element(
&mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>),
&mut self.#field_present,
child,
Expand Down Expand Up @@ -362,7 +348,7 @@ fn do_enum(enum_: &ElementEnum) -> TokenStream {
// This is a bit silly when we already know it's
// initialized, but it lets us reuse code.
let mut initialized = true;
::static_xml::de::DeserializeElementField::element(
::static_xml::de::ElementField::element(
std::mem::transmute::<&mut #ty, &mut std::mem::MaybeUninit<#ty>>(f),
&mut initialized,
child,
Expand Down Expand Up @@ -393,7 +379,7 @@ fn do_enum(enum_: &ElementEnum) -> TokenStream {
#ident::#vident(unsafe {
let mut value = ::std::mem::MaybeUninit::<#ty>::uninit();
let mut initialized = false;
::static_xml::de::DeserializeElementField::element(
::static_xml::de::ElementField::element(
&mut value,
&mut initialized,
child
Expand Down
123 changes: 85 additions & 38 deletions static-xml/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ pub trait Deserialize: Sized {
/// 3. `Vec<T>`, for repeated fields. In XML Schema terms,
/// `minOccurs="0" maxOccurs="unbounded"`.
#[doc(hidden)]
pub trait DeserializeElementField: Sized {
pub trait ElementField: Sized {
unsafe fn element(
field: &mut MaybeUninit<Self>,
initialized: &mut bool,
Expand Down Expand Up @@ -789,13 +789,13 @@ pub type DeserializeFn = unsafe fn(field: Field<'_>, child: ElementReader<'_>) -
pub type FinalizeFn = unsafe fn(field: Field<'_>, default_fn: Option<&()>, err_fn: &dyn Fn() -> VisitorError) -> Result<(), VisitorError>;

#[doc(hidden)]
pub struct ElementVtable {
pub type_: ElementVtableType,
pub struct FieldVtable {
pub type_: FieldVtableType,
pub finalize: FinalizeFn,
}

#[doc(hidden)]
pub enum ElementVtableType {
pub enum FieldVtableType {
Text { // or simple?
parse: Option<ParseFn>,
// TODO: write.
Expand All @@ -806,27 +806,42 @@ pub enum ElementVtableType {
},
}

impl ElementVtable {
impl FieldVtable {
pub unsafe fn deserialize(&self, field: Field, child: ElementReader<'_>) -> Result<(), VisitorError> {
if *field.initialized && matches!(field.field_type, FieldType::Direct | FieldType::Option) {
return Err(VisitorError::duplicate_element(&child.expanded_name()));
}
match self {
ElementVtable { type_: ElementVtableType::Text { parse }, .. } => {
FieldVtable { type_: FieldVtableType::Text { parse }, .. } => {
let parse = parse.unwrap();
parse(field, child.read_string()?).map_err(VisitorError::Wrap)
}
ElementVtable { type_: ElementVtableType::Element { deserialize }, .. } => {
FieldVtable { type_: FieldVtableType::Element { deserialize }, .. } => {
let deserialize = deserialize.unwrap();
deserialize(field, child)
}
}
}

pub unsafe fn parse(&self, field: Field, name: &ExpandedNameRef, value: String) -> Result<(), VisitorError> {
debug_assert!(matches!(field.field_type, FieldType::Direct | FieldType::Option));
if *field.initialized {
return Err(VisitorError::duplicate_attribute(name));
}
match self {
FieldVtable { type_: FieldVtableType::Text { parse }, .. } => {
let parse = parse.unwrap();
parse(field, value).map_err(VisitorError::Wrap)
}
FieldVtable { type_: FieldVtableType::Element { .. }, .. } => unreachable!(),
}

}
}

#[doc(hidden)]
pub unsafe trait HasElementVtable: 'static {
const VTABLE: &'static ElementVtable;
pub unsafe trait HasFieldVtable: 'static {
const VTABLE: &'static FieldVtable;
}

#[doc(hidden)]
Expand All @@ -846,15 +861,15 @@ macro_rules! text_vtables {
) -> Result<(), $crate::de::VisitorError> {
field.finalize::<$t>(default_fn, err_fn)
}
const VTABLE: $crate::de::ElementVtable = $crate::de::ElementVtable {
type_: $crate::de::ElementVtableType::Text {
const VTABLE: $crate::de::FieldVtable = $crate::de::FieldVtable {
type_: $crate::de::FieldVtableType::Text {
// XXX: why isn't this working? https://github.com/rust-lang/rust/issues/39817
parse: Some(parse),
},
finalize,
};
unsafe impl $crate::de::HasElementVtable for $t {
const VTABLE: &'static $crate::de::ElementVtable = &VTABLE;
unsafe impl $crate::de::HasFieldVtable for $t {
const VTABLE: &'static $crate::de::FieldVtable = &VTABLE;
}
};
// TODO: likewise for attr, text.
Expand All @@ -881,14 +896,14 @@ macro_rules! deserialize_vtable {
) -> Result<(), $crate::de::VisitorError> {
field.finalize::<$t>(default_fn, err_fn)
}
const VTABLE: $crate::de::ElementVtable = $crate::de::ElementVtable {
type_: $crate::de::ElementVtableType::Element {
const VTABLE: $crate::de::FieldVtable = $crate::de::FieldVtable {
type_: $crate::de::FieldVtableType::Element {
deserialize: Some(deserialize),
},
finalize,
};
unsafe impl $crate::de::HasElementVtable for $t {
const VTABLE: &'static $crate::de::ElementVtable = &VTABLE;
unsafe impl $crate::de::HasFieldVtable for $t {
const VTABLE: &'static $crate::de::FieldVtable = &VTABLE;
}
};
}
Expand All @@ -897,12 +912,21 @@ macro_rules! deserialize_vtable {
/// Deserializes an attribute into a field.
///
/// This is implemented via [`ParseText`] as noted there.
pub trait DeserializeAttrField: Sized {
/// Called iff this field's attribute was found within the parent.
fn init(value: String) -> Result<Self, VisitorError>;
#[doc(hidden)]
pub trait AttrField: Sized {
unsafe fn attribute(
field: &mut MaybeUninit<Self>,
initialized: &mut bool,
name: &ExpandedNameRef<'_>,
value: String,
) -> Result<(), VisitorError>;

/// Called iff this field's attribute was not found within the parent.
fn missing(expected: &ExpandedNameRef<'_>) -> Result<Self, VisitorError>;
unsafe fn finalize(
field: &mut MaybeUninit<Self>,
initialized: &mut bool,
expected: &ExpandedNameRef<'_>,
default: Option<fn() -> Self>,
) -> Result<(), VisitorError>;
}

#[doc(hidden)]
Expand Down Expand Up @@ -1208,7 +1232,7 @@ pub fn find(name: &ExpandedNameRef<'_>, sorted_slice: &[ExpandedNameRef<'_>]) ->

macro_rules! element_field {
( $out_type:ty, $field_type:ident ) => {
impl<T: HasElementVtable> DeserializeElementField for $out_type {
impl<T: HasFieldVtable + Deserialize> ElementField for $out_type {
#[inline]
unsafe fn element(
field: &mut MaybeUninit<Self>,
Expand Down Expand Up @@ -1248,25 +1272,48 @@ element_field!(T, Direct);
element_field!(Option<T>, Option);
element_field!(Vec<T>, Vec);

impl<T: ParseText> DeserializeAttrField for T {
fn init(value: String) -> Result<Self, VisitorError> {
Ok(T::parse(value).map_err(VisitorError::Wrap)?)
}
macro_rules! attr_field {
( $out_type:ty, $field_type:ident ) => {
impl<T: HasFieldVtable + ParseText> AttrField for $out_type {
#[inline]
unsafe fn attribute(
field: &mut MaybeUninit<Self>,
initialized: &mut bool,
name: &ExpandedNameRef,
value: String,
) -> Result<(), VisitorError> {
let field = Field {
ptr: field.as_mut_ptr() as *mut (),
field_type: FieldType::$field_type,
initialized,
};
T::VTABLE.parse(field, name, value)
}

fn missing(expected: &ExpandedNameRef<'_>) -> Result<Self, VisitorError> {
Err(VisitorError::missing_attribute(expected))
#[inline]
unsafe fn finalize(
field: &mut MaybeUninit<Self>,
initialized: &mut bool,
expected: &ExpandedNameRef<'_>,
default: Option<fn() -> Self>,
) -> Result<(), VisitorError> {
let field = Field {
ptr: field.as_mut_ptr() as *mut (),
field_type: FieldType::$field_type,
initialized,
};
(T::VTABLE.finalize)(
field,
std::mem::transmute::<_, _>(default),
&|| VisitorError::missing_element(expected),
)
}
}
}
}
attr_field!(T, Direct);
attr_field!(Option<T>, Option);

impl<T: ParseText> DeserializeAttrField for Option<T> {
fn init(value: String) -> Result<Self, VisitorError> {
Ok(Some(T::parse(value).map_err(VisitorError::Wrap)?))
}

fn missing(_expected: &ExpandedNameRef<'_>) -> Result<Self, VisitorError> {
Ok(None)
}
}

/*
#[doc(hidden)]
Expand Down

0 comments on commit cd706e5

Please sign in to comment.