diff --git a/Cargo.lock b/Cargo.lock index fec03e1..6b822f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "cfg-if" version = "1.0.0" @@ -62,6 +68,12 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.106" @@ -83,6 +95,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "proc-macro2" version = "1.0.32" @@ -124,7 +145,9 @@ version = "0.1.0" dependencies = [ "assert_matches", "env_logger", + "lazycell", "log", + "memoffset", "xml-rs", ] diff --git a/static-xml-derive/src/deserialize.rs b/static-xml-derive/src/deserialize.rs index deb77f8..cd00cf9 100644 --- a/static-xml-derive/src/deserialize.rs +++ b/static-xml-derive/src/deserialize.rs @@ -24,231 +24,141 @@ pub(crate) fn quote_flatten_visitors(struct_: &ElementStruct) -> Vec TokenStream { - let ident = field.ident; - quote! { unsafe { ::std::ptr::addr_of_mut!((*self.out).#ident) } } -} - -fn visitor_field_definitions(struct_: &ElementStruct) -> Vec { +fn flattened_field_definitions(struct_: &ElementStruct) -> Vec { struct_ .fields .iter() .filter_map(|field| { + if !matches!(field.mode, ElementFieldMode::Flatten) { + return None; + } let span = field.inner.span(); - let field_present = format_ident!("{}_present", field.ident); let field_visitor = format_ident!("{}_visitor", field.ident); let ty = &field.inner.ty; - match field.mode { - ElementFieldMode::Text => None, - ElementFieldMode::Flatten => Some(quote_spanned! {span=> - #field_visitor: <#ty as ::static_xml::de::RawDeserialize<'out>>::Visitor - }), - ElementFieldMode::Element { .. } | ElementFieldMode::Attribute { .. } => { - Some(quote_spanned! {span=> - #field_present: bool - }) - } - } + Some(quote_spanned! {span=> + #field_visitor: <#ty as ::static_xml::de::RawDeserialize<'out>>::Visitor + }) }) .collect() } -fn visitor_initializers(struct_: &ElementStruct) -> Vec { +fn flattened_field_initializers(struct_: &ElementStruct) -> Vec { struct_ .fields .iter() - .map(|field| { + .filter_map(|field| { + if !matches!(field.mode, ElementFieldMode::Flatten) { + return None; + } let span = field.inner.span(); - match field.mode { - ElementFieldMode::Flatten => { - let field_visitor = format_ident!("{}_visitor", field.ident); - let fident = field.ident; - let ty = &field.inner.ty; - quote_spanned! {span=> - #field_visitor: <#ty as ::static_xml::de::RawDeserialize>::Visitor::new( - // SAFETY: out points to valid, uninitialized memory. - unsafe { - &mut *( - ::std::ptr::addr_of_mut!((*out.as_mut_ptr()).#fident) - as *mut ::std::mem::MaybeUninit<#ty> - ) - } + let field_visitor = format_ident!("{}_visitor", field.ident); + let fident = field.ident; + let ty = &field.inner.ty; + Some(quote_spanned! {span=> + #field_visitor: <#ty as ::static_xml::de::RawDeserialize>::Visitor::new( + // SAFETY: out points to valid, uninitialized memory. + unsafe { + &mut *( + ::std::ptr::addr_of_mut!((*out.as_mut_ptr()).#fident) + as *mut ::std::mem::MaybeUninit<#ty> ) } - } - _ => { - let field_present = format_ident!("{}_present", field.ident); - quote_spanned! {span=> - #field_present: false - } - } - } + ) + }) }) .collect() } -fn finalize_visitor_fields(struct_: &ElementStruct) -> Vec { - struct_.fields.iter().map(|field| { +fn finalize_flattened_fields(struct_: &ElementStruct) -> Vec { + struct_.fields.iter().filter_map(|field| { + if !matches!(field.mode, ElementFieldMode::Flatten) { + return None; + } let span = field.inner.span(); let ty = &field.inner.ty; 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); - quote_spanned! {span=> - // SAFETY: #field_mut and #field_present are valid/consistent. - unsafe { - ::static_xml::de::ElementField::finalize( - &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), - &mut self.#field_present, - &ELEMENTS[#p], - #default, - )?; - } - } - } - ElementFieldMode::Attribute { sorted_attributes_pos: p } => { - let field_present = format_ident!("{}_present", field.ident); - let field_mut = field_mut(field); - quote_spanned! {span=> - // 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); - quote_spanned! {span=> - <#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #default)?; - } - } - ElementFieldMode::Text => todo!(), - } + let field_visitor = format_ident!("{}_visitor", field.ident); + Some(quote_spanned! {span=> + <#ty as ::static_xml::de::RawDeserialize>::Visitor::finalize(self.#field_visitor, #default)?; + }) }).collect() } -fn attributes(struct_: &ElementStruct) -> Vec { - struct_ - .sorted_attributes - .iter() - .map(|&p| struct_.fields[p].name.quote_expanded()) - .collect() -} - -fn elements(struct_: &ElementStruct) -> Vec { - struct_ - .sorted_elements - .iter() - .map(|&p| struct_.fields[p].name.quote_expanded()) - .collect() +fn vtable_field(struct_: &ElementStruct, field: &ElementField, trait_: TokenStream) -> TokenStream { + let ident = &struct_.input.ident; + let span = field.inner.span(); + let ty = &field.inner.ty; + let fident = field.ident; + let default = match field.default { + true => quote_spanned! {span=> + unsafe { + ::std::mem::transmute::<_, *const ()>( + <#ty as ::std::default::Default>::default as fn() -> #ty, + ) + } + }, + false => quote! { std::ptr::null() } + }; + quote_spanned! {span=> + ::static_xml::de::ElementVtableField { + // SAFETY: an assertion checks the struct's size doesn't exceed u32::MAX. + offset: unsafe { ::static_xml::offset_of!(#ident, #fident) } as u32, + field_type: <#ty as ::static_xml::de::#trait_>::TYPE, + vtable: <#ty as ::static_xml::de::#trait_>::VTABLE, + default: #default, + } + } } -fn attribute_match_branches(struct_: &ElementStruct) -> Vec { - struct_ - .sorted_attributes - .iter() - .enumerate() - .map(|(i, &p)| { - let field = &struct_.fields[p]; - 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_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) - } +fn attributes(struct_: &ElementStruct) -> Vec { + struct_.sorted_attributes.iter().map(|&p| { + let field = &struct_.fields[p]; + let name = field.name.quote_expanded(); + let vtable_field = vtable_field(struct_, field, quote! { AttrField }); + quote! { + ::static_xml::de::NamedField { + name: #name, + field: #vtable_field, } - }) - .collect() + } + }).collect() } -fn element_match_branches(struct_: &ElementStruct) -> Vec { - struct_ - .sorted_elements - .iter() - .enumerate() - .map(|(i, &p)| { - let field = &struct_.fields[p]; - 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_spanned! {span=> - // SAFETY: #field_mut and #field_present are consistent/valid. - Some(#i) => unsafe { - ::static_xml::de::ElementField::element( - &mut *(#field_mut as *mut ::std::mem::MaybeUninit<#ty>), - &mut self.#field_present, - child, - )?; - Ok(None) - } +fn elements(struct_: &ElementStruct) -> Vec { + struct_.sorted_elements.iter().map(|&p| { + let field = &struct_.fields[p]; + let name = field.name.quote_expanded(); + let vtable_field = vtable_field(struct_, field, quote! { ElementField }); + quote! { + ::static_xml::de::NamedField { + name: #name, + field: #vtable_field, } - }) - .collect() + } + }).collect() } fn do_struct(struct_: &ElementStruct) -> TokenStream { let attributes = attributes(&struct_); - let attribute_match_branches = attribute_match_branches(&struct_); let elements = elements(&struct_); - let element_match_branches = element_match_branches(&struct_); let ident = &struct_.input.ident; let visitor_type = format_ident!("{}Visitor", &struct_.input.ident); - let visitor_field_definitions = visitor_field_definitions(&struct_); - let visitor_initializers = visitor_initializers(&struct_); - let finalize_visitor_fields = finalize_visitor_fields(&struct_); + let flattened_field_definitions = flattened_field_definitions(&struct_); + let flattened_field_initializers = flattened_field_initializers(&struct_); + let finalize_flattened_fields = finalize_flattened_fields(&struct_); let flatten_visitors = quote_flatten_visitors(&struct_); - let (attribute_fallthrough, element_fallthrough); - if flatten_visitors.is_empty() { - attribute_fallthrough = quote! { Ok(Some(value)) }; - element_fallthrough = quote! { Ok(Some(child)) }; - } else { - attribute_fallthrough = quote! { - ::static_xml::de::delegate_attribute(&mut [#(#flatten_visitors),*], name, value) - }; - element_fallthrough = quote! { - ::static_xml::de::delegate_element(&mut [#(#flatten_visitors),*], child) - }; - } - let visitor_characters = if struct_.text_field_pos.is_some() { - quote! { - fn characters(&mut self, s: String, _p: ::static_xml::TextPosition) -> Result, ::static_xml::de::VisitorError> { - self._text_buf.push_str(&s); - Ok(None) - } - } - } else if !flatten_visitors.is_empty() { - quote! { - fn characters(&mut self, s: String, p: ::static_xml::TextPosition) -> Result, ::static_xml::BoxedStdError> { - ::static_xml::de::delegate_characters(&mut [#(#flatten_visitors),*], s, p) - } - } - } else { - TokenStream::new() - }; + let n_fields = elements.len() + attributes.len(); // TODO: text. quote! { - const ATTRIBUTES: &[::static_xml::ExpandedNameRef] = &[#(#attributes,)*]; - const ELEMENTS: &[::static_xml::ExpandedNameRef] = &[#(#elements,)*]; + const _: () = assert!(std::mem::size_of::<#visitor_type>() < u32::MAX as usize); + const VTABLE: ::static_xml::de::ElementVtable = ::static_xml::de::ElementVtable { + elements: &[#(#elements,)*], + attributes: &[#(#attributes,)*], + text: None, // TODO + }; // If there's an underlying field named e.g. `foo_`, then there will be // a generated field name e.g. `foo__required` or `foo__visitor`. Don't @@ -257,11 +167,26 @@ fn do_struct(struct_: &ElementStruct) -> TokenStream { struct #visitor_type<'out> { // This can't be &mut MaybeUninit<#type> because flattened fields // (if any) get a &mut MaybeUninit<#type> that aliases it. So - // use a raw pointer and addr_of! to access individual fields, and - // PhantomData for the lifetime. - out: *mut #ident, + // use a raw pointer. u8 is best for ptr::add(offset_of!(...)). + out: *mut u8, _phantom: ::std::marker::PhantomData<&'out mut #ident>, - #(#visitor_field_definitions, )* + text_buf: String, // TODO: can omit if there's no text field. + initialized: [bool; #n_fields], + #(#flattened_field_definitions, )* + } + + impl<'out> #visitor_type<'out> { + fn vtable_visitor<'a>(&'a mut self) -> ::static_xml::de::VtableVisitor<'a> { + unsafe { + ::static_xml::de::VtableVisitor::new( + self.out, + &VTABLE, + &mut self.initialized[..], + &mut self.text_buf, + //&mut [#(#flatten_visitors, )*], + ) + } + } } impl<'out> ::static_xml::de::ElementVisitor for #visitor_type<'out> { @@ -269,10 +194,7 @@ fn do_struct(struct_: &ElementStruct) -> TokenStream { &mut self, child: ::static_xml::de::ElementReader<'a>, ) -> Result>, ::static_xml::de::VisitorError> { - match ::static_xml::de::find(&child.expanded_name(), ELEMENTS) { - #(#element_match_branches,)* - _ => #element_fallthrough - } + ::static_xml::de::ElementVisitor::element(&mut self.vtable_visitor(), child) } fn attribute<'a>( @@ -280,13 +202,10 @@ fn do_struct(struct_: &ElementStruct) -> TokenStream { name: &::static_xml::ExpandedNameRef, value: String, ) -> Result, ::static_xml::de::VisitorError> { - match ::static_xml::de::find(name, ATTRIBUTES) { - #(#attribute_match_branches,)* - _ => #attribute_fallthrough - } + ::static_xml::de::ElementVisitor::attribute(&mut self.vtable_visitor(), name, value) } - #visitor_characters + // TODO: characters. } unsafe impl<'out> ::static_xml::de::RawDeserializeVisitor<'out> for #visitor_type<'out> { @@ -294,16 +213,19 @@ fn do_struct(struct_: &ElementStruct) -> TokenStream { fn new(out: &'out mut ::std::mem::MaybeUninit) -> Self { Self { - out: out.as_mut_ptr(), + out: out.as_mut_ptr() as *mut u8, + initialized: [false; #n_fields], + text_buf: String::new(), _phantom: ::std::marker::PhantomData, - #(#visitor_initializers, )* + #(#flattened_field_initializers, )* } } + #[warn(unused_mut)] // mut is needed iff there are flattened fields. fn finalize(mut self, _default: Option Self::Out>) -> Result<(), ::static_xml::de::VisitorError> { // Note _default is currently unsupported for structs. - #(#finalize_visitor_fields)* + #(#finalize_flattened_fields)* // SAFETY: returning `Ok` guarantees `self.out` is fully initialized. // finalize_visitor_fields has guaranteed that each field is fully initialized. // The padding doesn't matter. This is similar to this example diff --git a/static-xml/Cargo.toml b/static-xml/Cargo.toml index 99cc74c..624fcae 100644 --- a/static-xml/Cargo.toml +++ b/static-xml/Cargo.toml @@ -12,7 +12,9 @@ description = "serde-like serialization and deserialization of static Rust types # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lazycell = "1.3.0" log = "0.4.14" +memoffset = { version = "0.6.5", features = ["unstable_const"] } xml-rs = "0.8.4" [dev-dependencies] diff --git a/static-xml/src/de/mod.rs b/static-xml/src/de/mod.rs index 058849e..5ed0149 100644 --- a/static-xml/src/de/mod.rs +++ b/static-xml/src/de/mod.rs @@ -683,6 +683,9 @@ pub trait Deserialize: Sized { /// `minOccurs="0" maxOccurs="unbounded"`. #[doc(hidden)] pub trait ElementField: Sized { + const TYPE: FieldType; + const VTABLE: &'static FieldVtable; + unsafe fn element( field: &mut MaybeUninit, initialized: &mut bool, @@ -693,7 +696,7 @@ pub trait ElementField: Sized { field: &mut MaybeUninit, initialized: &mut bool, expected: &ExpandedNameRef<'_>, - default: Option Self>, + default: *const(), // Option Self>, ) -> Result<(), VisitorError>; } @@ -731,28 +734,28 @@ impl Field<'_> { // default_fn must return a value of the field type (not the value type). pub unsafe fn finalize( self, - default_fn: Option<&()>, + default_fn: *const (), //Option<&()>, err_fn: &dyn Fn() -> VisitorError, ) -> Result<(), VisitorError> { if !*self.initialized { - if let Some(d) = default_fn { + if !default_fn.is_null() { match self.field_type { FieldType::Direct => { std::ptr::write( self.ptr as *mut T, - std::mem::transmute::<*const (), fn() -> T>(d)(), + std::mem::transmute::<*const (), fn() -> T>(default_fn)(), ); } FieldType::Option => { std::ptr::write( self.ptr as *mut Option, - std::mem::transmute::<*const (), fn() -> Option>(d)(), + std::mem::transmute::<*const (), fn() -> Option>(default_fn)(), ); } FieldType::Vec => { std::ptr::write( self.ptr as *mut Vec, - std::mem::transmute::<*const (), fn() -> Vec>(d)(), + std::mem::transmute::<*const (), fn() -> Vec>(default_fn)(), ); } } @@ -767,8 +770,171 @@ impl Field<'_> { } } + +#[doc(hidden)] +pub struct NamedField { + pub name: ExpandedNameRef<'static>, + pub field: ElementVtableField, +} + +#[doc(hidden)] +pub struct ElementVtable { + pub elements: &'static [NamedField], + pub attributes: &'static [NamedField], + pub text: Option, +} + +#[doc(hidden)] +pub struct ElementVtableField { + pub offset: u32, + pub field_type: FieldType, + pub vtable: &'static FieldVtable, + pub default: *const (), +} + +#[doc(hidden)] +pub struct VtableVisitor<'out> { + out: *mut u8, + vtable: &'out ElementVtable, + + // elements, then attributes, then text. + initialized: &'out mut [bool], + text_buf: &'out mut String, + + // XXX: how will this lifetime work? ideas: + // * wrap VtableVisitor, create a new instance of it on every call + // * wrap VtableVisitor, do the delegate calls manually afterward + // * make this actually just a list of vtables and offsets, and instantiate + // an ElementVisitor from each every time. (Seems promising but the details + // elude me right now...) + // flatten_visitors: &'out mut [&'out mut dyn ElementVisitor], +} + +impl<'out> VtableVisitor<'out> { + // create a new one on every call? otherwise flatten_visitors lifetime won't work. + #[inline] + pub unsafe fn new( + out: *mut u8, + vtable: &'out ElementVtable, + initialized: &'out mut [bool], + text_buf: &'out mut String, + //flatten_visitors: &'out mut [&'out mut dyn ElementVisitor], + ) -> Self { + Self { + out, + vtable, + initialized, + text_buf, + //flatten_visitors, + } + } + + /// Finalizes all but the flattened fields. + pub fn finalize(&mut self) -> Result<(), VisitorError> { + unsafe { + let mut i = 0; + for nf in self.vtable.elements { + let field = Field { + ptr: self.out.add(nf.field.offset as usize) as *mut (), + field_type: nf.field.field_type, + initialized: &mut self.initialized[i], + }; + (nf.field.vtable.finalize)( + field, + nf.field.default, + &|| VisitorError::missing_element(&nf.name), + )?; + i += 1; + } + for nf in self.vtable.attributes { + let field = Field { + ptr: self.out.add(nf.field.offset as usize) as *mut (), + field_type: nf.field.field_type, + initialized: &mut self.initialized[i], + }; + (nf.field.vtable.finalize)( + field, + nf.field.default, + &|| VisitorError::missing_attribute(&nf.name), + )?; + i += 1; + } + if let Some(text) = &self.vtable.text { + let field = Field { + ptr: self.out.add(text.offset as usize) as *mut (), + field_type: text.field_type, + initialized: &mut self.initialized[i], + }; + (text.vtable.finalize)( + field, + text.default, + &|| panic!("missing text; does this even make sense?"), + )?; + } + } + Ok(()) + } +} + +impl<'out> ElementVisitor for VtableVisitor<'out> { + fn attribute( + &mut self, + name: &ExpandedNameRef<'_>, + value: String, + ) -> Result, VisitorError> { + let field_i = match my_find(name, self.vtable.attributes) { + Some(i) => i, + None => return Ok(Some(value)), + }; + let vtable_field = &self.vtable.attributes[field_i].field; + unsafe { + let field = Field { + ptr: self.out.add(vtable_field.offset as usize) as *mut (), + field_type: vtable_field.field_type, + initialized: &mut self.initialized[self.vtable.elements.len() + field_i], + }; + vtable_field.vtable.parse(field, name, value)?; + } + Ok(None) + } + + fn element<'a>( + &mut self, + child: ElementReader<'a>, + ) -> Result>, VisitorError> { + let field_i = match my_find(&child.expanded_name(), self.vtable.elements) { + Some(i) => i, + None => return Ok(Some(child)), + }; + let vtable_field = &self.vtable.elements[field_i].field; + unsafe { + let field = Field { + ptr: self.out.add(vtable_field.offset as usize) as *mut (), + field_type: vtable_field.field_type, + initialized: &mut self.initialized[field_i], + }; + vtable_field.vtable.deserialize(field, child)?; + } + Ok(None) + } + + fn characters( + &mut self, + s: String, + pos: TextPosition, + ) -> Result, crate::BoxedStdError> { + if self.vtable.text.is_some() { + self.text_buf.push_str(&s); + Ok(None) + } else { + Ok(Some(s)) //delegate_characters(self.flatten_visitors, s, pos) + } + } +} + #[doc(hidden)] #[derive(Copy, Clone)] +#[repr(u8)] pub enum FieldType { /// `T` Direct, @@ -790,7 +956,7 @@ pub type DeserializeFn = #[doc(hidden)] pub type FinalizeFn = unsafe fn( field: Field<'_>, - default_fn: Option<&()>, + default_fn: *const (), err_fn: &dyn Fn() -> VisitorError, ) -> Result<(), VisitorError>; @@ -890,7 +1056,7 @@ macro_rules! text_vtables { } unsafe fn finalize( field: $crate::de::Field<'_>, - default_fn: Option<&()>, + default_fn: *const (), err_fn: &dyn Fn() -> $crate::de::VisitorError, ) -> Result<(), $crate::de::VisitorError> { field.finalize::<$t>(default_fn, err_fn) @@ -925,7 +1091,7 @@ macro_rules! deserialize_vtable { } unsafe fn finalize( field: $crate::de::Field<'_>, - default_fn: Option<&()>, + default_fn: *const (), err_fn: &dyn Fn() -> $crate::de::VisitorError, ) -> Result<(), $crate::de::VisitorError> { field.finalize::<$t>(default_fn, err_fn) @@ -948,19 +1114,16 @@ macro_rules! deserialize_vtable { /// This is implemented via [`ParseText`] as noted there. #[doc(hidden)] pub trait AttrField: Sized { - unsafe fn attribute( - field: &mut MaybeUninit, - initialized: &mut bool, - name: &ExpandedNameRef<'_>, - value: String, - ) -> Result<(), VisitorError>; - - unsafe fn finalize( - field: &mut MaybeUninit, - initialized: &mut bool, - expected: &ExpandedNameRef<'_>, - default: Option Self>, - ) -> Result<(), VisitorError>; + const TYPE: FieldType; + const VTABLE: &'static FieldVtable; +} +impl AttrField for T { + const TYPE: FieldType = FieldType::Direct; + const VTABLE: &'static FieldVtable = T::VTABLE; +} +impl AttrField for Option { + const TYPE: FieldType = FieldType::Option; + const VTABLE: &'static FieldVtable = T::VTABLE; } #[doc(hidden)] @@ -1264,9 +1427,16 @@ pub fn find(name: &ExpandedNameRef<'_>, sorted_slice: &[ExpandedNameRef<'_>]) -> sorted_slice.binary_search(name).ok() } +fn my_find(name: &ExpandedNameRef<'_>, sorted_slice: &[NamedField]) -> Option { + sorted_slice.binary_search_by_key(name, |nf| nf.name).ok() +} + macro_rules! element_field { ( $out_type:ty, $field_type:ident ) => { impl ElementField for $out_type { + const TYPE: FieldType = FieldType::$field_type; + const VTABLE: &'static FieldVtable = T::VTABLE; + #[inline] unsafe fn element( field: &mut MaybeUninit, @@ -1286,7 +1456,7 @@ macro_rules! element_field { field: &mut MaybeUninit, initialized: &mut bool, expected: &ExpandedNameRef<'_>, - default: Option Self>, + default: *const (), //Option Self>, ) -> Result<(), VisitorError> { let field = Field { ptr: field.as_mut_ptr() as *mut (), @@ -1304,46 +1474,6 @@ element_field!(T, Direct); element_field!(Option, Option); element_field!(Vec, Vec); -macro_rules! attr_field { - ( $out_type:ty, $field_type:ident ) => { - impl AttrField for $out_type { - #[inline] - unsafe fn attribute( - field: &mut MaybeUninit, - 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) - } - - #[inline] - unsafe fn finalize( - field: &mut MaybeUninit, - initialized: &mut bool, - expected: &ExpandedNameRef<'_>, - default: Option 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, Option); - /* #[doc(hidden)] pub enum SingleContainerResult<'a, T> { diff --git a/static-xml/src/lib.rs b/static-xml/src/lib.rs index b4a6451..c33e913 100644 --- a/static-xml/src/lib.rs +++ b/static-xml/src/lib.rs @@ -11,6 +11,12 @@ const XML_NS: &str = "http://www.w3.org/XML/1998/namespace"; pub use xml::common::TextPosition; +#[doc(hidden)] +pub use memoffset::offset_of; + +#[doc(hidden)] +pub use lazycell::AtomicLazyCell; + /// A reference to an "expanded name": namespace and local name. /// /// See [Namespaces in XML 1.1 (Second Edition) section 2.1: Basic diff --git a/test-suite/tests/basic.rs b/test-suite/tests/basic.rs index 7e3143d..fc93136 100644 --- a/test-suite/tests/basic.rs +++ b/test-suite/tests/basic.rs @@ -1,6 +1,8 @@ // Copyright (C) 2021 Scott Lamb // SPDX-License-Identifier: MIT OR Apache-2.0 +#![feature(const_ptr_offset_from)] + use static_xml_derive::{Deserialize, ParseText, Serialize, ToText}; #[derive(Debug, Default, Deserialize, Eq, PartialEq, Serialize)]