use std::borrow::Cow;
use std::fmt;
pub struct CodecError {
pub upto: isize,
pub cause: Cow<'static, str>,
}
pub trait ByteWriter {
fn writer_hint(&mut self, _expectedlen: usize) {}
fn write_byte(&mut self, b: u8);
fn write_bytes(&mut self, v: &[u8]);
fn as_mut_vec(&mut self) -> Option<&mut Vec<u8>> {
None
}
}
impl ByteWriter for Vec<u8> {
fn writer_hint(&mut self, expectedlen: usize) {
self.reserve(expectedlen);
}
fn write_byte(&mut self, b: u8) {
self.push(b);
}
fn write_bytes(&mut self, v: &[u8]) {
self.extend_from_slice(v);
}
fn as_mut_vec(&mut self) -> Option<&mut Vec<u8>> {
Some(self)
}
}
pub trait StringWriter {
fn writer_hint(&mut self, _expectedlen: usize) {}
fn write_char(&mut self, c: char);
fn write_str(&mut self, s: &str);
fn as_mut_string(&mut self) -> Option<&mut String> {
None
}
}
impl StringWriter for String {
fn writer_hint(&mut self, expectedlen: usize) {
self.reserve(expectedlen);
}
fn write_char(&mut self, c: char) {
self.push(c);
}
fn write_str(&mut self, s: &str) {
self.push_str(s);
}
fn as_mut_string(&mut self) -> Option<&mut String> {
Some(self)
}
}
pub trait RawEncoder: Send + 'static {
#[allow(clippy::wrong_self_convention)]
fn from_self(&self) -> Box<dyn RawEncoder>;
fn is_ascii_compatible(&self) -> bool {
false
}
fn raw_feed(&mut self, input: &str, output: &mut dyn ByteWriter)
-> (usize, Option<CodecError>);
fn raw_finish(&mut self, output: &mut dyn ByteWriter) -> Option<CodecError>;
}
pub trait RawDecoder: Send + 'static {
#[allow(clippy::wrong_self_convention)]
fn from_self(&self) -> Box<dyn RawDecoder>;
fn is_ascii_compatible(&self) -> bool {
false
}
fn raw_feed(
&mut self,
input: &[u8],
output: &mut dyn StringWriter,
) -> (usize, Option<CodecError>);
fn raw_finish(&mut self, output: &mut dyn StringWriter) -> Option<CodecError>;
}
pub type EncodingRef = &'static (dyn Encoding + Send + Sync);
pub trait Encoding {
fn name(&self) -> &'static str;
fn whatwg_name(&self) -> Option<&'static str> {
None
}
fn raw_encoder(&self) -> Box<dyn RawEncoder>;
fn raw_decoder(&self) -> Box<dyn RawDecoder>;
fn encode(&self, input: &str, trap: EncoderTrap) -> Result<Vec<u8>, Cow<'static, str>> {
let mut ret = Vec::new();
self.encode_to(input, trap, &mut ret).map(|_| ret)
}
fn encode_to(
&self,
input: &str,
trap: EncoderTrap,
ret: &mut dyn ByteWriter,
) -> Result<(), Cow<'static, str>> {
let mut encoder = self.raw_encoder();
let mut remaining = 0;
loop {
let (offset, err) = encoder.raw_feed(&input[remaining..], ret);
let unprocessed = remaining + offset;
match err {
Some(err) => {
remaining = (remaining as isize + err.upto) as usize;
if !trap.trap(&mut *encoder, &input[unprocessed..remaining], ret) {
return Err(err.cause);
}
}
None => {
remaining = input.len();
match encoder.raw_finish(ret) {
Some(err) => {
remaining = (remaining as isize + err.upto) as usize;
if !trap.trap(&mut *encoder, &input[unprocessed..remaining], ret) {
return Err(err.cause);
}
}
None => {}
}
if remaining >= input.len() {
return Ok(());
}
}
}
}
}
fn decode(&self, input: &[u8], trap: DecoderTrap) -> Result<String, Cow<'static, str>> {
let mut ret = String::new();
self.decode_to(input, trap, &mut ret).map(|_| ret)
}
fn decode_to(
&self,
input: &[u8],
trap: DecoderTrap,
ret: &mut dyn StringWriter,
) -> Result<(), Cow<'static, str>> {
let mut decoder = self.raw_decoder();
let mut remaining = 0;
loop {
let (offset, err) = decoder.raw_feed(&input[remaining..], ret);
let unprocessed = remaining + offset;
match err {
Some(err) => {
remaining = (remaining as isize + err.upto) as usize;
if !trap.trap(&mut *decoder, &input[unprocessed..remaining], ret) {
return Err(err.cause);
}
}
None => {
remaining = input.len();
match decoder.raw_finish(ret) {
Some(err) => {
remaining = (remaining as isize + err.upto) as usize;
if !trap.trap(&mut *decoder, &input[unprocessed..remaining], ret) {
return Err(err.cause);
}
}
None => {}
}
if remaining >= input.len() {
return Ok(());
}
}
}
}
}
}
impl<'a> fmt::Debug for &'a dyn Encoding {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.write_str("Encoding(")?;
fmt.write_str(self.name())?;
fmt.write_str(")")?;
Ok(())
}
}
pub type EncoderTrapFunc =
fn(encoder: &mut dyn RawEncoder, input: &str, output: &mut dyn ByteWriter) -> bool;
pub type DecoderTrapFunc =
fn(decoder: &mut dyn RawDecoder, input: &[u8], output: &mut dyn StringWriter) -> bool;
#[derive(Copy)]
pub enum DecoderTrap {
Strict,
Replace,
Ignore,
Call(DecoderTrapFunc),
}
impl DecoderTrap {
pub fn trap(
&self,
decoder: &mut dyn RawDecoder,
input: &[u8],
output: &mut dyn StringWriter,
) -> bool {
match *self {
DecoderTrap::Strict => false,
DecoderTrap::Replace => {
output.write_char('\u{fffd}');
true
}
DecoderTrap::Ignore => true,
DecoderTrap::Call(func) => func(decoder, input, output),
}
}
}
impl Clone for DecoderTrap {
fn clone(&self) -> DecoderTrap {
match *self {
DecoderTrap::Strict => DecoderTrap::Strict,
DecoderTrap::Replace => DecoderTrap::Replace,
DecoderTrap::Ignore => DecoderTrap::Ignore,
DecoderTrap::Call(f) => DecoderTrap::Call(f),
}
}
}
#[derive(Copy)]
pub enum EncoderTrap {
Strict,
Replace,
Ignore,
NcrEscape,
Call(EncoderTrapFunc),
}
impl EncoderTrap {
pub fn trap(
&self,
encoder: &mut dyn RawEncoder,
input: &str,
output: &mut dyn ByteWriter,
) -> bool {
fn reencode(
encoder: &mut dyn RawEncoder,
input: &str,
output: &mut dyn ByteWriter,
trapname: &str,
) -> bool {
if encoder.is_ascii_compatible() {
output.write_bytes(input.as_bytes());
} else {
let (_, err) = encoder.raw_feed(input, output);
if err.is_some() {
panic!("{} cannot reencode a replacement string", trapname);
}
}
true
}
match *self {
EncoderTrap::Strict => false,
EncoderTrap::Replace => reencode(encoder, "?", output, "Replace"),
EncoderTrap::Ignore => true,
EncoderTrap::NcrEscape => {
let mut escapes = String::new();
for ch in input.chars() {
escapes.push_str(&format!("&#{};", ch as isize));
}
reencode(encoder, &escapes, output, "NcrEscape")
}
EncoderTrap::Call(func) => func(encoder, input, output),
}
}
}
impl Clone for EncoderTrap {
fn clone(&self) -> EncoderTrap {
match *self {
EncoderTrap::Strict => EncoderTrap::Strict,
EncoderTrap::Replace => EncoderTrap::Replace,
EncoderTrap::Ignore => EncoderTrap::Ignore,
EncoderTrap::NcrEscape => EncoderTrap::NcrEscape,
EncoderTrap::Call(f) => EncoderTrap::Call(f),
}
}
}