Skip to content

Commit 1edea8e

Browse files
committed
Optimizing runtime performance for the literal string case (fix #39)
1 parent 6a5c893 commit 1edea8e

File tree

2 files changed

+79
-18
lines changed

2 files changed

+79
-18
lines changed

parse-display-derive/src/lib.rs

+66-18
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod format_syntax;
1515

1616
use crate::{format_syntax::*, regex_utils::*, syn_utils::*};
1717
use proc_macro2::{Span, TokenStream};
18-
use quote::{format_ident, quote};
18+
use quote::{format_ident, quote, ToTokens};
1919
use regex::{Captures, Regex};
2020
use regex_syntax::hir::Hir;
2121
use std::{
@@ -62,7 +62,9 @@ fn derive_display_for_struct(input: &DeriveInput, data: &DataStruct) -> Result<T
6262
)
6363
};
6464
let mut bounds = Bounds::from_data(hattrs.bound_display);
65-
let args = format.format_args(context, &None, &mut bounds, &generics)?;
65+
let write = format
66+
.format_args(context, &None, &mut bounds, &generics)?
67+
.build_write(quote!(f))?;
6668
let trait_path = parse_quote!(::core::fmt::Display);
6769
let wheres = bounds.build_wheres(&trait_path);
6870
impl_trait_result(
@@ -71,7 +73,7 @@ fn derive_display_for_struct(input: &DeriveInput, data: &DataStruct) -> Result<T
7173
&wheres,
7274
quote! {
7375
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
74-
::core::write!(f, #args)
76+
#write
7577
}
7678
},
7779
hattrs.dump_display,
@@ -117,19 +119,21 @@ fn derive_display_for_enum(input: &DeriveInput, data: &DataEnum) -> Result<Token
117119
)
118120
};
119121
let variant_ident = &variant.ident;
120-
let args = format.format_args(
121-
DisplayContext::Variant {
122-
variant,
123-
style,
124-
crate_path: &hattrs_enum.crate_path,
125-
},
126-
&None,
127-
&mut bounds.child(hattrs_variant.bound_display),
128-
generics,
129-
)?;
122+
let write = format
123+
.format_args(
124+
DisplayContext::Variant {
125+
variant,
126+
style,
127+
crate_path: &hattrs_enum.crate_path,
128+
},
129+
&None,
130+
&mut bounds.child(hattrs_variant.bound_display),
131+
generics,
132+
)?
133+
.build_write(quote!(f))?;
130134
Ok(quote! {
131135
& Self::#variant_ident #fields => {
132-
::core::write!(f, #args)
136+
#write
133137
},
134138
})
135139
}
@@ -1080,7 +1084,7 @@ impl DisplayFormat {
10801084
with: &Option<Expr>,
10811085
bounds: &mut Bounds,
10821086
generics: &GenericParamSet,
1083-
) -> Result<TokenStream> {
1087+
) -> Result<FormatArgs> {
10841088
let mut format_str = String::new();
10851089
let mut format_args = Vec::new();
10861090
for p in &self.parts {
@@ -1102,8 +1106,42 @@ impl DisplayFormat {
11021106
}
11031107
}
11041108
}
1105-
let format_str = LitStr::new(&format_str, self.span);
1106-
Ok(quote! { #format_str #(,#format_args)* })
1109+
Ok(FormatArgs {
1110+
format_str,
1111+
format_args,
1112+
span: self.span,
1113+
})
1114+
}
1115+
1116+
fn try_unescape(&self) -> Option<String> {
1117+
let mut s = String::new();
1118+
for p in &self.parts {
1119+
s.push_str(p.try_unescape()?);
1120+
}
1121+
Some(s)
1122+
}
1123+
}
1124+
1125+
struct FormatArgs {
1126+
format_str: String,
1127+
format_args: Vec<TokenStream>,
1128+
span: Span,
1129+
}
1130+
impl FormatArgs {
1131+
fn build_write(&self, f: TokenStream) -> Result<TokenStream> {
1132+
if self.format_args.is_empty() {
1133+
if let Some(s) = DisplayFormat::parse(&self.format_str, self.span)?.try_unescape() {
1134+
return Ok(quote! { #f.write_str(#s) });
1135+
}
1136+
}
1137+
Ok(quote! { ::core::write!(#f, #self) })
1138+
}
1139+
}
1140+
impl ToTokens for FormatArgs {
1141+
fn to_tokens(&self, tokens: &mut TokenStream) {
1142+
let format_str = LitStr::new(&self.format_str, self.span);
1143+
let format_args = &self.format_args;
1144+
tokens.extend(quote!(#format_str #(,#format_args)*));
11071145
}
11081146
}
11091147

@@ -1114,6 +1152,16 @@ enum DisplayFormatPart {
11141152
EscapedEndBracket,
11151153
Var { arg: String, format_spec: String },
11161154
}
1155+
impl DisplayFormatPart {
1156+
fn try_unescape(&self) -> Option<&str> {
1157+
match self {
1158+
Self::Str(value) => Some(value),
1159+
Self::EscapedBeginBracket => Some("{"),
1160+
Self::EscapedEndBracket => Some("}"),
1161+
Self::Var { .. } => None,
1162+
}
1163+
}
1164+
}
11171165

11181166
enum DisplayContext<'a> {
11191167
Struct {
@@ -1542,7 +1590,7 @@ impl std::fmt::Display for FieldKey {
15421590
}
15431591
}
15441592
}
1545-
impl quote::ToTokens for FieldKey {
1593+
impl ToTokens for FieldKey {
15461594
fn to_tokens(&self, tokens: &mut TokenStream) {
15471595
self.to_member().to_tokens(tokens);
15481596
}

parse-display/tests/display.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,19 @@ fn by_debug() {
10231023
assert_display(E::B(10), &format!("{:?}", E::B(10)));
10241024
}
10251025

1026+
#[test]
1027+
fn escape() {
1028+
#[derive(Display)]
1029+
#[display("{{")]
1030+
struct X;
1031+
assert_display(X, "{");
1032+
1033+
#[derive(Display)]
1034+
#[display("}}")]
1035+
struct Y;
1036+
assert_display(Y, "}");
1037+
}
1038+
10261039
fn assert_display<T: core::fmt::Display>(value: T, display: &str) {
10271040
let value_display = format!("{value}");
10281041
assert_eq!(value_display, display);

0 commit comments

Comments
 (0)