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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
use ZError;
use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::AtomicUsize;
use cloudflare_zlib_sys::*;
use cloudflare_zlib_sys;
use std::os::raw::*;
use std::mem;

/// Compress data to `Vec` using default settings.
/// Use `Deflate` object if you need to customize compression.
pub fn deflate(data: &[u8]) -> Result<Vec<u8>, ZError> {
    let mut stream = Deflate::new_default()?;
    stream.compress(data)?;
    stream.finish()
}

pub struct Deflate {
    stream: z_stream,
    buf: Vec<u8>,
}

/// Compress
impl Deflate {
    pub fn new_default() -> Result<Self, ZError> {
        Self::new(Z_BEST_COMPRESSION, Z_DEFAULT_STRATEGY, 15)
    }

    /// Use zlib's magic constants:
    ///  * level = `Z_BEST_SPEED` (1) to `Z_BEST_COMPRESSION` (9)
    ///  * strategy = `Z_DEFAULT_STRATEGY`, `Z_FILTERED`, `Z_HUFFMAN_ONLY`, `Z_RLE`, `Z_FIXED`
    ///  * window_bits = 15
    pub fn new(level: c_int, strategy: c_int, window_bits: c_int) -> Result<Self, ZError> {
        Self::new_with_vec(level, strategy, window_bits, Vec::with_capacity(1<<16))
    }

    /// Same as new, but can append to any `Vec`
    pub fn new_with_vec(level: c_int, strategy: c_int, window_bits: c_int, buf: Vec<u8>) -> Result<Self, ZError> {
        if !::is_supported() {
            return Err(ZError::IncompatibleCPU);
        }
        unsafe {
            let mut stream = mem::zeroed();
            let res = deflateInit2(
                &mut stream,
                level,
                Z_DEFLATED,
                window_bits,
                MAX_MEM_LEVEL,
                strategy,
            );
            if Z_OK != res {
                return Err(ZError::new(res));
            }
            Ok(Deflate{
                stream,
                buf,
            })
        }
    }

    /// Expect (remaining) data to take this much space after compression
    pub fn reserve(&mut self, compressed_size: usize) {
        self.buf.reserve(compressed_size)
    }

    /// Add bytes from `data` to compressed data
    pub fn compress(&mut self, data: &[u8]) -> Result<(), ZError> {
        self.compress_internal(data, None, false)
    }

    /// dd bytes from `data` to compressed data, unless the total compressed output would exceed `max_size`
    pub fn compress_with_limit(&mut self, data: &[u8], max_size: &AtomicUsize) -> Result<(), ZError> {
        self.compress_internal(data, Some(max_size), false)
    }

    fn compress_internal(&mut self, data: &[u8], max_size: Option<&AtomicUsize>, finish: bool) -> Result<(), ZError> {
        assert!(data.len() < uInt::max_value() as usize);
        self.stream.next_in = data.as_ptr() as *mut _;
        self.stream.avail_in = data.len() as uInt;

        loop {
            // if we know max size, we don't want to compress or reserve more space than that
            let total_out_before = self.stream.total_out as usize;
            let remaining = max_size.map(|max| max.load(Relaxed).saturating_sub(total_out_before));
            unsafe {
                // unsafe - this is writing to the _reserved_ length of the vector,
                // and updating size only after the write.
                // this way uninitialized memory is never exposed to safe Rust.
                let len = self.buf.len();
                let mut avail_out = self.buf.capacity() - len;
                if let Some(r) = remaining {
                    avail_out = avail_out.min(r);
                }
                self.stream.avail_out = avail_out as uInt;
                self.stream.next_out = self.buf[len..].as_mut_ptr();

                let res = cloudflare_zlib_sys::deflate(&mut self.stream, if finish {Z_FINISH} else {Z_NO_FLUSH});

                // extend the vec length by number of bytes written by zlib
                let total_out_written = self.stream.total_out as usize;
                if total_out_written > total_out_before {
                    self.buf.set_len(len + total_out_written - total_out_before);
                } else {
                    debug_assert_eq!(total_out_before, self.stream.total_out as usize);
                }

                match res {
                    Z_STREAM_END => {
                        debug_assert_eq!(0, self.stream.avail_in);
                        return Ok(())
                    },
                    Z_OK | Z_BUF_ERROR => {
                        if !finish && self.stream.avail_in == 0 {
                            return Ok(());
                        }

                        // let remaining = max_size.get().map(|max| max.saturating_sub(self.stream.total_out as usize));
                        // by default doubles the buffer (or 64kb for empty vec)
                        let mut reserve = self.buf.capacity().max(1<<16);

                        if let Some(rem) = remaining {
                            if rem == 0 {
                                return Err(ZError::DeflatedDataTooLarge(total_out_written));
                            }
                            reserve = reserve.min(rem);
                        }
                        self.buf.reserve(reserve);
                    },
                    other => {
                        return Err(ZError::new(other));
                    }
                }
            }
        }
    }

    pub fn finish(mut self) -> Result<Vec<u8>, ZError> {
        self.compress_internal(&[], None, true)?;
        // it's like option.take(), but cheaper
        Ok(mem::replace(&mut self.buf, Vec::new()))
    }
}

impl Drop for Deflate {
    fn drop(&mut self) {
        unsafe {
            deflateEnd(&mut self.stream);
        }
    }
}

#[test]
fn compress_test() {
    let mut d = Deflate::new(1, 0, 15).unwrap();
    d.reserve(1);
    d.compress(b"a").unwrap();
    d.compress(b"").unwrap();
    d.compress_with_limit(b"zxcvbnm", &AtomicUsize::new(999)).unwrap();
    let vec = d.finish().unwrap();

    let res = ::inf::inflate(&vec).unwrap();
    assert_eq!(&res, b"azxcvbnm");
}