Я думаю, что нашел ошибку в java.util.zip.Deflater и его jni-материалах...
Я могу воспроизвести с помощью FileChannel.transferTo(), но не с помощью простого цикл с байтовыми буферами.
Полный код Java17 ниже.
Вы можете передать небольшой файл размером всего 100–150 байт, и проблема все равно будет видна.
Шаговое выполнение через Код дефлятора, по завершении результат вызова
result = deflateBytesBuffer(...) рядом со строкой 765 (jdk 17)
возвращает значение, биты которого сдвигаются на найдите прочитанные и записанные байты (строка 782/783). «Написанное» значение определенно неверно. Побочным эффектом является то, что дефлятор производит больше байтов, в основном повторяя часть того, что у него есть. Вот почему я использовал NO_COMPRESSION, чтобы с помощью простого короткого текстового файла можно было обнаружить повторяющийся шаблон.
Тестовый код:
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.StandardOpenOption;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
public class TestDeflaterBug2 {
static final boolean GZ = true;
public static void main(String[] args) throws IOException {
int complevel = Deflater.NO_COMPRESSION;
//int complevel = Deflater.DEFAULT_COMPRESSION;
test(args, complevel, false);
test(args, complevel, true);
}
static void test(String[] args, int complevel, boolean useTrfTo) throws IOException {
String inputFilename = args.length>0 ? args[0] : "x";
File f1 = new File(inputFilename);
if (!f1.isFile())
throw new IllegalArgumentException("input file does not exist: " + f1);
String outputFilename = inputFilename+"-L"+(complevel0) {
long len = inCh.transferTo(pos, remaining, deflChannel);
remaining -= len;
pos += len;
}
} else {
//ByteBuffer inBB = ByteBuffer.allocateDirect(8192);
ByteBuffer inBB = ByteBuffer.allocate(8192);
long lastDot = 0;
long pos = 0;
long end = inCh.size();
while(pos>> 20;
for (long d = lastDot + 1; d 2;
case Deflater.BEST_SPEED -> 4;
default -> 0;
});
workBuffer.put((byte)-1); //OS
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
@Override
public boolean isOpen() {
return !closed;
}
@Override
public int write(ByteBuffer src) throws IOException {
if (deflater.finished())
throw new IOException("compression already finished");
int len = src.remaining();
if (len > 0) {
src.mark();
deflater.setInput(src);
while (!deflater.needsInput())
drainDeflaterOnce();
src.reset();
crc.update(src);
}
return len;
}
private void drainDeflaterOnce() throws IOException {
workBuffer.clear();
deflater.deflate(workBuffer);
workBuffer.flip();
while (workBuffer.hasRemaining()) //this is only efficient on blocking channel, else may spin fast...
out.write(workBuffer);
}
@Override
public void close() throws IOException {
if (closed)
return;
try {
finish();
} finally {
deflater.end();
out.close();
closed = true;
}
}
public void finish() throws IOException {
if (deflater.finished())
return;
deflater.finish();
while (!deflater.finished())
drainDeflaterOnce();
if(GZ)
writeFooter();
}
private void writeFooter() throws IOException {
workBuffer.clear().order(ByteOrder.LITTLE_ENDIAN);
workBuffer.putInt((int)crc.getValue());
workBuffer.putInt(deflater.getTotalIn());
workBuffer.flip();
while(workBuffer.hasRemaining())
out.write(workBuffer);
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/792 ... nel-transf
Ошибка? в Deflate из кучи ByteBuffer для направления ByteBuffer через FileChannel.transferTo() ⇐ JAVA
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение