Skip to content

Commit ecce661

Browse files
committed
Fix LibdeflateDeflater/Inflater compatibility with ByteBuffer API.
LibdeflateDeflater/Inflater call super.end() in their constructors to free the unused JDK zlib stream, but only override the byte[] methods. Callers using the ByteBuffer overloads (e.g. GzipCodec) hit "Deflater has been closed" because the base Deflater's ByteBuffer methods check the freed native stream. - Add ByteBuffer overrides to LibdeflateDeflater (setInput, deflate) and LibdeflateInflater (setInput, inflate) that delegate to the byte[] implementations - Update GzipCodec compress/decompress to use byte[] deflater/inflater API as a belt-and-suspenders fix
1 parent 3e30cf1 commit ecce661

3 files changed

Lines changed: 87 additions & 12 deletions

File tree

src/main/java/htsjdk/samtools/util/GzipCodec.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,26 @@ public int compress(final ByteBuffer input, final ByteBuffer output, final Forma
154154
// Write header (reserves space; for BGZF the block size is patched after deflation)
155155
final int headerSize = writeHeader(output, format);
156156

157-
// Deflate
157+
// Extract input bytes for deflater (byte[] API for compatibility with LibdeflateDeflater)
158+
final byte[] inputBytes;
159+
final int inputOff;
160+
input.position(inputPos);
161+
if (input.hasArray()) {
162+
inputBytes = input.array();
163+
inputOff = input.arrayOffset() + inputPos;
164+
} else {
165+
inputBytes = new byte[inputSize];
166+
input.get(inputBytes);
167+
inputOff = 0;
168+
}
169+
170+
// Deflate into a temporary byte[] then copy to output buffer
158171
deflater.reset();
159-
input.position(inputPos); // ensure input is at start for deflater
160-
deflater.setInput(input);
172+
deflater.setInput(inputBytes, inputOff, inputSize);
161173
deflater.finish();
162-
163-
// Deflate into the output buffer (after the header)
164174
while (!deflater.finished()) {
165-
deflater.deflate(output);
175+
final int n = deflater.deflate(output.array(), output.arrayOffset() + output.position(), output.remaining());
176+
output.position(output.position() + n);
166177
}
167178

168179
// Write trailer: CRC32 + ISIZE (little-endian)
@@ -292,18 +303,29 @@ public int decompress(final ByteBuffer input, final ByteBuffer output) {
292303
throw new IllegalArgumentException("Invalid GZIP block: no room for deflated data and trailer");
293304
}
294305

295-
// Set up inflater with the deflated data
306+
// Extract deflated bytes for inflater (byte[] API for compatibility with LibdeflateInflater)
307+
final byte[] deflatedBytes;
308+
final int deflatedOff;
309+
if (input.hasArray()) {
310+
deflatedBytes = input.array();
311+
deflatedOff = input.arrayOffset() + deflatedStart;
312+
} else {
313+
deflatedBytes = new byte[deflatedSize];
314+
input.position(deflatedStart);
315+
input.get(deflatedBytes);
316+
deflatedOff = 0;
317+
}
318+
296319
inflater.reset();
297-
final ByteBuffer deflatedSlice = input.duplicate();
298-
deflatedSlice.position(deflatedStart);
299-
deflatedSlice.limit(deflatedEnd);
300-
inflater.setInput(deflatedSlice);
320+
inflater.setInput(deflatedBytes, deflatedOff, deflatedSize);
301321

302322
// Inflate into output
303323
try {
304324
int totalInflated = 0;
305325
while (!inflater.finished() && output.hasRemaining()) {
306-
totalInflated += inflater.inflate(output);
326+
final int n = inflater.inflate(output.array(), output.arrayOffset() + output.position(), output.remaining());
327+
output.position(output.position() + n);
328+
totalInflated += n;
307329
}
308330

309331
// Read trailer: CRC32 + ISIZE

src/main/java/htsjdk/samtools/util/zip/LibdeflateDeflater.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.fulcrumgenomics.jlibdeflate.LibdeflateCompressor;
2727

28+
import java.nio.ByteBuffer;
2829
import java.util.zip.Deflater;
2930

3031
/**
@@ -70,6 +71,19 @@ public void setInput(final byte[] input, final int off, final int len) {
7071
this.done = false;
7172
}
7273

74+
@Override
75+
public void setInput(final ByteBuffer input) {
76+
final int len = input.remaining();
77+
if (input.hasArray()) {
78+
setInput(input.array(), input.arrayOffset() + input.position(), len);
79+
input.position(input.limit());
80+
} else {
81+
final byte[] bytes = new byte[len];
82+
input.get(bytes);
83+
setInput(bytes, 0, len);
84+
}
85+
}
86+
7387
@Override
7488
public void finish() {
7589
this.finishing = true;
@@ -95,6 +109,21 @@ public int deflate(final byte[] output, final int off, final int len) {
95109
return compressed;
96110
}
97111

112+
@Override
113+
public int deflate(final ByteBuffer output) {
114+
return deflate(output, Deflater.NO_FLUSH);
115+
}
116+
117+
@Override
118+
public int deflate(final ByteBuffer output, final int flush) {
119+
if (!output.hasArray()) {
120+
throw new UnsupportedOperationException("LibdeflateDeflater requires a heap-backed ByteBuffer for output");
121+
}
122+
final int n = deflate(output.array(), output.arrayOffset() + output.position(), output.remaining());
123+
output.position(output.position() + n);
124+
return n;
125+
}
126+
98127
@Override
99128
public boolean finished() {
100129
return finishing && done;

src/main/java/htsjdk/samtools/util/zip/LibdeflateInflater.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.fulcrumgenomics.jlibdeflate.LibdeflateDecompressor;
2727

28+
import java.nio.ByteBuffer;
2829
import java.util.zip.DataFormatException;
2930
import java.util.zip.Inflater;
3031

@@ -66,6 +67,29 @@ public void setInput(final byte[] input, final int off, final int len) {
6667
this.inputLen = len;
6768
}
6869

70+
@Override
71+
public void setInput(final ByteBuffer input) {
72+
final int len = input.remaining();
73+
if (input.hasArray()) {
74+
setInput(input.array(), input.arrayOffset() + input.position(), len);
75+
input.position(input.limit());
76+
} else {
77+
final byte[] bytes = new byte[len];
78+
input.get(bytes);
79+
setInput(bytes, 0, len);
80+
}
81+
}
82+
83+
@Override
84+
public int inflate(final ByteBuffer output) throws DataFormatException {
85+
if (!output.hasArray()) {
86+
throw new UnsupportedOperationException("LibdeflateInflater requires a heap-backed ByteBuffer for output");
87+
}
88+
final int n = inflate(output.array(), output.arrayOffset() + output.position(), output.remaining());
89+
output.position(output.position() + n);
90+
return n;
91+
}
92+
6993
@Override
7094
public int inflate(final byte[] output, final int off, final int len) throws DataFormatException {
7195
if (inputBuf == null || inputLen == 0 || len == 0) {

0 commit comments

Comments
 (0)