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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Process whole files in a single call.
//!
//! Provides functions to compute signatures, delta and patches with a single call. Those functions
//! are useful if the application prefers to process whole files instead of using fine-grained APIs
//! over IO.
//!
//! If fine-grained control over IO is necessary, it is provided by `Signature`, `Delta` and
//! `Patch` structs.

use super::*;
use std::io::{self, BufRead, Read, Seek, Write};

/// Generates the signature of a basis input, and writes it out to an output stream.
///
/// This function will consume the given input stream and attempt to write the resulting signature
/// to the given output. In case of success, the number of bytes written is returned, otherwise
/// an error is reported.
///
/// The accepted arguments, among the input and output streams, are:
///
/// * `block_len`: the block size for signature generation, in bytes;
/// * `strong_len`: the truncated length of strong checksums, in bytes;
/// * `sig_type`: the signature format to be used.
pub fn signature_with_options<R: ?Sized, W: ?Sized>(
    input: &mut R,
    output: &mut W,
    block_len: usize,
    strong_len: usize,
    sig_type: SignatureType,
) -> Result<u64>
where
    R: BufRead,
    W: Write,
{
    let mut sig = Signature::with_options(input, block_len, strong_len, sig_type)?;
    let written = io::copy(&mut sig, output)?;
    Ok(written)
}

/// Generates the signature of a basis input, by using default settings.
///
/// This function will consume the given input stream and attempt to write the resulting signature
/// to the given output. In case of success, the number of bytes written is returned, otherwise
/// an error is reported. Default settings are used to produce the signature. BLAKE2 for the
/// hashing, 2048 bytes for the block length and full length for the strong signature size.
pub fn signature<R: ?Sized, W: ?Sized>(input: &mut R, output: &mut W) -> Result<u64>
where
    R: Read,
    W: Write,
{
    let mut sig = Signature::new(input)?;
    let written = io::copy(&mut sig, output)?;
    Ok(written)
}

/// Generates a delta between a signature and a new file streams.
///
/// This function will consume the new file and base signature inputs and writes to the given
/// output the delta between them. In case of success, the number of bytes written is returned,
/// otherwise an error is reported. The `new` parameter is the input stream representing a possibly
/// modified file with respect to some base, for which its signature is provided as `base_sig`
/// parameter.
///
/// To generate a signature, see the `signature` function, or the `Signature` struct.
pub fn delta<R: ?Sized, S: ?Sized, W: ?Sized>(
    new: &mut R,
    base_sig: &mut S,
    output: &mut W,
) -> Result<u64>
where
    R: Read,
    S: Read,
    W: Write,
{
    let mut delta = Delta::new(new, base_sig)?;
    let written = io::copy(&mut delta, output)?;
    Ok(written)
}

/// Applies a patch, relative to a basis, into an output stream.
///
/// This function will consume the base file and the new file delta inputs and writes to the given
/// output the patched input. In case of success, the number of bytes written is returned,
/// otherwise an error is reported. The `base` parameter is the input stream representing the base
/// file from which apply the patch. This stream must be seekable. The `delta` parameter is a
/// stream containing the delta between the base file and the new one. The output parameter will
/// be used to write the output.
///
/// To generate a delta, see the `delta` function, or the `Delta` struct.
pub fn patch<B: ?Sized, D: ?Sized, W: ?Sized>(
    base: &mut B,
    delta: &mut D,
    output: &mut W,
) -> Result<u64>
where
    B: Read + Seek,
    D: Read,
    W: Write,
{
    let mut patch = Patch::new(base, delta)?;
    let written = io::copy(&mut patch, output)?;
    Ok(written)
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::SignatureType;

    use std::io::Cursor;
    use std::str::from_utf8;

    const DATA: &'static str = "this is a string to be tested";
    const DATA2: &'static str = "this is another string to be tested";

    #[test]
    fn integration() {
        // signature
        let mut sig = Vec::new();
        signature_with_options(
            &mut Cursor::new(DATA),
            &mut sig,
            10,
            5,
            SignatureType::Blake2,
        )
        .unwrap();

        // delta
        let mut dlt = Vec::new();
        delta(&mut Cursor::new(DATA2), &mut Cursor::new(sig), &mut dlt).unwrap();

        // patch
        let mut out = Vec::new();
        patch(&mut Cursor::new(DATA), &mut Cursor::new(dlt), &mut out).unwrap();

        // check that patched version is the same as DATA2
        let out_str = from_utf8(&out).unwrap();
        assert_eq!(out_str, DATA2);
    }
}