positioned_io_preview/lib.rs
1//! This crate allows you to specify an offset for reads and writes,
2//! without changing the current position in a file. This is similar to
3//! [`pread()` and `pwrite()`][pread] in C.
4//!
5//! The major advantages of this type of I/O are:
6//!
7//! * You don't need to seek before doing a random-access read or write, which is convenient.
8//! * Reads don't modify the file at all, so don't require mutability.
9//!
10//! [pread]: http://man7.org/linux/man-pages/man2/pread.2.html
11//!
12//! # Preview release!
13//!
14//! This is a preview release of [positioned-io](https://docs.rs/positioned-io).
15//! All examples assume you are using it as:
16//!
17//! ```
18//! extern crate positioned_io_preview as positioned_io;
19//! ```
20//!
21//! # Examples
22//!
23//! Read the fifth 512-byte sector of a file:
24//!
25//! ```
26//! # use positioned_io_preview as positioned_io;
27//! # use std::error::Error;
28//! #
29//! # fn try_main() -> Result<(), Box<Error>> {
30//! use std::fs::File;
31//! use positioned_io::ReadAt;
32//!
33//! // note that file does not need to be mut
34//! let file = File::open("tests/pi.txt")?;
35//!
36//! // read up to 512 bytes
37//! let mut buf = [0; 512];
38//! let bytes_read = file.read_at(2048, &mut buf)?;
39//! # assert!(buf.starts_with(b"4"));
40//! # Ok(())
41//! # }
42//! #
43//! # fn main() {
44//! # try_main().unwrap();
45//! # }
46//! ```
47//!
48//! **Note:** If possible use the
49//! [`RandomAccessFile`](struct.RandomAccessFile.html) wrapper. `ReadAt`
50//! directly on `File` is very slow on Windows.
51//!
52//! Write an integer to the middle of a file:
53//!
54//! ```no_run
55//! # extern crate positioned_io_preview as positioned_io;
56//! # #[cfg(feature = "byteorder")]
57//! # extern crate byteorder;
58//! # use std::io;
59//! #
60//! # fn try_main() -> io::Result<()> {
61//! # #[cfg(feature = "byteorder")]
62//! # {
63//! use std::fs::OpenOptions;
64//! use positioned_io::WriteAt;
65//! use byteorder::{ByteOrder, LittleEndian};
66//!
67//! // put the integer in a buffer
68//! let mut buf = [0; 4];
69//! LittleEndian::write_u32(&mut buf, 1234);
70//!
71//! // write it to the file
72//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
73//! file.write_all_at(1 << 20, &buf)?;
74//! # }
75//! # Ok(())
76//! # }
77//! # fn main() {
78//! # try_main().unwrap()
79//! # }
80//! ```
81//!
82//! Or, more simply:
83//!
84//! ```no_run
85//! # extern crate positioned_io_preview as positioned_io;
86//! # #[cfg(feature = "byteorder")]
87//! # extern crate byteorder;
88//! # use std::io;
89//! #
90//! # fn try_main() -> io::Result<()> {
91//! # #[cfg(feature = "byteorder")]
92//! # {
93//! use std::fs::OpenOptions;
94//! use byteorder::LittleEndian;
95//! use positioned_io::WriteBytesAtExt;
96//!
97//! let mut file = OpenOptions::new().write(true).open("foo.data")?;
98//! file.write_u32_at::<LittleEndian>(1 << 20, 1234)?;
99//! # }
100//! # Ok(())
101//! # }
102//! # fn main() {
103//! # try_main().unwrap()
104//! # }
105//! ```
106//!
107//! Read from anything else that supports `ReadAt`, like a byte array:
108//!
109//! ```rust
110//! # extern crate positioned_io_preview as positioned_io;
111//! # #[cfg(feature = "byteorder")]
112//! # extern crate byteorder;
113//! # use std::io;
114//! #
115//! # fn try_main() -> io::Result<()> {
116//! # #[cfg(feature = "byteorder")]
117//! {
118//! use byteorder::BigEndian;
119//! use positioned_io::ReadBytesAtExt;
120//!
121//! let buf = [0, 5, 254, 212, 0, 3];
122//! let n = buf.as_ref().read_i16_at::<BigEndian>(2)?;
123//! assert_eq!(n, -300);
124//! # }
125//! # Ok(())
126//! # }
127//! # fn main() {
128//! # try_main().unwrap()
129//! # }
130//! ```
131
132#![doc(html_root_url = "https://docs.rs/positioned-io-preview/0.3.5")]
133
134#![warn(missing_debug_implementations)]
135#![warn(bare_trait_objects)]
136
137#[cfg(feature = "byteorder")]
138extern crate byteorder;
139#[cfg(unix)]
140extern crate libc;
141
142mod cursor;
143pub use cursor::{Cursor, SizeCursor};
144
145mod slice;
146pub use slice::Slice;
147
148#[cfg(feature = "byteorder")]
149mod byteio;
150#[cfg(feature = "byteorder")]
151pub use byteio::{ByteIo, ReadBytesAtExt, WriteBytesAtExt};
152
153use std::fs::File;
154use std::io;
155
156/// Trait for reading bytes at an offset.
157///
158/// Implementations should be able to read bytes without changing any sort of
159/// read position. Self should not change at all. Buffering reads is unlikely
160/// to be useful, since each time `read_at()` is called, the position may be
161/// completely different.
162///
163/// # Examples
164///
165/// Read the fifth 512-byte sector of a file:
166///
167/// ```
168/// # use positioned_io_preview as positioned_io;
169/// # use std::error::Error;
170/// #
171/// # fn try_main() -> Result<(), Box<Error>> {
172/// use std::fs::File;
173/// use positioned_io::ReadAt;
174///
175/// // note that file does not need to be mut
176/// let file = File::open("tests/pi.txt")?;
177///
178/// // read up to 512 bytes
179/// let mut buf = [0; 512];
180/// let bytes_read = file.read_at(2048, &mut buf)?;
181/// # assert!(buf.starts_with(b"4"));
182/// # Ok(())
183/// # }
184/// #
185/// # fn main() {
186/// # try_main().unwrap();
187/// # }
188/// ```
189pub trait ReadAt {
190 /// Reads bytes from an offset in this source into a buffer, returning how
191 /// many bytes were read.
192 ///
193 /// This function may yield fewer bytes than the size of `buf`, if it was
194 /// interrupted or hit the "end of file".
195 ///
196 /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read)
197 /// for details.
198 fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize>;
199
200 /// Reads the exact number of bytes required to fill `buf` from an offset.
201 ///
202 /// Errors if the "end of file" is encountered before filling the buffer.
203 ///
204 /// See [`Read::read_exact()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact)
205 /// for details.
206 fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> io::Result<()> {
207 while !buf.is_empty() {
208 match self.read_at(pos, buf) {
209 Ok(0) => break,
210 Ok(n) => {
211 let tmp = buf;
212 buf = &mut tmp[n..];
213 pos += n as u64;
214 }
215 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
216 Err(e) => return Err(e),
217 }
218 }
219 if !buf.is_empty() {
220 Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
221 } else {
222 Ok(())
223 }
224 }
225}
226
227/// Trait for writing bytes at an offset.
228///
229/// Implementations should be able to write bytes at an offset, without
230/// changing any sort of write position. Self should not change at all.
231///
232/// When writing beyond the end of the underlying object it is extended and
233/// intermediate bytes are filled with the value 0.
234///
235/// # Examples
236///
237/// ```no_run
238/// # use positioned_io_preview as positioned_io;
239/// # use std::error::Error;
240/// #
241/// # fn try_main() -> Result<(), Box<Error>> {
242/// use std::fs::OpenOptions;
243/// use positioned_io::WriteAt;
244///
245/// let mut file = OpenOptions::new().write(true).open("tests/pi.txt")?;
246///
247/// // write some bytes
248/// let bytes_written = file.write_at(2, b"1415926535897932384626433")?;
249/// # Ok(())
250/// # }
251/// #
252/// # fn main() {
253/// # try_main().unwrap();
254/// # }
255/// ```
256pub trait WriteAt {
257 /// Writes bytes from a buffer to an offset, returning the number of bytes
258 /// written.
259 ///
260 /// This function may write fewer bytes than the size of `buf`, for example
261 /// if it is interrupted.
262 ///
263 /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write)
264 /// for details.
265 fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize>;
266
267 /// Writes a complete buffer at an offset.
268 ///
269 /// Errors if it could not write the entire buffer.
270 ///
271 /// See [`Write::write_all()`](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all)
272 /// for details.
273 fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> io::Result<()> {
274 while !buf.is_empty() {
275 match self.write_at(pos, buf) {
276 Ok(0) => return Err(io::Error::new(io::ErrorKind::WriteZero, "failed to write whole buffer")),
277 Ok(n) => {
278 buf = &buf[n..];
279 pos += n as u64;
280 }
281 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
282 Err(e) => return Err(e),
283 }
284 }
285 Ok(())
286 }
287
288 /// Flush this writer, ensuring that any intermediately buffered data
289 /// reaches its destination.
290 ///
291 /// This should rarely do anything, since buffering is not very useful for
292 /// positioned writes.
293 ///
294 /// This should be equivalent to
295 /// [`Write::flush()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush),
296 /// so it does not actually sync changes to disk when writing a `File`.
297 /// Use
298 /// [`File::sync_data()`](https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data)
299 /// instead.
300 fn flush(&mut self) -> io::Result<()>;
301}
302
303/// Trait to get the size in bytes of an I/O object.
304///
305/// Implementing this for a types with `ReadAt` or `WriteAt` makes it easier
306/// for users to predict whether they will read past end-of-file. However, it
307/// may not be possible to implement for certain readers or writers that have
308/// unknown size.
309///
310/// # Examples
311///
312/// ```no_run
313/// # use positioned_io_preview as positioned_io;
314/// # use std::error::Error;
315/// #
316/// # fn try_main() -> Result<(), Box<Error>> {
317/// use std::fs::File;
318/// use positioned_io::Size;
319///
320/// let file = File::open("tests/pi.txt")?;
321/// let size = file.size()?;
322/// assert_eq!(size, Some(1000002));
323///
324/// // some special files do not have a known size
325/// let file = File::open("/dev/stdin")?;
326/// let size = file.size()?;
327/// assert_eq!(size, None);
328/// # Ok(())
329/// # }
330/// #
331/// # fn main() {
332/// # try_main().unwrap();
333/// # }
334/// ```
335pub trait Size {
336 /// Get the size of this object, in bytes.
337 ///
338 /// This function may return `Ok(None)` if the size is unknown, for example
339 /// for pipes.
340 fn size(&self) -> io::Result<Option<u64>>;
341}
342
343impl Size for File {
344 fn size(&self) -> io::Result<Option<u64>> {
345 let md = self.metadata()?;
346 if md.is_file() {
347 Ok(Some(md.len()))
348 } else {
349 Ok(None)
350 }
351 }
352}
353
354// Implementation for Unix files.
355#[cfg(unix)]
356mod unix;
357
358// Implementation for Windows files.
359#[cfg(windows)]
360mod windows;
361
362// RandomAccess file wrapper.
363mod raf;
364pub use raf::RandomAccessFile;
365
366// Implementation for arrays, vectors.
367mod array;
368mod vec;
369mod refs;
370
371#[cfg(test)]
372mod tests {
373 use super::*;
374
375 struct _AssertObjectSafe1(Box<dyn ReadAt>);
376 struct _AssertObjectSafe2(Box<dyn WriteAt>);
377 struct _AssertObjectSafe3(Box<dyn Size>);
378}