Skip to content

Commit

Permalink
Auto merge of #267 - kamalmarhubi:linux-splice-etc, r=arcnmx
Browse files Browse the repository at this point in the history
linux: Add splice(2), tee(2), vmsplice(2)
  • Loading branch information
homu committed Feb 17, 2016
2 parents 548f2b1 + c381997 commit 7fa7206
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 1 deletion.
42 changes: 41 additions & 1 deletion src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ use libc::{c_int, c_uint};
use sys::stat::Mode;
use std::os::unix::io::RawFd;

#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec; // For vmsplice
#[cfg(any(target_os = "linux", target_os = "android"))]
use libc;

pub use self::consts::*;
pub use self::ffi::flock;

Expand Down Expand Up @@ -181,9 +186,44 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
Errno::result(res).map(drop)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
len: usize, flags: SpliceFFlags) -> Result<usize> {
use std::ptr;
let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());

let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe {
libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
};
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
use libc::c_int;
use libc::{self, c_int, c_uint};

bitflags! {
flags SpliceFFlags: c_uint {
const SPLICE_F_MOVE = libc::SPLICE_F_MOVE,
const SPLICE_F_NONBLOCK = libc::SPLICE_F_NONBLOCK,
const SPLICE_F_MORE = libc::SPLICE_F_MORE,
const SPLICE_F_GIFT = libc::SPLICE_F_GIFT,
}
}

bitflags!(
flags OFlag: c_int {
Expand Down
1 change: 1 addition & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern crate tempfile;
extern crate nix_test as nixtest;

mod sys;
mod test_fcntl;
mod test_net;
mod test_nix_path;
#[cfg(any(target_os = "linux", target_os = "android"))]
Expand Down
85 changes: 85 additions & 0 deletions test/test_fcntl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use std::io::prelude::*;
use std::os::unix::prelude::*;

use libc::loff_t;

use nix::fcntl::{SpliceFFlags, splice, tee, vmsplice};
use nix::sys::uio::IoVec;
use nix::unistd::{close, pipe, read, write};

use tempfile::tempfile;

#[test]
fn test_splice() {
const CONTENTS: &'static [u8] = b"abcdef123456";
let mut tmp = tempfile().unwrap();
tmp.write(CONTENTS).unwrap();

let (rd, wr) = pipe().unwrap();
let mut offset: loff_t = 5;
let res = splice(tmp.as_raw_fd(), Some(&mut offset),
wr, None, 2, SpliceFFlags::empty()).unwrap();

assert_eq!(2, res);

let mut buf = [0u8; 1024];
assert_eq!(2, read(rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);

close(rd).unwrap();
close(wr).unwrap();
}

#[test]
fn test_tee() {
let (rd1, wr1) = pipe().unwrap();
let (rd2, wr2) = pipe().unwrap();

write(wr1, b"abc").unwrap();
let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

assert_eq!(2, res);

let mut buf = [0u8; 1024];

// Check the tee'd bytes are at rd2.
assert_eq!(2, read(rd2, &mut buf).unwrap());
assert_eq!(b"ab", &buf[0..2]);

// Check all the bytes are still at rd1.
assert_eq!(3, read(rd1, &mut buf).unwrap());
assert_eq!(b"abc", &buf[0..3]);

close(rd1).unwrap();
close(wr1).unwrap();
close(rd2).unwrap();
close(wr2).unwrap();
}

#[test]
fn test_vmsplice() {
let (rd, wr) = pipe().unwrap();

let buf1 = b"abcdef";
let buf2 = b"defghi";
let mut iovecs = Vec::with_capacity(2);
iovecs.push(IoVec::from_slice(&buf1[0..3]));
iovecs.push(IoVec::from_slice(&buf2[0..3]));

let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

assert_eq!(6, res);

// Check the bytes can be read at rd.
let mut buf = [0u8; 32];
assert_eq!(6, read(rd, &mut buf).unwrap());
assert_eq!(b"abcdef", &buf[0..6]);

close(rd).unwrap();
close(wr).unwrap();
}

}

0 comments on commit 7fa7206

Please sign in to comment.