Skip to content

Commit

Permalink
Performance improvement
Browse files Browse the repository at this point in the history
This slightly reduces the amount of code pased to LLVM, and also ensures
that full `TokenStream`s are only built when necessary. `TokenTree`s are
preferred wherever possible.
  • Loading branch information
jhpratt committed Mar 19, 2022
1 parent c051654 commit 24c36af
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 124 deletions.
10 changes: 5 additions & 5 deletions time-macros/src/date.rs
@@ -1,12 +1,12 @@
use std::iter::Peekable;

use proc_macro::{token_stream, TokenStream};
use proc_macro::{token_stream, TokenTree};

use crate::helpers::{
consume_any_ident, consume_number, consume_punct, days_in_year, days_in_year_month,
weeks_in_year, ymd_to_yo, ywd_to_yo,
};
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenTree;
use crate::Error;

#[cfg(feature = "large-dates")]
Expand Down Expand Up @@ -124,9 +124,9 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
}
}

impl ToTokens for Date {
fn into_token_stream(self) -> TokenStream {
quote! {{
impl ToTokenTree for Date {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
const DATE: ::time::Date = ::time::Date::__from_ordinal_date_unchecked(
#(self.year),
#(self.ordinal),
Expand Down
22 changes: 14 additions & 8 deletions time-macros/src/datetime.rs
@@ -1,12 +1,12 @@
use std::iter::Peekable;

use proc_macro::{token_stream, TokenStream};
use proc_macro::{token_stream, Ident, Span, TokenTree};

use crate::date::Date;
use crate::error::Error;
use crate::offset::Offset;
use crate::time::Time;
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenTree;
use crate::{date, offset, time};

pub(crate) struct DateTime {
Expand All @@ -33,18 +33,24 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Date
Ok(DateTime { date, time, offset })
}

impl ToTokens for DateTime {
fn into_token_stream(self) -> TokenStream {
impl ToTokenTree for DateTime {
fn into_token_tree(self) -> TokenTree {
let (type_name, maybe_offset) = match self.offset {
Some(offset) => (quote!(OffsetDateTime), quote!(.assume_offset(#(offset)))),
None => (quote!(PrimitiveDateTime), quote!()),
Some(offset) => (
Ident::new("OffsetDateTime", Span::mixed_site()),
quote!(.assume_offset(#(offset))),
),
None => (
Ident::new("PrimitiveDateTime", Span::mixed_site()),
quote!(),
),
};

quote! {{
quote_group! {{
const DATE_TIME: ::time::#(type_name) = ::time::PrimitiveDateTime::new(
#(self.date),
#(self.time),
) #(maybe_offset);
) #S(maybe_offset);
DATE_TIME
}}
}
Expand Down
58 changes: 37 additions & 21 deletions time-macros/src/format_description/component.rs
@@ -1,9 +1,9 @@
use proc_macro::TokenStream;
use proc_macro::{Ident, Span, TokenStream};

use crate::format_description::error::InvalidFormatDescription;
use crate::format_description::modifier;
use crate::format_description::modifier::Modifiers;
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenStream;

pub(crate) enum Component {
Day(modifier::Day),
Expand All @@ -22,25 +22,41 @@ pub(crate) enum Component {
OffsetSecond(modifier::OffsetSecond),
}

impl ToTokens for Component {
fn into_token_stream(self) -> TokenStream {
quote! {
::time::format_description::Component::#(match self {
Self::Day(modifier) => quote! { Day(#(modifier)) },
Self::Month(modifier) => quote! { Month(#(modifier)) },
Self::Ordinal(modifier) => quote! { Ordinal(#(modifier)) },
Self::Weekday(modifier) => quote! { Weekday(#(modifier)) },
Self::WeekNumber(modifier) => quote! { WeekNumber(#(modifier)) },
Self::Year(modifier) => quote! { Year(#(modifier)) },
Self::Hour(modifier) => quote! { Hour(#(modifier)) },
Self::Minute(modifier) => quote! { Minute(#(modifier)) },
Self::Period(modifier) => quote! { Period(#(modifier)) },
Self::Second(modifier) => quote! { Second(#(modifier)) },
Self::Subsecond(modifier) => quote! { Subsecond(#(modifier)) },
Self::OffsetHour(modifier) => quote! { OffsetHour(#(modifier)) },
Self::OffsetMinute(modifier) => quote! { OffsetMinute(#(modifier)) },
Self::OffsetSecond(modifier) => quote! { OffsetSecond(#(modifier)) },
})
impl ToTokenStream for Component {
fn append_to(self, ts: &mut TokenStream) {
let mut mts = TokenStream::new();

macro_rules! component_name_and_append {
($($name:ident)*) => {
match self {
$(Self::$name(modifier) => {
modifier.append_to(&mut mts);
stringify!($name)
})*
}
};
}

let component = component_name_and_append![
Day
Month
Ordinal
Weekday
WeekNumber
Year
Hour
Minute
Period
Second
Subsecond
OffsetHour
OffsetMinute
OffsetSecond
];
let component = Ident::new(component, Span::mixed_site());

quote_append! { ts
::time::format_description::Component::#(component)(#S(mts))
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions time-macros/src/format_description/mod.rs
Expand Up @@ -7,7 +7,7 @@ use proc_macro::{Literal, TokenStream};

pub(crate) use self::component::Component;
pub(crate) use self::parse::parse;
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenStream;

mod helper {
#[must_use = "This does not modify the original slice."]
Expand All @@ -28,12 +28,12 @@ pub(crate) enum FormatItem<'a> {
Component(Component),
}

impl ToTokens for FormatItem<'_> {
fn into_token_stream(self) -> TokenStream {
quote! {
::time::format_description::FormatItem::#(match self {
impl ToTokenStream for FormatItem<'_> {
fn append_to(self, ts: &mut TokenStream) {
quote_append! { ts
::time::format_description::FormatItem::#S(match self {
FormatItem::Literal(bytes) => quote! { Literal(#(Literal::byte_string(bytes))) },
FormatItem::Component(component) => quote! { Component(#(component)) },
FormatItem::Component(component) => quote! { Component(#S(component)) },
})
}
}
Expand Down
44 changes: 29 additions & 15 deletions time-macros/src/format_description/modifier.rs
@@ -1,10 +1,10 @@
use core::mem;

use proc_macro::TokenStream;
use proc_macro::{Ident, Span, TokenStream, TokenTree};

use crate::format_description::error::InvalidFormatDescription;
use crate::format_description::helper;
use crate::to_tokens::ToTokens;
use crate::to_tokens::{ToTokenStream, ToTokenTree};

macro_rules! to_tokens {
(
Expand All @@ -20,13 +20,25 @@ macro_rules! to_tokens {
$field_vis $field_name: $field_ty
),+}

impl ToTokens for $struct_name {
fn into_token_stream(self) -> TokenStream {
quote! {{
impl ToTokenTree for $struct_name {
fn into_token_tree(self) -> TokenTree {
let mut tokens = TokenStream::new();
let Self {$($field_name),+} = self;

quote_append! { tokens
let mut value = ::time::format_description::modifier::$struct_name::default();
$(value.$field_name = #(self.$field_name);)+
value
}}
};
$(
quote_append!(tokens value.$field_name =);
$field_name.append_to(&mut tokens);
quote_append!(tokens ;);
)+
quote_append!(tokens value);

proc_macro::TokenTree::Group(proc_macro::Group::new(
proc_macro::Delimiter::Brace,
tokens,
))
}
}
};
Expand All @@ -44,13 +56,15 @@ macro_rules! to_tokens {
$variant_name
),+}

impl ToTokens for $enum_name {
fn into_token_stream(self) -> TokenStream {
quote! {
::time::format_description::modifier::$enum_name::#(match self {
$(Self::$variant_name => quote!($variant_name)),+
})
}
impl ToTokenStream for $enum_name {
fn append_to(self, ts: &mut TokenStream) {
quote_append! { ts
::time::format_description::modifier::$enum_name::
};
let name = match self {
$(Self::$variant_name => stringify!($variant_name)),+
};
ts.extend([TokenTree::Ident(Ident::new(name, Span::mixed_site()))]);
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions time-macros/src/lib.rs
Expand Up @@ -51,13 +51,13 @@ macro_rules! impl_macros {
($($name:ident)*) => {$(
#[proc_macro]
pub fn $name(input: TokenStream) -> TokenStream {
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenTree;

let mut iter = input.into_iter().peekable();
match $name::parse(&mut iter) {
Ok(value) => match iter.peek() {
Some(tree) => Error::UnexpectedToken { tree: tree.clone() }.to_compile_error(),
None => value.into_token_stream(),
None => TokenStream::from(value.into_token_tree()),
},
Err(err) => err.to_compile_error(),
}
Expand All @@ -76,10 +76,10 @@ pub fn format_description(input: TokenStream) -> TokenStream {
let items = format_description::parse(&string, span)?;

Ok(quote! {{
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#(
const DESCRIPTION: &[::time::format_description::FormatItem<'_>] = &[#S(
items
.into_iter()
.map(|item| quote! { #(item), })
.map(|item| quote! { #S(item), })
.collect::<TokenStream>()
)];
DESCRIPTION
Expand Down Expand Up @@ -116,12 +116,12 @@ pub fn serde_format_description(input: TokenStream) -> TokenStream {
let (span, format_string) = helpers::get_string_literal(tokens.collect())?;

let items = format_description::parse(&format_string, span)?;
let items: TokenStream = items.into_iter().map(|item| quote! { #(item), }).collect();
let items: TokenStream = items.into_iter().map(|item| quote! { #S(item), }).collect();

Ok(serde_format_description::build(
mod_name,
items,
formattable.into(),
formattable,
&String::from_utf8_lossy(&format_string),
))
})()
Expand Down
10 changes: 5 additions & 5 deletions time-macros/src/offset.rs
@@ -1,9 +1,9 @@
use std::iter::Peekable;

use proc_macro::{token_stream, Span, TokenStream};
use proc_macro::{token_stream, Span, TokenTree};

use crate::helpers::{consume_any_ident, consume_number, consume_punct};
use crate::to_tokens::ToTokens;
use crate::to_tokens::ToTokenTree;
use crate::Error;

pub(crate) struct Offset {
Expand Down Expand Up @@ -81,9 +81,9 @@ pub(crate) fn parse(chars: &mut Peekable<token_stream::IntoIter>) -> Result<Offs
}
}

impl ToTokens for Offset {
fn into_token_stream(self) -> TokenStream {
quote! {{
impl ToTokenTree for Offset {
fn into_token_tree(self) -> TokenTree {
quote_group! {{
const OFFSET: ::time::UtcOffset = ::time::UtcOffset::__from_hms_unchecked(
#(self.hours),
#(self.minutes),
Expand Down
26 changes: 24 additions & 2 deletions time-macros/src/quote.rs
Expand Up @@ -2,11 +2,27 @@ macro_rules! quote {
() => (::proc_macro::TokenStream::new());
($($x:tt)*) => {{
let mut ts = ::proc_macro::TokenStream::new();
quote_inner!(ts $($x)*);
let ts_mut = &mut ts;
quote_inner!(ts_mut $($x)*);
ts
}};
}

macro_rules! quote_append {
($ts:ident $($x:tt)*) => {{
quote_inner!($ts $($x)*);
}};
}

macro_rules! quote_group {
({ $($x:tt)* }) => {
::proc_macro::TokenTree::Group(::proc_macro::Group::new(
::proc_macro::Delimiter::Brace,
quote!($($x)*)
))
};
}

macro_rules! sym {
($ts:ident $x:tt $y:tt) => {
$ts.extend([
Expand Down Expand Up @@ -103,8 +119,14 @@ macro_rules! quote_inner {
};

// Interpolated values
// TokenTree by default
($ts:ident #($e:expr) $($tail:tt)*) => {
$ts.extend($crate::to_tokens::ToTokens::into_token_stream($e));
$ts.extend([$crate::to_tokens::ToTokenTree::into_token_tree($e)]);
quote_inner!($ts $($tail)*);
};
// Allow a TokenStream by request. It's more expensive, so avoid if possible.
($ts:ident #S($e:expr) $($tail:tt)*) => {
$crate::to_tokens::ToTokenStream::append_to($e, $ts);
quote_inner!($ts $($tail)*);
};
}

0 comments on commit 24c36af

Please sign in to comment.