muter 0.7.0

utility for converting data between various formats and encodings
Documentation
#![allow(unknown_lints)]
#![allow(bare_trait_objects)]

use codec::helpers::codecs::AffixEncoder;
use codec::helpers::codecs::FilteredDecoder;
use codec::helpers::codecs::PaddedEncoder;
use codec::Codec;
use codec::CodecSettings;
use codec::CodecTransform;
use codec::Direction;
use codec::Error;
use codec::FlushState;
use codec::Status;
use codec::TransformableCodec;
use std::cmp;
use std::collections::BTreeMap;
use std::io;

const DIVISORS: [u64; 5] = [85 * 85 * 85 * 85, 85 * 85 * 85, 85 * 85, 85, 1];

#[derive(Default)]
pub struct Ascii85Encoder {}

impl Ascii85Encoder {
    fn new() -> Self {
        Ascii85Encoder {}
    }

    fn pad_bytes_needed(b: usize, is: usize, _os: usize) -> usize {
        is - b
    }
}

impl Codec for Ascii85Encoder {
    fn transform(&mut self, inp: &[u8], outp: &mut [u8], f: FlushState) -> Result<Status, Error> {
        let (is, os) = (4, 5);
        let mut j = 0;
        let n = cmp::min(inp.len() / is, outp.len() / os);
        for i in (0..n).map(|x| x * is) {
            let x: u64 = inp[i..i + is]
                .iter()
                .enumerate()
                .map(|(k, &v)| u64::from(v) << ((is - 1 - k) * 8))
                .sum();

            if x == 0 && f == FlushState::None {
                outp[j] = b'z';
                j += 1;
            } else {
                for (k, val) in outp[j..j + os].iter_mut().enumerate().take(os) {
                    *val = ((x / DIVISORS[k]) % 85) as u8 + b'!';
                }
                j += 5;
            }
        }
        Ok(Status::Ok(n * is, j))
    }

    fn chunk_size(&self) -> usize {
        4
    }

    fn buffer_size(&self) -> usize {
        5
    }
}

#[derive(Default)]
pub struct Ascii85TransformFactory {}

impl Ascii85TransformFactory {
    pub fn new() -> Self {
        Ascii85TransformFactory {}
    }
}

impl CodecTransform for Ascii85TransformFactory {
    fn factory(&self, r: Box<io::BufRead>, s: CodecSettings) -> Result<Box<io::BufRead>, Error> {
        match s.dir {
            Direction::Forward => {
                let enc = PaddedEncoder::new_with_pad_function(
                    Ascii85Encoder::new(),
                    4,
                    5,
                    None,
                    Ascii85Encoder::pad_bytes_needed,
                );
                if s.bool_arg("bare")? {
                    Ok(enc.into_bufread(r, s.bufsize))
                } else {
                    Ok(AffixEncoder::new(enc, vec![b'<', b'~'], vec![b'~', b'>'])
                        .into_bufread(r, s.bufsize))
                }
            }
            Direction::Reverse => Ok(Ascii85Decoder::new(s.strict).into_bufread(r, s.bufsize)),
        }
    }

    fn options(&self) -> BTreeMap<String, String> {
        let mut map = BTreeMap::new();
        map.insert("bare".to_string(), tr!("do not use delimiters"));
        map
    }

    fn can_reverse(&self) -> bool {
        true
    }

    fn name(&self) -> &'static str {
        "ascii85"
    }
}

pub struct Ascii85Decoder {
    start: bool,
    end: bool,
    bare: bool,
    strict: bool,
}

impl Ascii85Decoder {
    fn new(strict: bool) -> Self {
        Ascii85Decoder {
            start: false,
            end: false,
            bare: false,
            strict,
        }
    }

    fn dec(b: u8, i: usize) -> u64 {
        (b.wrapping_sub(b'!') as u64) * DIVISORS[i]
    }

    fn transform_chunk(&self, inp: &[u8], outp: &mut [u8]) -> (usize, usize) {
        let (is, os) = (5, 4);
        let mut tmpbuf = [b'u'; 5];

        if inp[0] == b'z' {
            outp[0..4].copy_from_slice(b"\x00\x00\x00\x00");
            return (1, 4);
        };

        let buf = if inp.len() < is {
            tmpbuf[0..inp.len()].copy_from_slice(inp);
            &tmpbuf
        } else {
            inp
        };

        // We remove as many characters from the output as from the input.
        let off = outp.len() - (is - inp.len());
        let x: u64 = buf.iter().enumerate().map(|(k, &v)| Self::dec(v, k)).sum();

        for (k, val) in outp[0..off].iter_mut().enumerate() {
            *val = (x >> ((os - 1 - k) * 8) & 0xff) as u8;
        }
        (inp.len(), off)
    }
}

impl FilteredDecoder for Ascii85Decoder {
    fn strict(&self) -> bool {
        self.strict
    }

    fn filter_byte(&self, b: u8) -> bool {
        b >= 33 && (b <= 117 || b == b'z' || b == b'~')
    }

    fn internal_transform(
        &mut self,
        src: &[u8],
        dst: &mut [u8],
        flush: FlushState,
    ) -> Result<Status, Error> {
        let (is, os) = (5, 4);
        let prefixlen = 2;

        if !self.start {
            match (src.len(), flush) {
                (x, FlushState::Finish) if x < prefixlen => self.bare = true,
                (x, FlushState::None) if x < prefixlen => return Ok(Status::SeqError(0, 0)),
                _ => {
                    self.start = true;
                    let start = if &src[0..prefixlen] != b"<~" {
                        self.bare = true;
                        0
                    } else {
                        2
                    };
                    let r = self.internal_transform(&src[start..], dst, flush)?;
                    let (a, b) = r.unpack();
                    return Ok(Status::Ok(a + start, b));
                }
            }
        }

        let loc = src.iter().position(|&x| x == b'~');
        let end = if self.bare {
            src.len()
        } else if let Some(x) = loc {
            match (x, src.len(), self.end) {
                (_, _, true) => return Err(Error::ExtraData),
                (0, 2, false) => {
                    self.end = true;
                    return Ok(Status::StreamEnd(2, 0));
                }
                (_, len, false) if x + 1 == len => x,
                (_, len, false) if x + 2 == len && src[x + 1] == b'>' => {
                    let r = self.internal_transform(&src[0..x], dst, FlushState::Finish)?;
                    let dstconsumed = r.unpack().1;
                    self.end = true;
                    return Ok(Status::StreamEnd(src.len(), dstconsumed));
                }
                (_, _, false) => {
                    return Err(Error::InvalidSequence(
                        "ascii85".to_string(),
                        src[x..].to_vec(),
                    ))
                }
            }
        } else {
            src.len()
        };

        let maxi = match (flush, end) {
            (FlushState::Finish, x) => x,
            (FlushState::None, x) if x <= is => return Ok(Status::SeqError(0, 0)),
            (FlushState::None, x) => x - is,
        };
        let maxj = dst.len() - os;

        let (mut i, mut j) = (0, 0);
        while i < maxi && j < maxj {
            let srcend = cmp::min(i + is, end);
            let (ri, rj) = self.transform_chunk(&src[i..srcend], &mut dst[j..j + os]);
            i += ri;
            j += rj;
        }
        Ok(Status::Ok(i, j))
    }
}

impl Codec for Ascii85Decoder {
    fn transform(
        &mut self,
        src: &[u8],
        dst: &mut [u8],
        flush: FlushState,
    ) -> Result<Status, Error> {
        self.wrap_transform(src, dst, flush)
    }

    fn chunk_size(&self) -> usize {
        5
    }

    fn buffer_size(&self) -> usize {
        4
    }
}

#[cfg(test)]
mod tests {
    use chain::Chain;
    use codec::registry::CodecRegistry;
    use codec::tests;

    fn check(name: &str, inp: &[u8], outp: &[u8]) {
        let reg = CodecRegistry::new();
        let rev = format!("-{}", name);
        for i in vec![5, 6, 7, 8, 512] {
            let c = Chain::new(&reg, name, i, true);
            assert_eq!(c.transform(inp.to_vec()).unwrap(), outp);
            let c = Chain::new(&reg, &rev, i, true);
            assert_eq!(c.transform(outp.to_vec()).unwrap(), inp);
            let c = Chain::new(&reg, &rev, i, false);
            assert_eq!(c.transform(outp.to_vec()).unwrap(), inp);
        }
    }

    #[test]
    fn encodes_bytes_ascii85() {
        check("ascii85", b"", b"<~~>");
        check("ascii85", b"a", b"<~@/~>");
        check("ascii85", b"ab", b"<~@:B~>");
        check("ascii85", b"abc", b"<~@:E^~>");
        check("ascii85", b"abcd", b"<~@:E_W~>");
        check("ascii85", b"\x00", b"<~!!~>");
        check("ascii85", b"\x00\x00", b"<~!!!~>");
        check("ascii85", b"\x00\x00\x00", b"<~!!!!~>");
        check("ascii85", b"\x00\x00\x00\x00", b"<~z~>");
        check(
            "ascii85",
            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
            b"<~zzz~>",
        );
        check("ascii85", b"\xff", b"<~rr~>");
        check("ascii85", b"\xff\xff", b"<~s8N~>");
        check("ascii85", b"\xff\xff\xff", b"<~s8W*~>");
        check("ascii85", b"\xff\xff\xff\xff", b"<~s8W-!~>");
        check(
            "ascii85",
            b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
            b"<~s8W-!s8W-!s8W-!~>",
        );
        check("ascii85", b"    ", b"<~+<VdL~>");

        check("ascii85,bare", b"", b"");
        check("ascii85,bare", b"a", b"@/");
        check("ascii85,bare", b"ab", b"@:B");
        check("ascii85,bare", b"abc", b"@:E^");
        check("ascii85,bare", b"abcd", b"@:E_W");
        check("ascii85,bare", b"\x00", b"!!");
        check("ascii85,bare", b"\x00\x00", b"!!!");
        check("ascii85,bare", b"\x00\x00\x00", b"!!!!");
        check("ascii85,bare", b"\x00\x00\x00\x00", b"z");
        check(
            "ascii85,bare",
            b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
            b"zzz",
        );
        check("ascii85,bare", b"\xff", b"rr");
        check("ascii85,bare", b"\xff\xff", b"s8N");
        check("ascii85,bare", b"\xff\xff\xff", b"s8W*");
        check("ascii85,bare", b"\xff\xff\xff\xff", b"s8W-!");
        check(
            "ascii85,bare",
            b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
            b"s8W-!s8W-!s8W-!",
        );
        check("ascii85,bare", b"    ", b"+<VdL");

        // Example from Wikipedia.
        check("ascii85", b"Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.", br#"<~9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF<GL>Cj@.4Gp$d7F!,L7@<6@)/0JDEF<G%<+EV:2F!,O<DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF"AGXBPCsi+DGm>@3BB/F*&OCAfu2/AKYi(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF<G:8+EV:.+Cf>-FD5W8ARlolDIal(DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G>uD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>"#);
    }

    #[test]
    fn default_tests_ascii85() {
        tests::round_trip("ascii85");
        tests::round_trip("ascii85,bare");
        tests::round_trip_stripped_whitespace("ascii85");
        tests::round_trip_stripped_whitespace("ascii85,bare");
        tests::basic_configuration("ascii85");
        tests::invalid_data("ascii85");
    }

    #[test]
    fn known_values_ascii85() {
        check("ascii85", tests::BYTE_SEQ, br#"<~!!*-'"9eu7#RLhG$k3[W&.oNg'GVB"(`=52*$$(B+<_pR,UFcb-n-Vr/1iJ-0JP==1c70M3&s#]4?Ykm5X@_(6q'R884cEH9MJ8X:f1+h<)lt#=BSg3>[:ZC?t!MSA7]@cBPD3sCi+'.E,fo>FEMbNG^4U^I!pHnJ:W<)KS>/9Ll%"IN/`jYOHG]iPa.Q$R$jD4S=Q7DTV8*TUnsrdW2ZetXKAY/Yd(L?['d?O\@K2_]Y2%o^qmn*`5Ta:aN;TJbg"GZd*^:jeCE.%f\,!5gtgiEi8N\UjQ5OekiqBum-X60nF?)@o_%qPq"ad`r;HWp~>"#);
    }
}