Skip to content

Commit 426c34d

Browse files
committed
add conditional formatting for Terminal
fmt functions produce big binaries. Since Debug and Display implementation of the match are equal except the Debug or Display representation of its child, this introduce conditional fmt trading performance for code size. The branch introduced should be easily predicted so the performance impact shouldn't be big.
1 parent 95b9337 commit 426c34d

File tree

1 file changed

+134
-141
lines changed

1 file changed

+134
-141
lines changed

src/miniscript/astelem.rs

+134-141
Original file line numberDiff line numberDiff line change
@@ -41,160 +41,48 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Terminal<Pk, Ctx> {
4141
_ => None,
4242
}
4343
}
44-
}
45-
46-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Terminal<Pk, Ctx> {
47-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48-
f.write_str("[")?;
49-
if let Ok(type_map) = types::Type::type_check(self) {
50-
f.write_str(match type_map.corr.base {
51-
types::Base::B => "B",
52-
types::Base::K => "K",
53-
types::Base::V => "V",
54-
types::Base::W => "W",
55-
})?;
56-
fmt::Write::write_char(f, '/')?;
57-
f.write_str(match type_map.corr.input {
58-
types::Input::Zero => "z",
59-
types::Input::One => "o",
60-
types::Input::OneNonZero => "on",
61-
types::Input::Any => "",
62-
types::Input::AnyNonZero => "n",
63-
})?;
64-
if type_map.corr.dissatisfiable {
65-
fmt::Write::write_char(f, 'd')?;
66-
}
67-
if type_map.corr.unit {
68-
fmt::Write::write_char(f, 'u')?;
69-
}
70-
f.write_str(match type_map.mall.dissat {
71-
types::Dissat::None => "f",
72-
types::Dissat::Unique => "e",
73-
types::Dissat::Unknown => "",
74-
})?;
75-
if type_map.mall.safe {
76-
fmt::Write::write_char(f, 's')?;
77-
}
78-
if type_map.mall.non_malleable {
79-
fmt::Write::write_char(f, 'm')?;
80-
}
81-
} else {
82-
f.write_str("TYPECHECK FAILED")?;
83-
}
84-
f.write_str("]")?;
85-
if let Some((ch, sub)) = self.wrap_char() {
86-
fmt::Write::write_char(f, ch)?;
87-
if sub.node.wrap_char().is_none() {
88-
fmt::Write::write_char(f, ':')?;
89-
}
90-
write!(f, "{:?}", sub)
91-
} else {
92-
match *self {
93-
Terminal::PkK(ref pk) => write!(f, "pk_k({:?})", pk),
94-
Terminal::PkH(ref pk) => write!(f, "pk_h({:?})", pk),
95-
Terminal::RawPkH(ref pkh) => write!(f, "expr_raw_pk_h({:?})", pkh),
96-
Terminal::After(t) => write!(f, "after({})", t),
97-
Terminal::Older(t) => write!(f, "older({})", t),
98-
Terminal::Sha256(ref h) => write!(f, "sha256({})", h),
99-
Terminal::Hash256(ref h) => write!(f, "hash256({})", h),
100-
Terminal::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
101-
Terminal::Hash160(ref h) => write!(f, "hash160({})", h),
102-
Terminal::True => f.write_str("1"),
103-
Terminal::False => f.write_str("0"),
104-
Terminal::AndV(ref l, ref r) => write!(f, "and_v({:?},{:?})", l, r),
105-
Terminal::AndB(ref l, ref r) => write!(f, "and_b({:?},{:?})", l, r),
106-
Terminal::AndOr(ref a, ref b, ref c) => {
107-
if c.node == Terminal::False {
108-
write!(f, "and_n({:?},{:?})", a, b)
109-
} else {
110-
write!(f, "andor({:?},{:?},{:?})", a, b, c)
111-
}
112-
}
113-
Terminal::OrB(ref l, ref r) => write!(f, "or_b({:?},{:?})", l, r),
114-
Terminal::OrD(ref l, ref r) => write!(f, "or_d({:?},{:?})", l, r),
115-
Terminal::OrC(ref l, ref r) => write!(f, "or_c({:?},{:?})", l, r),
116-
Terminal::OrI(ref l, ref r) => write!(f, "or_i({:?},{:?})", l, r),
117-
Terminal::Thresh(k, ref subs) => {
118-
write!(f, "thresh({}", k)?;
119-
for s in subs {
120-
write!(f, ",{:?}", s)?;
121-
}
122-
f.write_str(")")
123-
}
124-
Terminal::Multi(k, ref keys) => {
125-
write!(f, "multi({}", k)?;
126-
for k in keys {
127-
write!(f, ",{:?}", k)?;
128-
}
129-
f.write_str(")")
130-
}
131-
Terminal::MultiA(k, ref keys) => {
132-
write!(f, "multi_a({}", k)?;
133-
for k in keys {
134-
write!(f, ",{}", k)?;
135-
}
136-
f.write_str(")")
137-
}
138-
_ => unreachable!(),
139-
}
140-
}
141-
}
142-
}
14344

144-
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
145-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45+
fn conditional_fmt(&self, f: &mut fmt::Formatter, is_debug: bool) -> fmt::Result {
14646
match *self {
147-
Terminal::PkK(ref pk) => write!(f, "pk_k({})", pk),
148-
Terminal::PkH(ref pk) => write!(f, "pk_h({})", pk),
149-
Terminal::RawPkH(ref pkh) => write!(f, "expr_raw_pk_h({})", pkh),
150-
Terminal::After(t) => write!(f, "after({})", t),
151-
Terminal::Older(t) => write!(f, "older({})", t),
152-
Terminal::Sha256(ref h) => write!(f, "sha256({})", h),
153-
Terminal::Hash256(ref h) => write!(f, "hash256({})", h),
154-
Terminal::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
155-
Terminal::Hash160(ref h) => write!(f, "hash160({})", h),
47+
Terminal::PkK(ref pk) => fmt_1(f, "pk_k(", pk, is_debug),
48+
Terminal::PkH(ref pk) => fmt_1(f, "pk_h(", pk, is_debug),
49+
Terminal::RawPkH(ref pkh) => fmt_1(f, "expr_raw_pk_h(", pkh, is_debug),
50+
Terminal::After(ref t) => fmt_1(f, "after(", t, is_debug),
51+
Terminal::Older(ref t) => fmt_1(f, "older(", t, is_debug),
52+
Terminal::Sha256(ref h) => fmt_1(f, "sha256(", h, is_debug),
53+
Terminal::Hash256(ref h) => fmt_1(f, "hash256(", h, is_debug),
54+
Terminal::Ripemd160(ref h) => fmt_1(f, "ripemd160(", h, is_debug),
55+
Terminal::Hash160(ref h) => fmt_1(f, "hash160(", h, is_debug),
15656
Terminal::True => f.write_str("1"),
15757
Terminal::False => f.write_str("0"),
15858
Terminal::AndV(ref l, ref r) if r.node != Terminal::True => {
159-
write!(f, "and_v({},{})", l, r)
59+
fmt_2(f, "and_v(", l, r, is_debug)
16060
}
161-
Terminal::AndB(ref l, ref r) => write!(f, "and_b({},{})", l, r),
61+
Terminal::AndB(ref l, ref r) => fmt_2(f, "and_b(", l, r, is_debug),
16262
Terminal::AndOr(ref a, ref b, ref c) => {
16363
if c.node == Terminal::False {
164-
write!(f, "and_n({},{})", a, b)
64+
fmt_2(f, "and_b(", a, b, is_debug)
16565
} else {
166-
write!(f, "andor({},{},{})", a, b, c)
66+
f.write_str("andor(")?;
67+
conditional_fmt(f, a, is_debug)?;
68+
f.write_str(",")?;
69+
conditional_fmt(f, b, is_debug)?;
70+
f.write_str(",")?;
71+
conditional_fmt(f, c, is_debug)?;
72+
f.write_str(")")
16773
}
16874
}
169-
Terminal::OrB(ref l, ref r) => write!(f, "or_b({},{})", l, r),
170-
Terminal::OrD(ref l, ref r) => write!(f, "or_d({},{})", l, r),
171-
Terminal::OrC(ref l, ref r) => write!(f, "or_c({},{})", l, r),
75+
Terminal::OrB(ref l, ref r) => fmt_2(f, "or_b(", l, r, is_debug),
76+
Terminal::OrD(ref l, ref r) => fmt_2(f, "or_d(", l, r, is_debug),
77+
Terminal::OrC(ref l, ref r) => fmt_2(f, "or_c(", l, r, is_debug),
17278
Terminal::OrI(ref l, ref r)
17379
if l.node != Terminal::False && r.node != Terminal::False =>
17480
{
175-
write!(f, "or_i({},{})", l, r)
176-
}
177-
Terminal::Thresh(k, ref subs) => {
178-
write!(f, "thresh({}", k)?;
179-
for s in subs {
180-
write!(f, ",{}", s)?;
181-
}
182-
f.write_str(")")
183-
}
184-
Terminal::Multi(k, ref keys) => {
185-
write!(f, "multi({}", k)?;
186-
for k in keys {
187-
write!(f, ",{}", k)?;
188-
}
189-
f.write_str(")")
190-
}
191-
Terminal::MultiA(k, ref keys) => {
192-
write!(f, "multi_a({}", k)?;
193-
for k in keys {
194-
write!(f, ",{}", k)?;
195-
}
196-
f.write_str(")")
81+
fmt_2(f, "or_i(", l, r, is_debug)
19782
}
83+
Terminal::Thresh(k, ref subs) => fmt_n(f, "thresh(", k, subs, is_debug),
84+
Terminal::Multi(k, ref keys) => fmt_n(f, "multi(", k, keys, is_debug),
85+
Terminal::MultiA(k, ref keys) => fmt_n(f, "multi_a(", k, keys, is_debug),
19886
// wrappers
19987
_ => {
20088
if let Some((ch, sub)) = self.wrap_char() {
@@ -219,13 +107,13 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
219107
fmt::Write::write_char(f, ch)?;
220108
match sub.node.wrap_char() {
221109
None => {
222-
fmt::Write::write_char(f, ':')?;
110+
f.write_str(":")?;
223111
}
224112
// Add a ':' wrapper if there are other wrappers apart from c:pk_k()
225113
// tvc:pk_k() -> tv:pk()
226114
Some(('c', ms)) => match ms.node {
227115
Terminal::PkK(_) | Terminal::PkH(_) | Terminal::RawPkH(_) => {
228-
fmt::Write::write_char(f, ':')?
116+
f.write_str(":")?;
229117
}
230118
_ => {}
231119
},
@@ -240,6 +128,111 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
240128
}
241129
}
242130

131+
fn fmt_1<D: fmt::Debug + fmt::Display>(
132+
f: &mut fmt::Formatter,
133+
name: &str,
134+
a: &D,
135+
is_debug: bool,
136+
) -> fmt::Result {
137+
f.write_str(&name)?;
138+
conditional_fmt(f, a, is_debug)?;
139+
f.write_str(")")
140+
}
141+
fn fmt_2<D: fmt::Debug + fmt::Display>(
142+
f: &mut fmt::Formatter,
143+
name: &str,
144+
a: &D,
145+
b: &D,
146+
is_debug: bool,
147+
) -> fmt::Result {
148+
f.write_str(&name)?;
149+
conditional_fmt(f, a, is_debug)?;
150+
f.write_str(",")?;
151+
conditional_fmt(f, b, is_debug)?;
152+
f.write_str(")")
153+
}
154+
fn fmt_n<D: fmt::Debug + fmt::Display>(
155+
f: &mut fmt::Formatter,
156+
name: &str,
157+
first: usize,
158+
list: &[D],
159+
is_debug: bool,
160+
) -> fmt::Result {
161+
f.write_str(&name)?;
162+
write!(f, "{}", first)?;
163+
for el in list {
164+
f.write_str(",")?;
165+
conditional_fmt(f, el, is_debug)?;
166+
}
167+
f.write_str(")")
168+
}
169+
fn conditional_fmt<D: fmt::Debug + fmt::Display>(
170+
f: &mut fmt::Formatter,
171+
data: &D,
172+
is_debug: bool,
173+
) -> fmt::Result {
174+
if is_debug {
175+
fmt::Debug::fmt(data, f)
176+
} else {
177+
fmt::Display::fmt(data, f)
178+
}
179+
}
180+
181+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Terminal<Pk, Ctx> {
182+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183+
f.write_str("[")?;
184+
if let Ok(type_map) = types::Type::type_check(self) {
185+
f.write_str(match type_map.corr.base {
186+
types::Base::B => "B",
187+
types::Base::K => "K",
188+
types::Base::V => "V",
189+
types::Base::W => "W",
190+
})?;
191+
fmt::Write::write_char(f, '/')?;
192+
f.write_str(match type_map.corr.input {
193+
types::Input::Zero => "z",
194+
types::Input::One => "o",
195+
types::Input::OneNonZero => "on",
196+
types::Input::Any => "",
197+
types::Input::AnyNonZero => "n",
198+
})?;
199+
if type_map.corr.dissatisfiable {
200+
fmt::Write::write_char(f, 'd')?;
201+
}
202+
if type_map.corr.unit {
203+
fmt::Write::write_char(f, 'u')?;
204+
}
205+
f.write_str(match type_map.mall.dissat {
206+
types::Dissat::None => "f",
207+
types::Dissat::Unique => "e",
208+
types::Dissat::Unknown => "",
209+
})?;
210+
if type_map.mall.safe {
211+
fmt::Write::write_char(f, 's')?;
212+
}
213+
if type_map.mall.non_malleable {
214+
fmt::Write::write_char(f, 'm')?;
215+
}
216+
} else {
217+
f.write_str("TYPECHECK FAILED")?;
218+
}
219+
f.write_str("]")?;
220+
if let Some((ch, sub)) = self.wrap_char() {
221+
fmt::Write::write_char(f, ch)?;
222+
if sub.node.wrap_char().is_none() {
223+
fmt::Write::write_char(f, ':')?;
224+
}
225+
write!(f, "{:?}", sub)
226+
} else {
227+
self.conditional_fmt(f, true)
228+
}
229+
}
230+
}
231+
232+
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Terminal<Pk, Ctx> {
233+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.conditional_fmt(f, false) }
234+
}
235+
243236
impl<Pk: crate::FromStrKey, Ctx: ScriptContext> crate::expression::FromTree
244237
for Arc<Terminal<Pk, Ctx>>
245238
{

0 commit comments

Comments
 (0)