mod iso_8859;
mod shift_jis;
mod ucs4;
mod us_ascii;
mod utf16;
use std::{
borrow::Cow,
collections::BTreeMap,
str::{from_utf8, from_utf8_unchecked},
sync::{LazyLock, RwLock},
};
pub use crate::encoding::{iso_8859::*, shift_jis::*, ucs4::*, us_ascii::*, utf16::*};
pub trait Encoder {
fn name(&self) -> &'static str;
fn encode(
&mut self,
src: &str,
dst: &mut [u8],
finish: bool,
) -> Result<(usize, usize), EncodeError>;
}
pub trait Decoder {
fn name(&self) -> &'static str;
fn decode(
&mut self,
src: &[u8],
dst: &mut String,
finish: bool,
) -> Result<(usize, usize), DecodeError>;
}
#[derive(Debug)]
pub enum EncodeError {
InputIsEmpty,
OutputTooShort,
Unmappable { read: usize, write: usize, c: char },
Other { msg: Cow<'static, str> },
}
#[derive(Debug)]
pub enum DecodeError {
InputIsEmpty,
OutputTooShort,
Malformed {
read: usize,
write: usize,
length: usize,
offset: usize,
},
Other { msg: Cow<'static, str> },
}
pub const UTF8_NAME: &str = "UTF-8";
pub struct UTF8Encoder;
impl Encoder for UTF8Encoder {
fn name(&self) -> &'static str {
UTF8_NAME
}
fn encode(
&mut self,
src: &str,
dst: &mut [u8],
finish: bool,
) -> Result<(usize, usize), EncodeError> {
if src.is_empty() {
return if finish {
Ok((0, 0))
} else {
Err(EncodeError::InputIsEmpty)
};
}
if finish && src.len() > dst.len() {
return Err(EncodeError::OutputTooShort);
}
let len = src.len().min(dst.len());
dst[..len].copy_from_slice(&src.as_bytes()[..len]);
Ok((len, len))
}
}
pub struct UTF8Decoder;
impl Decoder for UTF8Decoder {
fn name(&self) -> &'static str {
UTF8_NAME
}
fn decode(
&mut self,
src: &[u8],
dst: &mut String,
finish: bool,
) -> Result<(usize, usize), DecodeError> {
if src.is_empty() {
return Err(DecodeError::InputIsEmpty);
}
let len = dst.capacity() - dst.len();
if len < 4 {
return Err(DecodeError::OutputTooShort);
}
let len = len.min(src.len());
match from_utf8(&src[..len]) {
Ok(s) => {
dst.push_str(s);
Ok((len, len))
}
Err(err) => {
let up_to = err.valid_up_to();
dst.push_str(unsafe {
from_utf8_unchecked(&src[..up_to])
});
match err.error_len() {
Some(len) => Err(DecodeError::Malformed {
read: up_to + len,
write: up_to,
length: len,
offset: 0,
}),
None => {
if finish {
Err(DecodeError::Malformed {
read: len,
write: up_to,
length: len - up_to,
offset: 0,
})
} else {
Ok((up_to, up_to))
}
}
}
}
}
}
}
pub const DEFAULT_SUPPORTED_ENCODINGS: &[&str] = {
const NAMES: &[&str] = &[
ISO_8859_10_NAME,
ISO_8859_13_NAME,
ISO_8859_14_NAME,
ISO_8859_15_NAME,
ISO_8859_16_NAME,
ISO_8859_1_NAME,
ISO_8859_2_NAME,
ISO_8859_3_NAME,
ISO_8859_4_NAME,
ISO_8859_5_NAME,
ISO_8859_6_NAME,
ISO_8859_7_NAME,
ISO_8859_8_NAME,
ISO_8859_9_NAME,
SHIFT_JIS_NAME,
ISO_8859_11_NAME,
US_ASCII_NAME,
UTF16_NAME,
UTF16BE_NAME,
UTF16LE_NAME,
UTF32_NAME,
UTF32BE_NAME,
UTF32LE_NAME,
UTF8_NAME,
];
let len = NAMES.len();
let mut i = 0;
while i + 1 < len {
let x = NAMES[i].as_bytes();
let y = NAMES[i + 1].as_bytes();
let mut j = 0;
while j < x.len() {
assert!(x[j] <= y[j]);
if x[j] < y[j] {
break;
}
j += 1;
if j == x.len() {
break;
}
assert!(j < y.len());
}
i += 1;
}
NAMES
};
pub static ENCODING_ALIASES: LazyLock<RwLock<BTreeMap<&'static str, &'static str>>> =
LazyLock::new(|| {
RwLock::new(BTreeMap::from([
("UTF8", UTF8_NAME),
("UTF16", UTF16_NAME),
("UTF16BE", UTF16BE_NAME),
("UTF16LE", UTF16LE_NAME),
("ISO-IR-100", ISO_8859_1_NAME),
("ISO_8859-1", ISO_8859_1_NAME),
("ISO-8859-1", ISO_8859_1_NAME),
("LATIN1", ISO_8859_1_NAME),
("L1", ISO_8859_1_NAME),
("IBM819", ISO_8859_1_NAME),
("CP819", ISO_8859_1_NAME),
("ISOLATIN1", ISO_8859_1_NAME),
("ISO-IR-101", ISO_8859_2_NAME),
("ISO_8859-2", ISO_8859_2_NAME),
("ISO-8859-2", ISO_8859_2_NAME),
("LATIN2", ISO_8859_2_NAME),
("L2", ISO_8859_2_NAME),
("ISOLATIN2", ISO_8859_2_NAME),
("ISO-IR-109", ISO_8859_3_NAME),
("ISO_8859-3", ISO_8859_3_NAME),
("ISO-8859-3", ISO_8859_3_NAME),
("LATIN3", ISO_8859_3_NAME),
("L3", ISO_8859_3_NAME),
("ISOLATIN3", ISO_8859_3_NAME),
("ISO-IR-110", ISO_8859_4_NAME),
("ISO_8859-4", ISO_8859_4_NAME),
("ISO-8859-4", ISO_8859_4_NAME),
("LATIN4", ISO_8859_4_NAME),
("L4", ISO_8859_4_NAME),
("ISOLATIN4", ISO_8859_4_NAME),
("ISO-IR-144", ISO_8859_5_NAME),
("ISO_8859-5", ISO_8859_5_NAME),
("ISO-8859-5", ISO_8859_5_NAME),
("CYRILLIC", ISO_8859_5_NAME),
("ISOLATINCYRILLIC", ISO_8859_5_NAME),
("ISO-IR-127", ISO_8859_6_NAME),
("ISO_8859-6", ISO_8859_6_NAME),
("ISO-8859-6", ISO_8859_6_NAME),
("ECMA-114", ISO_8859_6_NAME),
("ASMO-708", ISO_8859_6_NAME),
("ARABIC", ISO_8859_6_NAME),
("ISOLATINARABIC", ISO_8859_6_NAME),
("ISO-IR-126", ISO_8859_7_NAME),
("ISO_8859-7", ISO_8859_7_NAME),
("ISO-8859-7", ISO_8859_7_NAME),
("ELOT_928", ISO_8859_7_NAME),
("ECMA-118", ISO_8859_7_NAME),
("GREEK", ISO_8859_7_NAME),
("GREEK8", ISO_8859_7_NAME),
("ISOLATINGREEK", ISO_8859_7_NAME),
("ISO-IR-138", ISO_8859_8_NAME),
("ISO_8859-8", ISO_8859_8_NAME),
("ISO-8859-8", ISO_8859_8_NAME),
("HEBREW", ISO_8859_8_NAME),
("ISOLATINHEBREW", ISO_8859_8_NAME),
("ISO-IR-148", ISO_8859_9_NAME),
("ISO_8859-9", ISO_8859_9_NAME),
("ISO-8859-9", ISO_8859_9_NAME),
("LATIN5", ISO_8859_9_NAME),
("L5", ISO_8859_9_NAME),
("ISOLATIN5", ISO_8859_9_NAME),
("ISO-IR-157", ISO_8859_10_NAME),
("L6", ISO_8859_10_NAME),
("ISO_8859-10:1992", ISO_8859_10_NAME),
("ISOLATIN6", ISO_8859_10_NAME),
("LATIN6", ISO_8859_10_NAME),
("TIS620", ISO_8859_11_NAME),
("ISO-8859-11", ISO_8859_11_NAME),
("ISO885913", ISO_8859_13_NAME),
("ISO-IR-199", ISO_8859_14_NAME),
("ISO_8859-14:1998", ISO_8859_14_NAME),
("ISO_8859-14", ISO_8859_14_NAME),
("LATIN8", ISO_8859_14_NAME),
("ISO-CELTIC", ISO_8859_14_NAME),
("L8", ISO_8859_14_NAME),
("ISO885914", ISO_8859_14_NAME),
("ISO_8859-15", ISO_8859_15_NAME),
("LATIN-9", ISO_8859_15_NAME),
("ISO885915", ISO_8859_15_NAME),
("ISO-IR-226", ISO_8859_16_NAME),
("ISO_8859-16:2001", ISO_8859_16_NAME),
("ISO_8859-16", ISO_8859_16_NAME),
("LATIN10", ISO_8859_16_NAME),
("L10", ISO_8859_16_NAME),
("ISO885916", ISO_8859_16_NAME),
("UTF32", UTF32_NAME),
("UTF32BE", UTF32BE_NAME),
("UTF32LE", UTF32LE_NAME),
("MS_KANJI", SHIFT_JIS_NAME),
("SHIFTJIS", SHIFT_JIS_NAME),
("ISO-IR-6", US_ASCII_NAME),
("ANSI_X3.4-1968", US_ASCII_NAME),
("ANSI_X3.4-1986", US_ASCII_NAME),
("ISO_646.IRV:1991", US_ASCII_NAME),
("ISO646-US", US_ASCII_NAME),
("US-ASCII", US_ASCII_NAME),
("US", US_ASCII_NAME),
("IBM367", US_ASCII_NAME),
("CP367", US_ASCII_NAME),
("ASCII", US_ASCII_NAME),
]))
});
pub fn register_encoding_alias(alias: &'static str, real: &'static str) -> Option<&'static str> {
ENCODING_ALIASES.write().unwrap().insert(alias, real)
}
pub fn unregister_encoding_alias(alias: &'static str) -> Option<&'static str> {
ENCODING_ALIASES.write().unwrap().remove(alias)
}
pub fn resolve_encoding_alias(alias: &str) -> Option<&'static str> {
let aliases = ENCODING_ALIASES.read().unwrap();
aliases
.get(alias)
.or_else(|| aliases.get(alias.to_ascii_uppercase().as_str()))
.copied()
}
pub type EncoderFactory = fn() -> Box<dyn Encoder>;
pub static ENCODER_TABLE: LazyLock<RwLock<BTreeMap<&'static str, EncoderFactory>>> =
LazyLock::new(|| {
let mut map = BTreeMap::<&'static str, EncoderFactory>::new();
map.insert(UTF8_NAME, || Box::new(UTF8Encoder));
map.insert(UTF16_NAME, || Box::new(UTF16Encoder::default()));
map.insert(UTF16BE_NAME, || Box::new(UTF16BEEncoder));
map.insert(UTF16LE_NAME, || Box::new(UTF16LEEncoder));
map.insert(ISO_8859_1_NAME, || Box::new(ISO8859_1Encoder));
map.insert(ISO_8859_2_NAME, || Box::new(ISO8859_2Encoder));
map.insert(ISO_8859_3_NAME, || Box::new(ISO8859_3Encoder));
map.insert(ISO_8859_4_NAME, || Box::new(ISO8859_4Encoder));
map.insert(ISO_8859_5_NAME, || Box::new(ISO8859_5Encoder));
map.insert(ISO_8859_6_NAME, || Box::new(ISO8859_6Encoder));
map.insert(ISO_8859_7_NAME, || Box::new(ISO8859_7Encoder));
map.insert(ISO_8859_8_NAME, || Box::new(ISO8859_8Encoder));
map.insert(ISO_8859_9_NAME, || Box::new(ISO8859_9Encoder));
map.insert(ISO_8859_10_NAME, || Box::new(ISO8859_10Encoder));
map.insert(ISO_8859_11_NAME, || Box::new(ISO8859_11Encoder));
map.insert(ISO_8859_13_NAME, || Box::new(ISO8859_13Encoder));
map.insert(ISO_8859_14_NAME, || Box::new(ISO8859_14Encoder));
map.insert(ISO_8859_15_NAME, || Box::new(ISO8859_15Encoder));
map.insert(ISO_8859_16_NAME, || Box::new(ISO8859_16Encoder));
map.insert(UTF32_NAME, || Box::new(UTF32Encoder::default()));
map.insert(UTF32BE_NAME, || Box::new(UTF32BEEncoder));
map.insert(UTF32LE_NAME, || Box::new(UTF32LEEncoder));
map.insert(SHIFT_JIS_NAME, || Box::new(ShiftJISEncoder));
map.insert(US_ASCII_NAME, || Box::new(USASCIIEncoder));
RwLock::new(map)
});
pub fn find_encoder(encoding_name: &str) -> Option<Box<dyn Encoder>> {
let table = ENCODER_TABLE.read().unwrap();
if let Some(factory) = table.get(encoding_name) {
return Some(factory());
}
let alias = resolve_encoding_alias(encoding_name)?;
table.get(alias).map(|f| f())
}
pub fn register_encoder(
encoding_name: &'static str,
factory: EncoderFactory,
) -> Option<EncoderFactory> {
ENCODER_TABLE
.write()
.unwrap()
.insert(encoding_name, factory)
}
pub fn unregister_encoder(encoding_name: &str) -> Option<EncoderFactory> {
ENCODER_TABLE.write().unwrap().remove(encoding_name)
}
pub type DecoderFactory = fn() -> Box<dyn Decoder>;
pub static DECODER_TABLE: LazyLock<RwLock<BTreeMap<&'static str, DecoderFactory>>> =
LazyLock::new(|| {
let mut map = BTreeMap::<&'static str, DecoderFactory>::new();
map.insert(UTF8_NAME, || Box::new(UTF8Decoder));
map.insert(UTF16_NAME, || Box::new(UTF16Decoder::default()));
map.insert(UTF16BE_NAME, || Box::new(UTF16BEDecoder));
map.insert(UTF16LE_NAME, || Box::new(UTF16LEDecoder));
map.insert(ISO_8859_1_NAME, || Box::new(ISO8859_1Decoder));
map.insert(ISO_8859_2_NAME, || Box::new(ISO8859_2Decoder));
map.insert(ISO_8859_3_NAME, || Box::new(ISO8859_3Decoder));
map.insert(ISO_8859_4_NAME, || Box::new(ISO8859_4Decoder));
map.insert(ISO_8859_5_NAME, || Box::new(ISO8859_5Decoder));
map.insert(ISO_8859_6_NAME, || Box::new(ISO8859_6Decoder));
map.insert(ISO_8859_7_NAME, || Box::new(ISO8859_7Decoder));
map.insert(ISO_8859_8_NAME, || Box::new(ISO8859_8Decoder));
map.insert(ISO_8859_9_NAME, || Box::new(ISO8859_9Decoder));
map.insert(ISO_8859_10_NAME, || Box::new(ISO8859_10Decoder));
map.insert(ISO_8859_11_NAME, || Box::new(ISO8859_11Decoder));
map.insert(ISO_8859_13_NAME, || Box::new(ISO8859_13Decoder));
map.insert(ISO_8859_14_NAME, || Box::new(ISO8859_14Decoder));
map.insert(ISO_8859_15_NAME, || Box::new(ISO8859_15Decoder));
map.insert(ISO_8859_16_NAME, || Box::new(ISO8859_16Decoder));
map.insert(UTF32_NAME, || Box::new(UTF32Decoder::default()));
map.insert(UTF32BE_NAME, || Box::new(UTF32BEDecoder));
map.insert(UTF32LE_NAME, || Box::new(UTF32LEDecoder));
map.insert(SHIFT_JIS_NAME, || Box::new(ShiftJISDecoder));
map.insert(US_ASCII_NAME, || Box::new(USASCIIDecoder));
RwLock::new(map)
});
pub fn find_decoder(encoding_name: &str) -> Option<Box<dyn Decoder>> {
let table = DECODER_TABLE.read().unwrap();
if let Some(factory) = table.get(encoding_name) {
return Some(factory());
}
let alias = resolve_encoding_alias(encoding_name)?;
table.get(alias).map(|f| f())
}
pub fn register_decoder(
encoding_name: &'static str,
factory: DecoderFactory,
) -> Option<DecoderFactory> {
DECODER_TABLE
.write()
.unwrap()
.insert(encoding_name, factory)
}
pub fn unregister_decoder(encoding_name: &str) -> Option<DecoderFactory> {
DECODER_TABLE.write().unwrap().remove(encoding_name)
}