use super::TextSource;
use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::char;
use core::ops::Range;
use crate::{
compute_bidi_info_for_para, compute_initial_info, level, para_direction, reorder_levels,
reorder_visual, visual_runs_for_line,
};
use crate::{
BidiClass, BidiDataSource, Direction, Level, LevelRun, ParagraphInfo, ParagraphInfoFlags,
};
#[cfg(feature = "hardcoded-data")]
use crate::HardcodedBidiData;
#[derive(PartialEq, Debug)]
pub struct InitialInfo<'text> {
pub text: &'text [u16],
pub original_classes: Vec<BidiClass>,
pub paragraphs: Vec<ParagraphInfo>,
}
impl<'text> InitialInfo<'text> {
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[cfg(feature = "hardcoded-data")]
pub fn new(text: &[u16], default_para_level: Option<Level>) -> InitialInfo<'_> {
Self::new_with_data_source(&HardcodedBidiData, text, default_para_level)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn new_with_data_source<'a, D: BidiDataSource>(
data_source: &D,
text: &'a [u16],
default_para_level: Option<Level>,
) -> InitialInfo<'a> {
InitialInfoExt::new_with_data_source(data_source, text, default_para_level).base
}
}
#[derive(PartialEq, Debug)]
struct InitialInfoExt<'text> {
base: InitialInfo<'text>,
flags: Vec<ParagraphInfoFlags>,
}
impl<'text> InitialInfoExt<'text> {
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn new_with_data_source<'a, D: BidiDataSource>(
data_source: &D,
text: &'a [u16],
default_para_level: Option<Level>,
) -> InitialInfoExt<'a> {
let mut paragraphs = Vec::<ParagraphInfo>::new();
let mut flags = Vec::<ParagraphInfoFlags>::new();
let (original_classes, _, _, _) = compute_initial_info(
data_source,
text,
default_para_level,
Some((&mut paragraphs, &mut flags)),
);
InitialInfoExt {
base: InitialInfo {
text,
original_classes,
paragraphs,
},
flags,
}
}
}
#[derive(Debug, PartialEq)]
pub struct BidiInfo<'text> {
pub text: &'text [u16],
pub original_classes: Vec<BidiClass>,
pub levels: Vec<Level>,
pub paragraphs: Vec<ParagraphInfo>,
}
impl<'text> BidiInfo<'text> {
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[cfg(feature = "hardcoded-data")]
#[inline]
pub fn new(text: &[u16], default_para_level: Option<Level>) -> BidiInfo<'_> {
Self::new_with_data_source(&HardcodedBidiData, text, default_para_level)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn new_with_data_source<'a, D: BidiDataSource>(
data_source: &D,
text: &'a [u16],
default_para_level: Option<Level>,
) -> BidiInfo<'a> {
let InitialInfoExt { base, flags, .. } =
InitialInfoExt::new_with_data_source(data_source, text, default_para_level);
let mut levels = Vec::<Level>::with_capacity(text.len());
let mut processing_classes = base.original_classes.clone();
for (para, flags) in base.paragraphs.iter().zip(flags.iter()) {
let text = &text[para.range.clone()];
let original_classes = &base.original_classes[para.range.clone()];
compute_bidi_info_for_para(
data_source,
para,
flags.is_pure_ltr,
flags.has_isolate_controls,
text,
original_classes,
&mut processing_classes,
&mut levels,
);
}
BidiInfo {
text,
original_classes: base.original_classes,
paragraphs: base.paragraphs,
levels,
}
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reordered_levels(&self, para: &ParagraphInfo, line: Range<usize>) -> Vec<Level> {
assert!(line.start <= self.levels.len());
assert!(line.end <= self.levels.len());
let mut levels = self.levels.clone();
let line_classes = &self.original_classes[line.clone()];
let line_levels = &mut levels[line.clone()];
let line_str: &[u16] = &self.text[line.clone()];
reorder_levels(line_classes, line_levels, line_str, para.level);
levels
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reordered_levels_per_char(
&self,
para: &ParagraphInfo,
line: Range<usize>,
) -> Vec<Level> {
let levels = self.reordered_levels(para, line);
self.text.char_indices().map(|(i, _)| levels[i]).collect()
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reorder_line(&self, para: &ParagraphInfo, line: Range<usize>) -> Cow<'text, [u16]> {
if !level::has_rtl(&self.levels[line.clone()]) {
return self.text[line].into();
}
let (levels, runs) = self.visual_runs(para, line.clone());
reorder_line(self.text, line, levels, runs)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[inline]
pub fn reorder_visual(levels: &[Level]) -> Vec<usize> {
reorder_visual(levels)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[inline]
pub fn visual_runs(
&self,
para: &ParagraphInfo,
line: Range<usize>,
) -> (Vec<Level>, Vec<LevelRun>) {
let levels = self.reordered_levels(para, line.clone());
visual_runs_for_line(levels, &line)
}
#[inline]
pub fn has_rtl(&self) -> bool {
level::has_rtl(&self.levels)
}
}
#[derive(Debug, PartialEq)]
pub struct ParagraphBidiInfo<'text> {
pub text: &'text [u16],
pub original_classes: Vec<BidiClass>,
pub levels: Vec<Level>,
pub paragraph_level: Level,
pub is_pure_ltr: bool,
}
impl<'text> ParagraphBidiInfo<'text> {
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[cfg(feature = "hardcoded-data")]
#[inline]
pub fn new(text: &[u16], default_para_level: Option<Level>) -> ParagraphBidiInfo<'_> {
Self::new_with_data_source(&HardcodedBidiData, text, default_para_level)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn new_with_data_source<'a, D: BidiDataSource>(
data_source: &D,
text: &'a [u16],
default_para_level: Option<Level>,
) -> ParagraphBidiInfo<'a> {
let (original_classes, paragraph_level, is_pure_ltr, has_isolate_controls) =
compute_initial_info(data_source, text, default_para_level, None);
let mut levels = Vec::<Level>::with_capacity(text.len());
let mut processing_classes = original_classes.clone();
let para_info = ParagraphInfo {
range: Range {
start: 0,
end: text.len(),
},
level: paragraph_level,
};
compute_bidi_info_for_para(
data_source,
¶_info,
is_pure_ltr,
has_isolate_controls,
text,
&original_classes,
&mut processing_classes,
&mut levels,
);
ParagraphBidiInfo {
text,
original_classes,
levels,
paragraph_level,
is_pure_ltr,
}
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reordered_levels(&self, line: Range<usize>) -> Vec<Level> {
assert!(line.start <= self.levels.len());
assert!(line.end <= self.levels.len());
let mut levels = self.levels.clone();
let line_classes = &self.original_classes[line.clone()];
let line_levels = &mut levels[line.clone()];
reorder_levels(
line_classes,
line_levels,
self.text.subrange(line),
self.paragraph_level,
);
levels
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reordered_levels_per_char(&self, line: Range<usize>) -> Vec<Level> {
let levels = self.reordered_levels(line);
self.text.char_indices().map(|(i, _)| levels[i]).collect()
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
pub fn reorder_line(&self, line: Range<usize>) -> Cow<'text, [u16]> {
if !level::has_rtl(&self.levels[line.clone()]) {
return self.text[line].into();
}
let (levels, runs) = self.visual_runs(line.clone());
reorder_line(self.text, line, levels, runs)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[inline]
pub fn reorder_visual(levels: &[Level]) -> Vec<usize> {
reorder_visual(levels)
}
#[cfg_attr(feature = "flame_it", flamer::flame)]
#[inline]
pub fn visual_runs(&self, line: Range<usize>) -> (Vec<Level>, Vec<LevelRun>) {
let levels = self.reordered_levels(line.clone());
visual_runs_for_line(levels, &line)
}
#[inline]
pub fn has_rtl(&self) -> bool {
!self.is_pure_ltr
}
#[inline]
pub fn direction(&self) -> Direction {
para_direction(&self.levels)
}
}
fn reorder_line(
text: &[u16],
line: Range<usize>,
levels: Vec<Level>,
runs: Vec<LevelRun>,
) -> Cow<'_, [u16]> {
if runs.iter().all(|run| levels[run.start].is_ltr()) {
return text[line].into();
}
let mut result = Vec::<u16>::with_capacity(line.len());
for run in runs {
if levels[run.start].is_rtl() {
let mut buf = [0; 2];
for c in text[run].chars().rev() {
result.extend(c.encode_utf16(&mut buf).iter());
}
} else {
result.extend(text[run].iter());
}
}
result.into()
}
#[derive(Debug)]
pub struct Paragraph<'a, 'text> {
pub info: &'a BidiInfo<'text>,
pub para: &'a ParagraphInfo,
}
impl<'a, 'text> Paragraph<'a, 'text> {
#[inline]
pub fn new(info: &'a BidiInfo<'text>, para: &'a ParagraphInfo) -> Paragraph<'a, 'text> {
Paragraph { info, para }
}
#[inline]
pub fn direction(&self) -> Direction {
para_direction(&self.info.levels[self.para.range.clone()])
}
#[inline]
pub fn level_at(&self, pos: usize) -> Level {
let actual_position = self.para.range.start + pos;
self.info.levels[actual_position]
}
}
#[inline]
fn is_high_surrogate(code: u16) -> bool {
(code & 0xFC00) == 0xD800
}
#[inline]
fn is_low_surrogate(code: u16) -> bool {
(code & 0xFC00) == 0xDC00
}
impl<'text> TextSource<'text> for [u16] {
type CharIter = Utf16CharIter<'text>;
type CharIndexIter = Utf16CharIndexIter<'text>;
type IndexLenIter = Utf16IndexLenIter<'text>;
#[inline]
fn len(&self) -> usize {
(self as &[u16]).len()
}
fn char_at(&self, index: usize) -> Option<(char, usize)> {
if index >= self.len() {
return None;
}
let c = self[index];
if let Some(ch) = char::from_u32(c.into()) {
return Some((ch, 1));
}
if is_low_surrogate(c) && index > 0 && is_high_surrogate(self[index - 1]) {
return None;
}
if let Some(ch) = char::decode_utf16(self[index..].iter().cloned()).next() {
if let Ok(ch) = ch {
debug_assert!(ch.len_utf16() == 2, "BMP should have already been handled");
return Some((ch, ch.len_utf16()));
}
} else {
debug_assert!(
false,
"Why did decode_utf16 return None when we're not at the end?"
);
return None;
}
Some((char::REPLACEMENT_CHARACTER, 1))
}
#[inline]
fn subrange(&self, range: Range<usize>) -> &Self {
&(self as &[u16])[range]
}
#[inline]
fn chars(&'text self) -> Self::CharIter {
Utf16CharIter::new(self)
}
#[inline]
fn char_indices(&'text self) -> Self::CharIndexIter {
Utf16CharIndexIter::new(self)
}
#[inline]
fn indices_lengths(&'text self) -> Self::IndexLenIter {
Utf16IndexLenIter::new(self)
}
#[inline]
fn char_len(ch: char) -> usize {
ch.len_utf16()
}
}
#[derive(Debug)]
pub struct Utf16IndexLenIter<'text> {
text: &'text [u16],
cur_pos: usize,
}
impl<'text> Utf16IndexLenIter<'text> {
#[inline]
pub fn new(text: &'text [u16]) -> Self {
Utf16IndexLenIter { text, cur_pos: 0 }
}
}
impl Iterator for Utf16IndexLenIter<'_> {
type Item = (usize, usize);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if let Some((_, char_len)) = self.text.char_at(self.cur_pos) {
let result = (self.cur_pos, char_len);
self.cur_pos += char_len;
return Some(result);
}
None
}
}
#[derive(Debug)]
pub struct Utf16CharIndexIter<'text> {
text: &'text [u16],
cur_pos: usize,
}
impl<'text> Utf16CharIndexIter<'text> {
pub fn new(text: &'text [u16]) -> Self {
Utf16CharIndexIter { text, cur_pos: 0 }
}
}
impl Iterator for Utf16CharIndexIter<'_> {
type Item = (usize, char);
fn next(&mut self) -> Option<Self::Item> {
if let Some((ch, char_len)) = self.text.char_at(self.cur_pos) {
let result = (self.cur_pos, ch);
self.cur_pos += char_len;
return Some(result);
}
None
}
}
#[derive(Debug)]
pub struct Utf16CharIter<'text> {
text: &'text [u16],
cur_pos: usize,
end_pos: usize,
}
impl<'text> Utf16CharIter<'text> {
pub fn new(text: &'text [u16]) -> Self {
Utf16CharIter {
text,
cur_pos: 0,
end_pos: text.len(),
}
}
}
impl Iterator for Utf16CharIter<'_> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
if let Some((ch, char_len)) = self.text.char_at(self.cur_pos) {
self.cur_pos += char_len;
return Some(ch);
}
None
}
}
impl DoubleEndedIterator for Utf16CharIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.end_pos <= self.cur_pos {
return None;
}
self.end_pos -= 1;
if let Some(ch) = char::from_u32(self.text[self.end_pos] as u32) {
return Some(ch);
}
if self.end_pos > self.cur_pos {
if let Some((ch, char_len)) = self.text.char_at(self.end_pos - 1) {
if char_len == 2 {
self.end_pos -= 1;
return Some(ch);
}
}
}
Some(char::REPLACEMENT_CHARACTER)
}
}