use crate::error::PropertiesError;
use crate::props::Script;
use crate::props::ScriptULE;
use crate::provider::*;
use core::iter::FromIterator;
use core::ops::RangeInclusive;
use icu_collections::codepointinvlist::CodePointInversionList;
use icu_provider::prelude::*;
use zerovec::{ule::AsULE, ZeroSlice};
const SCRIPT_VAL_LENGTH: u16 = 10;
const SCRIPT_X_SCRIPT_VAL: u16 = (1 << SCRIPT_VAL_LENGTH) - 1;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "datagen", derive(databake::Bake))]
#[cfg_attr(feature = "datagen", databake(path = icu_properties::script))]
#[repr(transparent)]
#[doc(hidden)]
#[allow(clippy::exhaustive_structs)] pub struct ScriptWithExt(pub u16);
#[allow(missing_docs)] #[allow(non_upper_case_globals)]
#[doc(hidden)] impl ScriptWithExt {
pub const Unknown: ScriptWithExt = ScriptWithExt(0);
}
impl AsULE for ScriptWithExt {
type ULE = ScriptULE;
#[inline]
fn to_unaligned(self) -> Self::ULE {
Script(self.0).to_unaligned()
}
#[inline]
fn from_unaligned(unaligned: Self::ULE) -> Self {
ScriptWithExt(Script::from_unaligned(unaligned).0)
}
}
#[doc(hidden)] impl ScriptWithExt {
pub fn is_common(&self) -> bool {
self.0 >> SCRIPT_VAL_LENGTH == 1
}
pub fn is_inherited(&self) -> bool {
self.0 >> SCRIPT_VAL_LENGTH == 2
}
pub fn is_other(&self) -> bool {
self.0 >> SCRIPT_VAL_LENGTH == 3
}
pub fn has_extensions(&self) -> bool {
let high_order_bits = self.0 >> SCRIPT_VAL_LENGTH;
high_order_bits > 0
}
}
impl From<ScriptWithExt> for u32 {
fn from(swe: ScriptWithExt) -> Self {
swe.0 as u32
}
}
impl From<ScriptWithExt> for Script {
fn from(swe: ScriptWithExt) -> Self {
Script(swe.0)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ScriptExtensionsSet<'a> {
values: &'a ZeroSlice<Script>,
}
impl ScriptExtensionsSet<'_> {
pub fn contains(&self, x: &Script) -> bool {
ZeroSlice::binary_search(self.values, x).is_ok()
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = Script> + '_ {
ZeroSlice::iter(self.values)
}
#[doc(hidden)]
pub fn array_len(&self) -> usize {
self.values.len()
}
#[doc(hidden)]
pub fn array_get(&self, index: usize) -> Option<Script> {
self.values.get(index)
}
}
#[derive(Debug)]
pub struct ScriptWithExtensions {
data: DataPayload<ScriptWithExtensionsPropertyV1Marker>,
}
#[derive(Clone, Copy, Debug)]
pub struct ScriptWithExtensionsBorrowed<'a> {
data: &'a ScriptWithExtensionsPropertyV1<'a>,
}
impl ScriptWithExtensions {
#[inline]
pub fn as_borrowed(&self) -> ScriptWithExtensionsBorrowed<'_> {
ScriptWithExtensionsBorrowed {
data: self.data.get(),
}
}
pub fn from_data(data: DataPayload<ScriptWithExtensionsPropertyV1Marker>) -> Self {
Self { data }
}
}
impl<'a> ScriptWithExtensionsBorrowed<'a> {
pub fn get_script_val(self, code_point: u32) -> Script {
let sc_with_ext = self.data.trie.get32(code_point);
if sc_with_ext.is_other() {
let ext_idx = sc_with_ext.0 & SCRIPT_X_SCRIPT_VAL;
let scx_val = self.data.extensions.get(ext_idx as usize);
let scx_first_sc = scx_val.and_then(|scx| scx.get(0));
let default_sc_val = Script::Unknown;
scx_first_sc.unwrap_or(default_sc_val)
} else if sc_with_ext.is_common() {
Script::Common
} else if sc_with_ext.is_inherited() {
Script::Inherited
} else {
let script_val = sc_with_ext.0;
Script(script_val)
}
}
fn get_scx_val_using_trie_val(
self,
sc_with_ext_ule: &'a <ScriptWithExt as AsULE>::ULE,
) -> &'a ZeroSlice<Script> {
let sc_with_ext = ScriptWithExt::from_unaligned(*sc_with_ext_ule);
if sc_with_ext.is_other() {
let ext_idx = sc_with_ext.0 & SCRIPT_X_SCRIPT_VAL;
let ext_subarray = self.data.extensions.get(ext_idx as usize);
let scx_slice = ext_subarray
.and_then(|zslice| zslice.as_ule_slice().get(1..))
.unwrap_or_default();
ZeroSlice::from_ule_slice(scx_slice)
} else if sc_with_ext.is_common() || sc_with_ext.is_inherited() {
let ext_idx = sc_with_ext.0 & SCRIPT_X_SCRIPT_VAL;
let scx_val = self.data.extensions.get(ext_idx as usize);
scx_val.unwrap_or_default()
} else {
let script_ule_slice = core::slice::from_ref(sc_with_ext_ule);
ZeroSlice::from_ule_slice(script_ule_slice)
}
}
pub fn get_script_extensions_val(self, code_point: u32) -> ScriptExtensionsSet<'a> {
let sc_with_ext_ule = self.data.trie.get32_ule(code_point);
ScriptExtensionsSet {
values: match sc_with_ext_ule {
Some(ule_ref) => self.get_scx_val_using_trie_val(ule_ref),
None => ZeroSlice::from_ule_slice(&[]),
},
}
}
pub fn has_script(self, code_point: u32, script: Script) -> bool {
let sc_with_ext_ule = if let Some(scwe_ule) = self.data.trie.get32_ule(code_point) {
scwe_ule
} else {
return false;
};
let sc_with_ext = <ScriptWithExt as AsULE>::from_unaligned(*sc_with_ext_ule);
if !sc_with_ext.has_extensions() {
let script_val = sc_with_ext.0;
script == Script(script_val)
} else {
let scx_val = self.get_scx_val_using_trie_val(sc_with_ext_ule);
let script_find = scx_val.iter().find(|&sc| sc == script);
script_find.is_some()
}
}
pub fn get_script_extensions_ranges(
self,
script: Script,
) -> impl Iterator<Item = RangeInclusive<u32>> + 'a {
self.data
.trie
.iter_ranges_mapped(move |value| {
let sc_with_ext = ScriptWithExt(value.0);
if sc_with_ext.has_extensions() {
self.get_scx_val_using_trie_val(&sc_with_ext.to_unaligned())
.iter()
.any(|sc| sc == script)
} else {
script == sc_with_ext.into()
}
})
.filter(|v| v.value)
.map(|v| v.range)
}
pub fn get_script_extensions_set(self, script: Script) -> CodePointInversionList<'a> {
CodePointInversionList::from_iter(self.get_script_extensions_ranges(script))
}
}
impl ScriptWithExtensionsBorrowed<'static> {
pub const fn static_to_owned(self) -> ScriptWithExtensions {
ScriptWithExtensions {
data: DataPayload::from_static_ref(self.data),
}
}
}
#[cfg(feature = "compiled_data")]
pub const fn script_with_extensions() -> ScriptWithExtensionsBorrowed<'static> {
ScriptWithExtensionsBorrowed {
data: crate::provider::Baked::SINGLETON_PROPS_SCX_V1,
}
}
icu_provider::gen_any_buffer_data_constructors!(
locale: skip,
options: skip,
result: Result<ScriptWithExtensions, PropertiesError>,
#[cfg(skip)]
functions: [
script_with_extensions,
load_script_with_extensions_with_any_provider,
load_script_with_extensions_with_buffer_provider,
load_script_with_extensions_unstable,
]
);
#[doc = icu_provider::gen_any_buffer_unstable_docs!(UNSTABLE, script_with_extensions)]
pub fn load_script_with_extensions_unstable(
provider: &(impl DataProvider<ScriptWithExtensionsPropertyV1Marker> + ?Sized),
) -> Result<ScriptWithExtensions, PropertiesError> {
Ok(ScriptWithExtensions::from_data(
provider
.load(Default::default())
.and_then(DataResponse::take_payload)?,
))
}