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