io_arrays/
windows.rs

1//! Windows implementations of [`ReadAt`] and [`WriteAt`] functions for
2//! file-like types which implement [`AsFilelike`] on Windows.
3//!
4//! These can use `seek_read`/`seek_write` because the file's current position
5//! is not exposed.
6//!
7//! [`ReadAt`]: crate::ReadAt
8//! [`WriteAt`]: crate::WriteAt
9
10use crate::Metadata;
11use io_lifetimes::AsFilelike;
12use std::fs::File;
13use std::io::{self, IoSlice, IoSliceMut};
14use std::os::windows::fs::FileExt;
15use std::slice;
16#[cfg(feature = "io-streams")]
17use {
18    crate::owned_streamer::OwnedStreamer,
19    cap_fs_ext::{OpenOptions, Reopen},
20    io_streams::StreamReader,
21    std::io::SeekFrom,
22    system_interface::fs::FileIoExt,
23};
24
25/// Implement [`crate::Array::metadata`].
26#[inline]
27pub fn metadata<'a, Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<Metadata> {
28    filelike.as_filelike_view::<File>().metadata().map(|meta| {
29        Metadata {
30            len: meta.len(),
31
32            // Windows doesn't have a convenient way to query this, but
33            // it often uses this specific value.
34            blksize: 0x1000,
35        }
36    })
37}
38
39/// Implement [`crate::ReadAt::read_at`].
40#[inline]
41pub fn read_at<'a, Filelike: AsFilelike>(
42    filelike: &Filelike,
43    buf: &mut [u8],
44    offset: u64,
45) -> io::Result<usize> {
46    filelike.as_filelike_view::<File>().seek_read(buf, offset)
47}
48
49/// Implement [`crate::ReadAt::read_exact_at`].
50pub fn read_exact_at<Filelike: AsFilelike>(
51    filelike: &Filelike,
52    mut buf: &mut [u8],
53    mut offset: u64,
54) -> io::Result<()> {
55    loop {
56        match read_at(filelike, buf, offset) {
57            Ok(0) if !buf.is_empty() => {
58                return Err(io::Error::new(
59                    io::ErrorKind::UnexpectedEof,
60                    "failed to fill whole buffer",
61                ))
62            }
63            Ok(nread) => {
64                offset = offset
65                    .checked_add(nread.try_into().unwrap())
66                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
67                buf = &mut buf[nread..];
68                if buf.is_empty() {
69                    return Ok(());
70                }
71            }
72            Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
73            Err(err) => return Err(err),
74        }
75    }
76}
77
78/// Implement [`crate::ReadAt::read_vectored_at`].
79pub fn read_vectored_at<'a, Filelike: AsFilelike>(
80    filelike: &Filelike,
81    bufs: &mut [IoSliceMut],
82    offset: u64,
83) -> io::Result<usize> {
84    let buf = bufs
85        .iter_mut()
86        .find(|b| !b.is_empty())
87        .map_or(&mut [][..], |b| &mut **b);
88    read_at(filelike, buf, offset)
89}
90
91/// Implement [`crate::ReadAt::read_exact_vectored_at`].
92pub fn read_exact_vectored_at<'a, Filelike: AsFilelike>(
93    filelike: &Filelike,
94    mut bufs: &mut [IoSliceMut],
95    mut offset: u64,
96) -> io::Result<()> {
97    while !bufs.is_empty() {
98        match read_vectored_at(filelike, bufs, offset) {
99            Ok(nread) => {
100                offset = offset
101                    .checked_add(nread.try_into().unwrap())
102                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
103                bufs = advance_mut(bufs, nread);
104            }
105            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
106            Err(e) => return Err(e),
107        }
108    }
109    Ok(())
110}
111
112/// Implement [`crate::ReadAt::is_read_vectored_at`].
113#[inline]
114pub fn is_read_vectored_at<'a, Filelike: AsFilelike>(_filelike: &Filelike) -> bool {
115    false
116}
117
118/// Implement [`crate::ReadAt::read_via_stream_at`].
119#[cfg(feature = "io-streams")]
120pub fn read_via_stream_at<'a, Filelike: AsFilelike>(
121    filelike: &Filelike,
122    offset: u64,
123) -> io::Result<StreamReader> {
124    // On operating systems where we can do so, reopen the file so that we
125    // get an independent current position.
126    if let Ok(file) = filelike
127        .as_filelike_view::<File>()
128        .reopen(OpenOptions::new().read(true))
129    {
130        if offset != 0 {
131            file.seek(SeekFrom::Start(offset))?;
132        }
133        return Ok(StreamReader::file(file));
134    }
135
136    // Otherwise, manually stream the file.
137    StreamReader::piped_thread(Box::new(OwnedStreamer::new(
138        filelike.as_filelike_view::<File>().try_clone()?,
139        offset,
140    )))
141}
142
143/// Implement [`crate::WriteAt::write_at`].
144#[inline]
145pub fn write_at<'a, Filelike: AsFilelike>(
146    filelike: &Filelike,
147    buf: &[u8],
148    offset: u64,
149) -> io::Result<usize> {
150    filelike.as_filelike_view::<File>().seek_write(buf, offset)
151}
152
153/// Implement [`crate::WriteAt::write_all_at`].
154pub fn write_all_at<'a, Filelike: AsFilelike>(
155    filelike: &Filelike,
156    mut buf: &[u8],
157    mut offset: u64,
158) -> io::Result<()> {
159    loop {
160        match write_at(filelike, buf, offset) {
161            Ok(nwritten) => {
162                offset = offset
163                    .checked_add(nwritten.try_into().unwrap())
164                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
165                buf = &buf[nwritten..];
166                if buf.is_empty() {
167                    return Ok(());
168                }
169            }
170            Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
171            Err(err) => return Err(err),
172        }
173    }
174}
175
176/// Implement [`crate::WriteAt::write_vectored_at`].
177pub fn write_vectored_at<'a, Filelike: AsFilelike>(
178    filelike: &Filelike,
179    bufs: &[IoSlice],
180    offset: u64,
181) -> io::Result<usize> {
182    let buf = bufs
183        .iter()
184        .find(|b| !b.is_empty())
185        .map_or(&[][..], |b| &**b);
186    write_at(filelike, buf, offset)
187}
188
189/// Implement [`crate::WriteAt::write_all_vectored_at`].
190pub fn write_all_vectored_at<'a, Filelike: AsFilelike>(
191    filelike: &Filelike,
192    mut bufs: &mut [IoSlice],
193    mut offset: u64,
194) -> io::Result<()> {
195    while !bufs.is_empty() {
196        match write_vectored_at(filelike, bufs, offset) {
197            Ok(nwritten) => {
198                offset = offset
199                    .checked_add(nwritten.try_into().unwrap())
200                    .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
201                bufs = advance(bufs, nwritten);
202            }
203            Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
204            Err(e) => return Err(e),
205        }
206    }
207    Ok(())
208}
209
210/// Implement [`crate::WriteAt::is_write_vectored_at`].
211#[inline]
212pub fn is_write_vectored_at<'a, Filelike: AsFilelike>(_filelike: &Filelike) -> bool {
213    false
214}
215
216/// This will be obviated by [rust-lang/rust#62726].
217///
218/// [rust-lang/rust#62726]: https://github.com/rust-lang/rust/issues/62726.
219fn advance<'a, 'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] {
220    // Number of buffers to remove.
221    let mut remove = 0;
222    // Total length of all the to be removed buffers.
223    let mut accumulated_len = 0;
224    for buf in bufs.iter() {
225        if accumulated_len + buf.len() > n {
226            break;
227        } else {
228            accumulated_len += buf.len();
229            remove += 1;
230        }
231    }
232
233    #[allow(clippy::indexing_slicing)]
234    let bufs = &mut bufs[remove..];
235    if let Some(first) = bufs.first_mut() {
236        let advance_by = n - accumulated_len;
237        let mut ptr = first.as_ptr();
238        let mut len = first.len();
239        unsafe {
240            ptr = ptr.add(advance_by);
241            len -= advance_by;
242            *first = IoSlice::<'a>::new(slice::from_raw_parts::<'a>(ptr, len));
243        }
244    }
245    bufs
246}
247
248/// This will be obviated by [rust-lang/rust#62726].
249///
250/// [rust-lang/rust#62726]: https://github.com/rust-lang/rust/issues/62726.
251fn advance_mut<'a, 'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] {
252    // Number of buffers to remove.
253    let mut remove = 0;
254    // Total length of all the to be removed buffers.
255    let mut accumulated_len = 0;
256    for buf in bufs.iter() {
257        if accumulated_len + buf.len() > n {
258            break;
259        } else {
260            accumulated_len += buf.len();
261            remove += 1;
262        }
263    }
264
265    #[allow(clippy::indexing_slicing)]
266    let bufs = &mut bufs[remove..];
267    if let Some(first) = bufs.first_mut() {
268        let advance_by = n - accumulated_len;
269        let mut ptr = first.as_mut_ptr();
270        let mut len = first.len();
271        unsafe {
272            ptr = ptr.add(advance_by);
273            len -= advance_by;
274            *first = IoSliceMut::<'a>::new(slice::from_raw_parts_mut::<'a>(ptr, len));
275        }
276    }
277    bufs
278}