use super::Rune;
pub(crate) const RUNE_ERROR: Rune = 0xFFFD;
pub(crate) const RUNE_SELF: u8 = 0x80;
#[inline(always)]
fn continuation(b: u8) -> bool {
b & 0xC0 == 0x80
}
#[inline]
pub(crate) fn decode_rune(p: &[u8]) -> (Rune, usize) {
let Some(&p0) = p.first() else { return (RUNE_ERROR, 0) };
if p0 < RUNE_SELF {
return (p0 as Rune, 1);
}
match p0 {
0xC2..=0xDF if p.len() >= 2 && continuation(p[1]) => {
return (((p0 & 0x1F) as Rune) << 6 | (p[1] & 0x3F) as Rune, 2);
}
0xE0..=0xEF if p.len() >= 3 && continuation(p[2]) => {
let (lo, hi) = match p0 {
0xE0 => (0xA0, 0xBF),
0xED => (0x80, 0x9F),
_ => (0x80, 0xBF),
};
if p[1] >= lo && p[1] <= hi {
return (((p0 & 0x0F) as Rune) << 12 | ((p[1] & 0x3F) as Rune) << 6 | (p[2] & 0x3F) as Rune, 3);
}
}
0xF0..=0xF4 if p.len() >= 4 && continuation(p[2]) && continuation(p[3]) => {
let (lo, hi) = match p0 {
0xF0 => (0x90, 0xBF),
0xF4 => (0x80, 0x8F),
_ => (0x80, 0xBF),
};
if p[1] >= lo && p[1] <= hi {
return (
((p0 & 0x07) as Rune) << 18
| ((p[1] & 0x3F) as Rune) << 12
| ((p[2] & 0x3F) as Rune) << 6
| (p[3] & 0x3F) as Rune,
4,
);
}
}
_ => {}
}
(RUNE_ERROR, 1)
}
#[inline]
pub(crate) fn decode_last_rune(p: &[u8]) -> (Rune, usize) {
let end = p.len();
if end == 0 {
return (RUNE_ERROR, 0);
}
let last = p[end - 1];
if last < RUNE_SELF {
return (last as Rune, 1);
}
let lim = end.saturating_sub(4);
let mut start = end - 1;
while start > lim {
start -= 1;
if !continuation(p[start]) {
break;
}
}
let (r, size) = decode_rune(&p[start..end]);
if start + size != end {
return (RUNE_ERROR, 1);
}
(r, size)
}
#[inline]
pub(crate) fn high_surrogate(c: Rune) -> Rune {
0xD800 + ((c - 0x10000) >> 10)
}
#[inline]
pub(crate) fn low_surrogate(c: Rune) -> Rune {
0xDC00 + ((c - 0x10000) & 0x3FF)
}
#[inline]
pub(crate) fn has_supplementary(input: &[u8]) -> bool {
input.iter().any(|&c| c >= 0xF0)
}
#[inline]
pub(crate) fn unit_at(input: &[u8], pos: usize) -> (Rune, usize) {
let c = input[pos];
if c >= 0xF0 {
let (r, w) = decode_rune(&input[pos..]);
if w == 4 {
return (high_surrogate(r), 2);
}
return (RUNE_ERROR, 1);
}
if (0x80..0xC0).contains(&c) && pos >= 2 && input[pos - 2] >= 0xF0 {
let (r, w) = decode_rune(&input[pos - 2..]);
if w == 4 {
return (low_surrogate(r), 2);
}
}
decode_rune(&input[pos..])
}
#[inline]
pub(crate) fn unit_before(input: &[u8], pos: usize) -> (Rune, usize) {
let (r, w) = decode_last_rune(&input[..pos]);
if w == 4 {
return (low_surrogate(r), 2);
}
if w == 1 && r == RUNE_ERROR && pos >= 2 && input.len() - pos >= 2 && input[pos - 2] >= 0xF0 {
let (r, w) = decode_rune(&input[pos - 2..]);
if w == 4 {
return (high_surrogate(r), 2);
}
}
(r, w)
}
#[inline]
pub(crate) fn decode(input: &[u8], pos: usize, units: bool) -> (Rune, usize) {
if units { unit_at(input, pos) } else { decode_rune(&input[pos..]) }
}
#[inline]
pub(crate) fn decode_last(input: &[u8], pos: usize, units: bool) -> (Rune, usize) {
if units { unit_before(input, pos) } else { decode_last_rune(&input[..pos]) }
}