1#![cfg_attr(docsrs, feature(doc_cfg))]
44
45use std::{
46 fs::File,
47 io::{self, ErrorKind, Read, Seek, SeekFrom},
48};
49
50#[cfg(feature = "tokio")]
51use tokio::fs::File as AsyncFile;
52#[cfg(feature = "tokio")]
53use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeekExt};
54
55const DEFAULT_BUFFER_SIZE: usize = 8192;
56
57#[inline]
61pub fn same_content_from_files(a: &mut File, b: &mut File) -> Result<bool, io::Error> {
62 same_content_from_files_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b)
63}
64
65#[inline]
69pub fn same_content_from_files_with_buffer_size<const BUFFER_SIZE: usize>(
70 a: &mut File,
71 b: &mut File,
72) -> Result<bool, io::Error> {
73 const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
74
75 let metadata_a = a.metadata()?;
76 let metadata_b = b.metadata()?;
77
78 if metadata_a.len() != metadata_b.len() {
79 return Ok(false);
80 }
81
82 a.seek(SeekFrom::Start(0))?;
83 b.seek(SeekFrom::Start(0))?;
84
85 same_content_from_readers_with_buffer_size::<BUFFER_SIZE>(a, b)
86}
87
88#[inline]
92pub fn same_content_from_readers(a: &mut dyn Read, b: &mut dyn Read) -> Result<bool, io::Error> {
93 same_content_from_readers_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b)
94}
95
96pub fn same_content_from_readers_with_buffer_size<const BUFFER_SIZE: usize>(
100 a: &mut dyn Read,
101 b: &mut dyn Read,
102) -> Result<bool, io::Error> {
103 const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
104
105 let mut buffer1 = [0u8; BUFFER_SIZE];
106 let mut buffer2 = [0u8; BUFFER_SIZE];
107
108 loop {
109 let ca = read_retry(a, &mut buffer1)?;
110
111 if ca == 0 {
112 let cb = read_try_exact(b, &mut buffer2[..1])?;
113
114 return Ok(cb == 0);
115 } else {
116 let cb = read_try_exact(b, &mut buffer2[..ca])?;
117
118 if ca != cb {
119 return Ok(false);
120 }
121
122 if buffer1[..ca] != buffer2[..ca] {
123 return Ok(false);
124 }
125 }
126 }
127}
128
129#[inline]
130fn read_retry(a: &mut dyn Read, buffer: &mut [u8]) -> Result<usize, io::Error> {
131 loop {
132 match a.read(buffer) {
133 Ok(n) => return Ok(n),
134 Err(e) if e.kind() == ErrorKind::Interrupted => {},
135 Err(e) => return Err(e),
136 }
137 }
138}
139
140fn read_try_exact(a: &mut dyn Read, mut buffer: &mut [u8]) -> Result<usize, io::Error> {
141 let mut sum = 0;
142
143 while !buffer.is_empty() {
144 let n = read_retry(a, buffer)?;
145
146 if n == 0 {
147 break;
148 }
149
150 buffer = &mut buffer[n..];
151 sum += n;
152 }
153
154 Ok(sum)
155}
156
157#[cfg(feature = "tokio")]
161#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
162#[inline]
163pub async fn same_content_from_files_async(
164 a: &mut AsyncFile,
165 b: &mut AsyncFile,
166) -> Result<bool, io::Error> {
167 same_content_from_files_async_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b).await
168}
169
170#[cfg(feature = "tokio")]
174#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
175#[inline]
176pub async fn same_content_from_files_async_with_buffer_size<const BUFFER_SIZE: usize>(
177 a: &mut AsyncFile,
178 b: &mut AsyncFile,
179) -> Result<bool, io::Error> {
180 const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
181
182 let metadata_a = a.metadata().await?;
183 let metadata_b = b.metadata().await?;
184
185 if metadata_a.len() != metadata_b.len() {
186 return Ok(false);
187 }
188
189 a.seek(SeekFrom::Start(0)).await?;
190 b.seek(SeekFrom::Start(0)).await?;
191
192 same_content_from_readers_async_with_buffer_size::<BUFFER_SIZE>(a, b).await
193}
194
195#[cfg(feature = "tokio")]
199#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
200#[inline]
201pub async fn same_content_from_readers_async(
202 a: &mut (dyn AsyncRead + Unpin),
203 b: &mut (dyn AsyncRead + Unpin),
204) -> Result<bool, io::Error> {
205 same_content_from_readers_async_with_buffer_size::<DEFAULT_BUFFER_SIZE>(a, b).await
206}
207
208#[cfg(feature = "tokio")]
212#[cfg_attr(docsrs, doc(cfg(feature = "tokio")))]
213pub async fn same_content_from_readers_async_with_buffer_size<const BUFFER_SIZE: usize>(
214 a: &mut (dyn AsyncRead + Unpin),
215 b: &mut (dyn AsyncRead + Unpin),
216) -> Result<bool, io::Error> {
217 const { assert!(BUFFER_SIZE > 0, "BUFFER_SIZE must be greater than zero") }
218
219 let mut buffer1 = [0u8; BUFFER_SIZE];
220 let mut buffer2 = [0u8; BUFFER_SIZE];
221
222 loop {
223 let ca = read_retry_async(a, &mut buffer1).await?;
224
225 if ca == 0 {
226 let cb = read_try_exact_async(b, &mut buffer2[..1]).await?;
227
228 return Ok(cb == 0);
229 } else {
230 let cb = read_try_exact_async(b, &mut buffer2[..ca]).await?;
231
232 if ca != cb {
233 return Ok(false);
234 }
235
236 if buffer1[..ca] != buffer2[..ca] {
237 return Ok(false);
238 }
239 }
240 }
241}
242
243#[cfg(feature = "tokio")]
244async fn read_retry_async(
245 a: &mut (dyn AsyncRead + Unpin),
246 buffer: &mut [u8],
247) -> Result<usize, io::Error> {
248 loop {
249 match a.read(buffer).await {
250 Ok(n) => return Ok(n),
251 Err(e) if e.kind() == ErrorKind::Interrupted => {},
252 Err(e) => return Err(e),
253 }
254 }
255}
256
257#[cfg(feature = "tokio")]
258async fn read_try_exact_async(
259 a: &mut (dyn AsyncRead + Unpin),
260 mut buffer: &mut [u8],
261) -> Result<usize, io::Error> {
262 let mut sum = 0;
263
264 while !buffer.is_empty() {
265 let n = read_retry_async(a, buffer).await?;
266
267 if n == 0 {
268 break;
269 }
270
271 buffer = &mut buffer[n..];
272 sum += n;
273 }
274
275 Ok(sum)
276}