transform 0.1.0

An experimental trait and other helpers for efficiently transforming byte sequences.
Documentation
use std::io;

mod nop;
pub use nop::Nop;

/// Transforms streams of bytes.
pub trait Transformer {
    /// Writes to dst the transformed bytes read from src, and returns the number of dst bytes
    /// written and src bytes read. The eof argument tells whether src represents the last bytes
    /// of the input.
    ///
    /// Callers should always process the nDst bytes produced and account for the nSrc bytes
    /// consumed before considering the error err.
    ///
    /// A nil error means that all of the transformed bytes (whether freshly transformed from src
    /// or left over from previous Transform calls) were written to dst. A nil error can be
    /// returned regardless of whether eof is true. If err is nil then nSrc must equal len(src);
    /// the converse is not necessarily true.
    ///
    /// ErrShortDst means that dst was too short to receive all of the transformed bytes.
    /// ErrShortSrc means that src had insufficient data to complete the transformation. If both
    /// conditions apply, then either error may be returned. Other than the error conditions listed
    /// here, implementations are free to report other errors that arise.
    fn transform(&mut self, dst: &mut [u8], src: &[u8], eof: bool) -> io::Result<(usize, usize)>;

    /// Resets the state and allows a Transformer to be reused.
    fn reset(&mut self) {}

    /// Creates an adaptor which will chain this transformer with another.
    fn chain<T: Transformer>(self, next: T) -> Chain<Self, T>
        where Self: Sized
    {
        Chain {
            first: self,
            second: next,
            done_first: false,
        }
    }
}

/// Extends the [`Transformer`] interface with a Span method that determines how
/// much of the input already conforms to the Transformer.
///
/// [`Transformer`]: trait.Transformer.html
pub trait SpanningTransformer: Transformer {
    /// Span returns a position in src such that transforming src[:n] results in identical output
    /// src[:n] for these bytes. It does not necessarily return the largest such n. The atEOF
    /// argument tells whether src represents the last bytes of the input.
    ///
    /// Callers should always account for the n bytes consumed before considering the error err.
    ///
    /// A nil error means that all input bytes are known to be identical to the output produced by
    /// the Transformer. A nil error can be be returned regardless of whether atEOF is true. If err
    /// is nil, then then n must equal len(src); the converse is not necessarily true.
    ///
    /// ErrEndOfSpan means that the Transformer output may differ from the input after n bytes.
    /// Note that n may be len(src), meaning that the output would contain additional bytes after
    /// otherwise identical output.  ErrShortSrc means that src had insufficient data to determine
    /// whether the remaining bytes would change. Other than the error conditions listed here,
    /// implementations are free to report other errors that arise.
    ///
    /// Calling Span can modify the Transformer state as a side effect. In effect, it does the
    /// transformation just as calling Transform would, only without copying to a destination
    /// buffer and only up to a point it can determine the input and output bytes are the same.
    /// This is obviously more limited than calling Transform, but can be more efficient in terms
    /// of copying and allocating buffers. Calls to Span and Transform may be interleaved.
    fn span(&self, src: &[u8], eof: bool) -> io::Result<usize>;
}

/// Adapter to chain together two transformers.
///
/// This type is generally created by calling the [`chain()`] method on a transformer. Please see
/// the documentation of [`chain()`] for more details.
///
/// [`chain()`]: trait.Transformer.html#method.chain
pub struct Chain<T, U> {
    first: T,
    second: U,
    done_first: bool,
}

impl<T: Transformer, U: Transformer> Transformer for Chain<T, U> {
    fn transform(&mut self, dst: &mut [u8], src: &[u8], eof: bool) -> io::Result<(usize, usize)> {
        let mut ndst: usize = 0;
        let mut nsrc: usize = 0;

        if !self.done_first {
            let (d, s) = self.first.transform(dst, src, eof)?;
            ndst += d;
            nsrc += s;
            self.done_first = true;
        }
        let (d, s) = self.second.transform(dst, src, eof)?;
        Ok((d + ndst, s + nsrc))
    }

    fn reset(&mut self) {
        self.done_first = false;
    }
}