blob: a4fcc371c9d9785ea5f5ca8b2ac7533a7287e830 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.lucene.backward_codecs.lucene87;
import java.util.Arrays;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.SuppressForbidden;
/**
* This class is a workaround for JDK bug <a
* href="https://bugs.openjdk.java.net/browse/JDK-8252739">JDK-8252739</a>.
*
* @lucene.internal
*/
@FunctionalInterface
interface BugfixDeflater_JDK8252739 {
public static final boolean IS_BUGGY_JDK = detectBuggyJDK();
/**
* Creates a bugfix for {@link Deflater} instances, which works around JDK-8252739.
*
* <p>Use this whenever you intend to call {@link Deflater#setDictionary(byte[], int, int)} on a
* {@code Deflater}.
*/
@SuppressForbidden(reason = "Works around bug, so it must call forbidden method")
public static BugfixDeflater_JDK8252739 createBugfix(Deflater deflater) {
if (IS_BUGGY_JDK) {
final BytesRefBuilder dictBytesScratch = new BytesRefBuilder();
return (dictBytes, off, len) -> {
if (off > 0) {
dictBytesScratch.grow(len);
System.arraycopy(dictBytes, off, dictBytesScratch.bytes(), 0, len);
deflater.setDictionary(dictBytesScratch.bytes(), 0, len);
} else {
deflater.setDictionary(dictBytes, off, len);
}
};
} else {
return deflater::setDictionary;
}
}
/** Call this method as a workaround */
void setDictionary(byte[] dictBytes, int off, int len);
@SuppressForbidden(reason = "Detector for the bug, so it must call buggy method")
private static boolean detectBuggyJDK() {
final byte[] testData = new byte[] {1, 2, 3, 4, 5, 6, 7, 8};
final byte[] compressed = new byte[32]; // way enough space
final Deflater deflater = new Deflater(6, true);
int compressedSize;
try {
deflater.reset();
deflater.setDictionary(testData, 4, 4);
deflater.setInput(testData);
deflater.finish();
compressedSize = deflater.deflate(compressed, 0, compressed.length, Deflater.FULL_FLUSH);
} finally {
deflater.end();
}
// in nowrap mode we need extra 0-byte as padding, add explicit:
compressed[compressedSize] = 0;
compressedSize++;
final Inflater inflater = new Inflater(true);
final byte[] restored = new byte[testData.length];
try {
inflater.reset();
inflater.setDictionary(testData, 4, 4);
inflater.setInput(compressed, 0, compressedSize);
final int restoredLength = inflater.inflate(restored);
if (restoredLength != testData.length) {
return true;
}
} catch (DataFormatException e) {
return true;
} catch (RuntimeException e) {
return true;
} finally {
inflater.end();
}
if (Arrays.equals(testData, restored) == false) {
return true;
}
// all fine
return false;
}
}