Files
adler32
aho_corasick
alga
approx
ascii
atty
backtrace
backtrace_sys
base64
bitflags
blas_src
block_buffer
block_padding
brotli2
brotli_sys
buf_redux
byte_tools
byteorder
cauchy
cblas_sys
cfg_if
chrono
chunked_transfer
colored
crc32fast
crossbeam
crossbeam_channel
crossbeam_deque
crossbeam_epoch
crossbeam_queue
crossbeam_utils
ctrlc
deflate
digest
dirs
error_chain
filetime
futures
generic_array
getrandom
gzip_header
hex
httparse
hyper
idna
itoa
language_tags
lapack_src
lapacke
lapacke_sys
lazy_static
libc
libm
linked_hash_map
log
matches
matrixmultiply
maybe_uninit
md5
memchr
memoffset
mime
mime_guess
multipart
nalgebra
base
geometry
linalg
ndarray
ndarray_linalg
net2
netlib_src
nix
num_complex
num_cpus
num_integer
num_rational
num_traits
opaque_debug
percent_encoding
phf
phf_shared
ppv_lite86
proc_macro2
quick_error
quote
rand
rand_chacha
rand_core
rand_distr
rawpointer
regex
regex_syntax
remove_dir_all
rosrust
rosrust_codegen
rosrust_msg
rouille
rustc_demangle
rustros_tf
ryu
safemem
scopeguard
serde
serde_bytes
serde_derive
serde_json
serde_xml_rs
sha1
siphasher
smallvec
syn
tempdir
term
thread_local
threadpool
time
tiny_http
traitobject
twoway
typeable
typenum
ucd_util
unicase
unicode_bidi
unicode_normalization
unicode_xid
url
utf8_ranges
void
xml
xml_rpc
yaml_rust
  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
142
143
144
145
146
147
148
use std::cmp;

use chained_hash_table::WINDOW_SIZE;
use huffman_table;

const MAX_MATCH: usize = huffman_table::MAX_MATCH as usize;

/// The maximum size of the buffer.
pub const BUFFER_SIZE: usize = (WINDOW_SIZE * 2) + MAX_MATCH;

pub struct InputBuffer {
    buffer: Vec<u8>,
}

impl InputBuffer {
    #[cfg(test)]
    pub fn new<'a>(data: &'a [u8]) -> (InputBuffer, Option<&[u8]>) {
        let mut b = InputBuffer::empty();
        let rem = b.add_data(data);
        (b, rem)
    }

    pub fn empty() -> InputBuffer {
        InputBuffer {
            buffer: Vec::with_capacity(BUFFER_SIZE),
        }
    }

    /// Add data to the buffer.
    ///
    /// Returns a slice of the data that was not added (including the lookahead if any).
    pub fn add_data<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
        debug_assert!(self.current_end() <= BUFFER_SIZE);
        if self.current_end() + data.len() > BUFFER_SIZE {
            // Add data and return how much was left.
            let consumed = {
                let space_left = BUFFER_SIZE - self.buffer.len();
                self.buffer.extend_from_slice(&data[..space_left]);
                space_left
            };
            Some(&data[consumed..])
        } else {
            // There's space for all of the data.
            self.buffer.extend_from_slice(data);
            None
        }
    }

    /// Get the current amount of data in the buffer.
    pub fn current_end(&self) -> usize {
        self.buffer.len()
    }

    /// Slide the input window and add new data.
    ///
    /// Returns a slice containing the data that did not fit, or None if all data was consumed.
    pub fn slide<'a>(&mut self, data: &'a [u8]) -> Option<&'a [u8]> {
        // This should only be used when the buffer is full
        assert!(self.buffer.len() > WINDOW_SIZE * 2);

        // Do this in a closure to to end the borrow of buffer.
        let (final_len, upper_len, end) = {
            // Split into lower window and upper window + lookahead
            let (lower, upper) = self.buffer.split_at_mut(WINDOW_SIZE);
            // Copy the upper window to the lower window
            lower.copy_from_slice(&upper[..WINDOW_SIZE]);
            let lookahead_len = {
                // Copy the lookahead to the start of the upper window
                let (upper_2, lookahead) = upper.split_at_mut(WINDOW_SIZE);
                let lookahead_len = lookahead.len();
                debug_assert!(lookahead_len <= MAX_MATCH);
                upper_2[..lookahead_len].copy_from_slice(lookahead);
                lookahead_len
            };

            // Length of the upper window minus the lookahead bytes
            let upper_len = upper.len() - lookahead_len;
            let end = cmp::min(data.len(), upper_len);
            upper[lookahead_len..lookahead_len + end].copy_from_slice(&data[..end]);
            // Remove unused data if any.
            (lower.len() + lookahead_len + end, upper_len, end)
        };
        // Remove unused space.
        self.buffer.truncate(final_len);

        if data.len() > upper_len {
            // Return a slice of the data that was not added
            Some(&data[end..])
        } else {
            None
        }
    }

    /// Get a mutable slice of the used part of the buffer.
    pub fn get_buffer(&mut self) -> &mut [u8] {
        &mut self.buffer
    }
}

#[cfg(test)]
mod test {
    use super::MAX_MATCH;
    use chained_hash_table::WINDOW_SIZE;
    use super::*;
    #[test]
    pub fn buffer_add_full() {
        let data = [10u8; BUFFER_SIZE + 10];
        let (mut buf, extra) = InputBuffer::new(&data[..]);
        assert!(extra.unwrap() == &[10; 10]);
        let to_add = [2, 5, 3];
        let not_added = buf.add_data(&to_add);
        assert_eq!(not_added.unwrap(), to_add);
    }

    #[test]
    pub fn buffer_add_not_full() {
        let data = [10u8; BUFFER_SIZE - 5];
        let (mut buf, extra) = InputBuffer::new(&data[..]);
        assert_eq!(buf.current_end(), data.len());
        assert_eq!(extra, None);
        let to_add = [2, 5, 3];
        {
            let not_added = buf.add_data(&to_add);
            assert!(not_added.is_none());
        }
        let not_added = buf.add_data(&to_add);
        assert_eq!(not_added.unwrap()[0], 3);
    }

    #[test]
    fn slide() {
        let data = [10u8; BUFFER_SIZE];
        let (mut buf, extra) = InputBuffer::new(&data[..]);
        assert_eq!(extra, None);
        let to_add = [5; 5];
        let rem = buf.slide(&to_add);
        assert!(rem.is_none());
        {
            let slice = buf.get_buffer();
            assert!(slice[..WINDOW_SIZE + MAX_MATCH] == data[WINDOW_SIZE..]);
            assert_eq!(
                slice[WINDOW_SIZE + MAX_MATCH..WINDOW_SIZE + MAX_MATCH + 5],
                to_add
            );
        }
        assert_eq!(buf.current_end(), WINDOW_SIZE + MAX_MATCH + to_add.len());
    }
}