use std::io::Write;
use crate::{
Gb2312,
Geo,
Iso6937,
Iso8859_1,
Iso8859_2,
Iso8859_3,
Iso8859_4,
Iso8859_5,
Iso8859_6,
Iso8859_7,
Iso8859_8,
Iso8859_9,
Iso8859_10,
Iso8859_11,
Iso8859_13,
Iso8859_14,
Iso8859_15,
Iso8859_16,
Textcode,
Utf8,
Utf16,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Charset {
Iso6937,
Iso8859_1,
Iso8859_2,
Iso8859_3,
Iso8859_4,
Iso8859_5,
Iso8859_6,
Iso8859_7,
Iso8859_8,
Iso8859_9,
Iso8859_10,
Iso8859_11,
Iso8859_13,
Iso8859_14,
Iso8859_15,
Iso8859_16,
Utf16,
Gb2312,
Utf8,
Geo,
}
pub fn decode(src: impl AsRef<[u8]>) -> String {
let mut result = String::new();
let dst = unsafe { result.as_mut_vec() };
let _ = decode_inner(src.as_ref(), dst);
result
}
pub fn decode_to_slice(src: impl AsRef<[u8]>, dst: &mut [u8]) -> usize {
let mut cursor = std::io::Cursor::new(dst);
decode_inner(src.as_ref(), &mut cursor).unwrap_or(0)
}
pub fn encode(src: impl AsRef<str>, charset: Charset) -> Vec<u8> {
let mut ret = Vec::new();
let _ = encode_inner(src.as_ref(), charset, &mut ret);
ret
}
pub fn encode_to_slice(src: impl AsRef<str>, charset: Charset, dst: &mut [u8]) -> usize {
let mut cursor = std::io::Cursor::new(dst);
encode_inner(src.as_ref(), charset, &mut cursor).unwrap_or(0)
}
fn encode_with_header<T: Textcode, W: Write>(
src: &str,
header: &[u8],
dst: &mut W,
) -> std::io::Result<usize> {
dst.write_all(header)?;
Ok(header.len() + T::encode(src, dst)?)
}
fn encode_inner<W: Write>(src: &str, charset: Charset, dst: &mut W) -> std::io::Result<usize> {
if src.is_empty() {
return Ok(0);
}
match charset {
Charset::Iso6937 => Iso6937::encode(src, dst),
Charset::Iso8859_1 => encode_with_header::<Iso8859_1, W>(src, &[0x10, 0x00, 0x01], dst),
Charset::Iso8859_2 => encode_with_header::<Iso8859_2, W>(src, &[0x10, 0x00, 0x02], dst),
Charset::Iso8859_3 => encode_with_header::<Iso8859_3, W>(src, &[0x10, 0x00, 0x03], dst),
Charset::Iso8859_4 => encode_with_header::<Iso8859_4, W>(src, &[0x10, 0x00, 0x04], dst),
Charset::Iso8859_5 => encode_with_header::<Iso8859_5, W>(src, &[0x01], dst),
Charset::Iso8859_6 => encode_with_header::<Iso8859_6, W>(src, &[0x02], dst),
Charset::Iso8859_7 => encode_with_header::<Iso8859_7, W>(src, &[0x03], dst),
Charset::Iso8859_8 => encode_with_header::<Iso8859_8, W>(src, &[0x04], dst),
Charset::Iso8859_9 => encode_with_header::<Iso8859_9, W>(src, &[0x05], dst),
Charset::Iso8859_10 => encode_with_header::<Iso8859_10, W>(src, &[0x06], dst),
Charset::Iso8859_11 => encode_with_header::<Iso8859_11, W>(src, &[0x07], dst),
Charset::Iso8859_13 => encode_with_header::<Iso8859_13, W>(src, &[0x09], dst),
Charset::Iso8859_14 => encode_with_header::<Iso8859_14, W>(src, &[0x0A], dst),
Charset::Iso8859_15 => encode_with_header::<Iso8859_15, W>(src, &[0x0B], dst),
Charset::Iso8859_16 => encode_with_header::<Iso8859_16, W>(src, &[0x10, 0x00, 0x10], dst),
Charset::Utf16 => encode_with_header::<Utf16, W>(src, &[0x11], dst),
Charset::Gb2312 => encode_with_header::<Gb2312, W>(src, &[0x13], dst),
Charset::Utf8 => encode_with_header::<Utf8, W>(src, &[0x15], dst),
Charset::Geo => encode_with_header::<Geo, W>(src, &[0x1E], dst),
}
}
fn decode_inner<W: Write>(src: &[u8], dst: &mut W) -> std::io::Result<usize> {
if src.is_empty() {
return Ok(0);
}
let first = src[0];
match first {
0x01 => Iso8859_5::decode(&src[1 ..], dst),
0x02 => Iso8859_6::decode(&src[1 ..], dst),
0x03 => Iso8859_7::decode(&src[1 ..], dst),
0x04 => Iso8859_8::decode(&src[1 ..], dst),
0x05 => Iso8859_9::decode(&src[1 ..], dst),
0x06 => Iso8859_10::decode(&src[1 ..], dst),
0x07 => Iso8859_11::decode(&src[1 ..], dst),
0x09 => Iso8859_13::decode(&src[1 ..], dst),
0x0A => Iso8859_14::decode(&src[1 ..], dst),
0x0B => Iso8859_15::decode(&src[1 ..], dst),
0x10 => {
if src.len() < 3 {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"Insufficient data for ISO-8859-X part number",
));
}
let part = u16::from_be_bytes([src[1], src[2]]);
let data = &src[3 ..];
match part {
0x01 => Iso8859_1::decode(data, dst),
0x02 => Iso8859_2::decode(data, dst),
0x03 => Iso8859_3::decode(data, dst),
0x04 => Iso8859_4::decode(data, dst),
0x05 => Iso8859_5::decode(data, dst),
0x06 => Iso8859_6::decode(data, dst),
0x07 => Iso8859_7::decode(data, dst),
0x08 => Iso8859_8::decode(data, dst),
0x09 => Iso8859_9::decode(data, dst),
0x0A => Iso8859_10::decode(data, dst),
0x0B => Iso8859_11::decode(data, dst),
0x0D => Iso8859_13::decode(data, dst),
0x0E => Iso8859_14::decode(data, dst),
0x0F => Iso8859_15::decode(data, dst),
0x10 => Iso8859_16::decode(data, dst),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Unknown ISO-8859-X part number",
)),
}
}
0x11 => Utf16::decode(&src[1 ..], dst),
0x13 => Gb2312::decode(&src[1 ..], dst),
0x15 => Utf8::decode(&src[1 ..], dst),
0x1E => Geo::decode(&src[1 ..], dst),
0x00 => Iso6937::decode(&src[1 ..], dst),
0x20 ..= 0xFF => Iso6937::decode(src, dst),
_ => Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Undefined DVB text encoding",
)),
}
}