1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*!
# Same Content

Determine whether data from different sources are the same.

## Example

```rust
extern crate same_content;

use std::fs::File;

use same_content::*;

assert!(!same_content_from_files(&mut File::open("tests/data/P1140310.jpg").unwrap(), &mut File::open("tests/data/P1140558.jpg").unwrap()).unwrap());
```

## Change the Buffer Size

The default buffer size for the `same_content_from_files` function and the `same_content_from_readers` function is 256 bytes per stream. If you want to change that, you can use the `same_content_from_files2` function or the `same_content_from_readers2` function, and define a length explicitly.

For example, to change the buffer size to 4096 bytes,

```rust
#[macro_use] extern crate same_content;

use std::fs::File;

use same_content::*;
use same_content::generic_array::typenum::U4096;

assert!(!same_content_from_files2::<U4096>(&mut File::open("tests/data/P1140310.jpg").unwrap(), &mut File::open("tests/data/P1140558.jpg").unwrap()).unwrap());
```
*/

pub extern crate generic_array;

use std::fs::File;
use std::io::{self, ErrorKind, Read, Seek, SeekFrom};

use generic_array::typenum::{IsGreaterOrEqual, True, U1, U256};
use generic_array::{ArrayLength, GenericArray};

#[inline]
pub fn same_content_from_files(a: &mut File, b: &mut File) -> Result<bool, io::Error> {
    same_content_from_files2::<U256>(a, b)
}

#[inline]
pub fn same_content_from_files2<N: ArrayLength<u8> + IsGreaterOrEqual<U1, Output = True>>(
    a: &mut File,
    b: &mut File,
) -> Result<bool, io::Error> {
    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_readers2::<N>(a, b)
}

#[inline]
pub fn same_content_from_readers(a: &mut dyn Read, b: &mut dyn Read) -> Result<bool, io::Error> {
    same_content_from_readers2::<U256>(a, b)
}

pub fn same_content_from_readers2<N: ArrayLength<u8> + IsGreaterOrEqual<U1, Output = True>>(
    a: &mut dyn Read,
    b: &mut dyn Read,
) -> Result<bool, io::Error> {
    let mut buffer1: GenericArray<u8, N> = GenericArray::default();
    let mut buffer2: GenericArray<u8, N> = GenericArray::default();

    loop {
        let ca = a.read(&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);
            }
        }
    }
}

fn read_try_exact(a: &mut dyn Read, mut buffer: &mut [u8]) -> Result<usize, io::Error> {
    let mut sum = 0;

    while !buffer.is_empty() {
        match a.read(buffer) {
            Ok(0) => break,
            Ok(n) => {
                buffer = &mut buffer[n..];

                sum += n;
            }
            Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
            Err(e) => return Err(e),
        }
    }

    Ok(sum)
}