use crate::result::{DecoderResult, EncoderResult};
use crate::sink::{ByteSink, DECODER_HEADROOM, ENCODER_HEADROOM};
const STATIC: [u32; 8] = [
0x0000, 0x0080, 0x0100, 0x0300, 0x2000, 0x2080, 0x2100, 0x3000,
];
const DEFAULT_DYNAMIC: [u32; 8] = [
0x0080, 0x00C0, 0x0400, 0x0600, 0x0900, 0x3040, 0x30A0, 0xFF00,
];
const SQ0: u8 = 0x01;
const SDX: u8 = 0x0B;
const SQU: u8 = 0x0E;
const SCU: u8 = 0x0F;
const SC0: u8 = 0x10;
const SD0: u8 = 0x18;
const UC0: u8 = 0xE0;
const UD0: u8 = 0xE8;
const UQU: u8 = 0xF0;
const UDX: u8 = 0xF1;
fn window_offset(index: u8) -> Option<u32> {
match index {
0x01..=0x67 => Some(u32::from(index) * 0x80),
0x68..=0xA7 => Some(u32::from(index) * 0x80 + 0xAC00),
0xF9 => Some(0x00C0),
0xFA => Some(0x0250),
0xFB => Some(0x0370),
0xFC => Some(0x0530),
0xFD => Some(0x3040),
0xFE => Some(0x30A0),
0xFF => Some(0xFF60),
_ => None,
}
}
fn window_index(scalar: u32) -> Option<u8> {
match scalar {
0x0080..=0x33FF => Some((scalar / 0x80) as u8),
0xE000..=0xFFFF => Some(((scalar - 0xAC00) / 0x80) as u8),
_ => None,
}
}
#[inline]
fn extended_offset(high: u8, low: u8) -> u32 {
0x10000 + 0x80 * ((u32::from(high & 0x1F) << 8) | u32::from(low))
}
#[derive(Debug, Clone, Copy)]
struct Windows {
offset: [u32; 8],
active: usize,
}
impl Default for Windows {
fn default() -> Self {
Windows {
offset: DEFAULT_DYNAMIC,
active: 0,
}
}
}
impl Windows {
#[inline]
fn code_point(&self, n: usize, byte: u8) -> u32 {
self.offset[n] + u32::from(byte - 0x80)
}
#[inline]
fn byte_for(&self, n: usize, scalar: u32) -> Option<u8> {
scalar
.checked_sub(self.offset[n])
.filter(|&d| d < 0x80)
.map(|d| d as u8 + 0x80)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
Ground,
Quote(u8),
QuoteUnicode(bool),
QuoteUnicodeLow(bool, u8),
Define(u8),
DefineExtended,
DefineExtendedLow(u8),
Unicode,
UnicodeLow(u8),
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ScsuDecoder {
state: State,
windows: Windows,
high_surrogate: u16,
}
impl Default for ScsuDecoder {
fn default() -> Self {
ScsuDecoder {
state: State::Ground,
windows: Windows::default(),
high_surrogate: 0,
}
}
}
enum Emitted {
Ok,
Malformed,
}
impl ScsuDecoder {
pub(crate) fn new() -> Self {
Self::default()
}
fn emit_unit(&mut self, unit: u16, sink: &mut ByteSink) -> Emitted {
if self.high_surrogate != 0 {
let high = core::mem::replace(&mut self.high_surrogate, 0);
if (0xDC00..=0xDFFF).contains(&unit) {
let scalar =
0x10000 + ((u32::from(high) - 0xD800) << 10) + (u32::from(unit) - 0xDC00);
sink.write_code_point(scalar);
return Emitted::Ok;
}
return Emitted::Malformed;
}
match unit {
0xD800..=0xDBFF => {
self.high_surrogate = unit;
Emitted::Ok
}
0xDC00..=0xDFFF => Emitted::Malformed,
_ => {
sink.write_code_point(u32::from(unit));
Emitted::Ok
}
}
}
fn emit_scalar(&mut self, scalar: u32, sink: &mut ByteSink) -> Emitted {
debug_assert!(!(0xD800..=0xDFFF).contains(&scalar));
if self.high_surrogate != 0 {
self.high_surrogate = 0;
return Emitted::Malformed;
}
sink.write_code_point(scalar);
Emitted::Ok
}
pub(crate) fn decode(
&mut self,
src: &[u8],
sink: &mut ByteSink,
last: bool,
) -> (DecoderResult, usize) {
let mut read = 0usize;
loop {
if !sink.has_room(DECODER_HEADROOM) {
return (DecoderResult::OutputFull, read);
}
let Some(byte) = src.get(read).copied() else {
if !last {
return (DecoderResult::InputEmpty, read);
}
if self.state != State::Ground && self.state != State::Unicode {
let unicode = matches!(
self.state,
State::Unicode | State::UnicodeLow(_) | State::QuoteUnicode(true)
);
self.state = if unicode {
State::Unicode
} else {
State::Ground
};
self.high_surrogate = 0;
return (DecoderResult::Malformed(1), read);
}
if self.high_surrogate != 0 {
self.high_surrogate = 0;
return (DecoderResult::Malformed(1), read);
}
return (DecoderResult::InputEmpty, read);
};
read += 1;
let emitted = match self.state {
State::Ground => match byte {
0x00 | 0x09 | 0x0A | 0x0D | 0x20..=0x7F => {
self.emit_scalar(u32::from(byte), sink)
}
SQ0..=0x08 => {
self.state = State::Quote(byte - SQ0);
continue;
}
SDX => {
self.state = State::DefineExtended;
continue;
}
SQU => {
self.state = State::QuoteUnicode(false);
continue;
}
SCU => {
self.state = State::Unicode;
continue;
}
SC0..=0x17 => {
self.windows.active = usize::from(byte - SC0);
continue;
}
SD0..=0x1F => {
self.state = State::Define(byte - SD0);
continue;
}
0x0C => return (DecoderResult::Malformed(1), read),
_ => {
let scalar = self.windows.code_point(self.windows.active, byte);
self.emit_scalar(scalar, sink)
}
},
State::Quote(n) => {
self.state = State::Ground;
let n = usize::from(n);
if byte < 0x80 {
self.emit_scalar(STATIC[n] + u32::from(byte), sink)
} else {
self.emit_scalar(self.windows.code_point(n, byte), sink)
}
}
State::QuoteUnicode(unicode) => {
self.state = State::QuoteUnicodeLow(unicode, byte);
continue;
}
State::QuoteUnicodeLow(unicode, high) => {
self.state = if unicode {
State::Unicode
} else {
State::Ground
};
self.emit_unit((u16::from(high) << 8) | u16::from(byte), sink)
}
State::Define(n) => {
self.state = State::Ground;
let Some(offset) = window_offset(byte) else {
return (DecoderResult::Malformed(1), read);
};
self.windows.offset[usize::from(n)] = offset;
self.windows.active = usize::from(n);
continue;
}
State::DefineExtended => {
self.state = State::DefineExtendedLow(byte);
continue;
}
State::DefineExtendedLow(high) => {
self.state = State::Ground;
let n = usize::from(high >> 5);
self.windows.offset[n] = extended_offset(high, byte);
self.windows.active = n;
continue;
}
State::Unicode => match byte {
UC0..=0xE7 => {
self.windows.active = usize::from(byte - UC0);
self.state = State::Ground;
continue;
}
UD0..=0xEF => {
self.state = State::Define(byte - UD0);
continue;
}
UQU => {
self.state = State::QuoteUnicode(true);
continue;
}
UDX => {
self.state = State::DefineExtended;
continue;
}
0xF2 => return (DecoderResult::Malformed(1), read),
_ => {
self.state = State::UnicodeLow(byte);
continue;
}
},
State::UnicodeLow(high) => {
self.state = State::Unicode;
self.emit_unit((u16::from(high) << 8) | u16::from(byte), sink)
}
};
if let Emitted::Malformed = emitted {
return (DecoderResult::Malformed(1), read);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
enum Mode {
#[default]
SingleByte,
Unicode,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ScsuEncoder {
mode: Mode,
windows: Windows,
next_to_move: usize,
}
impl Default for ScsuEncoder {
fn default() -> Self {
ScsuEncoder {
mode: Mode::SingleByte,
windows: Windows::default(),
next_to_move: 0,
}
}
}
impl ScsuEncoder {
fn window_holding(&self, scalar: u32) -> Option<(usize, u8)> {
let active = self.windows.active;
if let Some(byte) = self.windows.byte_for(active, scalar) {
return Some((active, byte));
}
(0..8).find_map(|n| self.windows.byte_for(n, scalar).map(|byte| (n, byte)))
}
fn static_holding(scalar: u32) -> Option<(usize, u8)> {
(1..8).find_map(|n| {
scalar
.checked_sub(STATIC[n])
.filter(|&d| d < 0x80)
.map(|d| (n, d as u8))
})
}
pub(crate) fn encode(
&mut self,
src: &str,
sink: &mut ByteSink,
_last: bool,
) -> (EncoderResult, usize) {
let mut read = 0usize;
loop {
if !sink.has_room(ENCODER_HEADROOM) {
return (EncoderResult::OutputFull, read);
}
let Some(c) = src[read..].chars().next() else {
return (EncoderResult::InputEmpty, read);
};
let width = c.len_utf8();
let scalar = u32::from(c);
let passes_through = matches!(scalar, 0x00 | 0x09 | 0x0A | 0x0D | 0x20..=0x7F);
if self.mode == Mode::Unicode {
if passes_through || self.window_holding(scalar).is_some() {
let n = self
.window_holding(scalar)
.map_or(self.windows.active, |(n, _)| n);
sink.write_byte(UC0 + n as u8);
self.windows.active = n;
self.mode = Mode::SingleByte;
continue;
}
if let Some(index) = window_index(scalar) {
let n = self.take_window();
sink.write_slice(&[UD0 + n as u8, index]);
self.windows.offset[n] =
window_offset(index).expect("index came from the table");
self.windows.active = n;
self.mode = Mode::SingleByte;
continue;
}
if scalar > 0xFFFF {
let n = self.take_window();
let (high, low) = self.extended_arguments(n, scalar);
sink.write_slice(&[UDX, high, low]);
self.mode = Mode::SingleByte;
continue;
}
let unit = scalar as u16;
if matches!((unit >> 8) as u8, UC0..=0xF2) {
sink.write_byte(UQU);
}
sink.write_slice(&unit.to_be_bytes());
read += width;
continue;
}
if passes_through {
sink.write_byte(scalar as u8);
read += width;
continue;
}
if scalar < 0x20 {
sink.write_slice(&[SQ0, scalar as u8]);
read += width;
continue;
}
if let Some((n, byte)) = self.window_holding(scalar) {
if n != self.windows.active {
sink.write_byte(SC0 + n as u8);
self.windows.active = n;
}
sink.write_byte(byte);
read += width;
continue;
}
if let Some((n, byte)) = Self::static_holding(scalar) {
sink.write_slice(&[SQ0 + n as u8, byte]);
read += width;
continue;
}
if let Some(index) = window_index(scalar) {
let n = self.take_window();
sink.write_slice(&[SD0 + n as u8, index]);
self.windows.offset[n] = window_offset(index).expect("index came from the table");
self.windows.active = n;
continue;
}
if scalar > 0xFFFF {
let n = self.take_window();
let (high, low) = self.extended_arguments(n, scalar);
sink.write_slice(&[SDX, high, low]);
continue;
}
sink.write_byte(SCU);
self.mode = Mode::Unicode;
}
}
fn take_window(&mut self) -> usize {
let n = self.next_to_move;
self.next_to_move = (n + 1) % 8;
n
}
fn extended_arguments(&mut self, n: usize, scalar: u32) -> (u8, u8) {
let block = (scalar - 0x10000) / 0x80;
let high = ((n as u32) << 5) as u8 | ((block >> 8) & 0x1F) as u8;
let low = (block & 0xFF) as u8;
self.windows.offset[n] = extended_offset(high, low);
self.windows.active = n;
(high, low)
}
}