Skip to content

Commit 5670b23

Browse files
committed
optimize era storage
1 parent 8f21ac8 commit 5670b23

File tree

4 files changed

+138
-116
lines changed

4 files changed

+138
-116
lines changed

components/calendar/src/cal/japanese.rs

Lines changed: 131 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::error::{DateError, UnknownEraError};
1111
use crate::provider::{CalendarJapaneseModernV1, EraStartDate};
1212
use crate::{types, AsCalendar, Date};
1313
use icu_provider::prelude::*;
14-
use tinystr::tinystr;
14+
use tinystr::{tinystr, TinyAsciiStr};
1515

1616
/// The [Japanese Calendar] (with modern eras only)
1717
///
@@ -36,9 +36,49 @@ use tinystr::tinystr;
3636
///
3737
/// These eras are loaded from data, requiring a data provider capable of providing [`CalendarJapaneseModernV1`]
3838
/// data.
39-
#[derive(Clone, Debug, Default)]
39+
#[derive(Clone, Debug, Copy)]
4040
pub struct Japanese {
41-
eras: DataPayload<CalendarJapaneseModernV1>,
41+
last_era: PackedEra,
42+
}
43+
44+
impl Default for Japanese {
45+
fn default() -> Self {
46+
Self {
47+
last_era: PackedEra::new(REIWA),
48+
}
49+
}
50+
}
51+
52+
#[derive(Clone, Debug, Copy)]
53+
struct PackedEra {
54+
// The year is stored as an offset from `REIWA`,
55+
// which works until 2274
56+
year: u8,
57+
month: u8,
58+
day: u8,
59+
name: TinyAsciiStr<8>,
60+
}
61+
62+
impl PackedEra {
63+
const fn new(v: (EraStartDate, TinyAsciiStr<16>)) -> Self {
64+
Self {
65+
year: (v.0.year - REIWA.0.year) as u8,
66+
month: v.0.month,
67+
day: v.0.day,
68+
name: v.1.resize(),
69+
}
70+
}
71+
72+
const fn expand(self) -> (EraStartDate, TinyAsciiStr<16>) {
73+
(
74+
EraStartDate {
75+
year: self.year as i32 + REIWA.0.year,
76+
month: self.month,
77+
day: self.day,
78+
},
79+
self.name.resize(),
80+
)
81+
}
4282
}
4383

4484
impl Japanese {
@@ -49,11 +89,27 @@ impl Japanese {
4989
/// [📚 Help choosing a constructor](icu_provider::constructors)
5090
#[cfg(feature = "compiled_data")]
5191
pub const fn new() -> Self {
52-
Self {
53-
eras: DataPayload::from_static_ref(
54-
crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1,
55-
),
56-
}
92+
let last_era = const {
93+
if let Some(&zerovec::ule::tuple::Tuple2ULE(start, name)) =
94+
crate::provider::Baked::SINGLETON_CALENDAR_JAPANESE_MODERN_V1
95+
.dates_to_eras
96+
.as_slice()
97+
.as_ule_slice()
98+
.last()
99+
{
100+
PackedEra::new((
101+
EraStartDate {
102+
year: i32::from_le_bytes(start.year.0),
103+
month: start.month,
104+
day: start.day,
105+
},
106+
name,
107+
))
108+
} else {
109+
panic!("Invalid era data")
110+
}
111+
};
112+
Self { last_era }
57113
}
58114

59115
icu_provider::gen_buffer_data_constructors!(() -> error: DataError,
@@ -69,36 +125,59 @@ impl Japanese {
69125
provider: &D,
70126
) -> Result<Self, DataError> {
71127
Ok(Self {
72-
eras: provider.load(Default::default())?.payload,
128+
last_era: PackedEra::new(
129+
provider
130+
.load(Default::default())?
131+
.payload
132+
.get()
133+
.dates_to_eras
134+
.last()
135+
.ok_or_else(|| DataError::custom("Invalid era data"))?,
136+
),
73137
})
74138
}
75139
}
76140

77-
const MEIJI_START: EraStartDate = EraStartDate {
78-
year: 1868,
79-
month: 10,
80-
day: 23,
81-
};
82-
const TAISHO_START: EraStartDate = EraStartDate {
83-
year: 1912,
84-
month: 7,
85-
day: 30,
86-
};
87-
const SHOWA_START: EraStartDate = EraStartDate {
88-
year: 1926,
89-
month: 12,
90-
day: 25,
91-
};
92-
const HEISEI_START: EraStartDate = EraStartDate {
93-
year: 1989,
94-
month: 1,
95-
day: 8,
96-
};
97-
const REIWA_START: EraStartDate = EraStartDate {
98-
year: 2019,
99-
month: 5,
100-
day: 1,
101-
};
141+
const MEIJI: (EraStartDate, TinyAsciiStr<16>) = (
142+
EraStartDate {
143+
year: 1868,
144+
month: 10,
145+
day: 23,
146+
},
147+
tinystr!(16, "meiji"),
148+
);
149+
const TAISHO: (EraStartDate, TinyAsciiStr<16>) = (
150+
EraStartDate {
151+
year: 1912,
152+
month: 7,
153+
day: 30,
154+
},
155+
tinystr!(16, "taisho"),
156+
);
157+
const SHOWA: (EraStartDate, TinyAsciiStr<16>) = (
158+
EraStartDate {
159+
year: 1926,
160+
month: 12,
161+
day: 25,
162+
},
163+
tinystr!(16, "showa"),
164+
);
165+
const HEISEI: (EraStartDate, TinyAsciiStr<16>) = (
166+
EraStartDate {
167+
year: 1989,
168+
month: 1,
169+
day: 8,
170+
},
171+
tinystr!(16, "heisei"),
172+
);
173+
const REIWA: (EraStartDate, TinyAsciiStr<16>) = (
174+
EraStartDate {
175+
year: 2019,
176+
month: 5,
177+
day: 1,
178+
},
179+
tinystr!(16, "reiwa"),
180+
);
102181

103182
impl GregorianYears for &'_ Japanese {
104183
fn extended_from_era_year(
@@ -109,89 +188,36 @@ impl GregorianYears for &'_ Japanese {
109188
if let Ok(g) = CeBce.extended_from_era_year(era, year) {
110189
return Ok(g);
111190
}
112-
let Some(era) = era else {
113-
// unreachable, handled by CeBce
114-
return Err(UnknownEraError);
115-
};
116191

117-
// Avoid linear search by trying well known eras
118-
if era == b"reiwa" {
119-
return Ok(year - 1 + REIWA_START.year);
120-
} else if era == b"heisei" {
121-
return Ok(year - 1 + HEISEI_START.year);
122-
} else if era == b"showa" {
123-
return Ok(year - 1 + SHOWA_START.year);
124-
} else if era == b"taisho" {
125-
return Ok(year - 1 + TAISHO_START.year);
126-
} else if era == b"meiji" {
127-
return Ok(year - 1 + MEIJI_START.year);
128-
}
129-
130-
let era_start = self
131-
.eras
132-
.get()
133-
.dates_to_eras
134-
.iter()
135-
.rev()
136-
.find_map(|(s, e)| (e.as_bytes() == era).then_some(s))
192+
let (start, _) = [self.last_era.expand(), REIWA, HEISEI, SHOWA, TAISHO, MEIJI]
193+
.into_iter()
194+
.find(|(_, name)| era == Some(name.as_bytes()))
137195
.ok_or(UnknownEraError)?;
138-
Ok(era_start.year + year - 1)
196+
197+
Ok(year - 1 + start.year)
139198
}
140199

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

144-
if year < 1873 {
145-
return types::EraYear {
203+
if let Some((start, era)) = [self.last_era.expand(), REIWA, HEISEI, SHOWA, TAISHO, MEIJI]
204+
.into_iter()
205+
.find(|&(start, _)| date >= start && year >= 1873)
206+
{
207+
types::EraYear {
208+
era,
146209
// TODO: return era indices?
147210
era_index: None,
148-
..CeBce.era_year_from_extended(year, month, day)
149-
};
150-
}
151-
152-
let (start, era) = if self
153-
.eras
154-
.get()
155-
.dates_to_eras
156-
.last()
157-
.is_some_and(|(_, e)| e == tinystr!(16, "reiwa"))
158-
{
159-
// We optimize for the five "modern" post-Meiji eras, which are stored in a smaller
160-
// array and also hardcoded. The hardcoded version is not used if data indicates the
161-
// presence of newer eras.
162-
if date >= REIWA_START {
163-
(REIWA_START, tinystr!(16, "reiwa"))
164-
} else if date >= HEISEI_START {
165-
(HEISEI_START, tinystr!(16, "heisei"))
166-
} else if date >= SHOWA_START {
167-
(SHOWA_START, tinystr!(16, "showa"))
168-
} else if date >= TAISHO_START {
169-
(TAISHO_START, tinystr!(16, "taisho"))
170-
} else {
171-
(MEIJI_START, tinystr!(16, "meiji"))
211+
year: year - start.year + 1,
212+
extended_year: year,
213+
ambiguity: types::YearAmbiguity::CenturyRequired,
172214
}
173215
} else {
174-
let data = &self.eras.get().dates_to_eras;
175-
#[allow(clippy::unwrap_used)] // binary search
176-
match data.binary_search_by(|(d, _)| d.cmp(&date)) {
177-
Err(0) => {
178-
return types::EraYear {
179-
// TODO: return era indices?
180-
era_index: None,
181-
..CeBce.era_year_from_extended(year, month, day)
182-
};
183-
}
184-
Ok(index) => data.get(index).unwrap(),
185-
Err(index) => data.get(index - 1).unwrap(),
216+
types::EraYear {
217+
// TODO: return era indices?
218+
era_index: None,
219+
..CeBce.era_year_from_extended(year, month, day)
186220
}
187-
};
188-
189-
types::EraYear {
190-
era,
191-
era_index: None,
192-
year: year - start.year + 1,
193-
extended_year: year,
194-
ambiguity: types::YearAmbiguity::CenturyRequired,
195221
}
196222
}
197223

components/datetime/src/neo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ where
391391
size_test!(
392392
DateTimeFormatter<crate::fieldsets::YMD>,
393393
neo_year_month_day_formatter_size,
394-
368
394+
344
395395
);
396396

397397
/// [`DateTimeFormatter`] is a formatter capable of formatting dates and/or times from

ffi/capi/src/calendar.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#[diplomat::abi_rename = "icu4x_{0}_mv1"]
77
pub mod ffi {
88
use alloc::boxed::Box;
9-
use alloc::sync::Arc;
109

1110
#[cfg(feature = "buffer_provider")]
1211
use crate::unstable::errors::ffi::DataError;
@@ -72,17 +71,15 @@ pub mod ffi {
7271
#[diplomat::opaque]
7372
#[diplomat::transparent_convert]
7473
#[diplomat::rust_link(icu::calendar::AnyCalendar, Enum)]
75-
pub struct Calendar(pub Arc<icu_calendar::AnyCalendar>);
74+
pub struct Calendar(pub icu_calendar::AnyCalendar);
7675

7776
impl Calendar {
7877
/// Creates a new [`Calendar`] for the specified kind, using compiled data.
7978
#[diplomat::rust_link(icu::calendar::AnyCalendar::new, FnInEnum)]
8079
#[diplomat::attr(auto, constructor)]
8180
#[cfg(feature = "compiled_data")]
8281
pub fn create(kind: CalendarKind) -> Box<Calendar> {
83-
Box::new(Calendar(Arc::new(icu_calendar::AnyCalendar::new(
84-
kind.into(),
85-
))))
82+
Box::new(Calendar(icu_calendar::AnyCalendar::new(kind.into())))
8683
}
8784

8885
/// Creates a new [`Calendar`] for the specified kind, using a particular data source.
@@ -93,12 +90,12 @@ pub mod ffi {
9390
provider: &DataProvider,
9491
kind: CalendarKind,
9592
) -> Result<Box<Calendar>, DataError> {
96-
Ok(Box::new(Calendar(Arc::new(
93+
Ok(Box::new(Calendar(
9794
icu_calendar::AnyCalendar::try_new_with_buffer_provider(
9895
provider.get()?,
9996
kind.into(),
10097
)?,
101-
))))
98+
)))
10299
}
103100

104101
/// Returns the kind of this calendar

ffi/capi/src/date.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use ffi::IsoWeekOfYear;
88
#[diplomat::abi_rename = "icu4x_{0}_mv1"]
99
pub mod ffi {
1010
use alloc::boxed::Box;
11-
use alloc::sync::Arc;
1211
use core::fmt::Write;
1312
#[cfg(feature = "unstable")]
1413
use diplomat_runtime::DiplomatOption;
@@ -81,7 +80,7 @@ pub mod ffi {
8180
#[diplomat::rust_link(icu::calendar::Date::to_any, FnInStruct)]
8281
#[diplomat::attr(demo_gen, disable)] // covered by Date
8382
pub fn to_any(&self) -> Box<Date> {
84-
Box::new(Date(self.0.to_any().into_atomic_ref_counted()))
83+
Box::new(Date(self.0.to_any()))
8584
}
8685

8786
/// Returns this date's Rata Die
@@ -234,7 +233,7 @@ pub mod ffi {
234233
#[diplomat::transparent_convert]
235234
/// An ICU4X Date object capable of containing a date for any calendar.
236235
#[diplomat::rust_link(icu::calendar::Date, Struct)]
237-
pub struct Date(pub icu_calendar::Date<Arc<icu_calendar::AnyCalendar>>);
236+
pub struct Date(pub icu_calendar::Date<icu_calendar::AnyCalendar>);
238237

239238
impl Date {
240239
/// Creates a new [`Date`] representing the ISO date

0 commit comments

Comments
 (0)