Repository: dropbox/rust-brotli (language: rust)

Issue:
Flushing sometimes leaves the compressor in an inconsistent state
Thanks for your work porting brotli to Rust :)

I've had an issue when using your crate, specifically when flushing streams: after a flush, all write operations would fail. This would only happen sometimes, depending on the number of bytes compressed so far, so it was pretty hard to track down.

I think I found the culprit: the `flush_or_close()` method used in the implementation of `Write`, when called to `flush()`, improperly stops calling the compressor without checking if there is more data to flush (since the first implementation in c1959aeca4e6deda8021a2a5bcc5ebac753dd599)

The documentation on how to use `BROTLI_OPERATION_FLUSH` (https://brotli.org/encode.html#aed2 , https://brotli.org/encode.html#a512) mentions the compressor should be called until it has no available input data and `BrotliEncoderHasMoreOutput()` returns false:

```
	Under some circumstances (e.g. lack of output stream capacity) this operation would require several calls to BrotliEncoderCompressStream
	[...]
	Warning
	When flushing and finishing, op should not change until operation is complete; input stream should not be swapped, reduced or extended as well.
```

The current implementation calls `BrotliEncoderCompressStream()` once and always returns `Ok()`, which may leave the compressor in `BROTLI_OPERATION_FLUSH` state and makes future write operations return an `Err()` (and allow the caller to modify the input stream, leaving room for unexpected compression results).

I'm suggesting a patch in a tiny pull request, which (at least in all the usecases I've been able to reproduce sporadically), solves the issue.


Discussion / hints:

Fix patch:
diff --git a/src/enc/writer.rs b/src/enc/writer.rs
index bea6f98d..c484909f 100755
--- a/src/enc/writer.rs
+++ b/src/enc/writer.rs
@@ -2,7 +2,8 @@
 use core;
 use super::encode::{BrotliEncoderCreateInstance, BrotliEncoderDestroyInstance,
                     BrotliEncoderParameter, BrotliEncoderSetParameter, BrotliEncoderOperation,
-                    BrotliEncoderStateStruct, BrotliEncoderCompressStream, BrotliEncoderIsFinished};
+                    BrotliEncoderStateStruct, BrotliEncoderCompressStream, BrotliEncoderIsFinished,
+                    BrotliEncoderHasMoreOutput};
 use super::backward_references::BrotliEncoderParams;
 use brotli_decompressor::CustomWrite;
 use super::interface;
@@ -207,6 +208,9 @@ CompressorWriterCustomIo<ErrType, W, BufferType, Alloc>
               return Err(self.error_if_invalid_data.take().unwrap());
            }
            if let BrotliEncoderOperation::BROTLI_OPERATION_FLUSH = op {
+              if BrotliEncoderHasMoreOutput(&mut self.state) != 0 {
+                 continue;
+              }
               return Ok(());
            }
            if BrotliEncoderIsFinished(&mut self.state) != 0 {


Test patch: