Repository: zopfli-rs/zopfli (language: rust)

Issue:
panics when compressing empty data
Hi! Attempting to compress empty data (my example uses compress_seekable but I have tested a file also) causes a panic. 

```rust
fn main() {
    let cursor = std::io::Cursor::new(&[]);
    let mut out = Vec::new();
    zopfli::compress_seekable(
        &zopfli::Options::default(),
        &zopfli::Format::Gzip,
        cursor,
        &mut out,
    );
}
```

```text
thread 'main' panicked at 'attempt to subtract with overflow', /home/sky/git/zopfli/src/deflate.rs:311:19
stack backtrace:
   0: rust_begin_unwind
             at /rustc/45e2c2881d11324d610815bfff097e25c412199e/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/45e2c2881d11324d610815bfff097e25c412199e/library/core/src/panicking.rs:142:14
   2: core::panicking::panic
             at /rustc/45e2c2881d11324d610815bfff097e25c412199e/library/core/src/panicking.rs:48:5
   3: zopfli::deflate::calculate_block_symbol_size_small
             at /home/sky/git/zopfli/src/deflate.rs:311:19
   4: zopfli::deflate::calculate_block_symbol_size_given_counts
             at /home/sky/git/zopfli/src/deflate.rs:345:9
   5: zopfli::deflate::try_optimize_huffman_for_rle
             at /home/sky/git/zopfli/src/deflate.rs:856:20
   6: zopfli::deflate::get_dynamic_lengths
             at /home/sky/git/zopfli/src/deflate.rs:905:5
   7: zopfli::deflate::calculate_block_size
             at /home/sky/git/zopfli/src/deflate.rs:836:31
   8: zopfli::squeeze::lz77_optimal
             at /home/sky/git/zopfli/src/squeeze.rs:506:20
   9: zopfli::deflate::blocksplit_attempt
             at /home/sky/git/zopfli/src/deflate.rs:1147:17
  10: zopfli::deflate::deflate_part
             at /home/sky/git/zopfli/src/deflate.rs:164:31
  11: zopfli::deflate::deflate
             at /home/sky/git/zopfli/src/deflate.rs:104:9
  12: zopfli::gzip::gzip_compress
             at /home/sky/git/zopfli/src/gzip.rs:49:5
  13: zopfli::compress
             at /home/sky/git/zopfli/src/lib.rs:98:25
  14: zopfli::compress_seekable
             at /home/sky/git/zopfli/src/lib.rs:83:5
  15: scratch::main
             at ./src/main.rs:4:5
  16: core::ops::function::FnOnce::call_once
             at /rustc/45e2c2881d11324d610815bfff097e25c412199e/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```


Discussion / hints:

Fix patch:
diff --git a/src/deflate.rs b/src/deflate.rs
index 7316ee0..9c9ea87 100644
--- a/src/deflate.rs
+++ b/src/deflate.rs
@@ -95,7 +95,9 @@ where
     }
 
     if bytes_read == 0 {
-        let instart = if j == ZOPFLI_MASTER_BLOCK_SIZE {
+        let instart = if total_bytes_read == 0 {
+            0
+        } else if j == ZOPFLI_MASTER_BLOCK_SIZE {
             0
         } else {
             ZOPFLI_WINDOW_SIZE
@@ -308,7 +310,7 @@ fn calculate_block_symbol_size_small(
 ) -> usize {
     let mut result = 0;
 
-    debug_assert!(lend - 1 < lz77.size());
+    debug_assert!(lend == lstart || lend - 1 < lz77.size());
 
     for &item in &lz77.litlens[lstart..lend] {
         match item {
@@ -1132,7 +1134,7 @@ where
         totalcost += calculate_block_size_auto_type(&store, 0, store.size());
 
         // ZopfliAppendLZ77Store(&store, &lz77);
-        debug_assert!(store.size() > 0);
+        debug_assert!(instart == inend || store.size() > 0);
         for (&litlens, &pos) in store.litlens.iter().zip(store.pos.iter()) {
             lz77.append_store_item(litlens, pos);
         }
@@ -1148,7 +1150,7 @@ where
     totalcost += calculate_block_size_auto_type(&store, 0, store.size());
 
     // ZopfliAppendLZ77Store(&store, &lz77);
-    debug_assert!(store.size() > 0);
+    debug_assert!(instart == inend || store.size() > 0);
     for (&litlens, &pos) in store.litlens.iter().zip(store.pos.iter()) {
         lz77.append_store_item(litlens, pos);
     }


Test patch:
diff --git a/test/results/empty.txt.gz b/test/results/empty.txt.gz
new file mode 100644
index 0000000..d4b9926
Binary files /dev/null and b/test/results/empty.txt.gz differ