snedfile 0.1.0

Cross-platform sendfile() abstractions
Documentation
#![allow(unused_imports)]
#![allow(dead_code)]

use std::fs::File;
use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom, Write};
use std::net::TcpStream;

pub fn send_file(file: &mut File, stream: &mut TcpStream) -> io::Result<()> {
    let length = file.metadata()?.len();

    if length == 0 {
        return Ok(());
    };

    send_file_imp(file, stream, length)
}

pub fn send_exact(
    file: &mut File,
    stream: &mut TcpStream,
    length: u64,
    offset: u64,
) -> io::Result<u64> {
    file.seek(SeekFrom::Start(offset))?;
    io::copy(&mut file.take(length), stream)
}

#[cfg(not(any(feature = "fallback-bufreader", feature = "fallback-buf")))]
pub fn send_file_imp(file: &mut File, stream: &mut TcpStream, length: u64) -> io::Result<()> {
    let mut sent = io::copy(file, stream)?;

    while sent < length {
        sent += io::copy(file, stream)?;
    }

    Ok(())
}

#[cfg(feature = "fallback-bufreader")]
pub fn send_file_imp(file: &mut File, stream: &mut TcpStream, length: u64) -> io::Result<()> {
    let mut reader = BufReader::new(file);

    let mut sent = io::copy(&mut reader, stream)?;

    while sent < length {
        sent += io::copy(&mut reader, stream)?;
    }

    Ok(())
}

#[cfg(all(feature = "fallback-buf", not(feature = "large-files")))]
pub fn send_file_imp(file: &mut File, stream: &mut TcpStream, length: u64) -> io::Result<()> {
    let mut buf = Vec::with_capacity(length as usize);

    file.read_to_end(&mut buf)?;
    stream.write_all(&buf)?;

    Ok(())
}

#[cfg(all(feature = "fallback-buf", feature = "large-files"))]
pub fn send_file_imp(file: &mut File, stream: &mut TcpStream, length: u64) -> io::Result<()> {
    let mut remaining = length.checked_sub(usize::max_value() as u64).unwrap_or(0);

    let mut buf = Vec::with_capacity(length as usize);

    file.read_to_end(&mut buf)?;
    stream.write_all(&buf)?;

    while remaining > 0 {
        remaining -= io::copy(file, stream)?;
    }

    Ok(())
}

pub fn copy_to_end(file: &mut File, stream: &mut TcpStream, offset: u64) -> io::Result<()> {
    file.seek(SeekFrom::Start(offset))?;

    loop {
        match io::copy(file, stream) {
            Ok(0) => return Ok(()),
            Err(e) => {
                if e.kind() != ErrorKind::WouldBlock {
                    return Err(e);
                }
            }
            _ => continue,
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Read, Seek, SeekFrom, Write};

    #[test]
    fn send_file_imp() {
        let mut file = tempfile::tempfile().unwrap();
        let (mut a, mut b) = tcp_test::channel();
        let data = b"dR#QaIw,";

        file.write_all(data).unwrap();

        file.seek(SeekFrom::Start(0)).unwrap();

        super::send_file_imp(&mut file, &mut a, 8).unwrap();

        let mut buf = [0; 8];
        b.read_exact(&mut buf).unwrap();
        assert_eq!(data, &buf);
    }

    #[test]
    fn send_file_exact() {
        let mut file = tempfile::tempfile().unwrap();
        let (mut a, mut b) = tcp_test::channel();
        let data = b"dR#QaIw,";

        file.write_all(data).unwrap();

        super::send_exact(&mut file, &mut a, 8, 1).unwrap();

        let mut buf = [0; 7];
        b.read_exact(&mut buf).unwrap();
        assert_eq!(&data[1..], &buf);
    }

    #[test]
    fn copy_to_end() {
        let mut file = tempfile::tempfile().unwrap();
        let (mut a, mut b) = tcp_test::channel();
        let data = b"Ht9!Kwk}";

        file.write_all(data).unwrap();

        super::copy_to_end(&mut file, &mut a, 3).unwrap();

        let mut buf = [0; 5];
        b.read_exact(&mut buf).unwrap();
        assert_eq!(&data[3..], &buf);
    }
}