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}