Skip to content

Commit 2de7f6e

Browse files
author
Luca Bruno
authored
Merge pull request #37 from lucab/ups/pread-exact
FileExt: add an exact-size pread() helper
2 parents eb2d3d3 + d436e72 commit 2de7f6e

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

src/lib.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,10 @@ pub trait FileExt {
699699

700700
/// Update timestamps (both access and modification) to the current time.
701701
fn update_timestamps(&self) -> io::Result<()>;
702+
703+
/// Read the exact number of bytes required to fill `buf` starting from `position`,
704+
/// without affecting file offset.
705+
fn pread_exact(&self, buf: &mut [u8], position: usize) -> io::Result<()>;
702706
}
703707

704708
impl FileExt for File {
@@ -777,6 +781,41 @@ impl FileExt for File {
777781
});
778782
retry_eintr!(futimens(self.as_raw_fd(), &now, &now,).map_err(map_nix_error))
779783
}
784+
785+
fn pread_exact(&self, buf: &mut [u8], start_pos: usize) -> io::Result<()> {
786+
use nix::sys::uio::pread;
787+
788+
if buf.len() == 0 {
789+
return Err(io::Error::new(
790+
io::ErrorKind::InvalidInput,
791+
"zero-sized buffer in input",
792+
));
793+
}
794+
795+
let mut total_bytes_read = 0;
796+
while total_bytes_read < buf.len() {
797+
let remaining_buf = &mut buf[total_bytes_read..];
798+
let cur_offset = start_pos.saturating_add(total_bytes_read);
799+
let bytes_read =
800+
retry_eintr!(
801+
pread(self.as_raw_fd(), remaining_buf, cur_offset as libc::off_t)
802+
.map_err(map_nix_error)
803+
)?;
804+
total_bytes_read += bytes_read;
805+
if bytes_read == 0 {
806+
break;
807+
}
808+
}
809+
810+
if total_bytes_read < buf.len() {
811+
return Err(io::Error::new(
812+
io::ErrorKind::UnexpectedEof,
813+
format!("pread reached EOF after {} bytes", total_bytes_read),
814+
));
815+
}
816+
817+
Ok(())
818+
}
780819
}
781820

782821
fn to_cstr<P: openat::AsPath>(path: P) -> io::Result<P::Buffer> {
@@ -1042,4 +1081,37 @@ mod tests {
10421081
assert_eq!(&srcbuf, b"test content");
10431082
assert_eq!(&srcbuf, &destbuf);
10441083
}
1084+
1085+
#[test]
1086+
fn test_pread_exact() {
1087+
let td = tempfile::tempdir().unwrap();
1088+
let d = openat::Dir::open(td.path()).unwrap();
1089+
static TESTINPUT: &str = "test1 test2 test3";
1090+
d.write_file_contents("foo", 0o700, TESTINPUT).unwrap();
1091+
let mut testfile = d.open_file("foo").unwrap();
1092+
{
1093+
let mut buf = [0; 0];
1094+
let _ = testfile.pread_exact(&mut buf, 0).unwrap_err();
1095+
}
1096+
{
1097+
let mut buf = [0; 18];
1098+
let _ = testfile.pread_exact(&mut buf, 0).unwrap_err();
1099+
}
1100+
{
1101+
let mut buf = [0; 1];
1102+
let _ = testfile.pread_exact(&mut buf, 2000).unwrap_err();
1103+
}
1104+
{
1105+
let mut buf1 = [0; 5];
1106+
let mut buf2 = [0; 5];
1107+
let _ = testfile.pread_exact(&mut buf1, 6).unwrap();
1108+
let _ = testfile.pread_exact(&mut buf2, 6).unwrap();
1109+
assert_eq!(buf1, "test2".as_bytes());
1110+
assert_eq!(buf1, buf2);
1111+
1112+
let mut str_buf = String::new();
1113+
let _ = testfile.read_to_string(&mut str_buf).unwrap();
1114+
assert_eq!(str_buf, TESTINPUT);
1115+
}
1116+
}
10451117
}

0 commit comments

Comments
 (0)