Skip to content

Commit a3fc979

Browse files
authored
Rollup merge of rust-lang#51809 - drrlvn:rw_exact_all_at, r=alexcrichton
Add read_exact_at and write_all_at methods to FileExt on unix This PR adds `FileExt::read_exact_at()` and `FileExt::write_all_at()`, which are to `read_at()` and `write_at()` as `read_exact()` and `write_all()` are to `read()` and `write()`. This allows the user to not have to deal with `ErrorKind::Interrupted` and calling the functions in a loop. I was unsure as to how to mark these new methods so I marked them `unstable`, please let me know if I should have done it differently. I asked in Discord and was told that as this change is small it does not require an RFC.
2 parents 7fa03fb + 73166f7 commit a3fc979

File tree

1 file changed

+127
-0
lines changed
  • src/libstd/sys/unix/ext

1 file changed

+127
-0
lines changed

src/libstd/sys/unix/ext/fs.rs

+127
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,78 @@ pub trait FileExt {
5959
#[stable(feature = "file_offset", since = "1.15.0")]
6060
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
6161

62+
/// Reads the exact number of byte required to fill `buf` from the given offset.
63+
///
64+
/// The offset is relative to the start of the file and thus independent
65+
/// from the current cursor.
66+
///
67+
/// The current file cursor is not affected by this function.
68+
///
69+
/// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`.
70+
///
71+
/// [`Read::read_exact`]: ../../../../std/io/trait.Read.html#method.read_exact
72+
/// [`read_at`]: #tymethod.read_at
73+
///
74+
/// # Errors
75+
///
76+
/// If this function encounters an error of the kind
77+
/// [`ErrorKind::Interrupted`] then the error is ignored and the operation
78+
/// will continue.
79+
///
80+
/// If this function encounters an "end of file" before completely filling
81+
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
82+
/// The contents of `buf` are unspecified in this case.
83+
///
84+
/// If any other read error is encountered then this function immediately
85+
/// returns. The contents of `buf` are unspecified in this case.
86+
///
87+
/// If this function returns an error, it is unspecified how many bytes it
88+
/// has read, but it will never read more than would be necessary to
89+
/// completely fill the buffer.
90+
///
91+
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
92+
/// [`ErrorKind::UnexpectedEof`]: ../../../../std/io/enum.ErrorKind.html#variant.UnexpectedEof
93+
///
94+
/// # Examples
95+
///
96+
/// ```no_run
97+
/// #![feature(rw_exact_all_at)]
98+
/// use std::io;
99+
/// use std::fs::File;
100+
/// use std::os::unix::prelude::FileExt;
101+
///
102+
/// fn main() -> io::Result<()> {
103+
/// let mut buf = [0u8; 8];
104+
/// let file = File::open("foo.txt")?;
105+
///
106+
/// // We now read exactly 8 bytes from the offset 10.
107+
/// file.read_exact_at(&mut buf, 10)?;
108+
/// println!("read {} bytes: {:?}", buf.len(), buf);
109+
/// Ok(())
110+
/// }
111+
/// ```
112+
#[unstable(feature = "rw_exact_all_at", issue = "51984")]
113+
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
114+
while !buf.is_empty() {
115+
match self.read_at(buf, offset) {
116+
Ok(0) => break,
117+
Ok(n) => {
118+
let tmp = buf;
119+
buf = &mut tmp[n..];
120+
offset += n as u64;
121+
}
122+
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
123+
Err(e) => return Err(e),
124+
}
125+
}
126+
if !buf.is_empty() {
127+
Err(io::Error::new(io::ErrorKind::UnexpectedEof,
128+
"failed to fill whole buffer"))
129+
} else {
130+
Ok(())
131+
}
132+
}
133+
62134
/// Writes a number of bytes starting from a given offset.
63135
///
64136
/// Returns the number of bytes written.
@@ -93,6 +165,61 @@ pub trait FileExt {
93165
/// ```
94166
#[stable(feature = "file_offset", since = "1.15.0")]
95167
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
168+
169+
/// Attempts to write an entire buffer starting from a given offset.
170+
///
171+
/// The offset is relative to the start of the file and thus independent
172+
/// from the current cursor.
173+
///
174+
/// The current file cursor is not affected by this function.
175+
///
176+
/// This method will continuously call [`write_at`] until there is no more data
177+
/// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
178+
/// returned. This method will not return until the entire buffer has been
179+
/// successfully written or such an error occurs. The first error that is
180+
/// not of [`ErrorKind::Interrupted`] kind generated from this method will be
181+
/// returned.
182+
///
183+
/// # Errors
184+
///
185+
/// This function will return the first error of
186+
/// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns.
187+
///
188+
/// [`ErrorKind::Interrupted`]: ../../../../std/io/enum.ErrorKind.html#variant.Interrupted
189+
/// [`write_at`]: #tymethod.write_at
190+
///
191+
/// # Examples
192+
///
193+
/// ```no_run
194+
/// #![feature(rw_exact_all_at)]
195+
/// use std::fs::File;
196+
/// use std::io;
197+
/// use std::os::unix::prelude::FileExt;
198+
///
199+
/// fn main() -> io::Result<()> {
200+
/// let file = File::open("foo.txt")?;
201+
///
202+
/// // We now write at the offset 10.
203+
/// file.write_all_at(b"sushi", 10)?;
204+
/// Ok(())
205+
/// }
206+
/// ```
207+
#[unstable(feature = "rw_exact_all_at", issue = "51984")]
208+
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
209+
while !buf.is_empty() {
210+
match self.write_at(buf, offset) {
211+
Ok(0) => return Err(io::Error::new(io::ErrorKind::WriteZero,
212+
"failed to write whole buffer")),
213+
Ok(n) => {
214+
buf = &buf[n..];
215+
offset += n as u64
216+
}
217+
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
218+
Err(e) => return Err(e),
219+
}
220+
}
221+
Ok(())
222+
}
96223
}
97224

98225
#[stable(feature = "file_offset", since = "1.15.0")]

0 commit comments

Comments
 (0)