Skip to content

Commit 0cfc405

Browse files
committed
Optimize number formatting
1 parent 74ba83b commit 0cfc405

File tree

1 file changed

+82
-42
lines changed

1 file changed

+82
-42
lines changed

src/format/formatting.rs

+82-42
Original file line numberDiff line numberDiff line change
@@ -115,48 +115,88 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
115115
fn format_numeric(&self, w: &mut impl Write, spec: &Numeric, pad: Pad) -> fmt::Result {
116116
use self::Numeric::*;
117117

118-
let (width, v) = match (spec, self.date, self.time) {
119-
(Year, Some(d), _) => (4, i64::from(d.year())),
120-
(YearDiv100, Some(d), _) => (2, i64::from(d.year()).div_euclid(100)),
121-
(YearMod100, Some(d), _) => (2, i64::from(d.year()).rem_euclid(100)),
122-
(IsoYear, Some(d), _) => (4, i64::from(d.iso_week().year())),
123-
(IsoYearDiv100, Some(d), _) => (2, i64::from(d.iso_week().year()).div_euclid(100)),
124-
(IsoYearMod100, Some(d), _) => (2, i64::from(d.iso_week().year()).rem_euclid(100)),
125-
(Month, Some(d), _) => (2, i64::from(d.month())),
126-
(Day, Some(d), _) => (2, i64::from(d.day())),
127-
(WeekFromSun, Some(d), _) => (2, i64::from(d.weeks_from(Weekday::Sun))),
128-
(WeekFromMon, Some(d), _) => (2, i64::from(d.weeks_from(Weekday::Mon))),
129-
(IsoWeek, Some(d), _) => (2, i64::from(d.iso_week().week())),
130-
(NumDaysFromSun, Some(d), _) => (1, i64::from(d.weekday().num_days_from_sunday())),
131-
(WeekdayFromMon, Some(d), _) => (1, i64::from(d.weekday().number_from_monday())),
132-
(Ordinal, Some(d), _) => (3, i64::from(d.ordinal())),
133-
(Hour, _, Some(t)) => (2, i64::from(t.hour())),
134-
(Hour12, _, Some(t)) => (2, i64::from(t.hour12().1)),
135-
(Minute, _, Some(t)) => (2, i64::from(t.minute())),
136-
(Second, _, Some(t)) => (2, i64::from(t.second() + t.nanosecond() / 1_000_000_000)),
137-
(Nanosecond, _, Some(t)) => (9, i64::from(t.nanosecond() % 1_000_000_000)),
138-
(Timestamp, Some(d), Some(t)) => {
139-
let offset = self.off.as_ref().map(|(_, o)| i64::from(o.local_minus_utc()));
140-
let timestamp = d.and_time(t).and_utc().timestamp() - offset.unwrap_or(0);
141-
(1, timestamp)
118+
fn write_one(w: &mut impl Write, v: u8) -> fmt::Result {
119+
w.write_char((b'0' + v) as char)
120+
}
121+
122+
fn write_two(w: &mut impl Write, v: u8, pad: Pad) -> fmt::Result {
123+
let ones = b'0' + v % 10;
124+
match (v / 10, pad) {
125+
(0, Pad::None) => {}
126+
(0, Pad::Space) => w.write_char(' ')?,
127+
(tens, _) => w.write_char((b'0' + tens) as char)?,
142128
}
143-
(Internal(_), _, _) => return Ok(()), // for future expansion
144-
_ => return Err(fmt::Error), // insufficient arguments for given format
145-
};
129+
w.write_char(ones as char)
130+
}
146131

147-
if (spec == &Year || spec == &IsoYear) && !(0..10_000).contains(&v) {
148-
// non-four-digit years require an explicit sign as per ISO 8601
149-
match pad {
150-
Pad::None => write!(w, "{:+}", v),
151-
Pad::Zero => write!(w, "{:+01$}", v, width + 1),
152-
Pad::Space => write!(w, "{:+1$}", v, width + 1),
132+
#[inline]
133+
fn write_year(w: &mut impl Write, year: i32, pad: Pad) -> fmt::Result {
134+
if (1000..=9999).contains(&year) {
135+
// fast path
136+
write_hundreds(w, (year / 100) as u8)?;
137+
write_hundreds(w, (year % 100) as u8)
138+
} else {
139+
write_n(w, 4, year as i64, pad, !(0..10_000).contains(&year))
153140
}
154-
} else {
155-
match pad {
156-
Pad::None => write!(w, "{}", v),
157-
Pad::Zero => write!(w, "{:01$}", v, width),
158-
Pad::Space => write!(w, "{:1$}", v, width),
141+
}
142+
143+
fn write_n(
144+
w: &mut impl Write,
145+
n: usize,
146+
v: i64,
147+
pad: Pad,
148+
always_sign: bool,
149+
) -> fmt::Result {
150+
if always_sign {
151+
match pad {
152+
Pad::None => write!(w, "{:+}", v),
153+
Pad::Zero => write!(w, "{:+01$}", v, n + 1),
154+
Pad::Space => write!(w, "{:+1$}", v, n + 1),
155+
}
156+
} else {
157+
match pad {
158+
Pad::None => write!(w, "{}", v),
159+
Pad::Zero => write!(w, "{:01$}", v, n),
160+
Pad::Space => write!(w, "{:1$}", v, n),
161+
}
162+
}
163+
}
164+
165+
match (spec, self.date, self.time) {
166+
(Year, Some(d), _) => write_year(w, d.year(), pad),
167+
(YearDiv100, Some(d), _) => write_two(w, d.year().div_euclid(100) as u8, pad),
168+
(YearMod100, Some(d), _) => write_two(w, d.year().rem_euclid(100) as u8, pad),
169+
(IsoYear, Some(d), _) => write_year(w, d.iso_week().year(), pad),
170+
(IsoYearDiv100, Some(d), _) => {
171+
write_two(w, d.iso_week().year().div_euclid(100) as u8, pad)
172+
}
173+
(IsoYearMod100, Some(d), _) => {
174+
write_two(w, d.iso_week().year().rem_euclid(100) as u8, pad)
175+
}
176+
(Month, Some(d), _) => write_two(w, d.month() as u8, pad),
177+
(Day, Some(d), _) => write_two(w, d.day() as u8, pad),
178+
(WeekFromSun, Some(d), _) => write_two(w, d.weeks_from(Weekday::Sun) as u8, pad),
179+
(WeekFromMon, Some(d), _) => write_two(w, d.weeks_from(Weekday::Mon) as u8, pad),
180+
(IsoWeek, Some(d), _) => write_two(w, d.iso_week().week() as u8, pad),
181+
(NumDaysFromSun, Some(d), _) => write_one(w, d.weekday().num_days_from_sunday() as u8),
182+
(WeekdayFromMon, Some(d), _) => write_one(w, d.weekday().number_from_monday() as u8),
183+
(Ordinal, Some(d), _) => write_n(w, 3, d.ordinal() as i64, pad, false),
184+
(Hour, _, Some(t)) => write_two(w, t.hour() as u8, pad),
185+
(Hour12, _, Some(t)) => write_two(w, t.hour12().1 as u8, pad),
186+
(Minute, _, Some(t)) => write_two(w, t.minute() as u8, pad),
187+
(Second, _, Some(t)) => {
188+
write_two(w, (t.second() + t.nanosecond() / 1_000_000_000) as u8, pad)
189+
}
190+
(Nanosecond, _, Some(t)) => {
191+
write_n(w, 9, (t.nanosecond() % 1_000_000_000) as i64, pad, false)
192+
}
193+
(Timestamp, Some(d), Some(t)) => {
194+
let offset = self.off.as_ref().map(|(_, o)| i64::from(o.local_minus_utc()));
195+
let timestamp = d.and_time(t).and_utc().timestamp() - offset.unwrap_or(0);
196+
write_n(w, 9, timestamp, pad, false)
159197
}
198+
(Internal(_), _, _) => Ok(()), // for future expansion
199+
_ => Err(fmt::Error), // insufficient arguments for given format
160200
}
161201
}
162202

@@ -206,21 +246,21 @@ impl<'a, I: Iterator<Item = B> + Clone, B: Borrow<Item<'a>>> DelayedFormat<I> {
206246
}
207247
(Nanosecond3, _, Some(t), _) => {
208248
w.write_str(decimal_point(self.locale))?;
209-
write!(w, "{:03}", t.nanosecond() % 1_000_000_000 / 1_000_000)
249+
write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1000)
210250
}
211251
(Nanosecond6, _, Some(t), _) => {
212252
w.write_str(decimal_point(self.locale))?;
213-
write!(w, "{:06}", t.nanosecond() % 1_000_000_000 / 1_000)
253+
write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000)
214254
}
215255
(Nanosecond9, _, Some(t), _) => {
216256
w.write_str(decimal_point(self.locale))?;
217257
write!(w, "{:09}", t.nanosecond() % 1_000_000_000)
218258
}
219259
(Internal(InternalFixed { val: Nanosecond3NoDot }), _, Some(t), _) => {
220-
write!(w, "{:03}", t.nanosecond() % 1_000_000_000 / 1_000_000)
260+
write!(w, "{:03}", t.nanosecond() / 1_000_000 % 1_000)
221261
}
222262
(Internal(InternalFixed { val: Nanosecond6NoDot }), _, Some(t), _) => {
223-
write!(w, "{:06}", t.nanosecond() % 1_000_000_000 / 1_000)
263+
write!(w, "{:06}", t.nanosecond() / 1_000 % 1_000_000)
224264
}
225265
(Internal(InternalFixed { val: Nanosecond9NoDot }), _, Some(t), _) => {
226266
write!(w, "{:09}", t.nanosecond() % 1_000_000_000)

0 commit comments

Comments
 (0)