Merge branch 'GP-6862_CompressNativeMemoryLeak' into patch

This commit is contained in:
ghidra1
2026-05-26 17:16:50 -04:00
11 changed files with 326 additions and 289 deletions

View File

@@ -18,12 +18,13 @@ public final class GFileUtilityMethods {
public final static File writeTemporaryFile( InputStream inputStream, int maxBytesToWrite ) throws IOException {
File tempOutputFile = File.createTempFile( GHIDRA_FILE_SYSTEM_PREFIX, GHIDRA_FILE_SYSTEM_SUFFIX );
tempOutputFile.deleteOnExit();
OutputStream outputStream = new FileOutputStream( tempOutputFile );
try {
try (OutputStream outputStream = new FileOutputStream(tempOutputFile)) {
int nWritten = 0;
byte [] buffer = new byte[ 8192 ];
byte[] buffer = new byte[8192];
while ( true ) {
int nRead = inputStream.read( buffer );
int limit = Math.min(maxBytesToWrite - nWritten, buffer.length);
int nRead = inputStream.read(buffer, 0, limit);
if ( nRead == -1 ) {
break;
}
@@ -34,13 +35,10 @@ public final class GFileUtilityMethods {
}
}
}
finally {
outputStream.close();
}
return tempOutputFile;
}
public final static File writeTemporaryFile( byte [] bytes, String prefix ) throws IOException {
public final static File writeTemporaryFile(byte[] bytes, String prefix) throws IOException {
if ( prefix == null ) {
prefix = GHIDRA_FILE_SYSTEM_PREFIX;
}
@@ -51,13 +49,11 @@ public final class GFileUtilityMethods {
}
File tempFile = File.createTempFile( prefix , GHIDRA_FILE_SYSTEM_SUFFIX );
tempFile.deleteOnExit();
OutputStream tempFileOut = new FileOutputStream( tempFile );
try {
try (OutputStream tempFileOut = new FileOutputStream(tempFile)) {
tempFileOut.write( bytes );
}
finally {
tempFileOut.close();
}
return tempFile;
}
}

View File

@@ -6,6 +6,7 @@ package mobiledevices.dmg.reader;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.InflaterInputStream;
import org.catacombae.dmgextractor.encodings.encrypted.ReadableCEncryptedEncodingStream;
import org.catacombae.hfsexplorer.FileSystemRecognizer;
@@ -27,7 +28,6 @@ import mobiledevices.dmg.decmpfs.DecmpfsCompressionTypes;
import mobiledevices.dmg.decmpfs.DecmpfsHeader;
import mobiledevices.dmg.ghidra.*;
import mobiledevices.dmg.hfsplus.AttributesFileParser;
import mobiledevices.dmg.zlib.ZLIB;
public class DmgFileReader implements Closeable {
private final static GDataConverter ledc = new GDataConverterLE();
@@ -191,24 +191,16 @@ public class DmgFileReader implements Closeable {
if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type3 ) {
if ( decmpfsHeader.getAttrBytes()[ 0 ] == -1 ) {
return new ByteArrayInputStream( decmpfsHeader.getAttrBytes(), 1, decmpfsHeader.getAttrBytes().length - 1 );
return new ByteArrayInputStream(decmpfsHeader.getAttrBytes(), 1,
decmpfsHeader.getAttrBytes().length - 1);
}
ZLIB zlib = new ZLIB();
InputStream inputStream =
new ByteArrayInputStream(decmpfsHeader.getAttrBytes());
ByteArrayOutputStream uncompressedBytes =
zlib.decompress(inputStream, (int) decmpfsHeader.getUncompressedSize());
File tempDecompressedFile = GFileUtilityMethods.writeTemporaryFile(
uncompressedBytes.toByteArray(), entry.getName());
return new FileInputStream(tempDecompressedFile);
return new RestrictedInflaterInputStream(decmpfsHeader.getAttrBytes(),
(int) decmpfsHeader.getUncompressedSize());
}
else if ( decmpfsHeader.getCompressionType() == DecmpfsCompressionTypes.CMP_Type4 ) {
return decompressResourceFork( entry, resourceForkStream, (int)decmpfsHeader.getUncompressedSize() );
return decompressResourceFork(entry, resourceForkStream,
(int) decmpfsHeader.getUncompressedSize());
}
}
}
@@ -223,34 +215,102 @@ public class DmgFileReader implements Closeable {
System.err.println(
"dmg resource fork for " + entry.getName() + ": " + tempFile.getAbsolutePath());
InputStream input = new FileInputStream( tempFile );
// Copy compressed portion of tempFile to tempCompressedFile
File tempCompressedFile;
try (InputStream input = new FileInputStream(tempFile)) {
for ( int i = 0 ; i < 0x100 ; ++i ) {
input.read();
for (int i = 0; i < 0x100; ++i) {
input.read();
}
byte[] sizeBytes = new byte[4];
input.read(sizeBytes);
int size = sizeBytes[0] == 0 ? bedc.getInt(sizeBytes) : ledc.getInt(sizeBytes);
byte[] flagsBytes = new byte[4];
input.read(flagsBytes);
byte[] startDistanceBytes = new byte[4];
input.read(startDistanceBytes);
int startDistance = ledc.getInt(startDistanceBytes);
input.skip(startDistance - 8);//skip to the start of the zlib compressed file
tempCompressedFile =
GFileUtilityMethods.writeTemporaryFile(input, size - startDistance);
}
finally {
tempFile.delete();
}
byte [] sizeBytes = new byte[ 4 ];
input.read( sizeBytes );
int size = sizeBytes[ 0 ] == 0 ?
bedc.getInt( sizeBytes ) :
ledc.getInt( sizeBytes );
return new RestrictedInflaterInputStream(tempCompressedFile, expectedLength);
}
byte [] flagsBytes = new byte[ 4 ];
input.read( flagsBytes );
private class RestrictedInflaterInputStream extends InflaterInputStream {
byte [] startDistanceBytes = new byte[ 4 ];
input.read( startDistanceBytes );
int startDistance = ledc.getInt( startDistanceBytes );
private File tempCompressedFile;
private int readLimit;
private int readCount = 0;
input.skip( startDistance - 8 );//skip to the start of the zlib compressed file
/**
* Creates a new Inflater input stream with a default decompressor and buffer size.
* NOTE: The default Inflater instance will be ended when this stream closes.
* @param tempCompressedFile the temporary file containing compressed data. File will be
* removed when this stream is closed.
* @param readLimit maximum data read count. Exceeding this limit will result in an
* IOException.
* @throws FileNotFoundException if tempCompressedFile does not exist
*/
RestrictedInflaterInputStream(File tempCompressedFile, int readLimit)
throws FileNotFoundException {
super(new FileInputStream(tempCompressedFile));
this.tempCompressedFile = tempCompressedFile;
this.readLimit = readLimit;
}
File tempCompressedFile = GFileUtilityMethods.writeTemporaryFile( input, size - startDistance );
InputStream inputStream = new FileInputStream( tempCompressedFile );
/**
* Creates a new Inflater input stream with a default decompressor and buffer size.
* NOTE: The default Inflater instance will be ended when this stream closes.
* @param compressedData the byte array containing compressed data.
* @param readLimit maximum data read count. Exceeding this limit will result in an
* IOException.
*/
RestrictedInflaterInputStream(byte[] compressedData, int readLimit) {
super(new ByteArrayInputStream(compressedData));
this.tempCompressedFile = null;
this.readLimit = readLimit;
}
ZLIB zlib = new ZLIB( );
ByteArrayOutputStream uncompressedByteStream = zlib.decompress( inputStream, expectedLength );
@Override
public void close() throws IOException {
try {
super.close();
}
finally {
// Cleanup temporary file input stream and remove file
in.close();
if (tempCompressedFile != null) {
tempCompressedFile.delete();
}
}
}
return new ByteArrayInputStream( uncompressedByteStream.toByteArray() );
@Override
public int read(byte[] b, int off, int length) throws IOException {
if (length == 0) {
return 0;
}
if (readCount >= readLimit) {
throw new IOException("Decompression limit exceeded: " + readLimit);
}
// Limit read length to avoid exceeding readLimit
int limit = Math.min(readLimit - readCount, length);
int count = super.read(b, off, limit);
if (count > 0) {
readCount += count;
}
return count;
}
}
public List<String> getInfo( String path ) {

View File

@@ -128,6 +128,8 @@ public class DmgServer {
sendResponse(temporaryFile.getAbsolutePath());
// TODO: When can we remove temporaryFile
if (expectedFileLength != temporaryFile.length()) {
log("file sizes do not match!");
}

View File

@@ -1,120 +0,0 @@
/* ###
* IP: Public Domain
*/
package mobiledevices.dmg.zlib;
import java.io.*;
import java.util.zip.*;
/**
*
* TODO make this more memory efficient!!
*
*/
public class ZLIB {
public ZLIB() {
}
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength ) throws IOException {
return decompress( compressedIn, expectedDecompressedLength, false );
}
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength, boolean noWrap ) throws IOException {
byte [] compressedBytes = convertInputStreamToByteArray( compressedIn );
ByteArrayOutputStream decompressedBOS = new ByteArrayOutputStream();
byte [] tempDecompressedBytes = new byte[ 0x10000 ];
int totalDecompressed = 0;
int offset = 0;
try {
while ( offset < compressedBytes.length && totalDecompressed < expectedDecompressedLength ) {
if ( !noWrap && compressedBytes [ offset ] != 0x78 ) {
break;
}
Inflater inflater = new Inflater( noWrap );
inflater.setInput( compressedBytes, offset, compressedBytes.length - offset );
int nDecompressed = inflater.inflate( tempDecompressedBytes );
if ( nDecompressed == 0 ) {
break;
}
totalDecompressed += nDecompressed;
decompressedBOS.write( tempDecompressedBytes, 0, nDecompressed );
offset += inflater.getTotalIn();//increment total compressed bytes consumed
}
}
catch ( DataFormatException e ) {
throw new IOException( e.getMessage() );
}
return decompressedBOS;
}
public ByteArrayOutputStream compress(byte[] decompressedBytes) {
return compress( false, decompressedBytes );
}
public ByteArrayOutputStream compress(boolean noWrap, byte[] decompressedBytes) {
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
byte [] tempBuffer = new byte[ 0x10000 ];
int offset = 0;
while ( offset < decompressedBytes.length ) {
Deflater deflater = new Deflater( 0, noWrap );
deflater.setInput( decompressedBytes, offset, decompressedBytes.length - offset );
deflater.finish();
if ( deflater.needsInput() ) {
System.err.println( "needs input??" );
}
int nDeflated = deflater.deflate( tempBuffer );
if ( nDeflated == 0 ) {
break;
}
compressedBOS.write( tempBuffer, 0, nDeflated );
offset += deflater.getTotalIn();
}
return compressedBOS;
}
/**
* Converts the contents of an input stream to a byte array
* @param compressedIn
* @return
* @throws IOException
*/
private byte [] convertInputStreamToByteArray( InputStream compressedIn ) throws IOException {
byte [] bytes = new byte[ 8096 ];
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
while ( true ) {
int nRead = compressedIn.read( bytes );
if ( nRead == -1 ) {
break;
}
compressedBOS.write( bytes, 0, nRead );
}
return compressedBOS.toByteArray();
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,12 +15,13 @@
*/
package ghidra.file.formats.zlib;
import ghidra.app.util.bin.ByteProvider;
import java.io.*;
import java.util.Arrays;
import java.util.zip.*;
import ghidra.app.util.bin.ByteProvider;
import ghidra.util.Msg;
/**
*
* TODO make this more memory efficient!!
@@ -57,12 +58,14 @@ public class ZLIB {
* Note: When using the 'noWrap' option it is also necessary to provide an extra "dummy" byte as input.
* This is required by the ZLIB native library in order to support certain optimizations.
* @param compressedIn an input stream containing the compressed data
* @param expectedDecompressedLength the expected length of the decompressed data
* @param decompressedSizeLimit the maximum length of the decompressed data. Actual decompressed
* data within returned ByteArrayOutputStream may still exceed this limit.
* @param noWrap if true then support GZIP compatible compression
* @return an output stream containing the decompressed data
* @throws IOException
* @throws IOException if IO error occurs while reading compressedIn stream
*/
public ByteArrayOutputStream decompress( InputStream compressedIn, int expectedDecompressedLength, boolean noWrap ) throws IOException {
public ByteArrayOutputStream decompress(InputStream compressedIn, int decompressedSizeLimit,
boolean noWrap) throws IOException {
byte [] compressedBytes = convertInputStreamToByteArray( compressedIn );
@@ -70,37 +73,25 @@ public class ZLIB {
byte [] tempDecompressedBytes = new byte[ 0x10000 ];
int totalDecompressed = 0;
int offset = 0;
try {
while ( offset < compressedBytes.length && totalDecompressed < expectedDecompressedLength ) {
if ( !noWrap && compressedBytes [ offset ] != 0x78 ) {
break;
}
Inflater inflater = new Inflater( noWrap );
inflater.setInput( compressedBytes, offset, compressedBytes.length - offset );
int nDecompressed = inflater.inflate( tempDecompressedBytes );
if ( nDecompressed == 0 ) {
break;
}
totalDecompressed += nDecompressed;
decompressedBOS.write( tempDecompressedBytes, 0, nDecompressed );
offset += inflater.getTotalIn();//increment total compressed bytes consumed
}
}
catch ( DataFormatException e ) {
throw new IOException( e.getMessage() );
}
Inflater inflater = new Inflater(noWrap);
try {
inflater.setInput(compressedBytes);
while (!inflater.finished()) {
int nDecompressed = inflater.inflate(tempDecompressedBytes);
decompressedBOS.write(tempDecompressedBytes, 0, nDecompressed);
if (decompressedBOS.size() > decompressedSizeLimit) {
Msg.warn(this, "ZLIB decompress exceeded specified limit (" +
decompressedBOS.size() + " > " + decompressedSizeLimit + ")");
break;
}
}
}
catch (DataFormatException e) {
throw new IOException(e.getMessage());
}
finally {
inflater.end();
}
return decompressedBOS;
}
@@ -129,9 +120,8 @@ public class ZLIB {
* to support the compression format used in both GZIP and PKZIP.
* @param decompressedBytes the decompressed bytes
* @return an output stream containing the compressed data
* @throws IOException
*/
public ByteArrayOutputStream compress( byte [] decompressedBytes ) throws IOException {
public ByteArrayOutputStream compress(byte[] decompressedBytes) {
return compress( false, decompressedBytes );
}
@@ -142,38 +132,24 @@ public class ZLIB {
* @param noWrap if true then use GZIP compatible compression
* @param decompressedBytes the decompressed bytes
* @return an output stream containing the compressed data
* @throws IOException
*/
public ByteArrayOutputStream compress( boolean noWrap, byte [] decompressedBytes ) throws IOException {
public ByteArrayOutputStream compress(boolean noWrap, byte[] decompressedBytes) {
ByteArrayOutputStream compressedBOS = new ByteArrayOutputStream();
byte [] tempBuffer = new byte[ 0x10000 ];
int offset = 0;
while ( offset < decompressedBytes.length ) {
Deflater deflater = new Deflater( 0, noWrap );
deflater.setInput( decompressedBytes, offset, decompressedBytes.length - offset );
deflater.finish();
if ( deflater.needsInput() ) {
System.out.println( "needs input??" );
}
int nDeflated = deflater.deflate( tempBuffer );
if ( nDeflated == 0 ) {
break;
}
compressedBOS.write( tempBuffer, 0, nDeflated );
offset += deflater.getTotalIn();
}
Deflater deflater = new Deflater(0, noWrap);
try {
deflater.setInput(decompressedBytes);
deflater.finish();
while (!deflater.finished()) {
int nDeflated = deflater.deflate(tempBuffer);
compressedBOS.write(tempBuffer, 0, nDeflated);
}
}
finally {
deflater.end();
}
return compressedBOS;
}

View File

@@ -0,0 +1,53 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.file.formats.zlib;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import java.util.Arrays;
import org.junit.Test;
import generic.test.AbstractGTest;
public class ZLIBTest extends AbstractGTest {
@Test
public void testCompressDecompress() throws Exception {
ZLIB zlib = new ZLIB();
byte[] data = new byte[100000];
SecureRandom random = new SecureRandom();
random.nextBytes(data);
// TODO: ZLIB API should be revised to avoid senseless exposure of ByteArrayOutputStream
try (ByteArrayOutputStream compress = zlib.compress(data)) {
byte[] compressedBytes = compress.toByteArray();
new ByteArrayInputStream(compressedBytes);
ByteArrayOutputStream decompressOut =
zlib.decompress(new ByteArrayInputStream(compressedBytes), data.length);
byte[] decompressedBytes = decompressOut.toByteArray();
assertTrue("Zlib round-trip failed", Arrays.equals(data, decompressedBytes));
}
}
}

View File

@@ -0,0 +1,53 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.server.stream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
/**
* {@link RemoteDeflaterOutputStream} extends {@link DeflaterOutputStream} and
* allows a non-default {@link Deflater} to "end" properly when this output stream
* is closed.
*/
class RemoteDeflaterOutputStream extends DeflaterOutputStream {
private boolean closed = false;
/**
* Construct a deflater output stream using a specified compression level.
* @param out output stream providing the uncompressed data
* @param deflaterLevel {@link Deflater} compression level
*/
RemoteDeflaterOutputStream(OutputStream out, int deflaterLevel) {
super(out, new Deflater(deflaterLevel));
}
@Override
public void close() throws IOException {
if (!closed) {
try {
super.close();
}
finally {
closed = true;
def.end(); // Must end on close since we did not use default Deflater
}
}
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,7 +18,8 @@ package ghidra.server.stream;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.zip.*;
import java.util.zip.Deflater;
import java.util.zip.InflaterInputStream;
import db.buffers.*;
@@ -56,13 +57,17 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
private final Socket socket;
private final InputStream in;
private int blocksRemaining = getBlockCount();
ClientInputBlockStream(Socket socket) throws IOException {
this.socket = socket;
in = compressed ? new InflaterInputStream(socket.getInputStream())
: socket.getInputStream();
if (compressed) {
// Uses default Inflater with nowrap=false
in = new InflaterInputStream(socket.getInputStream());
} else {
in = socket.getInputStream();
}
}
@Override
@@ -133,7 +138,7 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
socket.setSendBufferSize(getPreferredBufferSize());
InputBlockStream inputBlockStream = (InputBlockStream) blockStream;
try (OutputStream out = socket.getOutputStream()) {
try (OutputStream out = getBlockInputStream(socket)) {
copyBlockData(inputBlockStream, out);
@@ -150,13 +155,17 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
}
}
private OutputStream getBlockInputStream(Socket socket) throws IOException {
OutputStream out = socket.getOutputStream();
if (compressed) {
out = new RemoteDeflaterOutputStream(out, Deflater.BEST_SPEED);
}
return out;
}
private void copyBlockData(InputBlockStream inputBlockStream, OutputStream out)
throws IOException {
if (compressed) {
out = new DeflaterOutputStream(out, new Deflater(Deflater.BEST_SPEED));
}
int blocksRemaining = getBlockCount();
BufferFileBlock block;
@@ -167,11 +176,6 @@ public class RemoteInputBlockStreamHandle extends RemoteBlockStreamHandle<InputB
out.write(block.toBytes());
--blocksRemaining;
}
// done with compressed stream, force compressed data to flush
if (out instanceof DeflaterOutputStream) {
((DeflaterOutputStream) out).finish();
}
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -60,10 +60,11 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
ClientOutputBlockStream(Socket socket) throws IOException {
this.socket = socket;
out = compressed
? new DeflaterOutputStream(socket.getOutputStream(),
new Deflater(Deflater.BEST_SPEED))
: socket.getOutputStream();
if (compressed) {
out = new RemoteDeflaterOutputStream(socket.getOutputStream(), Deflater.BEST_SPEED);
} else {
out = socket.getOutputStream();
}
}
@Override
@@ -123,7 +124,7 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
socket.setReceiveBufferSize(getPreferredBufferSize());
OutputBlockStream outputBlockStream = (OutputBlockStream) blockStream;
try (InputStream in = socket.getInputStream()) {
try (InputStream in = getBlockInputStream(socket)) {
copyBlockData(outputBlockStream, in);
@@ -148,12 +149,16 @@ public class RemoteOutputBlockStreamHandle extends RemoteBlockStreamHandle<Outpu
}
private void copyBlockData(OutputBlockStream outputBlockStream, InputStream in)
throws IOException, EOFException {
private InputStream getBlockInputStream(Socket socket) throws IOException {
InputStream in = socket.getInputStream();
if (compressed) {
in = new InflaterInputStream(in);
}
return in;
}
private void copyBlockData(OutputBlockStream outputBlockStream, InputStream in)
throws IOException, EOFException {
int blocksRemaining = getBlockCount();

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -330,23 +330,29 @@ public class DataBuffer implements Buffer, Externalizable {
*/
private static int deflateData(byte[] data, byte[] compressedData) {
Deflater deflate = new Deflater(Deflater.BEST_COMPRESSION, true);
deflate.setStrategy(Deflater.HUFFMAN_ONLY);
deflate.setInput(data, 0, data.length);
deflate.finish();
// Deflater must be consistent with inflateData nowrap option and should
// not be changed since client/server must match.
// NOTE: compression mode may be adjusted to optimize performance
Deflater deflate = new Deflater(Deflater.BEST_SPEED, true);
try {
deflate.setInput(data, 0, data.length);
deflate.finish();
int compressedDataOffset = 0;
while (!deflate.finished() && compressedDataOffset < compressedData.length) {
compressedDataOffset += deflate.deflate(compressedData, compressedDataOffset,
compressedData.length - compressedDataOffset, Deflater.SYNC_FLUSH);
int compressedDataOffset = 0;
while (!deflate.finished() && compressedDataOffset < compressedData.length) {
compressedDataOffset += deflate.deflate(compressedData, compressedDataOffset,
compressedData.length - compressedDataOffset);
}
if (!deflate.finished()) {
return -1;
}
return compressedDataOffset;
} finally {
deflate.end(); // get rid of any native memory rather than waiting for GC
}
if (!deflate.finished()) {
return -1;
}
return compressedDataOffset;
}
@Override
@@ -412,10 +418,11 @@ public class DataBuffer implements Buffer, Externalizable {
*/
private static void inflateData(byte[] compressedData, byte[] data) throws IOException {
// Inflater must be consistent with deflateData nowrap option and should
// not be changed since client/server must match.
Inflater inflater = new Inflater(true);
inflater.setInput(compressedData, 0, compressedData.length);
try {
inflater.setInput(compressedData);
int off = 0;
while (!inflater.finished() && off < data.length) {
off += inflater.inflate(data, off, data.length - off);
@@ -430,6 +437,9 @@ public class DataBuffer implements Buffer, Externalizable {
catch (DataFormatException e) {
throw new IOException("DataBuffer inflation failed", e);
}
finally {
inflater.end(); // get rid of any native memory rather than waiting for GC
}
}
}

View File

@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -248,21 +248,19 @@ public class SlaFormat {
* @throws IOException if the header is invalid or there are problems reading the file
*/
public static PackedDecode buildDecoder(ResourceFile sleighFile) throws IOException {
InputStream stream = sleighFile.getInputStream();
try {
try (InputStream stream = sleighFile.getInputStream()) {
if (!isSlaFormat(stream)) {
throw new IOException("Missing SLA format header");
}
InflaterInputStream inflaterStream = new InflaterInputStream(stream);
PackedDecode decoder = new PackedDecode();
decoder.open(MAX_FILE_SIZE, ".sla file loader");
decoder.ingestStream(inflaterStream);
try (InflaterInputStream inflaterStream = new InflaterInputStream(stream)) {
decoder.ingestStream(inflaterStream);
}
decoder.endIngest();
inflaterStream.close();
return decoder;
}
finally {
stream.close();
}
}
}