Skip to content

Commit ea38358

Browse files
committed
optimize era storage
1 parent 592d46e commit ea38358

File tree

4 files changed

+139
-110
lines changed

4 files changed

+139
-110
lines changed

components/calendar/src/cal/japanese.rs

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

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

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

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

104183
impl GregorianYears for &'_ Japanese {
105184
fn extended_from_era_year(
@@ -110,82 +189,36 @@ impl GregorianYears for &'_ Japanese {
110189
if let Ok(g) = CeBce.extended_from_era_year(era, year) {
111190
return Ok(g);
112191
}
113-
let Some(era) = era else {
114-
// unreachable, handled by CeBce
115-
return Err(UnknownEraError);
116-
};
117-
118-
// Avoid linear search by trying well known eras
119-
if era == b"reiwa" {
120-
return Ok(year - 1 + REIWA_START.year);
121-
} else if era == b"heisei" {
122-
return Ok(year - 1 + HEISEI_START.year);
123-
} else if era == b"showa" {
124-
return Ok(year - 1 + SHOWA_START.year);
125-
} else if era == b"taisho" {
126-
return Ok(year - 1 + TAISHO_START.year);
127-
} else if era == b"meiji" {
128-
return Ok(year - 1 + MEIJI_START.year);
129-
}
130192

131-
let era_start = self
132-
.eras
133-
.get()
134-
.dates_to_eras
135-
.iter()
136-
.rev()
137-
.find_map(|(s, e)| (e.as_bytes() == era).then_some(s))
193+
let (start, _) = [self.last_era.expand(), REIWA, HEISEI, SHOWA, TAISHO, MEIJI]
194+
.into_iter()
195+
.find(|(_, name)| era == Some(name.as_bytes()))
138196
.ok_or(UnknownEraError)?;
139-
Ok(era_start.year + year - 1)
197+
198+
Ok(year - 1 + start.year)
140199
}
141200

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

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

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)