#![cfg_attr(docsrs, feature(doc_cfg))]
use std::{
fs::File,
io::{self, ErrorKind, Read, Seek, SeekFrom},
};
#[cfg(feature = "tokio")]
use tokio::fs::File as AsyncFile;
#[cfg(feature = "tokio")]
use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt};
const DEFAULT_BUFFER_SIZE: usize = 8192;
#[inline]
pub fn same_content_from_files(a: &mut File, b: &mut File) -> Result<bool, io::Error> {
same_content_from_files_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b)
}
#[inline]
pub fn same_content_from_files_with_buffer_size<const BUFFER_SIZE: usize>(
a: &mut File,
b: &mut File,
) -> Result<bool, io::Error> {
const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
let metadata_a = a.metadata()?;
let metadata_b = b.metadata()?;
if metadata_a.len() != metadata_b.len() {
return Ok(false);
}
a.seek(SeekFrom::Start(0))?;
b.seek(SeekFrom::Start(0))?;
same_content_from_readers_with_buffer_size::<BUFFER_SIZE>(a, b)
}
#[inline]
pub fn same_content_from_readers(a: &mut dyn Read, b: &mut dyn Read) -> Result<bool, io::Error> {
same_content_from_readers_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b)
}
pub fn same_content_from_readers_with_buffer_size<const BUFFER_SIZE: usize>(
a: &mut dyn Read,
b: &mut dyn Read,
) -> Result<bool, io::Error> {
const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
let mut buffer1 = [0u8; BUFFER_SIZE];
let mut buffer2 = [0u8; BUFFER_SIZE];
loop {
let ca = read_retry(a, &mut buffer1)?;
if ca == 0 {
let cb = read_try_exact(b, &mut buffer2[..1])?;
return Ok(cb == 0);
} else {
let cb = read_try_exact(b, &mut buffer2[..ca])?;
if ca != cb {
return Ok(false);
}
if buffer1[..ca] != buffer2[..ca] {
return Ok(false);
}
}
}
}
#[inline]
fn read_retry(a: &mut dyn Read, buffer: &mut [u8]) -> Result<usize, io::Error> {
loop {
match a.read(buffer) {
Ok(n) => return Ok(n),
Err(e) if e.kind() == ErrorKind::Interrupted => {},
Err(e) => return Err(e),
}
}
}
fn read_try_exact(a: &mut dyn Read, mut buffer: &mut [u8]) -> Result<usize, io::Error> {
let mut sum = 0;
while !buffer.is_empty() {
let n = read_retry(a, buffer)?;
if n == 0 {
break;
}
buffer = &mut buffer[n..];
sum += n;
}
Ok(sum)
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
#[inline]
pub async fn same_content_from_files_async(
a: &mut AsyncFile,
b: &mut AsyncFile,
) -> Result<bool, io::Error> {
same_content_from_files_async_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b).await
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
#[inline]
pub async fn same_content_from_files_async_with_buffer_size<const BUFFER_SIZE: usize>(
a: &mut AsyncFile,
b: &mut AsyncFile,
) -> Result<bool, io::Error> {
const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
let metadata_a = a.metadata().await?;
let metadata_b = b.metadata().await?;
if metadata_a.len() != metadata_b.len() {
return Ok(false);
}
a.seek(SeekFrom::Start(0)).await?;
b.seek(SeekFrom::Start(0)).await?;
same_content_from_readers_async_with_buffer_size::<BUFFER_SIZE>(a, b).await
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
#[inline]
pub async fn same_content_from_readers_async(
a: &mut (dyn AsyncRead + Unpin),
b: &mut (dyn AsyncRead + Unpin),
) -> Result<bool, io::Error> {
same_content_from_readers_async_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b).await
}
#[cfg(feature = "tokio")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
pub async fn same_content_from_readers_async_with_buffer_size<const BUFFER_SIZE: usize>(
a: &mut (dyn AsyncRead + Unpin),
b: &mut (dyn AsyncRead + Unpin),
) -> Result<bool, io::Error> {
const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
let mut buffer1 = [0u8; BUFFER_SIZE];
let mut buffer2 = [0u8; BUFFER_SIZE];
loop {
let ca = read_retry_async(a, &mut buffer1).await?;
if ca == 0 {
let cb = read_try_exact_async(b, &mut buffer2[..1]).await?;
return Ok(cb == 0);
} else {
let cb = read_try_exact_async(b, &mut buffer2[..ca]).await?;
if ca != cb {
return Ok(false);
}
if buffer1[..ca] != buffer2[..ca] {
return Ok(false);
}
}
}
}
#[cfg(feature = "tokio")]
async fn read_retry_async(
a: &mut (dyn AsyncRead + Unpin),
buffer: &mut [u8],
) -> Result<usize, io::Error> {
loop {
match a.read(buffer).await {
Ok(n) => return Ok(n),
Err(e) if e.kind() == ErrorKind::Interrupted => {},
Err(e) => return Err(e),
}
}
}
#[cfg(feature = "tokio")]
async fn read_try_exact_async(
a: &mut (dyn AsyncRead + Unpin),
mut buffer: &mut [u8],
) -> Result<usize, io::Error> {
let mut sum = 0;
while !buffer.is_empty() {
let n = read_retry_async(a, buffer).await?;
if n == 0 {
break;
}
buffer = &mut buffer[n..];
sum += n;
}
Ok(sum)
}