8342562: Enhance Deflater operations

Reviewed-by: yan, mbalao, andrew
Backport-of: 4bda75fd57a9a350d74d6f373526e1804d1fbdb4
This commit is contained in:
Aleksei Voitylov 2025-02-06 13:59:23 +01:00 committed by Andrew John Hughes
parent 949c6ffc54
commit 93e8e20271
2 changed files with 39 additions and 9 deletions

View File

@ -40,6 +40,26 @@ import java.io.IOException;
*/ */
public public
class DeflaterOutputStream extends FilterOutputStream { class DeflaterOutputStream extends FilterOutputStream {
/*
* The default size of the output buffer
*/
static final int DEFAULT_BUF_SIZE = 512;
/*
* When calling Deflater.deflate() with Deflater.SYNC_FLUSH or Deflater.FULL_FLUSH,
* the callers are expected to ensure that the size of the buffer is greater than 6.
* This expectation comes from the underlying zlib library which in its zlib.h
* states:
* "If deflate returns with avail_out == 0, this function must be called again
* with the same value of the flush parameter and more output space (updated
* avail_out), until the flush is complete (deflate returns with non-zero
* avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
* avail_out is greater than six when the flush marker begins, in order to avoid
* repeated flush markers upon calling deflate() again when avail_out == 0."
*/
private static final int SYNC_FLUSH_MIN_BUF_SIZE = 7;
/** /**
* Compressor for this stream. * Compressor for this stream.
*/ */
@ -124,7 +144,7 @@ class DeflaterOutputStream extends FilterOutputStream {
public DeflaterOutputStream(OutputStream out, public DeflaterOutputStream(OutputStream out,
Deflater def, Deflater def,
boolean syncFlush) { boolean syncFlush) {
this(out, def, 512, syncFlush); this(out, def, DEFAULT_BUF_SIZE, syncFlush);
} }
@ -139,7 +159,7 @@ class DeflaterOutputStream extends FilterOutputStream {
* @param def the compressor ("deflater") * @param def the compressor ("deflater")
*/ */
public DeflaterOutputStream(OutputStream out, Deflater def) { public DeflaterOutputStream(OutputStream out, Deflater def) {
this(out, def, 512, false); this(out, def, DEFAULT_BUF_SIZE, false);
} }
boolean usesDefaultDeflater = false; boolean usesDefaultDeflater = false;
@ -159,7 +179,7 @@ class DeflaterOutputStream extends FilterOutputStream {
* @since 1.7 * @since 1.7
*/ */
public DeflaterOutputStream(OutputStream out, boolean syncFlush) { public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
this(out, new Deflater(), 512, syncFlush); this(out, new Deflater(), DEFAULT_BUF_SIZE, syncFlush);
usesDefaultDeflater = true; usesDefaultDeflater = true;
} }
@ -182,6 +202,7 @@ class DeflaterOutputStream extends FilterOutputStream {
* @param b the byte to be written * @param b the byte to be written
* @exception IOException if an I/O error has occurred * @exception IOException if an I/O error has occurred
*/ */
@Override
public void write(int b) throws IOException { public void write(int b) throws IOException {
byte[] buf = new byte[1]; byte[] buf = new byte[1];
buf[0] = (byte)(b & 0xff); buf[0] = (byte)(b & 0xff);
@ -196,6 +217,7 @@ class DeflaterOutputStream extends FilterOutputStream {
* @param len the length of the data * @param len the length of the data
* @exception IOException if an I/O error has occurred * @exception IOException if an I/O error has occurred
*/ */
@Override
public void write(byte[] b, int off, int len) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
if (def.finished()) { if (def.finished()) {
throw new IOException("write beyond end of stream"); throw new IOException("write beyond end of stream");
@ -239,6 +261,7 @@ class DeflaterOutputStream extends FilterOutputStream {
* underlying stream. * underlying stream.
* @exception IOException if an I/O error has occurred * @exception IOException if an I/O error has occurred
*/ */
@Override
public void close() throws IOException { public void close() throws IOException {
if (!closed) { if (!closed) {
try { try {
@ -278,13 +301,20 @@ class DeflaterOutputStream extends FilterOutputStream {
* *
* @since 1.7 * @since 1.7
*/ */
@Override
public void flush() throws IOException { public void flush() throws IOException {
if (syncFlush && !def.finished()) { if (syncFlush && !def.finished()) {
int len = 0; int len = 0;
while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0) // For SYNC_FLUSH, the Deflater.deflate() expects the callers
{ // to use a buffer whose length is greater than 6 to avoid
out.write(buf, 0, len); // flush marker (5 bytes) being repeatedly output to the output buffer
if (len < buf.length) // every time it is invoked.
final byte[] flushBuf = buf.length < SYNC_FLUSH_MIN_BUF_SIZE
? new byte[DEFAULT_BUF_SIZE]
: buf;
while ((len = def.deflate(flushBuf, 0, flushBuf.length, Deflater.SYNC_FLUSH)) > 0) {
out.write(flushBuf, 0, len);
if (len < flushBuf.length)
break; break;
} }
} }

View File

@ -106,7 +106,7 @@ class GZIPOutputStream extends DeflaterOutputStream {
* @exception IOException If an I/O error has occurred. * @exception IOException If an I/O error has occurred.
*/ */
public GZIPOutputStream(OutputStream out) throws IOException { public GZIPOutputStream(OutputStream out) throws IOException {
this(out, 512, false); this(out, DeflaterOutputStream.DEFAULT_BUF_SIZE, false);
} }
/** /**
@ -128,7 +128,7 @@ class GZIPOutputStream extends DeflaterOutputStream {
public GZIPOutputStream(OutputStream out, boolean syncFlush) public GZIPOutputStream(OutputStream out, boolean syncFlush)
throws IOException throws IOException
{ {
this(out, 512, syncFlush); this(out, DeflaterOutputStream.DEFAULT_BUF_SIZE, syncFlush);
} }
/** /**