Closes gitpython-developers#120
DecompressMemMapReader.read(N) could return b'' mid-stream when zlib
consumed input without producing output on a single decompress call
(small N, header / dictionary frames in flight). The original
`if dcompdat and ...` guard at the recursion site skipped the
"refill to size" recursion in that case, so a caller using the
standard idiom
while chunk := stream.read(4096):
yield chunk
terminated at the first empty chunk -- before _br == _s.
The guard exists for compressed_bytes_read(), which manipulates
_br=0 and then drains the inner zip past its EOF. Recursing there
would loop forever because the inner zip is already done.
The fix uses zlib's own `eof` attribute (available on standard
zlib.Decompress objects since Python 3.6) to distinguish:
- dcompdat empty AND zip not at EOF -> still digesting, recurse
- dcompdat empty AND zip at EOF -> compressed_bytes_read
scrub or genuine EOF; do
not recurse.
`getattr(_zip, 'eof', False)` keeps the conservative behavior
when running against a custom zlib object that does not expose
the attribute.
Adds a regression test that reads with chunk_size in
{1, 4, 16, 64} from a 13 KB highly-compressible stream. With the
old guard, the chunk_size <= 16 cases stopped at byte 0; the new
test asserts they read all 13000 bytes.
The full existing test suite (24 tests) still passes, including
test_decompress_reader_special_case and test_pack which exercise
the compressed_bytes_read scrub path that the original guard
existed to protect.