Skip to content

Commit d1ad434

Browse files
committed
optimize era storage
1 parent 2060b84 commit d1ad434

File tree

9 files changed

+254
-180
lines changed

9 files changed

+254
-180
lines changed

components/calendar/src/cal/japanese.rs

Lines changed: 102 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use crate::cal::abstract_gregorian::{
88
use crate::cal::gregorian::CeBce;
99
use crate::calendar_arithmetic::ArithmeticDate;
1010
use crate::error::{DateError, UnknownEraError};
11-
use crate::provider::{CalendarJapaneseExtendedV1, CalendarJapaneseModernV1, EraStartDate};
11+
use crate::provider::{
12+
CalendarJapaneseExtendedV1, CalendarJapaneseModernV1, EraStartDate, PackedEra,
13+
};
1214
use crate::{types, AsCalendar, Date};
1315
use icu_provider::prelude::*;
14-
use tinystr::tinystr;
16+
use tinystr::{tinystr, TinyAsciiStr};
1517

1618
/// The [Japanese Calendar] (with modern eras only)
1719
///
@@ -39,9 +41,17 @@ use tinystr::tinystr;
3941
///
4042
/// These eras are loaded from data, requiring a data provider capable of providing [`CalendarJapaneseModernV1`]
4143
/// data.
42-
#[derive(Clone, Debug, Default)]
44+
#[derive(Clone, Debug, Copy)]
4345
pub struct Japanese {
44-
eras: DataPayload<CalendarJapaneseModernV1>,
46+
last_era: PackedEra,
47+
}
48+
49+
impl Default for Japanese {
50+
fn default() -> Self {
51+
Self {
52+
last_era: PackedEra::pack(REIWA),
53+
}
54+
}
4555
}
4656

4757
/// The [Japanese Calendar] (with historical eras)
@@ -86,9 +96,7 @@ impl Japanese {
8696
#[cfg(feature = "compiled_data")]
8797
pub const fn new() -> Self {
8898
Self {
89-
eras: DataPayload::from_static_ref(
90-
crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1,
91-
),
99+
last_era: *crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1,
92100
}
93101
}
94102

@@ -105,7 +113,7 @@ impl Japanese {
105113
provider: &D,
106114
) -> Result<Self, DataError> {
107115
Ok(Self {
108-
eras: provider.load(Default::default())?.payload,
116+
last_era: *provider.load(Default::default())?.payload.get(),
109117
})
110118
}
111119
}
@@ -143,31 +151,46 @@ impl JapaneseExtended {
143151
}
144152
}
145153

146-
const MEIJI_START: EraStartDate = EraStartDate {
147-
year: 1868,
148-
month: 10,
149-
day: 23,
150-
};
151-
const TAISHO_START: EraStartDate = EraStartDate {
152-
year: 1912,
153-
month: 7,
154-
day: 30,
155-
};
156-
const SHOWA_START: EraStartDate = EraStartDate {
157-
year: 1926,
158-
month: 12,
159-
day: 25,
160-
};
161-
const HEISEI_START: EraStartDate = EraStartDate {
162-
year: 1989,
163-
month: 1,
164-
day: 8,
165-
};
166-
const REIWA_START: EraStartDate = EraStartDate {
167-
year: 2019,
168-
month: 5,
169-
day: 1,
170-
};
154+
const MEIJI: (EraStartDate, TinyAsciiStr<16>) = (
155+
EraStartDate {
156+
year: 1868,
157+
month: 10,
158+
day: 23,
159+
},
160+
tinystr!(16, "meiji"),
161+
);
162+
const TAISHO: (EraStartDate, TinyAsciiStr<16>) = (
163+
EraStartDate {
164+
year: 1912,
165+
month: 7,
166+
day: 30,
167+
},
168+
tinystr!(16, "taisho"),
169+
);
170+
const SHOWA: (EraStartDate, TinyAsciiStr<16>) = (
171+
EraStartDate {
172+
year: 1926,
173+
month: 12,
174+
day: 25,
175+
},
176+
tinystr!(16, "showa"),
177+
);
178+
const HEISEI: (EraStartDate, TinyAsciiStr<16>) = (
179+
EraStartDate {
180+
year: 1989,
181+
month: 1,
182+
day: 8,
183+
},
184+
tinystr!(16, "heisei"),
185+
);
186+
pub(crate) const REIWA: (EraStartDate, TinyAsciiStr<16>) = (
187+
EraStartDate {
188+
year: 2019,
189+
month: 5,
190+
day: 1,
191+
},
192+
tinystr!(16, "reiwa"),
193+
);
171194

172195
impl GregorianYears for &'_ Japanese {
173196
fn extended_from_era_year(
@@ -178,80 +201,36 @@ impl GregorianYears for &'_ Japanese {
178201
if let Ok(g) = CeBce.extended_from_era_year(era, year) {
179202
return Ok(g);
180203
}
181-
let Some(era) = era else {
182-
// unreachable, handled by CeBce
183-
return Err(UnknownEraError);
184-
};
185-
186-
// Avoid linear search by trying well known eras
187-
if era == b"reiwa" {
188-
return Ok(year - 1 + REIWA_START.year);
189-
} else if era == b"heisei" {
190-
return Ok(year - 1 + HEISEI_START.year);
191-
} else if era == b"showa" {
192-
return Ok(year - 1 + SHOWA_START.year);
193-
} else if era == b"taisho" {
194-
return Ok(year - 1 + TAISHO_START.year);
195-
} else if era == b"meiji" {
196-
return Ok(year - 1 + MEIJI_START.year);
197-
}
198204

199-
let era_start = self
200-
.eras
201-
.get()
202-
.dates_to_eras
203-
.iter()
204-
.rev()
205-
.find_map(|(s, e)| (e.as_bytes() == era).then_some(s))
205+
let (start, _) = [self.last_era.unpack(), REIWA, HEISEI, SHOWA, TAISHO, MEIJI]
206+
.into_iter()
207+
.find(|(_, name)| era == Some(name.as_bytes()))
206208
.ok_or(UnknownEraError)?;
207-
Ok(era_start.year + year - 1)
209+
210+
Ok(year - 1 + start.year)
208211
}
209212

210213
fn era_year_from_extended(&self, year: i32, month: u8, day: u8) -> types::EraYear {
211214
let date: EraStartDate = EraStartDate { year, month, day };
212215

213-
let (start, era) = if date >= MEIJI_START
214-
&& self
215-
.eras
216-
.get()
217-
.dates_to_eras
218-
.last()
219-
.is_some_and(|(_, e)| e == tinystr!(16, "reiwa"))
216+
if let Some((start, era)) = [self.last_era.unpack(), REIWA, HEISEI, SHOWA, TAISHO, MEIJI]
217+
.into_iter()
218+
.find(|&(start, _)| date >= start)
220219
{
221-
// We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
222-
// array and also hardcoded. The hardcoded version is not used if data indicates the
223-
// presence of newer eras.
224-
if date >= REIWA_START {
225-
(REIWA_START, tinystr!(16, "reiwa"))
226-
} else if date >= HEISEI_START {
227-
(HEISEI_START, tinystr!(16, "heisei"))
228-
} else if date >= SHOWA_START {
229-
(SHOWA_START, tinystr!(16, "showa"))
230-
} else if date >= TAISHO_START {
231-
(TAISHO_START, tinystr!(16, "taisho"))
232-
} else {
233-
(MEIJI_START, tinystr!(16, "meiji"))
220+
types::EraYear {
221+
era,
222+
// TODO: return era indices?
223+
era_index: None,
224+
year: year - start.year + 1,
225+
extended_year: year,
226+
ambiguity: types::YearAmbiguity::CenturyRequired,
234227
}
235228
} else {
236-
let data = &self.eras.get().dates_to_eras;
237-
match data.iter().rfind(|&(s, _)| date >= s) {
238-
None => {
239-
return types::EraYear {
240-
// TODO: return era indices?
241-
era_index: None,
242-
..CeBce.era_year_from_extended(year, month, day)
243-
};
244-
}
245-
Some((s, e)) => (s, e),
229+
types::EraYear {
230+
// TODO: return era indices?
231+
era_index: None,
232+
..CeBce.era_year_from_extended(year, month, day)
246233
}
247-
};
248-
249-
types::EraYear {
250-
era,
251-
era_index: None,
252-
year: year - start.year + 1,
253-
extended_year: year,
254-
ambiguity: types::YearAmbiguity::CenturyRequired,
255234
}
256235
}
257236

@@ -281,16 +260,16 @@ impl GregorianYears for &'_ JapaneseExtended {
281260
};
282261

283262
// Avoid linear search by trying well known eras
284-
if era == b"reiwa" {
285-
return Ok(year - 1 + REIWA_START.year);
286-
} else if era == b"heisei" {
287-
return Ok(year - 1 + HEISEI_START.year);
288-
} else if era == b"showa" {
289-
return Ok(year - 1 + SHOWA_START.year);
290-
} else if era == b"taisho" {
291-
return Ok(year - 1 + TAISHO_START.year);
292-
} else if era == b"meiji" {
293-
return Ok(year - 1 + MEIJI_START.year);
263+
if era == REIWA.1.as_bytes() {
264+
return Ok(year - 1 + REIWA.0.year);
265+
} else if era == HEISEI.1.as_bytes() {
266+
return Ok(year - 1 + HEISEI.0.year);
267+
} else if era == SHOWA.1.as_bytes() {
268+
return Ok(year - 1 + SHOWA.0.year);
269+
} else if era == TAISHO.1.as_bytes() {
270+
return Ok(year - 1 + TAISHO.0.year);
271+
} else if era == MEIJI.1.as_bytes() {
272+
return Ok(year - 1 + MEIJI.0.year);
294273
}
295274

296275
let data = &self.eras.get().dates_to_eras;
@@ -324,7 +303,7 @@ impl GregorianYears for &'_ JapaneseExtended {
324303
fn era_year_from_extended(&self, year: i32, month: u8, day: u8) -> types::EraYear {
325304
let date: EraStartDate = EraStartDate { year, month, day };
326305

327-
let (start, era) = if date >= MEIJI_START
306+
let (start, era) = if date >= MEIJI.0
328307
&& self
329308
.eras
330309
.get()
@@ -335,16 +314,16 @@ impl GregorianYears for &'_ JapaneseExtended {
335314
// We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
336315
// array and also hardcoded. The hardcoded version is not used if data indicates the
337316
// presence of newer eras.
338-
if date >= REIWA_START {
339-
(REIWA_START, tinystr!(16, "reiwa"))
340-
} else if date >= HEISEI_START {
341-
(HEISEI_START, tinystr!(16, "heisei"))
342-
} else if date >= SHOWA_START {
343-
(SHOWA_START, tinystr!(16, "showa"))
344-
} else if date >= TAISHO_START {
345-
(TAISHO_START, tinystr!(16, "taisho"))
317+
if date >= REIWA.0 {
318+
REIWA
319+
} else if date >= HEISEI.0 {
320+
HEISEI
321+
} else if date >= SHOWA.0 {
322+
SHOWA
323+
} else if date >= TAISHO.0 {
324+
TAISHO
346325
} else {
347-
(MEIJI_START, tinystr!(16, "meiji"))
326+
MEIJI
348327
}
349328
} else {
350329
let data = &self.eras.get().dates_to_eras;
@@ -616,6 +595,13 @@ mod tests {
616595
)
617596
}
618597

598+
#[test]
599+
#[ignore]
600+
fn sizes() {
601+
assert_eq!(core::mem::size_of::<Japanese>(), 11);
602+
assert_eq!(core::mem::size_of::<JapaneseExtended>(), 32);
603+
}
604+
619605
#[test]
620606
fn test_japanese() {
621607
let calendar = Japanese::new();

0 commit comments

Comments
 (0)