Skip to content

Commit 332fd82

Browse files
committed
Add zlib decompressor
1 parent 2fcaa65 commit 332fd82

File tree

8 files changed

+713
-1
lines changed

8 files changed

+713
-1
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package io.airlift.compress.zlib;
15+
16+
import io.airlift.compress.Decompressor;
17+
import io.airlift.compress.MalformedInputException;
18+
19+
import java.nio.Buffer;
20+
import java.nio.ByteBuffer;
21+
22+
import static io.airlift.compress.zlib.UnsafeUtil.getAddress;
23+
import static java.lang.String.format;
24+
import static java.util.Objects.requireNonNull;
25+
import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
26+
27+
public class InflateDecompressor
28+
implements Decompressor
29+
{
30+
@Override
31+
public int decompress(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset, int maxOutputLength)
32+
throws MalformedInputException
33+
{
34+
verifyRange(input, inputOffset, inputLength);
35+
verifyRange(output, outputOffset, maxOutputLength);
36+
37+
long inputAddress = ARRAY_BYTE_BASE_OFFSET + inputOffset;
38+
long inputLimit = inputAddress + inputLength;
39+
long outputAddress = ARRAY_BYTE_BASE_OFFSET + outputOffset;
40+
long outputLimit = outputAddress + maxOutputLength;
41+
42+
return InflateRawDecompressor.decompress(input, inputAddress, inputLimit, output, outputAddress, outputLimit);
43+
}
44+
45+
@Override
46+
public void decompress(ByteBuffer inputBuffer, ByteBuffer outputBuffer)
47+
throws MalformedInputException
48+
{
49+
// Java 9+ added an overload of various methods in ByteBuffer. When compiling with Java 11+ and targeting Java 8 bytecode
50+
// the resulting signatures are invalid for JDK 8, so accesses below result in NoSuchMethodError. Accessing the
51+
// methods through the interface class works around the problem
52+
// Sidenote: we can't target "javac --release 8" because Unsafe is not available in the signature data for that profile
53+
Buffer input = inputBuffer;
54+
Buffer output = outputBuffer;
55+
56+
Object inputBase;
57+
long inputAddress;
58+
long inputLimit;
59+
if (input.isDirect()) {
60+
inputBase = null;
61+
long address = getAddress(input);
62+
inputAddress = address + input.position();
63+
inputLimit = address + input.limit();
64+
}
65+
else if (input.hasArray()) {
66+
inputBase = input.array();
67+
inputAddress = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.position();
68+
inputLimit = ARRAY_BYTE_BASE_OFFSET + input.arrayOffset() + input.limit();
69+
}
70+
else {
71+
throw new IllegalArgumentException("Unsupported input ByteBuffer implementation " + input.getClass().getName());
72+
}
73+
74+
Object outputBase;
75+
long outputAddress;
76+
long outputLimit;
77+
if (output.isDirect()) {
78+
outputBase = null;
79+
long address = getAddress(output);
80+
outputAddress = address + output.position();
81+
outputLimit = address + output.limit();
82+
}
83+
else if (output.hasArray()) {
84+
outputBase = output.array();
85+
outputAddress = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.position();
86+
outputLimit = ARRAY_BYTE_BASE_OFFSET + output.arrayOffset() + output.limit();
87+
}
88+
else {
89+
throw new IllegalArgumentException("Unsupported output ByteBuffer implementation " + output.getClass().getName());
90+
}
91+
92+
// HACK: Assure JVM does not collect Slice wrappers while decompressing, since the
93+
// collection may trigger freeing of the underlying memory resulting in a segfault
94+
// There is no other known way to signal to the JVM that an object should not be
95+
// collected in a block, and technically, the JVM is allowed to eliminate these locks.
96+
synchronized (input) {
97+
synchronized (output) {
98+
int written = InflateRawDecompressor.decompress(inputBase, inputAddress, inputLimit, outputBase, outputAddress, outputLimit);
99+
output.position(output.position() + written);
100+
}
101+
}
102+
}
103+
104+
private static void verifyRange(byte[] data, int offset, int length)
105+
{
106+
requireNonNull(data, "data is null");
107+
if (offset < 0 || length < 0 || offset + length > data.length) {
108+
throw new IllegalArgumentException(format("Invalid offset or length (%s, %s) in array of length %s", offset, length, data.length));
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)