1use crate::io::IoExt;
4use io_lifetimes::AsFilelike;
5#[cfg(not(any(
6 windows,
7 target_os = "ios",
8 target_os = "macos",
9 target_os = "netbsd",
10 target_os = "openbsd",
11 target_os = "redox",
12)))]
13use rustix::fs::fadvise;
14#[cfg(any(target_os = "ios", target_os = "macos"))]
15use rustix::fs::fcntl_rdadvise;
16#[cfg(not(any(
17 windows,
18 target_os = "netbsd",
19 target_os = "redox",
20 target_os = "openbsd"
21)))]
22use rustix::fs::{fallocate, FallocateFlags};
23#[cfg(not(any(windows, target_os = "ios", target_os = "macos", target_os = "redox")))]
24use rustix::io::{preadv, pwritev};
25use std::io::{self, IoSlice, IoSliceMut, Seek, SeekFrom};
26use std::slice;
27#[cfg(windows)]
28use {cap_fs_ext::Reopen, std::fs, std::os::windows::fs::FileExt};
29#[cfg(not(windows))]
30use {rustix::fs::tell, rustix::fs::FileExt};
31
32#[cfg(not(any(
34 windows,
35 target_os = "ios",
36 target_os = "macos",
37 target_os = "netbsd",
38 target_os = "openbsd",
39 target_os = "redox"
40)))]
41#[derive(Debug, Eq, PartialEq, Hash)]
42#[repr(i32)]
43pub enum Advice {
44 Normal = rustix::fs::Advice::Normal as i32,
46 Sequential = rustix::fs::Advice::Sequential as i32,
48 Random = rustix::fs::Advice::Random as i32,
50 WillNeed = rustix::fs::Advice::WillNeed as i32,
52 DontNeed = rustix::fs::Advice::DontNeed as i32,
54 NoReuse = rustix::fs::Advice::NoReuse as i32,
56}
57
58#[cfg(any(
60 windows,
61 target_os = "ios",
62 target_os = "macos",
63 target_os = "netbsd",
64 target_os = "openbsd",
65 target_os = "redox"
66))]
67#[derive(Debug, Eq, PartialEq, Hash)]
68pub enum Advice {
69 Normal,
71 Sequential,
73 Random,
75 WillNeed,
77 DontNeed,
79 NoReuse,
81}
82
83pub trait FileIoExt: IoExt {
85 fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()>;
87
88 fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
91
92 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
100
101 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()>;
111
112 fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
114 let buf = bufs
116 .iter_mut()
117 .find(|b| !b.is_empty())
118 .map_or(&mut [][..], |b| &mut **b);
119 self.read_at(buf, offset)
120 }
121
122 fn read_exact_vectored_at(
124 &self,
125 mut bufs: &mut [IoSliceMut],
126 mut offset: u64,
127 ) -> io::Result<()> {
128 bufs = skip_leading_empties(bufs);
129 while !bufs.is_empty() {
130 match self.read_vectored_at(bufs, offset) {
131 Ok(0) => {
132 return Err(io::Error::new(
133 io::ErrorKind::UnexpectedEof,
134 "failed to fill whole buffer",
135 ))
136 }
137 Ok(nread) => {
138 offset = offset
139 .checked_add(nread.try_into().unwrap())
140 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
141 bufs = advance_mut(bufs, nread);
142 }
143 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
144 Err(e) => return Err(e),
145 }
146 bufs = skip_leading_empties(bufs);
147 }
148 Ok(())
149 }
150
151 #[inline]
154 fn is_read_vectored_at(&self) -> bool {
155 false
156 }
157
158 fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize>;
161
162 fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize>;
165
166 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
181
182 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
197 while !buf.is_empty() {
198 match self.write_at(buf, offset) {
199 Ok(nwritten) => {
200 buf = &buf[nwritten..];
201 offset += nwritten as u64;
202 }
203 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
204 Err(e) => return Err(e),
205 }
206 }
207 Ok(())
208 }
209
210 fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
212 let buf = bufs
214 .iter()
215 .find(|b| !b.is_empty())
216 .map_or(&[][..], |b| &**b);
217 self.write_at(buf, offset)
218 }
219
220 fn write_all_vectored_at(&self, mut bufs: &mut [IoSlice], mut offset: u64) -> io::Result<()> {
222 while !bufs.is_empty() {
223 match self.write_vectored_at(bufs, offset) {
224 Ok(nwritten) => {
225 offset = offset
226 .checked_add(nwritten.try_into().unwrap())
227 .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
228 bufs = advance(bufs, nwritten);
229 }
230 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
231 Err(e) => return Err(e),
232 }
233 }
234 Ok(())
235 }
236
237 #[inline]
240 fn is_write_vectored_at(&self) -> bool {
241 false
242 }
243
244 fn append(&self, buf: &[u8]) -> io::Result<usize>;
254
255 fn append_all(&self, mut buf: &[u8]) -> io::Result<()> {
265 while !buf.is_empty() {
266 match self.append(buf) {
267 Ok(nwritten) => {
268 buf = &buf[nwritten..];
269 }
270 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
271 Err(e) => return Err(e),
272 }
273 }
274 Ok(())
275 }
276
277 fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
279 let buf = bufs
281 .iter()
282 .find(|b| !b.is_empty())
283 .map_or(&[][..], |b| &**b);
284 self.append(buf)
285 }
286
287 fn append_all_vectored(&self, mut bufs: &mut [IoSlice]) -> io::Result<()> {
289 while !bufs.is_empty() {
290 match self.append_vectored(bufs) {
291 Ok(nwritten) => {
292 bufs = advance(bufs, nwritten);
293 }
294 Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
295 Err(e) => return Err(e),
296 }
297 }
298 Ok(())
299 }
300
301 #[inline]
304 fn is_append_vectored(&self) -> bool {
305 false
306 }
307
308 fn seek(&self, pos: SeekFrom) -> io::Result<u64>;
315
316 fn stream_position(&self) -> io::Result<u64>;
326}
327
328fn skip_leading_empties<'a, 'b>(mut bufs: &'b mut [IoSliceMut<'a>]) -> &'b mut [IoSliceMut<'a>] {
330 while !bufs.is_empty() {
331 if !bufs[0].is_empty() {
332 break;
333 }
334 bufs = &mut bufs[1..];
335 }
336 bufs
337}
338
339fn advance<'a, 'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] {
343 let mut remove = 0;
345 let mut accumulated_len = 0;
347 for buf in bufs.iter() {
348 if accumulated_len + buf.len() > n {
349 break;
350 } else {
351 accumulated_len += buf.len();
352 remove += 1;
353 }
354 }
355
356 #[allow(clippy::indexing_slicing)]
357 let bufs = &mut bufs[remove..];
358 if let Some(first) = bufs.first_mut() {
359 let advance_by = n - accumulated_len;
360 let mut ptr = first.as_ptr();
361 let mut len = first.len();
362 unsafe {
363 ptr = ptr.add(advance_by);
364 len -= advance_by;
365 *first = IoSlice::<'a>::new(slice::from_raw_parts::<'a>(ptr, len));
366 }
367 }
368 bufs
369}
370
371fn advance_mut<'a, 'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] {
375 let mut remove = 0;
377 let mut accumulated_len = 0;
379 for buf in bufs.iter() {
380 if accumulated_len + buf.len() > n {
381 break;
382 } else {
383 accumulated_len += buf.len();
384 remove += 1;
385 }
386 }
387
388 #[allow(clippy::indexing_slicing)]
389 let bufs = &mut bufs[remove..];
390 if let Some(first) = bufs.first_mut() {
391 let advance_by = n - accumulated_len;
392 let mut ptr = first.as_mut_ptr();
393 let mut len = first.len();
394 unsafe {
395 ptr = ptr.add(advance_by);
396 len -= advance_by;
397 *first = IoSliceMut::<'a>::new(slice::from_raw_parts_mut::<'a>(ptr, len));
398 }
399 }
400 bufs
401}
402
403#[cfg(not(windows))]
405impl<T: AsFilelike + IoExt> FileIoExt for T {
406 #[cfg(not(any(
407 target_os = "ios",
408 target_os = "macos",
409 target_os = "netbsd",
410 target_os = "openbsd",
411 target_os = "redox"
412 )))]
413 #[inline]
414 fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
415 let advice = match advice {
416 Advice::WillNeed => rustix::fs::Advice::WillNeed,
417 Advice::Normal => rustix::fs::Advice::Normal,
418 Advice::Sequential => rustix::fs::Advice::Sequential,
419 Advice::NoReuse => rustix::fs::Advice::NoReuse,
420 Advice::Random => rustix::fs::Advice::Random,
421 Advice::DontNeed => rustix::fs::Advice::DontNeed,
422 };
423 Ok(fadvise(self, offset, len, advice)?)
424 }
425
426 #[cfg(any(target_os = "ios", target_os = "macos"))]
427 #[inline]
428 fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
429 match advice {
433 Advice::WillNeed => (),
434 Advice::Normal
435 | Advice::Sequential
436 | Advice::NoReuse
437 | Advice::Random
438 | Advice::DontNeed => return Ok(()),
439 }
440
441 Ok(fcntl_rdadvise(self, offset, len)?)
442 }
443
444 #[cfg(any(target_os = "netbsd", target_os = "redox", target_os = "openbsd"))]
445 #[inline]
446 fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> io::Result<()> {
447 Ok(())
450 }
451
452 #[cfg(not(any(target_os = "netbsd", target_os = "redox", target_os = "openbsd")))]
453 #[inline]
454 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
455 Ok(fallocate(self, FallocateFlags::empty(), offset, len)?)
456 }
457
458 #[cfg(target_os = "netbsd")]
459 fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
460 todo!("NetBSD 7.0 supports posix_fallocate; add bindings for it")
461 }
462
463 #[cfg(target_os = "openbsd")]
464 fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
465 todo!("OpenBSD does not support posix_fallocate; figure out what to do")
466 }
467
468 #[cfg(target_os = "redox")]
469 fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
470 todo!("figure out what to do on redox for posix_fallocate")
471 }
472
473 #[inline]
474 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
475 FileExt::read_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
476 }
477
478 #[inline]
479 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
480 FileExt::read_exact_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
481 }
482
483 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
484 #[inline]
485 fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
486 Ok(preadv(self, bufs, offset)?)
487 }
488
489 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
490 #[inline]
491 fn is_read_vectored_at(&self) -> bool {
492 true
493 }
494
495 #[inline]
496 fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
497 read_to_end_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
498 }
499
500 #[inline]
501 fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
502 read_to_string_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
503 }
504
505 #[inline]
506 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
507 FileExt::write_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
508 }
509
510 #[inline]
511 fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
512 FileExt::write_all_at(&*self.as_filelike_view::<std::fs::File>(), buf, offset)
513 }
514
515 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
516 #[inline]
517 fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
518 Ok(pwritev(self, bufs, offset)?)
519 }
520
521 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
522 #[inline]
523 fn is_write_vectored_at(&self) -> bool {
524 true
525 }
526
527 fn append(&self, buf: &[u8]) -> io::Result<usize> {
528 use rustix::fs::{fcntl_getfl, fcntl_setfl, seek, OFlags, SeekFrom};
529 use rustix::io::write;
530
531 #[cfg(any(target_os = "android", target_os = "linux"))]
533 {
534 use rustix::io::{pwritev2, Errno, ReadWriteFlags};
535
536 let iovs = [IoSlice::new(buf)];
537 match pwritev2(self, &iovs, 0, ReadWriteFlags::APPEND) {
538 Err(Errno::NOSYS) | Err(Errno::NOTSUP) => {}
539 otherwise => return Ok(otherwise?),
540 }
541 }
542
543 let old_flags = fcntl_getfl(self)?;
552 let old_pos = tell(self)?;
553 fcntl_setfl(self, old_flags | OFlags::APPEND)?;
554 let result = write(self, buf);
555 fcntl_setfl(self, old_flags).unwrap();
556 seek(self, SeekFrom::Start(old_pos)).unwrap();
557 Ok(result?)
558 }
559
560 fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
561 use rustix::fs::{fcntl_getfl, fcntl_setfl, seek, OFlags, SeekFrom};
562 use rustix::io::writev;
563
564 #[cfg(any(target_os = "android", target_os = "linux"))]
566 {
567 use rustix::io::{pwritev2, Errno, ReadWriteFlags};
568
569 match pwritev2(self, bufs, 0, ReadWriteFlags::APPEND) {
570 Err(Errno::NOSYS) | Err(Errno::NOTSUP) => {}
571 otherwise => return Ok(otherwise?),
572 }
573 }
574
575 let old_flags = fcntl_getfl(self)?;
580 let old_pos = tell(self)?;
581 fcntl_setfl(self, old_flags | OFlags::APPEND)?;
582 let result = writev(self, bufs);
583 fcntl_setfl(self, old_flags).unwrap();
584 seek(self, SeekFrom::Start(old_pos)).unwrap();
585 Ok(result?)
586 }
587
588 #[inline]
589 fn is_append_vectored(&self) -> bool {
590 true
591 }
592
593 #[inline]
594 fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
595 Seek::seek(&mut &*self.as_filelike_view::<std::fs::File>(), pos)
596 }
597
598 #[inline]
599 fn stream_position(&self) -> io::Result<u64> {
600 Ok(tell(self)?)
603 }
604}
605
606#[cfg(windows)]
607impl FileIoExt for std::fs::File {
608 #[inline]
609 fn advise(&self, _offset: u64, _len: u64, _advice: Advice) -> io::Result<()> {
610 Ok(())
612 }
613
614 #[inline]
615 fn allocate(&self, _offset: u64, _len: u64) -> io::Result<()> {
616 Err(io::Error::new(
619 io::ErrorKind::PermissionDenied,
620 "file allocate is not supported on Windows",
621 ))
622 }
623
624 #[inline]
625 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
626 let reopened = reopen(self)?;
633 reopened.seek_read(buf, offset)
634 }
635
636 #[inline]
637 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
638 let reopened = loop {
641 match reopen(self) {
642 Ok(file) => break file,
643 Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
644 Err(err) => return Err(err),
645 }
646 };
647 loop {
648 match reopened.seek(SeekFrom::Start(offset)) {
649 Ok(_) => break,
650 Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
651 Err(err) => return Err(err),
652 }
653 }
654 reopened.read_exact(buf)
655 }
656
657 #[inline]
658 fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
659 let reopened = reopen(self)?;
662 reopened.seek(SeekFrom::Start(offset))?;
663 reopened.read_vectored(bufs)
664 }
665
666 #[inline]
667 fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
668 let reopened = loop {
671 match reopen(self) {
672 Ok(file) => break file,
673 Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
674 Err(err) => return Err(err),
675 }
676 };
677 loop {
678 match reopened.seek(SeekFrom::Start(offset)) {
679 Ok(_) => break,
680 Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
681 Err(err) => return Err(err),
682 }
683 }
684 reopened.read_exact_vectored(bufs)
685 }
686
687 #[inline]
688 fn is_read_vectored_at(&self) -> bool {
689 true
690 }
691
692 #[inline]
693 fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
694 read_to_end_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
695 }
696
697 #[inline]
698 fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
699 read_to_string_at(&self.as_filelike_view::<std::fs::File>(), buf, offset)
700 }
701
702 #[inline]
703 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
704 reopen_write(self)?.seek_write(buf, offset)
707 }
708
709 #[inline]
710 fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
711 let reopened = reopen_write(self)?;
714 while buf.len() > 0 {
715 let n = reopened.seek_write(buf, offset)?;
716 offset += u64::try_from(n).unwrap();
717 buf = &buf[n..];
718 }
719 Ok(())
720 }
721
722 #[inline]
723 fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
724 match bufs.iter().find(|p| p.len() > 0) {
727 Some(buf) => self.write_at(buf, offset),
728 None => Ok(0),
729 }
730 }
731
732 #[inline]
733 fn write_all_vectored_at(&self, bufs: &mut [IoSlice], mut offset: u64) -> io::Result<()> {
734 for buf in bufs {
735 self.write_all_at(buf, offset)?;
736 offset += u64::try_from(buf.len()).unwrap();
737 }
738 Ok(())
739 }
740
741 #[inline]
742 fn is_write_vectored_at(&self) -> bool {
743 true
744 }
745
746 fn append(&self, buf: &[u8]) -> io::Result<usize> {
747 let reopened = reopen_append(self)?;
749 reopened.write(buf)
750 }
751
752 fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
753 let reopened = reopen_append(self)?;
755 reopened.write_vectored(bufs)
756 }
757
758 #[inline]
759 fn is_append_vectored(&self) -> bool {
760 true
761 }
762
763 #[inline]
764 fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
765 Seek::seek(&mut &*self.as_filelike_view::<std::fs::File>(), pos)
766 }
767
768 #[inline]
769 fn stream_position(&self) -> io::Result<u64> {
770 Seek::seek(
773 &mut &*self.as_filelike_view::<std::fs::File>(),
774 SeekFrom::Current(0),
775 )
776 }
777}
778
779#[cfg(windows)]
780#[cfg(feature = "cap_std_impls")]
781impl FileIoExt for cap_std::fs::File {
782 #[inline]
783 fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
784 self.as_filelike_view::<std::fs::File>()
785 .advise(offset, len, advice)
786 }
787
788 #[inline]
789 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
790 self.as_filelike_view::<std::fs::File>()
791 .allocate(offset, len)
792 }
793
794 #[inline]
795 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
796 self.as_filelike_view::<std::fs::File>()
797 .read_at(buf, offset)
798 }
799
800 #[inline]
801 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
802 self.as_filelike_view::<std::fs::File>()
803 .read_exact_at(buf, offset)
804 }
805
806 #[inline]
807 fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
808 self.as_filelike_view::<std::fs::File>()
809 .read_vectored_at(bufs, offset)
810 }
811
812 #[inline]
813 fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
814 self.as_filelike_view::<std::fs::File>()
815 .read_exact_vectored_at(bufs, offset)
816 }
817
818 #[inline]
819 fn is_read_vectored_at(&self) -> bool {
820 self.as_filelike_view::<std::fs::File>()
821 .is_read_vectored_at()
822 }
823
824 #[inline]
825 fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
826 self.as_filelike_view::<std::fs::File>()
827 .read_to_end_at(buf, offset)
828 }
829
830 #[inline]
831 fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
832 self.as_filelike_view::<std::fs::File>()
833 .read_to_string_at(buf, offset)
834 }
835
836 #[inline]
837 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
838 self.as_filelike_view::<std::fs::File>()
839 .write_at(buf, offset)
840 }
841
842 #[inline]
843 fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
844 self.as_filelike_view::<std::fs::File>()
845 .write_all_at(buf, offset)
846 }
847
848 #[inline]
849 fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
850 self.as_filelike_view::<std::fs::File>()
851 .write_vectored_at(bufs, offset)
852 }
853
854 #[inline]
855 fn write_all_vectored_at(&self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
856 self.as_filelike_view::<std::fs::File>()
857 .write_all_vectored_at(bufs, offset)
858 }
859
860 #[inline]
861 fn is_write_vectored_at(&self) -> bool {
862 self.as_filelike_view::<std::fs::File>()
863 .is_write_vectored_at()
864 }
865
866 fn append(&self, buf: &[u8]) -> io::Result<usize> {
867 self.as_filelike_view::<std::fs::File>().append(buf)
868 }
869
870 fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
871 self.as_filelike_view::<std::fs::File>()
872 .append_vectored(bufs)
873 }
874
875 #[inline]
876 fn is_append_vectored(&self) -> bool {
877 true
878 }
879
880 #[inline]
881 fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
882 self.as_filelike_view::<std::fs::File>().seek(pos)
883 }
884
885 #[inline]
886 fn stream_position(&self) -> io::Result<u64> {
887 self.as_filelike_view::<std::fs::File>().stream_position()
888 }
889}
890
891#[cfg(windows)]
892#[cfg(feature = "cap_std_impls_fs_utf8")]
893impl FileIoExt for cap_std::fs_utf8::File {
894 #[inline]
895 fn advise(&self, offset: u64, len: u64, advice: Advice) -> io::Result<()> {
896 self.as_filelike_view::<std::fs::File>()
897 .advise(offset, len, advice)
898 }
899
900 #[inline]
901 fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
902 self.as_filelike_view::<std::fs::File>()
903 .allocate(offset, len)
904 }
905
906 #[inline]
907 fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
908 self.as_filelike_view::<std::fs::File>()
909 .read_at(buf, offset)
910 }
911
912 #[inline]
913 fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> {
914 self.as_filelike_view::<std::fs::File>()
915 .read_exact_at(buf, offset)
916 }
917
918 #[inline]
919 fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<usize> {
920 self.as_filelike_view::<std::fs::File>()
921 .read_vectored_at(bufs, offset)
922 }
923
924 #[inline]
925 fn read_exact_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result<()> {
926 self.as_filelike_view::<std::fs::File>()
927 .read_exact_vectored_at(bufs, offset)
928 }
929
930 #[inline]
931 fn is_read_vectored_at(&self) -> bool {
932 self.as_filelike_view::<std::fs::File>()
933 .is_read_vectored_at()
934 }
935
936 #[inline]
937 fn read_to_end_at(&self, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
938 self.as_filelike_view::<std::fs::File>()
939 .read_to_end_at(buf, offset)
940 }
941
942 #[inline]
943 fn read_to_string_at(&self, buf: &mut String, offset: u64) -> io::Result<usize> {
944 self.as_filelike_view::<std::fs::File>()
945 .read_to_string_at(buf, offset)
946 }
947
948 #[inline]
949 fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
950 self.as_filelike_view::<std::fs::File>()
951 .write_at(buf, offset)
952 }
953
954 #[inline]
955 fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> {
956 self.as_filelike_view::<std::fs::File>()
957 .write_all_at(buf, offset)
958 }
959
960 #[inline]
961 fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result<usize> {
962 self.as_filelike_view::<std::fs::File>()
963 .write_vectored_at(bufs, offset)
964 }
965
966 #[inline]
967 fn write_all_vectored_at(&self, bufs: &mut [IoSlice], offset: u64) -> io::Result<()> {
968 self.as_filelike_view::<std::fs::File>()
969 .write_all_vectored_at(bufs, offset)
970 }
971
972 #[inline]
973 fn is_write_vectored_at(&self) -> bool {
974 self.as_filelike_view::<std::fs::File>()
975 .is_write_vectored_at()
976 }
977
978 fn append(&self, buf: &[u8]) -> io::Result<usize> {
979 self.as_filelike_view::<std::fs::File>().append(buf)
980 }
981
982 fn append_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
983 self.as_filelike_view::<std::fs::File>()
984 .append_vectored(bufs)
985 }
986
987 #[inline]
988 fn is_append_vectored(&self) -> bool {
989 true
990 }
991
992 #[inline]
993 fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
994 self.as_filelike_view::<std::fs::File>().seek(pos)
995 }
996
997 #[inline]
998 fn stream_position(&self) -> io::Result<u64> {
999 self.as_filelike_view::<std::fs::File>().stream_position()
1000 }
1001}
1002
1003#[cfg(windows)]
1004#[inline]
1005fn reopen<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
1006 let file = filelike.as_filelike_view::<std::fs::File>();
1007 unsafe { _reopen(&file) }
1008}
1009
1010#[cfg(windows)]
1011unsafe fn _reopen(file: &fs::File) -> io::Result<fs::File> {
1012 file.reopen(cap_fs_ext::OpenOptions::new().read(true))
1013}
1014
1015#[cfg(windows)]
1016#[inline]
1017fn reopen_write<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
1018 let file = filelike.as_filelike_view::<std::fs::File>();
1019 unsafe { _reopen_write(&file) }
1020}
1021
1022#[cfg(windows)]
1023unsafe fn _reopen_write(file: &fs::File) -> io::Result<fs::File> {
1024 file.reopen(cap_fs_ext::OpenOptions::new().write(true))
1025}
1026
1027#[cfg(windows)]
1028#[inline]
1029fn reopen_append<Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<fs::File> {
1030 let file = filelike.as_filelike_view::<std::fs::File>();
1031 unsafe { _reopen_append(&file) }
1032}
1033
1034#[cfg(windows)]
1035unsafe fn _reopen_append(file: &fs::File) -> io::Result<fs::File> {
1036 file.reopen(cap_fs_ext::OpenOptions::new().append(true))
1037}
1038
1039fn read_to_end_at(file: &std::fs::File, buf: &mut Vec<u8>, offset: u64) -> io::Result<usize> {
1040 let len = match file.metadata()?.len().checked_sub(offset) {
1041 None => return Ok(0),
1042 Some(len) => len,
1043 };
1044
1045 buf.resize(
1048 (buf.len() as u64)
1049 .saturating_add(len)
1050 .try_into()
1051 .unwrap_or(usize::MAX),
1052 0_u8,
1053 );
1054 FileIoExt::read_exact_at(file, buf, offset)?;
1055 Ok(len as usize)
1056}
1057
1058fn read_to_string_at(file: &std::fs::File, buf: &mut String, offset: u64) -> io::Result<usize> {
1059 let len = match file.metadata()?.len().checked_sub(offset) {
1060 None => return Ok(0),
1061 Some(len) => len,
1062 };
1063
1064 let mut tmp = vec![0_u8; len.try_into().unwrap_or(usize::MAX)];
1067 FileIoExt::read_exact_at(file, &mut tmp, offset)?;
1068 let s = String::from_utf8(tmp).map_err(|_| {
1069 io::Error::new(
1070 io::ErrorKind::InvalidData,
1071 "stream did not contain valid UTF-8",
1072 )
1073 })?;
1074 buf.push_str(&s);
1075 Ok(len as usize)
1076}
1077
1078fn _file_io_ext_can_be_trait_object(_: &dyn FileIoExt) {}