use crate::Metadata;
use io_lifetimes::AsFilelike;
use std::convert::TryInto;
use std::fs::File;
use std::io::{self, IoSlice, IoSliceMut};
use std::os::windows::fs::FileExt;
use std::slice;
#[cfg(feature = "io-streams")]
use {
crate::owned_streamer::OwnedStreamer,
cap_fs_ext::{OpenOptions, Reopen},
io_streams::StreamReader,
std::io::SeekFrom,
system_interface::fs::FileIoExt,
};
#[inline]
pub fn metadata<'a, Filelike: AsFilelike>(filelike: &Filelike) -> io::Result<Metadata> {
filelike.as_filelike_view::<File>().metadata().map(|meta| {
Metadata {
len: meta.len(),
blksize: 0x1000,
}
})
}
#[inline]
pub fn read_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
buf: &mut [u8],
offset: u64,
) -> io::Result<usize> {
filelike.as_filelike_view::<File>().seek_read(buf, offset)
}
pub fn read_exact_at<Filelike: AsFilelike>(
filelike: &Filelike,
mut buf: &mut [u8],
mut offset: u64,
) -> io::Result<()> {
loop {
match read_at(filelike, buf, offset) {
Ok(0) if !buf.is_empty() => {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
}
Ok(nread) => {
offset = offset
.checked_add(nread.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
buf = &mut buf[nread..];
if buf.is_empty() {
return Ok(());
}
}
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
}
pub fn read_vectored_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
bufs: &mut [IoSliceMut],
offset: u64,
) -> io::Result<usize> {
let buf = bufs
.iter_mut()
.find(|b| !b.is_empty())
.map_or(&mut [][..], |b| &mut **b);
read_at(filelike, buf, offset)
}
pub fn read_exact_vectored_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
mut bufs: &mut [IoSliceMut],
mut offset: u64,
) -> io::Result<()> {
while !bufs.is_empty() {
match read_vectored_at(filelike, bufs, offset) {
Ok(nread) => {
offset = offset
.checked_add(nread.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
bufs = advance_mut(bufs, nread);
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
pub fn is_read_vectored_at<'a, Filelike: AsFilelike>(_filelike: &Filelike) -> bool {
false
}
#[cfg(feature = "io-streams")]
pub fn read_via_stream_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
offset: u64,
) -> io::Result<StreamReader> {
if let Ok(file) = filelike
.as_filelike_view::<File>()
.reopen(OpenOptions::new().read(true))
{
if offset != 0 {
file.seek(SeekFrom::Start(offset))?;
}
return Ok(StreamReader::file(file));
}
StreamReader::piped_thread(Box::new(OwnedStreamer::new(
filelike.as_filelike_view::<File>().try_clone()?,
offset,
)))
}
#[inline]
pub fn write_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
buf: &[u8],
offset: u64,
) -> io::Result<usize> {
filelike.as_filelike_view::<File>().seek_write(buf, offset)
}
pub fn write_all_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
mut buf: &[u8],
mut offset: u64,
) -> io::Result<()> {
loop {
match write_at(filelike, buf, offset) {
Ok(nwritten) => {
offset = offset
.checked_add(nwritten.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
buf = &buf[nwritten..];
if buf.is_empty() {
return Ok(());
}
}
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
}
pub fn write_vectored_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
bufs: &[IoSlice],
offset: u64,
) -> io::Result<usize> {
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
write_at(filelike, buf, offset)
}
pub fn write_all_vectored_at<'a, Filelike: AsFilelike>(
filelike: &Filelike,
mut bufs: &mut [IoSlice],
mut offset: u64,
) -> io::Result<()> {
while !bufs.is_empty() {
match write_vectored_at(filelike, bufs, offset) {
Ok(nwritten) => {
offset = offset
.checked_add(nwritten.try_into().unwrap())
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "offset overflow"))?;
bufs = advance(bufs, nwritten);
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
Err(e) => return Err(e),
}
}
Ok(())
}
#[inline]
pub fn is_write_vectored_at<'a, Filelike: AsFilelike>(_filelike: &Filelike) -> bool {
false
}
fn advance<'a, 'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] {
let mut remove = 0;
let mut accumulated_len = 0;
for buf in bufs.iter() {
if accumulated_len + buf.len() > n {
break;
} else {
accumulated_len += buf.len();
remove += 1;
}
}
#[allow(clippy::indexing_slicing)]
let bufs = &mut bufs[remove..];
if let Some(first) = bufs.first_mut() {
let advance_by = n - accumulated_len;
let mut ptr = first.as_ptr();
let mut len = first.len();
unsafe {
ptr = ptr.add(advance_by);
len -= advance_by;
*first = IoSlice::<'a>::new(slice::from_raw_parts::<'a>(ptr, len));
}
}
bufs
}
fn advance_mut<'a, 'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] {
let mut remove = 0;
let mut accumulated_len = 0;
for buf in bufs.iter() {
if accumulated_len + buf.len() > n {
break;
} else {
accumulated_len += buf.len();
remove += 1;
}
}
#[allow(clippy::indexing_slicing)]
let bufs = &mut bufs[remove..];
if let Some(first) = bufs.first_mut() {
let advance_by = n - accumulated_len;
let mut ptr = first.as_mut_ptr();
let mut len = first.len();
unsafe {
ptr = ptr.add(advance_by);
len -= advance_by;
*first = IoSliceMut::<'a>::new(slice::from_raw_parts_mut::<'a>(ptr, len));
}
}
bufs
}