positioned_io2/
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<Error>> {
20//! use std::fs::File;
21//! use positioned_io2::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_io2::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_io2::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_io2::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-preview/0.3.3")]
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 cursor::{Cursor, SizeCursor};
130
131mod slice;
132pub use slice::Slice;
133
134#[cfg(feature = "byteorder")]
135mod byteio;
136#[cfg(feature = "byteorder")]
137pub use byteio::{ByteIo, ReadBytesAtExt, WriteBytesAtExt};
138
139use std::fs::File;
140use std::io;
141
142/// Trait for reading bytes at an offset.
143///
144/// Implementations should be able to read bytes without changing any sort of
145/// read position. Self should not change at all. Buffering reads is unlikely
146/// to be useful, since each time `read_at()` is called, the position may be
147/// completely different.
148///
149/// # Examples
150///
151/// Read the fifth 512-byte sector of a file:
152///
153/// ```
154/// # use std::error::Error;
155/// #
156/// # fn try_main() -> Result<(), Box<Error>> {
157/// use std::fs::File;
158/// use positioned_io2::ReadAt;
159///
160/// // note that file does not need to be mut
161/// let file = File::open("tests/pi.txt")?;
162///
163/// // read up to 512 bytes
164/// let mut buf = [0; 512];
165/// let bytes_read = file.read_at(2048, &mut buf)?;
166/// #     assert!(buf.starts_with(b"4"));
167/// #     Ok(())
168/// # }
169/// #
170/// # fn main() {
171/// #     try_main().unwrap();
172/// # }
173/// ```
174pub trait ReadAt {
175    /// Reads bytes from an offset in this source into a buffer, returning how
176    /// many bytes were read.
177    ///
178    /// This function may yield fewer bytes than the size of `buf`, if it was
179    /// interrupted or hit the "end of file".
180    ///
181    /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read)
182    /// for details.
183    fn read_at(&self, pos: u64, buf: &mut [u8]) -> io::Result<usize>;
184
185    /// Reads the exact number of bytes required to fill `buf` from an offset.
186    ///
187    /// Errors if the "end of file" is encountered before filling the buffer.
188    ///
189    /// See [`Read::read_exact()`](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact)
190    /// for details.
191    fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> io::Result<()> {
192        while !buf.is_empty() {
193            match self.read_at(pos, buf) {
194                Ok(0) => break,
195                Ok(n) => {
196                    let tmp = buf;
197                    buf = &mut tmp[n..];
198                    pos += n as u64;
199                }
200                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
201                Err(e) => return Err(e),
202            }
203        }
204        if !buf.is_empty() {
205            Err(io::Error::new(
206                io::ErrorKind::UnexpectedEof,
207                "failed to fill whole buffer",
208            ))
209        } else {
210            Ok(())
211        }
212    }
213}
214
215/// Trait for writing bytes at an offset.
216///
217/// Implementations should be able to write bytes at an offset, without
218/// changing any sort of write position. Self should not change at all.
219///
220/// When writing beyond the end of the underlying object it is extended and
221/// intermediate bytes are filled with the value 0.
222///
223/// # Examples
224///
225/// ```no_run
226/// # use std::error::Error;
227/// #
228/// # fn try_main() -> Result<(), Box<Error>> {
229/// use std::fs::OpenOptions;
230/// use positioned_io2::WriteAt;
231///
232/// let mut file = OpenOptions::new().write(true).open("tests/pi.txt")?;
233///
234/// // write some bytes
235/// let bytes_written = file.write_at(2, b"1415926535897932384626433")?;
236/// #     Ok(())
237/// # }
238/// #
239/// # fn main() {
240/// #     try_main().unwrap();
241/// # }
242/// ```
243pub trait WriteAt {
244    /// Writes bytes from a buffer to an offset, returning the number of bytes
245    /// written.
246    ///
247    /// This function may write fewer bytes than the size of `buf`, for example
248    /// if it is interrupted.
249    ///
250    /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write)
251    /// for details.
252    fn write_at(&mut self, pos: u64, buf: &[u8]) -> io::Result<usize>;
253
254    /// Writes a complete buffer at an offset.
255    ///
256    /// Errors if it could not write the entire buffer.
257    ///
258    /// See [`Write::write_all()`](https://doc.rust-lang.org/std/io/trait.Write.html#method.write_all)
259    /// for details.
260    fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> io::Result<()> {
261        while !buf.is_empty() {
262            match self.write_at(pos, buf) {
263                Ok(0) => {
264                    return Err(io::Error::new(
265                        io::ErrorKind::WriteZero,
266                        "failed to write whole buffer",
267                    ))
268                }
269                Ok(n) => {
270                    buf = &buf[n..];
271                    pos += n as u64;
272                }
273                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
274                Err(e) => return Err(e),
275            }
276        }
277        Ok(())
278    }
279
280    /// Flush this writer, ensuring that any intermediately buffered data
281    /// reaches its destination.
282    ///
283    /// This should rarely do anything, since buffering is not very useful for
284    /// positioned writes.
285    ///
286    /// This should be equivalent to
287    /// [`Write::flush()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush),
288    /// so it does not actually sync changes to disk when writing a `File`.
289    /// Use
290    /// [`File::sync_data()`](https://doc.rust-lang.org/std/fs/struct.File.html#method.sync_data)
291    /// instead.
292    fn flush(&mut self) -> io::Result<()>;
293}
294
295/// Trait to get the size in bytes of an I/O object.
296///
297/// Implementing this for a types with `ReadAt` or `WriteAt` makes it easier
298/// for users to predict whether they will read past end-of-file. However, it
299/// may not be possible to implement for certain readers or writers that have
300/// unknown size.
301///
302/// # Examples
303///
304/// ```no_run
305/// # use std::error::Error;
306/// #
307/// # fn try_main() -> Result<(), Box<Error>> {
308/// use std::fs::File;
309/// use positioned_io2::Size;
310///
311/// let file = File::open("tests/pi.txt")?;
312/// let size = file.size()?;
313/// assert_eq!(size, Some(1000002));
314///
315/// // some special files do not have a known size
316/// let file = File::open("/dev/stdin")?;
317/// let size = file.size()?;
318/// assert_eq!(size, None);
319/// #     Ok(())
320/// # }
321/// #
322/// # fn main() {
323/// #    try_main().unwrap();
324/// # }
325/// ```
326pub trait Size {
327    /// Get the size of this object, in bytes.
328    ///
329    /// This function may return `Ok(None)` if the size is unknown, for example
330    /// for pipes.
331    fn size(&self) -> io::Result<Option<u64>>;
332}
333
334impl Size for File {
335    fn size(&self) -> io::Result<Option<u64>> {
336        let md = self.metadata()?;
337        if md.is_file() {
338            Ok(Some(md.len()))
339        } else {
340            Ok(None)
341        }
342    }
343}
344
345// Implementation for Unix files.
346#[cfg(unix)]
347mod unix;
348
349// Implementation for Windows files.
350#[cfg(windows)]
351mod windows;
352
353// RandomAccess file wrapper.
354mod raf;
355pub use raf::RandomAccessFile;
356
357// Implementation for arrays, vectors.
358mod array;
359mod refs;
360mod vec;
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365
366    struct _AssertObjectSafe1(Box<dyn ReadAt>);
367    struct _AssertObjectSafe2(Box<dyn WriteAt>);
368    struct _AssertObjectSafe3(Box<dyn Size>);
369}