Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 60c69e1

Browse files
committed
Add read_buf equivalents for positioned reads
Adds the following items under the `read_buf_at` feature: - `std::os::unix::fs::FileExt::read_buf_at` - `std::os::unix::fs::FileExt::read_buf_exact_at` - `std::os::windows::fs::FileExt::seek_read_buf`
1 parent 239e8b1 commit 60c69e1

File tree

8 files changed

+308
-23
lines changed

8 files changed

+308
-23
lines changed

‎library/std/src/fs/tests.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,85 @@ fn file_test_io_read_write_at() {
490490
check!(fs::remove_file(&filename));
491491
}
492492

493+
#[test]
494+
#[cfg(unix)]
495+
fn test_read_buf_at() {
496+
use crate::os::unix::fs::FileExt;
497+
498+
let tmpdir = tmpdir();
499+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_at.txt");
500+
{
501+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
502+
let mut file = check!(oo.open(&filename));
503+
check!(file.write_all(b"0123456789"));
504+
}
505+
{
506+
let mut file = check!(File::open(&filename));
507+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
508+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
509+
510+
// Fill entire buffer with potentially short reads
511+
while buf.unfilled().capacity() > 0 {
512+
let len = buf.len();
513+
check!(file.read_buf_at(buf.unfilled(), 2 + len as u64));
514+
assert!(!buf.filled().is_empty());
515+
assert!(b"23456".starts_with(buf.filled()));
516+
assert_eq!(check!(file.stream_position()), 0);
517+
}
518+
assert_eq!(buf.filled(), b"23456");
519+
520+
// Already full
521+
check!(file.read_buf_at(buf.unfilled(), 3));
522+
check!(file.read_buf_at(buf.unfilled(), 10));
523+
assert_eq!(buf.filled(), b"23456");
524+
assert_eq!(check!(file.stream_position()), 0);
525+
526+
// Read past eof is noop
527+
check!(file.read_buf_at(buf.clear().unfilled(), 10));
528+
assert_eq!(buf.filled(), b"");
529+
check!(file.read_buf_at(buf.clear().unfilled(), 11));
530+
assert_eq!(buf.filled(), b"");
531+
assert_eq!(check!(file.stream_position()), 0);
532+
}
533+
check!(fs::remove_file(&filename));
534+
}
535+
536+
#[test]
537+
#[cfg(unix)]
538+
fn test_read_buf_exact_at() {
539+
use crate::os::unix::fs::FileExt;
540+
541+
let tmpdir = tmpdir();
542+
let filename = tmpdir.join("file_rt_io_file_test_read_buf_exact_at.txt");
543+
{
544+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
545+
let mut file = check!(oo.open(&filename));
546+
check!(file.write_all(b"0123456789"));
547+
}
548+
{
549+
let mut file = check!(File::open(&filename));
550+
let mut buf: [MaybeUninit<u8>; 5] = [MaybeUninit::uninit(); 5];
551+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
552+
553+
// Exact read
554+
check!(file.read_buf_exact_at(buf.unfilled(), 2));
555+
assert_eq!(buf.filled(), b"23456");
556+
assert_eq!(check!(file.stream_position()), 0);
557+
558+
// Already full
559+
check!(file.read_buf_exact_at(buf.unfilled(), 3));
560+
check!(file.read_buf_exact_at(buf.unfilled(), 10));
561+
assert_eq!(buf.filled(), b"23456");
562+
assert_eq!(check!(file.stream_position()), 0);
563+
564+
// Non-empty exact read past eof fails
565+
let err = file.read_buf_exact_at(buf.clear().unfilled(), 6).unwrap_err();
566+
assert_eq!(err.kind(), ErrorKind::UnexpectedEof);
567+
assert_eq!(check!(file.stream_position()), 0);
568+
}
569+
check!(fs::remove_file(&filename));
570+
}
571+
493572
#[test]
494573
#[cfg(unix)]
495574
fn set_get_unix_permissions() {
@@ -566,6 +645,39 @@ fn file_test_io_seek_read_write() {
566645
check!(fs::remove_file(&filename));
567646
}
568647

648+
#[test]
649+
#[cfg(windows)]
650+
fn test_seek_read_buf() {
651+
use crate::os::windows::fs::FileExt;
652+
653+
let tmpdir = tmpdir();
654+
let filename = tmpdir.join("file_rt_io_file_test_seek_read_buf.txt");
655+
{
656+
let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
657+
let mut file = check!(oo.open(&filename));
658+
check!(file.write_all(b"0123456789"));
659+
}
660+
{
661+
let mut file = check!(File::open(&filename));
662+
let mut buf: [MaybeUninit<u8>; 1] = [MaybeUninit::uninit()];
663+
let mut buf = BorrowedBuf::from(buf.as_mut_slice());
664+
665+
// Seek read
666+
check!(file.seek_read_buf(buf.unfilled(), 8));
667+
assert_eq!(buf.filled(), b"8");
668+
assert_eq!(check!(file.stream_position()), 9);
669+
670+
// Empty seek read
671+
check!(file.seek_read_buf(buf.unfilled(), 0));
672+
assert_eq!(buf.filled(), b"8");
673+
674+
// Seek read past eof
675+
check!(file.seek_read_buf(buf.clear().unfilled(), 10));
676+
assert_eq!(buf.filled(), b"");
677+
}
678+
check!(fs::remove_file(&filename));
679+
}
680+
569681
#[test]
570682
fn file_test_read_buf() {
571683
let tmpdir = tmpdir();

‎library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@
330330
#![feature(bstr)]
331331
#![feature(bstr_internals)]
332332
#![feature(cast_maybe_uninit)]
333+
#![feature(cfg_select)]
333334
#![feature(char_internals)]
334335
#![feature(clone_to_uninit)]
335336
#![feature(const_cmp)]

‎library/std/src/os/unix/fs.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use super::platform::fs::MetadataExt as _;
1111
// Used for `File::read` on intra-doc links
1212
use crate::ffi::OsStr;
1313
use crate::fs::{self, OpenOptions, Permissions};
14+
use crate::io::BorrowedCursor;
1415
use crate::os::unix::io::{AsFd, AsRawFd};
1516
use crate::path::Path;
1617
use crate::sealed::Sealed;
@@ -130,6 +131,91 @@ pub trait FileExt {
130131
if !buf.is_empty() { Err(io::Error::READ_EXACT_EOF) } else { Ok(()) }
131132
}
132133

134+
/// Reads some bytes starting from a given offset into the buffer.
135+
///
136+
/// This equivalent to the [`read_at`](FileExt::read_at) method, except that it is passed a
137+
/// [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The new
138+
/// data will be appended to any existing contents of `buf`.
139+
///
140+
/// # Examples
141+
///
142+
/// ```no_run
143+
/// #![feature(core_io_borrowed_buf)]
144+
/// #![feature(read_buf_at)]
145+
///
146+
/// use std::io;
147+
/// use std::io::BorrowedBuf;
148+
/// use std::fs::File;
149+
/// use std::mem::MaybeUninit;
150+
/// use std::os::unix::prelude::*;
151+
///
152+
/// fn main() -> io::Result<()> {
153+
/// let mut file = File::open("pi.txt")?;
154+
///
155+
/// // Read some bytes starting from offset 2
156+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
157+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
158+
/// file.read_buf_at(buf.unfilled(), 2)?;
159+
///
160+
/// assert!(buf.filled().starts_with(b"1"));
161+
///
162+
/// Ok(())
163+
/// }
164+
/// ```
165+
#[unstable(feature = "read_buf_at", issue = "140771")]
166+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
167+
io::default_read_buf(|b| self.read_at(b, offset), buf)
168+
}
169+
170+
/// Reads the exact number of bytes required to fill the buffer from a given offset.
171+
///
172+
/// This is equivalent to the [`read_exact_at`](FileExt::read_exact_at) method, except that it
173+
/// is passed a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized
174+
/// buffers. The new data will be appended to any existing contents of `buf`.
175+
///
176+
/// # Examples
177+
///
178+
/// ```no_run
179+
/// #![feature(core_io_borrowed_buf)]
180+
/// #![feature(read_buf_at)]
181+
///
182+
/// use std::io;
183+
/// use std::io::BorrowedBuf;
184+
/// use std::fs::File;
185+
/// use std::mem::MaybeUninit;
186+
/// use std::os::unix::prelude::*;
187+
///
188+
/// fn main() -> io::Result<()> {
189+
/// let mut file = File::open("pi.txt")?;
190+
///
191+
/// // Read exactly 10 bytes starting from offset 2
192+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
193+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
194+
/// file.read_buf_exact_at(buf.unfilled(), 2)?;
195+
///
196+
/// assert_eq!(buf.filled(), b"1415926535");
197+
///
198+
/// Ok(())
199+
/// }
200+
/// ```
201+
#[unstable(feature = "read_buf_at", issue = "140771")]
202+
fn read_buf_exact_at(&self, mut buf: BorrowedCursor<'_>, mut offset: u64) -> io::Result<()> {
203+
while buf.capacity() > 0 {
204+
let prev_written = buf.written();
205+
match self.read_buf_at(buf.reborrow(), offset) {
206+
Ok(()) => {}
207+
Err(e) if e.is_interrupted() => {}
208+
Err(e) => return Err(e),
209+
}
210+
let n = buf.written() - prev_written;
211+
offset += n as u64;
212+
if n == 0 {
213+
return Err(io::Error::READ_EXACT_EOF);
214+
}
215+
}
216+
Ok(())
217+
}
218+
133219
/// Writes a number of bytes starting from a given offset.
134220
///
135221
/// Returns the number of bytes written.
@@ -264,6 +350,9 @@ impl FileExt for fs::File {
264350
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
265351
self.as_inner().read_at(buf, offset)
266352
}
353+
fn read_buf_at(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
354+
self.as_inner().read_buf_at(buf, offset)
355+
}
267356
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
268357
self.as_inner().read_vectored_at(bufs, offset)
269358
}

‎library/std/src/os/windows/fs.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![stable(feature = "rust1", since = "1.0.0")]
66

77
use crate::fs::{self, Metadata, OpenOptions};
8+
use crate::io::BorrowedCursor;
89
use crate::path::Path;
910
use crate::sealed::Sealed;
1011
use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
@@ -49,6 +50,44 @@ pub trait FileExt {
4950
#[stable(feature = "file_offset", since = "1.15.0")]
5051
fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
5152

53+
/// Seeks to a given position and reads some bytes into the buffer.
54+
///
55+
/// This is equivalent to the [`seek_read`](FileExt::seek_read) method, except that it is passed
56+
/// a [`BorrowedCursor`] rather than `&mut [u8]` to allow use with uninitialized buffers. The
57+
/// new data will be appended to any existing contents of `buf`.
58+
///
59+
/// Reading beyond the end of the file will always succeed without reading any bytes.
60+
///
61+
/// # Examples
62+
///
63+
/// ```no_run
64+
/// #![feature(core_io_borrowed_buf)]
65+
/// #![feature(read_buf_at)]
66+
///
67+
/// use std::io;
68+
/// use std::io::BorrowedBuf;
69+
/// use std::fs::File;
70+
/// use std::mem::MaybeUninit;
71+
/// use std::os::windows::prelude::*;
72+
///
73+
/// fn main() -> io::Result<()> {
74+
/// let mut file = File::open("pi.txt")?;
75+
///
76+
/// // Read some bytes starting from offset 2
77+
/// let mut buf: [MaybeUninit<u8>; 10] = [MaybeUninit::uninit(); 10];
78+
/// let mut buf = BorrowedBuf::from(buf.as_mut_slice());
79+
/// file.seek_read_buf(buf.unfilled(), 2)?;
80+
///
81+
/// assert!(buf.filled().starts_with(b"1"));
82+
///
83+
/// Ok(())
84+
/// }
85+
/// ```
86+
#[unstable(feature = "read_buf_at", issue = "140771")]
87+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
88+
io::default_read_buf(|b| self.seek_read(b, offset), buf)
89+
}
90+
5291
/// Seeks to a given position and writes a number of bytes.
5392
///
5493
/// Returns the number of bytes written.
@@ -89,6 +128,10 @@ impl FileExt for fs::File {
89128
self.as_inner().read_at(buf, offset)
90129
}
91130

131+
fn seek_read_buf(&self, buf: BorrowedCursor<'_>, offset: u64) -> io::Result<()> {
132+
self.as_inner().read_buf_at(buf, offset)
133+
}
134+
92135
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
93136
self.as_inner().write_at(buf, offset)
94137
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /