1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use {Write, BufRead, Result, Error, ErrorKind};
use memchr;

/// Copies the entire content of a buffered reader into a writer.
///
/// Similar to [`std::io::copy`], this function will continuously read data from reader and
/// then write it into writer in a streaming fashion until reader returns EOF.
///
/// Errors
/// ======
/// This function will return an error immediately if any call to [`fill_buf`] or
/// [`write`] returns any kind of error.
/// Instances of [`ErrorKind`]`::Interrupted` are *not* handled by this function.
///
/// All bytes consumed from the buffered reader will be written to the specified writer and vice
/// versa.
/// It is guaranteed that no data is lost in case of error.
///
/// Differences to `std::io::copy`
/// ==============================
/// - Does not retry on [`ErrorKind`]`::Interrupted`.
/// - Uses [`BufRead`] instead of [`Read`].
/// - Does not return the number of bytes that are copied.
///
/// Advantages
/// ----------
/// - Allows for reliable retry on errors.
/// - Function is interruptable, e.g. to allow graceful shutdown for server applications.
/// - Avoids double buffering if the source already implements [`BufRead`].
/// - Allows different buffer sizes by using [`BufReader::with_capacity`].
///
/// Disadvantages
/// -------------
/// The fact that it does not return the number of bytes copied stems from the fact that it cannot
/// return this information in case of error.
/// This would go against the goal of allowing reliable retry.
///
/// [`fill_buf`]: ./trait.BufRead.html#tymethod.fill_buf
/// [`write`]: ./trait.Write.html#tymethod.write
/// [`Read`]: ./trait.Read.html
/// [`BufRead`]: ./trait.BufRead.html
/// [`BufReader::with_capacity`]: ./buf_redux_reexport/struct.BufReader.html#method.with_capacity
/// [`std::io::copy`]: https://doc.rust-lang.org/nightly/std/io/fn.copy.html
/// [`ErrorKind`]: https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> Result<()>
    where R: BufRead,
          W: Write
{
    loop {
        let written = {
            let buf = try!(reader.fill_buf());
            if buf.len() == 0 {
                return Ok(());
            }
            try!(writer.write(buf))
        };
        if written == 0 {
            return Err(Error::new(ErrorKind::WriteZero, "no bytes could be written"));
        }
        reader.consume(written);
    }
}

/// Copies the content of a buffered reader into a writer until a delimiter is reached.
///
/// This function will continuously read data from reader and then write it into writer in a
/// streaming fashion until until the delimiter or EOF is found.
///
/// Errors
/// ======
/// This function will return an error immediately if any call to [`fill_buf`] or
/// [`write`] returns any kind of error.
/// Instances of [`ErrorKind`]`::Interrupted` are *not* handled by this function.
///
/// All bytes consumed from the buffered reader will be written to the specified writer and vice
/// versa.
/// It is guaranteed that no data is lost in case of error.
///
/// [`fill_buf`]: ./trait.BufRead.html#tymethod.fill_buf
/// [`write`]: ./trait.Write.html#tymethod.write
/// [`ErrorKind`]: https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html
pub fn copy_until<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W, delim: u8) -> Result<()>
    where R: BufRead,
          W: Write
{
    loop {
        let (found, used) = {
            let buf = try!(reader.fill_buf());
            if buf.len() == 0 {
                return Err(Error::new(ErrorKind::UnexpectedEof,
                                      "Stream did not contain the delimiter"));
            }
            match memchr::memchr(delim, buf) {
                Some(i) => {
                    let written = try!(writer.write(&buf[..i + 1]));
                    (written == i + 1, written)
                }
                None => (false, try!(writer.write(buf))),
            }
        };
        if used == 0 {
            return Err(Error::new(ErrorKind::WriteZero, "no bytes could be written"));
        }
        reader.consume(used);
        if found {
            return Ok(());
        }
    }
}