#[cfg(feature = "alloc")]
use alloc::string::String;
use yoke::Yokeable;
use zerofrom::ZeroFrom;
use zerovec::{ZeroSlice, ZeroVec};
const MAX_BRANCH_LINEAR_SUB_NODE_LENGTH: usize = 5;
const MIN_LINEAR_MATCH: u16 = 0x30;
const MAX_LINEAR_MATCH_LENGTH: u16 = 0x10;
const MIN_VALUE_LEAD: u16 = MIN_LINEAR_MATCH + MAX_LINEAR_MATCH_LENGTH; const NODE_TYPE_MASK: u16 = MIN_VALUE_LEAD - 1;
const VALUE_IS_FINAL: u16 = 0x8000;
const MAX_ONE_UNIT_VALUE: u16 = 0x3fff;
const MIN_TWO_UNIT_VALUE_LEAD: u16 = MAX_ONE_UNIT_VALUE + 1;
const MAX_ONE_UNIT_NODE_VALUE: u16 = 0xff;
const MIN_TWO_UNIT_NODE_VALUE_LEAD: u16 = MIN_VALUE_LEAD + ((MAX_ONE_UNIT_NODE_VALUE + 1) << 6);
const THREE_UNIT_NODE_VALUE_LEAD: u16 = 0x7fc0;
const THREE_UNIT_VALUE_LEAD: u16 = 0x7fff;
const MAX_ONE_UNIT_DELTA: u16 = 0xfbff;
const MIN_TWO_UNIT_DELTA_LEAD: u16 = MAX_ONE_UNIT_DELTA + 1; const THREE_UNIT_DELTA_LEAD: u16 = 0xffff;
fn skip_value(pos: usize, lead: u16) -> usize {
if lead < MIN_TWO_UNIT_VALUE_LEAD {
pos
} else if lead < THREE_UNIT_VALUE_LEAD {
pos + 1
} else {
pos + 2
}
}
fn skip_node_value(pos: usize, lead: u16) -> usize {
if lead < MIN_TWO_UNIT_NODE_VALUE_LEAD {
pos
} else if lead < THREE_UNIT_NODE_VALUE_LEAD {
pos + 1
} else {
pos + 2
}
}
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "databake", derive(databake::Bake))]
#[cfg_attr(feature = "databake", databake(path = icu_collections::char16trie))]
#[derive(Clone, Debug, PartialEq, Eq, ZeroFrom, Yokeable)]
#[allow(clippy::exhaustive_structs)] pub struct Char16Trie<'data> {
#[cfg_attr(feature = "serde", serde(borrow, alias = "trie_data"))]
#[doc(hidden)] pub data: ZeroVec<'data, u16>,
}
impl<'data> Char16Trie<'data> {
#[inline]
pub fn new(data: ZeroVec<'data, u16>) -> Self {
Self { data }
}
#[inline]
pub fn cursor(&self) -> Char16TrieCursor<'_> {
Char16TrieCursor::new(&self.data)
}
#[deprecated(since = "2.4.0", note = "use `Char16Trie::cursor`")]
#[allow(deprecated)]
#[inline]
pub fn iter(&self) -> Char16TrieIterator<'_> {
Char16TrieIterator::new(&self.data)
}
#[cfg(feature = "alloc")]
pub fn iter2(&self) -> impl Iterator<Item = (String, i32)> + '_ {
let cursor = self.cursor();
let mut root_value = cursor.value();
let mut stack = alloc::vec![(cursor, 0)];
let mut prefix = String::new();
let mut lead_surrogate = None;
core::iter::from_fn(move || {
if let Some(v) = root_value.take() {
return Some((String::new(), v));
}
while let Some((cursor, i)) = stack.last_mut() {
let mut next_cursor = cursor.clone();
if let Some(c) = next_cursor.probe(*i) {
*i += 1;
if let Some(lead) = lead_surrogate.take() {
prefix.push(
char::decode_utf16([lead, c])
.next()
.and_then(Result::ok)
.unwrap_or(char::REPLACEMENT_CHARACTER),
);
} else if matches!(c, 0xD800..0xDC00) {
lead_surrogate = Some(c);
} else {
prefix
.push(char::from_u32(c as u32).unwrap_or(char::REPLACEMENT_CHARACTER));
}
let v = next_cursor.value();
stack.push((next_cursor, 0));
if let Some(v) = v {
return Some((prefix.clone(), v));
}
} else {
stack.pop();
if lead_surrogate.take().is_none()
&& let Some(ch @ '\u{10000}'..) = prefix.pop()
{
lead_surrogate = Some(u16_lead(ch as u32));
}
}
}
None
})
}
}
#[derive(Clone, Debug)]
pub struct Char16TrieCursor<'a> {
trie: &'a ZeroSlice<u16>,
pos: Option<usize>,
remaining_match_length: Option<u8>,
}
#[deprecated(since = "2.4.0", note = "use `Char16TrieCursor`")]
#[derive(Clone, Debug)]
pub struct Char16TrieIterator<'a>(Char16TrieCursor<'a>);
#[deprecated(since = "2.4.0", note = "use `Char16TrieCursor`")]
#[derive(Clone, Copy, Debug, PartialEq)]
#[allow(clippy::exhaustive_enums)]
pub enum TrieResult {
NoMatch,
NoValue,
FinalValue(i32),
Intermediate(i32),
}
fn u16_lead(supplementary: u32) -> u16 {
(((supplementary) >> 10) + 0xd7c0) as u16
}
fn u16_tail(supplementary: u32) -> u16 {
(((supplementary) & 0x3ff) | 0xdc00) as u16
}
impl<'a> Char16TrieCursor<'a> {
#[inline]
pub fn new(trie: &'a ZeroSlice<u16>) -> Self {
Self {
trie,
pos: Some(0),
remaining_match_length: None,
}
}
#[inline]
pub fn step(&mut self, c: char) {
self.step32(c as u32);
}
#[inline]
pub fn step32(&mut self, c: u32) {
let c = if let Ok(c) = u16::try_from(c) {
c
} else {
self.step16(u16_lead(c));
u16_tail(c)
};
self.step16(c);
}
#[inline]
pub fn step16(&mut self, c: u16) {
let Some(pos) = self.pos else {
return;
};
self.pos = if let Some(length) = self.remaining_match_length {
if Some(c) == self.trie.get(pos) {
self.remaining_match_length = length.checked_sub(1);
Some(pos + 1)
} else {
None
}
} else {
self.next_impl(pos, c)
};
}
#[cfg(feature = "alloc")]
fn probe(&mut self, mut index: usize) -> Option<u16> {
let mut pos = self.pos?;
if let Some(length) = self.remaining_match_length {
if index > 0 {
return None;
}
let unit = self.trie.get(pos)?;
self.remaining_match_length = length.checked_sub(1);
self.pos = Some(pos + 1);
return Some(unit);
}
let mut node = self.trie.get(pos)?;
pos += 1;
if node >= MIN_VALUE_LEAD {
if (node & VALUE_IS_FINAL) != 0 {
return None;
}
pos = skip_node_value(pos, node);
node &= NODE_TYPE_MASK;
}
if node >= MIN_LINEAR_MATCH {
if index > 0 {
return None;
}
let length = (node - MIN_LINEAR_MATCH) as u8;
let unit = self.trie.get(pos)?;
self.remaining_match_length = length.checked_sub(1);
self.pos = Some(pos + 1);
return Some(unit);
}
let mut length = node as usize;
if length == 0 {
length = self.trie.get(pos)? as usize;
pos += 1;
}
length += 1;
if index >= length {
return None;
}
while length > MAX_BRANCH_LINEAR_SUB_NODE_LENGTH {
let half = length >> 1;
if index < half {
length = half;
pos = self.jump_by_delta(pos + 1)?;
} else {
index -= half;
length -= half;
pos = self.skip_delta(pos + 1)?;
}
}
while index > 0 {
index -= 1;
length -= 1;
pos = self.skip_value(pos + 1)?;
}
let unit = self.trie.get(pos)?;
pos += 1;
if length > 1 {
let node = self.trie.get(pos)?;
if node & VALUE_IS_FINAL == 0 {
pos += 1;
if node < MIN_TWO_UNIT_VALUE_LEAD {
pos += node as usize;
} else if node < THREE_UNIT_VALUE_LEAD {
pos += (((node - MIN_TWO_UNIT_VALUE_LEAD) as u32) << 16) as usize
| self.trie.get(pos)? as usize;
pos += 1;
} else {
pos +=
((self.trie.get(pos)? as usize) << 16) | self.trie.get(pos + 1)? as usize;
pos += 2;
}
}
}
self.pos = Some(pos);
Some(unit)
}
#[inline]
pub fn value(&self) -> Option<i32> {
if self.remaining_match_length.is_some() {
return None;
}
let pos = self.pos?;
let lead_unit = self.trie.get(pos)?;
if lead_unit < MIN_VALUE_LEAD {
None
} else if lead_unit & VALUE_IS_FINAL != 0 {
let v = self.read_value(pos + 1, lead_unit & 0x7fff);
debug_assert!(v.is_some());
Some(v.unwrap_or(0))
} else {
let v = self.read_node_value(pos + 1, lead_unit);
debug_assert!(v.is_some());
Some(v.unwrap_or(0))
}
}
#[inline]
pub fn is_empty(&self) -> bool {
let Some(pos) = self.pos else {
return true;
};
self.remaining_match_length.is_none()
&& self.trie.get(pos).is_none_or(|u| u & VALUE_IS_FINAL != 0)
}
fn branch_next(&self, mut pos: usize, mut length: usize, in_unit: u16) -> Option<usize> {
if length == 0 {
length = self.trie.get(pos)? as usize;
pos += 1;
}
length += 1;
while length > MAX_BRANCH_LINEAR_SUB_NODE_LENGTH {
if in_unit < self.trie.get(pos)? {
length >>= 1;
pos = self.jump_by_delta(pos + 1)?;
} else {
length = length - (length >> 1);
pos = self.skip_delta(pos + 1)?;
}
}
loop {
if in_unit == self.trie.get(pos)? {
pos += 1;
let node = self.trie.get(pos)?;
if node & VALUE_IS_FINAL != 0 {
return Some(pos);
}
pos += 1;
if node < MIN_TWO_UNIT_VALUE_LEAD {
pos += node as usize;
} else if node < THREE_UNIT_VALUE_LEAD {
pos += (((node - MIN_TWO_UNIT_VALUE_LEAD) as u32) << 16) as usize
| self.trie.get(pos)? as usize;
pos += 1;
} else {
pos +=
((self.trie.get(pos)? as usize) << 16) | self.trie.get(pos + 1)? as usize;
pos += 2;
}
return Some(pos);
}
length -= 1;
pos = self.skip_value(pos + 1)?;
if length <= 1 {
break;
}
}
(in_unit == self.trie.get(pos)?).then_some(pos + 1)
}
fn next_impl(&mut self, pos: usize, in_unit: u16) -> Option<usize> {
let mut node = self.trie.get(pos)?;
let mut pos = pos + 1;
loop {
if node < MIN_LINEAR_MATCH {
return self.branch_next(pos, node as usize, in_unit);
} else if node < MIN_VALUE_LEAD {
let length = (node - MIN_LINEAR_MATCH) as u8;
if in_unit == self.trie.get(pos)? {
self.remaining_match_length = length.checked_sub(1);
return Some(pos + 1);
}
break;
} else if (node & VALUE_IS_FINAL) != 0 {
break;
} else {
pos = skip_node_value(pos, node);
node &= NODE_TYPE_MASK;
}
}
None
}
#[inline(always)] fn jump_by_delta(&self, pos: usize) -> Option<usize> {
let delta = self.trie.get(pos)?;
let v = if delta < MIN_TWO_UNIT_DELTA_LEAD {
pos + 1 + delta as usize
} else if delta == THREE_UNIT_DELTA_LEAD {
let delta =
((self.trie.get(pos + 1)? as usize) << 16) | (self.trie.get(pos + 2)? as usize);
pos + delta + 3
} else {
let delta = (((delta - MIN_TWO_UNIT_DELTA_LEAD) as usize) << 16)
| (self.trie.get(pos + 1)? as usize);
pos + delta + 2
};
Some(v)
}
#[inline(always)] fn skip_value(&self, pos: usize) -> Option<usize> {
let lead_unit = self.trie.get(pos)?;
Some(skip_value(pos + 1, lead_unit & 0x7fff))
}
#[inline(always)] fn skip_delta(&self, pos: usize) -> Option<usize> {
let delta = self.trie.get(pos)?;
let v = if delta < MIN_TWO_UNIT_DELTA_LEAD {
pos + 1
} else if delta == THREE_UNIT_DELTA_LEAD {
pos + 3
} else {
pos + 2
};
Some(v)
}
#[inline(always)] fn read_value(&self, pos: usize, lead_unit: u16) -> Option<i32> {
let v = if lead_unit < MIN_TWO_UNIT_VALUE_LEAD {
lead_unit.into()
} else if lead_unit < THREE_UNIT_VALUE_LEAD {
(((lead_unit - MIN_TWO_UNIT_VALUE_LEAD) as i32) << 16) | self.trie.get(pos)? as i32
} else {
((self.trie.get(pos)? as i32) << 16) | self.trie.get(pos + 1)? as i32
};
Some(v)
}
#[inline(always)] fn read_node_value(&self, pos: usize, lead_unit: u16) -> Option<i32> {
let v = if lead_unit < (MIN_TWO_UNIT_NODE_VALUE_LEAD) {
((lead_unit >> 6) - 1).into()
} else if lead_unit < THREE_UNIT_NODE_VALUE_LEAD {
((((lead_unit & 0x7fc0) - MIN_TWO_UNIT_NODE_VALUE_LEAD) as i32) << 10)
| self.trie.get(pos)? as i32
} else {
((self.trie.get(pos)? as i32) << 16) | self.trie.get(pos + 1)? as i32
};
Some(v)
}
}
impl core::fmt::Write for Char16TrieCursor<'_> {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
for c in s.encode_utf16() {
self.step16(c);
}
Ok(())
}
fn write_char(&mut self, c: char) -> core::fmt::Result {
self.step(c);
Ok(())
}
}
#[allow(deprecated)]
impl<'a> Char16TrieIterator<'a> {
#[inline]
pub fn new(trie: &'a ZeroSlice<u16>) -> Self {
Self(Char16TrieCursor::new(trie))
}
#[inline]
pub fn next(&mut self, c: char) -> TrieResult {
self.next32(c as u32)
}
#[inline]
pub fn next32(&mut self, c: u32) -> TrieResult {
if c <= 0xffff {
self.next16(c as u16)
} else {
self.0.step16(u16_lead(c));
self.next16(u16_tail(c))
}
}
#[inline]
pub fn next16(&mut self, c: u16) -> TrieResult {
self.0.step16(c);
match (self.0.value(), self.0.is_empty()) {
(Some(v), true) => TrieResult::FinalValue(v),
(Some(v), false) => TrieResult::Intermediate(v),
(None, true) => TrieResult::NoMatch,
(None, false) => TrieResult::NoValue,
}
}
}