Skip to content

Commit

Permalink
Add structs and traits for supporting implicit finishing when drops
Browse files Browse the repository at this point in the history
  • Loading branch information
sile committed Dec 25, 2017
1 parent 553ecc5 commit 2fd7870
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 4 deletions.
11 changes: 10 additions & 1 deletion src/deflate/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use byteorder::WriteBytesExt;

use bit;
use lz77;
use finish::Finish;
use finish::{Complete, Finish};
use super::symbol;
use super::BlockType;

Expand Down Expand Up @@ -237,6 +237,15 @@ where
self.writer.as_inner_mut().flush()
}
}
impl<W, E> Complete for Encoder<W, E>
where
W: io::Write,
E: lz77::Lz77Encode,
{
fn complete(self) -> io::Result<()> {
self.finish().into_result().map(|_| ())
}
}

#[derive(Debug)]
struct Block<E> {
Expand Down
134 changes: 134 additions & 0 deletions src/finish.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
//! `Finish` and related types.
use std::io::{self, Write};
use std::ops::{Deref, DerefMut};

/// `Finish` is a type that represents a value which
/// may have an error occurred during the computation.
///
Expand Down Expand Up @@ -85,3 +89,133 @@ impl<T, E> Finish<T, E> {
}
}
}

/// A wrapper struct that completes the processing of the underlying instance when drops.
///
/// This calls `Complete:::complete` method of `T` when drops.
///
/// # Panics
///
/// If the invocation of `Complete::complete(T)` returns an error, `AutoFinish::drop()` will panic.
#[derive(Debug)]
pub struct AutoFinish<T: Complete> {
inner: Option<T>,
}
impl<T: Complete> AutoFinish<T> {
/// Makes a new `AutoFinish` instance.
///
/// # Examples
///
/// ```
/// use std::io;
/// use libflate::finish::AutoFinish;
/// use libflate::gzip::Encoder;
///
/// let plain = b"Hello World!";
/// let mut buf = Vec::new();
/// let mut encoder = AutoFinish::new(Encoder::new(&mut buf).unwrap());
/// io::copy(&mut &plain[..], &mut encoder).unwrap();
/// ```
pub fn new(inner: T) -> Self {
AutoFinish { inner: Some(inner) }
}

/// Unwraps this `AutoFinish` instance, returning the underlying instance.
pub fn into_inner(mut self) -> T {
self.inner.take().expect("Never fails")
}
}
impl<T: Complete> Drop for AutoFinish<T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
if let Err(e) = inner.complete() {
panic!("{}", e);
}
}
}
}
impl<T: Complete> Deref for AutoFinish<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().expect("Never fails")
}
}
impl<T: Complete> DerefMut for AutoFinish<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().expect("Never fails")
}
}
impl<T: Complete + Write> Write for AutoFinish<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.deref_mut().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.deref_mut().flush()
}
}

/// A wrapper struct that completes the processing of the underlying instance when drops.
///
/// This calls `Complete:::complete` method of `T` when drops.
///
/// Note that this ignores the result of the invocation of `Complete::complete(T)`.
#[derive(Debug)]
pub struct AutoFinishUnchecked<T: Complete> {
inner: Option<T>,
}
impl<T: Complete> AutoFinishUnchecked<T> {
/// Makes a new `AutoFinishUnchecked` instance.
///
/// # Examples
///
/// ```
/// use std::io;
/// use libflate::finish::AutoFinishUnchecked;
/// use libflate::gzip::Encoder;
///
/// let plain = b"Hello World!";
/// let mut buf = Vec::new();
/// let mut encoder = AutoFinishUnchecked::new(Encoder::new(&mut buf).unwrap());
/// io::copy(&mut &plain[..], &mut encoder).unwrap();
/// ```
pub fn new(inner: T) -> Self {
AutoFinishUnchecked { inner: Some(inner) }
}

/// Unwraps this `AutoFinishUnchecked` instance, returning the underlying instance.
pub fn into_inner(mut self) -> T {
self.inner.take().expect("Never fails")
}
}
impl<T: Complete> Drop for AutoFinishUnchecked<T> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let _ = inner.complete();
}
}
}
impl<T: Complete> Deref for AutoFinishUnchecked<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().expect("Never fails")
}
}
impl<T: Complete> DerefMut for AutoFinishUnchecked<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.as_mut().expect("Never fails")
}
}
impl<T: Complete + Write> Write for AutoFinishUnchecked<T> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.deref_mut().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.deref_mut().flush()
}
}

/// This trait allows to complete an I/O related processing.
pub trait Complete {
/// Completes the current processing and returns the result.
fn complete(self) -> io::Result<()>;
}
39 changes: 38 additions & 1 deletion src/gzip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use byteorder::LittleEndian;
use lz77;
use deflate;
use checksum;
use finish::Finish;
use finish::{Complete, Finish};

const GZIP_ID: [u8; 2] = [31, 139];
const COMPRESSION_METHOD_DEFLATE: u8 = 8;
Expand Down Expand Up @@ -779,6 +779,22 @@ where
///
/// assert!(encoder.finish().as_result().is_ok())
/// ```
///
/// # Note
///
/// If you are not concerned the result of this encoding,
/// it may be convenient to use `AutoFinishUnchecked` instead of the explicit invocation of this method.
///
/// ```
/// use std::io;
/// use libflate::finish::AutoFinishUnchecked;
/// use libflate::gzip::Encoder;
///
/// let plain = b"Hello World!";
/// let mut buf = Vec::new();
/// let mut encoder = AutoFinishUnchecked::new(Encoder::new(&mut buf).unwrap());
/// io::copy(&mut &plain[..], &mut encoder).unwrap();
/// ```
pub fn finish(self) -> Finish<W, io::Error> {
let trailer = Trailer {
crc32: self.crc32.value(),
Expand Down Expand Up @@ -806,6 +822,15 @@ where
self.writer.flush()
}
}
impl<W, E> Complete for Encoder<W, E>
where
W: io::Write,
E: lz77::Lz77Encode,
{
fn complete(self) -> io::Result<()> {
self.finish().into_result().map(|_| ())
}
}

/// GZIP decoder.
#[derive(Debug)]
Expand Down Expand Up @@ -915,6 +940,7 @@ where
#[cfg(test)]
mod test {
use std::io;
use finish::AutoFinish;
use super::*;

fn decode_all(buf: &[u8]) -> io::Result<Vec<u8>> {
Expand All @@ -932,4 +958,15 @@ mod test {
let encoded = encoder.finish().into_result().unwrap();
assert_eq!(decode_all(&encoded).unwrap(), plain);
}

#[test]
fn encoder_auto_finish_works() {
let plain = b"Hello World! Hello GZIP!!";
let mut buf = Vec::new();
{
let mut encoder = AutoFinish::new(Encoder::new(&mut buf).unwrap());
io::copy(&mut &plain[..], &mut encoder).unwrap();
}
assert_eq!(decode_all(&buf).unwrap(), plain);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ pub mod zlib;
pub mod gzip;
pub mod deflate;
pub mod non_blocking;
pub mod finish;

mod bit;
mod finish;
mod huffman;
mod checksum;
mod util;
39 changes: 38 additions & 1 deletion src/zlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use byteorder::WriteBytesExt;
use lz77;
use deflate;
use checksum;
use finish::Finish;
use finish::{Complete, Finish};

const COMPRESSION_METHOD_DEFLATE: u8 = 8;

Expand Down Expand Up @@ -550,6 +550,22 @@ where
/// [120, 156, 5, 128, 65, 9, 0, 0, 8, 3, 171, 104, 27, 27, 88, 64, 127,
/// 7, 131, 245, 127, 140, 121, 80, 173, 204, 117, 0, 28, 73, 4, 62]);
/// ```
///
/// # Note
///
/// If you are not concerned the result of this encoding,
/// it may be convenient to use `AutoFinishUnchecked` instead of the explicit invocation of this method.
///
/// ```
/// use std::io;
/// use libflate::finish::AutoFinishUnchecked;
/// use libflate::zlib::Encoder;
///
/// let plain = b"Hello World!";
/// let mut buf = Vec::new();
/// let mut encoder = AutoFinishUnchecked::new(Encoder::new(&mut buf).unwrap());
/// io::copy(&mut &plain[..], &mut encoder).unwrap();
/// ```
pub fn finish(self) -> Finish<W, io::Error> {
let mut inner = finish_try!(self.writer.finish());
match inner
Expand All @@ -575,10 +591,20 @@ where
self.writer.flush()
}
}
impl<W, E> Complete for Encoder<W, E>
where
W: io::Write,
E: lz77::Lz77Encode,
{
fn complete(self) -> io::Result<()> {
self.finish().into_result().map(|_| ())
}
}

#[cfg(test)]
mod test {
use std::io;
use finish::AutoFinish;
use super::*;

fn decode_all(buf: &[u8]) -> io::Result<Vec<u8>> {
Expand Down Expand Up @@ -659,6 +685,17 @@ mod test {
assert_eq!(decode_all(&encoded).unwrap(), plain);
}

#[test]
fn encoder_auto_finish_works() {
let plain = b"Hello World! Hello ZLIB!!";
let mut buf = Vec::new();
{
let mut encoder = AutoFinish::new(Encoder::new(&mut buf).unwrap());
io::copy(&mut &plain[..], &mut encoder).unwrap();
}
assert_eq!(decode_all(&buf).unwrap(), plain);
}

#[test]
fn test_issue_2() {
// See: https://github.com/sile/libflate/issues/2
Expand Down

0 comments on commit 2fd7870

Please sign in to comment.