Skip to content

Commit 11ece1a

Browse files
Refactor date field resolution to reduce monomorphized code size
1 parent 930703d commit 11ece1a

File tree

1 file changed

+111
-105
lines changed

1 file changed

+111
-105
lines changed

components/calendar/src/calendar_arithmetic.rs

Lines changed: 111 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,35 @@ pub(crate) trait DateFieldsResolver: Calendar {
197197
}
198198
}
199199

200-
#[derive(Debug)]
201-
struct PreDateFields {
202-
extended_year: i32,
203-
validated_month_code: Option<Month>,
204-
ordinal_month: Option<u8>,
205-
day: u8,
200+
#[derive(Copy, Clone)]
201+
enum ValidatedYearInput<'a> {
202+
Extended(i32),
203+
Era(&'a [u8], i32),
204+
EcmaReference { month: Month, day: u8 },
205+
}
206+
207+
#[derive(Copy, Clone)]
208+
enum ValidatedMonthInput {
209+
Resolve(Month),
210+
UseOrdinal(u8),
206211
}
207212

213+
struct Preparation<'a> {
214+
year_input: ValidatedYearInput<'a>,
215+
month_input: ValidatedMonthInput,
216+
day: u8,
217+
overflow: Overflow,
218+
check_era_consistency: Option<i32>,
219+
check_month_consistency: Option<u8>,
220+
}
208221

209-
fn preprocess_date_fields(
210-
fields: &DateFields,
211-
missing: MissingFieldsStrategy,
212-
) -> Result<PreDateFields, DateFromFieldsError> {
222+
#[inline(never)]
223+
fn prepare_from_fields<'a>(
224+
fields: &'a DateFields,
225+
options: DateFromFieldsOptions,
226+
) -> Result<Preparation<'a>, DateFromFieldsError> {
227+
let missing = options.missing_fields_strategy.unwrap_or_default();
213228

214-
// day
215229
let day = match fields.day {
216230
Some(d) => d,
217231
None => match missing {
@@ -230,93 +244,85 @@ fn preprocess_date_fields(
230244
return Err(DateFromFieldsError::NotEnoughFields);
231245
}
232246

233-
let validated_month_code = match fields.month_code {
234-
Some(mc) => Some(Month::try_from_utf8(mc)?),
235-
None => None,
247+
let validated_month_code = fields.month_code.map(Month::try_from_utf8).transpose()?;
248+
249+
let (year_input, check_era_consistency) = match (fields.era, fields.era_year) {
250+
(None, None) => match fields.extended_year {
251+
Some(y) => {
252+
let checked = range_check(y, "year", VALID_YEAR_RANGE)?;
253+
(ValidatedYearInput::Extended(checked), None)
254+
}
255+
None => match missing {
256+
MissingFieldsStrategy::Reject => return Err(DateFromFieldsError::NotEnoughFields),
257+
MissingFieldsStrategy::Ecma => match (validated_month_code, fields.ordinal_month) {
258+
(Some(mc), None) => {
259+
(ValidatedYearInput::EcmaReference { month: mc, day }, None)
260+
}
261+
_ => return Err(DateFromFieldsError::NotEnoughFields),
262+
},
263+
},
264+
},
265+
(Some(era), Some(era_year)) => {
266+
let checked_era = range_check(era_year.to_extended_year(), "year", VALID_YEAR_RANGE)?;
267+
(
268+
ValidatedYearInput::Era(era, checked_era),
269+
fields.extended_year,
270+
)
271+
}
272+
_ => return Err(DateFromFieldsError::NotEnoughFields),
273+
};
274+
275+
let month_input = if let Some(mc) = validated_month_code {
276+
ValidatedMonthInput::Resolve(mc)
277+
} else {
278+
ValidatedMonthInput::UseOrdinal(fields.ordinal_month.unwrap_or(1))
236279
};
237280

238-
239-
let extended_year = fields.extended_year.unwrap_or(0);
281+
let check_month_consistency = if validated_month_code.is_some() {
282+
fields.ordinal_month
283+
} else {
284+
None
285+
};
240286

241-
Ok(PreDateFields {
242-
extended_year,
243-
validated_month_code,
244-
ordinal_month: fields.ordinal_month,
287+
Ok(Preparation {
288+
year_input,
289+
month_input,
245290
day,
291+
overflow: options.overflow.unwrap_or_default(),
292+
check_era_consistency,
293+
check_month_consistency,
246294
})
247295
}
248296

249-
struct ResolvedFields {
250-
extended_year: i32,
251-
month_code: Option<Month>,
252-
ordinal_month: Option<u8>,
253-
day: u8,
254-
}
255-
256-
fn resolve_fields_non_generic(
257-
fields: &DateFields,
258-
pre: PreDateFields,
259-
missing: MissingFieldsStrategy,
260-
) -> Result<ResolvedFields, DateFromFieldsError> {
261-
262-
let day = pre.day;
263-
264-
265-
let month_code = pre.validated_month_code;
266-
let ordinal_month = pre.ordinal_month;
267-
268-
if month_code.is_none() && ordinal_month.is_none() {
269-
return Err(DateFromFieldsError::NotEnoughFields);
297+
#[inline(never)]
298+
fn finalize_from_fields(
299+
computed_year: i32,
300+
computed_month: u8,
301+
max_month: u8,
302+
max_day: u8,
303+
prep: &Preparation,
304+
) -> Result<(u8, u8), DateFromFieldsError> {
305+
if let Some(req_ext) = prep.check_era_consistency {
306+
if computed_year != req_ext {
307+
return Err(DateFromFieldsError::InconsistentYear);
308+
}
270309
}
271310

272-
311+
range_check(computed_year, "extended_year", VALID_YEAR_RANGE)?;
273312

274-
let extended_year = match (fields.era, fields.era_year) {
275-
(None, None) => {
276-
match fields.extended_year {
277-
Some(y) => y,
278-
None => match missing {
279-
MissingFieldsStrategy::Reject => {
280-
return Err(DateFromFieldsError::NotEnoughFields)
281-
}
282-
MissingFieldsStrategy::Ecma => {
283-
// Valid only if month_code is present AND ordinal_month is absent
284-
match (fields.month_code, fields.ordinal_month) {
285-
(Some(_), None) => {
286-
287-
0
288-
}
289-
_ => return Err(DateFromFieldsError::NotEnoughFields),
290-
}
291-
}
292-
}
293-
}
294-
}
295-
296-
(Some(_era), Some(era_year)) => {
297-
if let Some(ext) = fields.extended_year {
298-
if ext != era_year.to_extended_year() {
299-
return Err(DateFromFieldsError::InconsistentYear);
300-
}
301-
}
302-
era_year.to_extended_year()
313+
if let Some(expected) = prep.check_month_consistency {
314+
if computed_month != expected {
315+
return Err(DateFromFieldsError::InconsistentMonth);
303316
}
317+
}
304318

305-
(Some(_), None) | (None, Some(_)) => {
306-
return Err(DateFromFieldsError::NotEnoughFields)
307-
}
308-
};
319+
let final_month =
320+
range_check_with_overflow(computed_month, "month", 1..=max_month, prep.overflow)?;
321+
let final_day = range_check_with_overflow(prep.day, "day", 1..=max_day, prep.overflow)?;
309322

310-
Ok(ResolvedFields {
311-
extended_year,
312-
month_code,
313-
ordinal_month,
314-
day,
315-
})
323+
Ok((final_month, final_day))
316324
}
317325

318-
319-
320326
impl<C: DateFieldsResolver> ArithmeticDate<C> {
321327
pub(crate) fn year(self) -> C::YearInfo {
322328
C::YearInfo::unpack_year(self.0)
@@ -377,37 +383,37 @@ impl<C: DateFieldsResolver> ArithmeticDate<C> {
377383
options: DateFromFieldsOptions,
378384
calendar: &C,
379385
) -> Result<Self, DateFromFieldsError> {
380-
let missing = options.missing_fields_strategy.unwrap_or_default();
386+
let prep = prepare_from_fields(&fields, options)?;
381387

382-
let pre = preprocess_date_fields(&fields, missing)?;
383-
let resolved = resolve_fields_non_generic(&fields, pre, missing)?;
388+
let year_info = match prep.year_input {
389+
ValidatedYearInput::Extended(y) => calendar.year_info_from_extended(y),
390+
ValidatedYearInput::Era(era_slice, y) => calendar.year_info_from_era(era_slice, y)?,
391+
ValidatedYearInput::EcmaReference { month, day } => {
392+
calendar.reference_year_from_month_day(month, day)?
393+
}
394+
};
384395

385-
let year = calendar.year_info_from_extended(resolved.extended_year);
386-
let month = match resolved.month_code {
387-
Some(mc) => calendar.ordinal_from_month(year, mc, options)?,
388-
None => resolved.ordinal_month.unwrap(),
396+
let raw_month = match prep.month_input {
397+
ValidatedMonthInput::Resolve(mc) => {
398+
calendar.ordinal_from_month(year_info, mc, options)?
399+
}
400+
ValidatedMonthInput::UseOrdinal(ord) => ord,
389401
};
390-
let day = resolved.day;
391402

392-
let month = range_check_with_overflow(
393-
month,
394-
"month",
395-
1..=C::months_in_provided_year(year),
396-
options.overflow.unwrap_or_default(),
397-
)?;
403+
let max_month = C::months_in_provided_year(year_info);
404+
let max_day = C::days_in_provided_month(year_info, raw_month.clamp(1, max_month));
398405

399-
let day = range_check_with_overflow(
400-
day,
401-
"day",
402-
1..=C::days_in_provided_month(year, month),
403-
options.overflow.unwrap_or_default(),
406+
let (final_month, final_day) = finalize_from_fields(
407+
year_info.to_extended_year(),
408+
raw_month,
409+
max_month,
410+
max_day,
411+
&prep,
404412
)?;
405413

406-
Ok(Self::new_unchecked(year, month, day))
414+
Ok(Self::new_unchecked(year_info, final_month, final_day))
407415
}
408416

409-
410-
411417
pub(crate) fn from_year_month_day(
412418
year: i32,
413419
month: u8,

0 commit comments

Comments
 (0)