You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2016/10/12 15:12:27 UTC
commons-compress git commit: COMPRESS-327 read 7z archives from
arbitrary SeekableByteChannels - needs optimization
Repository: commons-compress
Updated Branches:
refs/heads/master 87f0f2ec3 -> daeb07457
COMPRESS-327 read 7z archives from arbitrary SeekableByteChannels - needs optimization
Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/daeb0745
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/daeb0745
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/daeb0745
Branch: refs/heads/master
Commit: daeb07457183232eb9a9fb71bfb31a3700404427
Parents: 87f0f2e
Author: Stefan Bodewig <bo...@apache.org>
Authored: Wed Oct 12 17:06:28 2016 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Wed Oct 12 17:06:28 2016 +0200
----------------------------------------------------------------------
.../BoundedSeekableByteChannelInputStream.java | 92 ++++++++
.../compress/archivers/sevenz/SevenZFile.java | 219 +++++++++++--------
2 files changed, 224 insertions(+), 87 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/commons-compress/blob/daeb0745/src/main/java/org/apache/commons/compress/archivers/sevenz/BoundedSeekableByteChannelInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/sevenz/BoundedSeekableByteChannelInputStream.java b/src/main/java/org/apache/commons/compress/archivers/sevenz/BoundedSeekableByteChannelInputStream.java
new file mode 100644
index 0000000..32b3bda
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/BoundedSeekableByteChannelInputStream.java
@@ -0,0 +1,92 @@
+/*
+ * 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.commons.compress.archivers.sevenz;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+
+class BoundedSeekableByteChannelInputStream extends InputStream {
+ private static final int MAX_BUF_LEN = 8192;
+ private final ByteBuffer buffer;
+ private final SeekableByteChannel channel;
+ private long bytesRemaining;
+
+ public BoundedSeekableByteChannelInputStream(final SeekableByteChannel channel,
+ final long size) {
+ this.channel = channel;
+ this.bytesRemaining = size;
+ if (size < MAX_BUF_LEN && size > 0) {
+ buffer = ByteBuffer.allocate((int) size);
+ } else {
+ buffer = ByteBuffer.allocate(MAX_BUF_LEN);
+ }
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (bytesRemaining > 0) {
+ --bytesRemaining;
+ int read = read(1);
+ if (read < 0) {
+ return read;
+ }
+ return buffer.get() & 0xff;
+ }
+ return -1;
+ }
+
+ @Override
+ public int read(final byte[] b, final int off, final int len) throws IOException {
+ if (bytesRemaining == 0) {
+ return -1;
+ }
+ int bytesToRead = len;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = (int) bytesRemaining;
+ }
+ int bytesRead;
+ ByteBuffer buf;
+ if (bytesToRead <= buffer.capacity()) {
+ buf = buffer;
+ bytesRead = read(bytesToRead);
+ } else {
+ buf = ByteBuffer.allocate(bytesToRead);
+ bytesRead = channel.read(buf);
+ buf.flip();
+ }
+ if (bytesRead >= 0) {
+ buf.get(b, off, bytesRead);
+ bytesRemaining -= bytesRead;
+ }
+ return bytesRead;
+ }
+
+ private int read(int len) throws IOException {
+ buffer.rewind().limit(len);
+ int read = channel.read(buffer);
+ buffer.flip();
+ return read;
+ }
+
+ @Override
+ public void close() {
+ // the nested channel is controlled externally
+ }
+}
http://git-wip-us.apache.org/repos/asf/commons-compress/blob/daeb0745/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
index 51ba9e6..e4115a1 100644
--- a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZFile.java
@@ -25,10 +25,14 @@ import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.EnumSet;
import java.util.LinkedList;
import java.util.zip.CRC32;
@@ -38,7 +42,7 @@ import org.apache.commons.compress.utils.CharsetNames;
import org.apache.commons.compress.utils.IOUtils;
/**
- * Reads a 7z file, using RandomAccessFile under
+ * Reads a 7z file, using SeekableByteChannel under
* the covers.
* <p>
* The 7z file format is a flexible container
@@ -55,7 +59,7 @@ import org.apache.commons.compress.utils.IOUtils;
* Hence the official tools recommend against
* using it for backup purposes on *nix, and
* recommend .tar.7z or .tar.lzma or .tar.xz
- * instead.
+ * instead.
* <p>
* Both the header and file contents may be
* compressed and/or encrypted. With both
@@ -70,7 +74,7 @@ public class SevenZFile implements Closeable {
static final int SIGNATURE_HEADER_SIZE = 32;
private final String fileName;
- private RandomAccessFile file;
+ private SeekableByteChannel channel;
private final Archive archive;
private int currentEntryIndex = -1;
private int currentFolderIndex = -1;
@@ -82,7 +86,7 @@ public class SevenZFile implements Closeable {
static final byte[] sevenZSignature = {
(byte)'7', (byte)'z', (byte)0xBC, (byte)0xAF, (byte)0x27, (byte)0x1C
};
-
+
/**
* Reads a file as 7z archive
*
@@ -93,9 +97,30 @@ public class SevenZFile implements Closeable {
* @throws IOException if reading the archive fails
*/
public SevenZFile(final File filename, final byte[] password) throws IOException {
+ this(Files.newByteChannel(filename.toPath(), EnumSet.of(StandardOpenOption.READ)),
+ filename.getAbsolutePath(), password, true);
+ }
+
+ /**
+ * Reads a SeekableByteChannel as 7z archive
+ *
+ * @param channel the channel to read
+ * @param filename name of the archive - only used for error reporting
+ * @param password optional password if the archive is encrypted -
+ * the byte array is supposed to be the UTF16-LE encoded
+ * representation of the password.
+ * @throws IOException if reading the archive fails
+ */
+ public SevenZFile(final SeekableByteChannel channel, String filename,
+ final byte[] password) throws IOException {
+ this(channel, filename, password, false);
+ }
+
+ private SevenZFile(final SeekableByteChannel channel, String filename,
+ final byte[] password, boolean closeOnError) throws IOException {
boolean succeeded = false;
- this.file = new RandomAccessFile(filename, "r");
- this.fileName = filename.getAbsolutePath();
+ this.channel = channel;
+ this.fileName = filename;
try {
archive = readHeaders(password);
if (password != null) {
@@ -106,12 +131,12 @@ public class SevenZFile implements Closeable {
}
succeeded = true;
} finally {
- if (!succeeded) {
- this.file.close();
+ if (!succeeded && closeOnError) {
+ this.channel.close();
}
}
}
-
+
/**
* Reads a file as unencrypted 7z archive
*
@@ -128,11 +153,11 @@ public class SevenZFile implements Closeable {
*/
@Override
public void close() throws IOException {
- if (file != null) {
+ if (channel != null) {
try {
- file.close();
+ channel.close();
} finally {
- file = null;
+ channel = null;
if (password != null) {
Arrays.fill(password, (byte) 0);
}
@@ -140,7 +165,7 @@ public class SevenZFile implements Closeable {
}
}
}
-
+
/**
* Returns the next Archive Entry in this archive.
*
@@ -157,7 +182,7 @@ public class SevenZFile implements Closeable {
buildDecodingStream();
return entry;
}
-
+
/**
* Returns meta-data of all archive entries.
*
@@ -174,37 +199,42 @@ public class SevenZFile implements Closeable {
public Iterable<SevenZArchiveEntry> getEntries() {
return Arrays.asList(archive.files);
}
-
+
private Archive readHeaders(final byte[] password) throws IOException {
+ ByteBuffer buf = ByteBuffer.allocate(12 /* signature + 2 bytes version + 4 bytes CRC */);
+ readFully(buf);
final byte[] signature = new byte[6];
- file.readFully(signature);
+ buf.get(signature);
if (!Arrays.equals(signature, sevenZSignature)) {
throw new IOException("Bad 7z signature");
}
// 7zFormat.txt has it wrong - it's first major then minor
- final byte archiveVersionMajor = file.readByte();
- final byte archiveVersionMinor = file.readByte();
+ final byte archiveVersionMajor = buf.get();
+ final byte archiveVersionMinor = buf.get();
if (archiveVersionMajor != 0) {
throw new IOException(String.format("Unsupported 7z version (%d,%d)",
archiveVersionMajor, archiveVersionMinor));
}
- final long startHeaderCrc = 0xffffFFFFL & Integer.reverseBytes(file.readInt());
+ final long startHeaderCrc = 0xffffFFFFL & Integer.reverseBytes(buf.getInt());
final StartHeader startHeader = readStartHeader(startHeaderCrc);
-
+
final int nextHeaderSizeInt = (int) startHeader.nextHeaderSize;
if (nextHeaderSizeInt != startHeader.nextHeaderSize) {
throw new IOException("cannot handle nextHeaderSize " + startHeader.nextHeaderSize);
}
- file.seek(SIGNATURE_HEADER_SIZE + startHeader.nextHeaderOffset);
+ channel.position(SIGNATURE_HEADER_SIZE + startHeader.nextHeaderOffset);
+ buf = ByteBuffer.allocate(nextHeaderSizeInt);
+ // TODO rewrite to use ByteBuffer directly rather than DataInputStream
final byte[] nextHeader = new byte[nextHeaderSizeInt];
- file.readFully(nextHeader);
+ readFully(buf);
+ buf.get(nextHeader);
final CRC32 crc = new CRC32();
crc.update(nextHeader);
if (startHeader.nextHeaderCrc != crc.getValue()) {
throw new IOException("NextHeader CRC mismatch");
}
-
+
final ByteArrayInputStream byteStream = new ByteArrayInputStream(nextHeader);
DataInputStream nextHeaderInputStream = new DataInputStream(
byteStream);
@@ -225,46 +255,47 @@ public class SevenZFile implements Closeable {
}
return archive;
}
-
+
private StartHeader readStartHeader(final long startHeaderCrc) throws IOException {
final StartHeader startHeader = new StartHeader();
+ // TODO rewrite to use ByteBuffer directly rather than DataInputStream
try (DataInputStream dataInputStream = new DataInputStream(new CRC32VerifyingInputStream(
- new BoundedRandomAccessFileInputStream(file, 20), 20, startHeaderCrc))) {
+ new BoundedSeekableByteChannelInputStream(channel, 20), 20, startHeaderCrc))) {
startHeader.nextHeaderOffset = Long.reverseBytes(dataInputStream.readLong());
startHeader.nextHeaderSize = Long.reverseBytes(dataInputStream.readLong());
startHeader.nextHeaderCrc = 0xffffFFFFL & Integer.reverseBytes(dataInputStream.readInt());
return startHeader;
}
}
-
+
private void readHeader(final DataInput header, final Archive archive) throws IOException {
int nid = header.readUnsignedByte();
-
+
if (nid == NID.kArchiveProperties) {
readArchiveProperties(header);
nid = header.readUnsignedByte();
}
-
+
if (nid == NID.kAdditionalStreamsInfo) {
throw new IOException("Additional streams unsupported");
//nid = header.readUnsignedByte();
}
-
+
if (nid == NID.kMainStreamsInfo) {
readStreamsInfo(header, archive);
nid = header.readUnsignedByte();
}
-
+
if (nid == NID.kFilesInfo) {
readFilesInfo(header, archive);
nid = header.readUnsignedByte();
}
-
+
if (nid != NID.kEnd) {
throw new IOException("Badly terminated header, found " + nid);
}
}
-
+
private void readArchiveProperties(final DataInput input) throws IOException {
// FIXME: the reference implementation just throws them away?
int nid = input.readUnsignedByte();
@@ -275,19 +306,19 @@ public class SevenZFile implements Closeable {
nid = input.readUnsignedByte();
}
}
-
+
private DataInputStream readEncodedHeader(final DataInputStream header, final Archive archive,
final byte[] password) throws IOException {
readStreamsInfo(header, archive);
-
+
// FIXME: merge with buildDecodingStream()/buildDecoderStack() at some stage?
final Folder folder = archive.folders[0];
final int firstPackStreamIndex = 0;
final long folderOffset = SIGNATURE_HEADER_SIZE + archive.packPos +
0;
-
- file.seek(folderOffset);
- InputStream inputStreamStack = new BoundedRandomAccessFileInputStream(file,
+
+ channel.position(folderOffset);
+ InputStream inputStreamStack = new BoundedSeekableByteChannelInputStream(channel,
archive.packSizes[firstPackStreamIndex]);
for (final Coder coder : folder.getOrderedCoders()) {
if (coder.numInStreams != 1 || coder.numOutStreams != 1) {
@@ -306,15 +337,15 @@ public class SevenZFile implements Closeable {
}
return new DataInputStream(new ByteArrayInputStream(nextHeader));
}
-
+
private void readStreamsInfo(final DataInput header, final Archive archive) throws IOException {
int nid = header.readUnsignedByte();
-
+
if (nid == NID.kPackInfo) {
readPackInfo(header, archive);
nid = header.readUnsignedByte();
}
-
+
if (nid == NID.kUnpackInfo) {
readUnpackInfo(header, archive);
nid = header.readUnsignedByte();
@@ -322,17 +353,17 @@ public class SevenZFile implements Closeable {
// archive without unpack/coders info
archive.folders = new Folder[0];
}
-
+
if (nid == NID.kSubStreamsInfo) {
readSubStreamsInfo(header, archive);
nid = header.readUnsignedByte();
}
-
+
if (nid != NID.kEnd) {
throw new IOException("Badly terminated StreamsInfo");
}
}
-
+
private void readPackInfo(final DataInput header, final Archive archive) throws IOException {
archive.packPos = readUint64(header);
final long numPackStreams = readUint64(header);
@@ -344,7 +375,7 @@ public class SevenZFile implements Closeable {
}
nid = header.readUnsignedByte();
}
-
+
if (nid == NID.kCRC) {
archive.packCrcsDefined = readAllOrBits(header, (int)numPackStreams);
archive.packCrcs = new long[(int)numPackStreams];
@@ -353,15 +384,15 @@ public class SevenZFile implements Closeable {
archive.packCrcs[i] = 0xffffFFFFL & Integer.reverseBytes(header.readInt());
}
}
-
+
nid = header.readUnsignedByte();
}
-
+
if (nid != NID.kEnd) {
throw new IOException("Badly terminated PackInfo (" + nid + ")");
}
}
-
+
private void readUnpackInfo(final DataInput header, final Archive archive) throws IOException {
int nid = header.readUnsignedByte();
if (nid != NID.kFolder) {
@@ -377,7 +408,7 @@ public class SevenZFile implements Closeable {
for (int i = 0; i < (int)numFolders; i++) {
folders[i] = readFolder(header);
}
-
+
nid = header.readUnsignedByte();
if (nid != NID.kCodersUnpackSize) {
throw new IOException("Expected kCodersUnpackSize, got " + nid);
@@ -388,7 +419,7 @@ public class SevenZFile implements Closeable {
folder.unpackSizes[i] = readUint64(header);
}
}
-
+
nid = header.readUnsignedByte();
if (nid == NID.kCRC) {
final BitSet crcsDefined = readAllOrBits(header, (int)numFolders);
@@ -400,21 +431,21 @@ public class SevenZFile implements Closeable {
folders[i].hasCrc = false;
}
}
-
+
nid = header.readUnsignedByte();
}
-
+
if (nid != NID.kEnd) {
throw new IOException("Badly terminated UnpackInfo");
}
}
-
+
private void readSubStreamsInfo(final DataInput header, final Archive archive) throws IOException {
for (final Folder folder : archive.folders) {
folder.numUnpackSubStreams = 1;
}
int totalUnpackStreams = archive.folders.length;
-
+
int nid = header.readUnsignedByte();
if (nid == NID.kNumUnpackStream) {
totalUnpackStreams = 0;
@@ -425,12 +456,12 @@ public class SevenZFile implements Closeable {
}
nid = header.readUnsignedByte();
}
-
+
final SubStreamsInfo subStreamsInfo = new SubStreamsInfo();
subStreamsInfo.unpackSizes = new long[totalUnpackStreams];
subStreamsInfo.hasCrc = new BitSet(totalUnpackStreams);
subStreamsInfo.crcs = new long[totalUnpackStreams];
-
+
int nextUnpackStream = 0;
for (final Folder folder : archive.folders) {
if (folder.numUnpackSubStreams == 0) {
@@ -449,14 +480,14 @@ public class SevenZFile implements Closeable {
if (nid == NID.kSize) {
nid = header.readUnsignedByte();
}
-
+
int numDigests = 0;
for (final Folder folder : archive.folders) {
if (folder.numUnpackSubStreams != 1 || !folder.hasCrc) {
numDigests += folder.numUnpackSubStreams;
}
}
-
+
if (nid == NID.kCRC) {
final BitSet hasMissingCrc = readAllOrBits(header, numDigests);
final long[] missingCrcs = new long[numDigests];
@@ -481,20 +512,20 @@ public class SevenZFile implements Closeable {
}
}
}
-
+
nid = header.readUnsignedByte();
}
-
+
if (nid != NID.kEnd) {
throw new IOException("Badly terminated SubStreamsInfo");
}
-
+
archive.subStreamsInfo = subStreamsInfo;
}
-
+
private Folder readFolder(final DataInput header) throws IOException {
final Folder folder = new Folder();
-
+
final long numCoders = readUint64(header);
final Coder[] coders = new Coder[(int)numCoders];
long totalInStreams = 0;
@@ -506,7 +537,7 @@ public class SevenZFile implements Closeable {
final boolean isSimple = (bits & 0x10) == 0;
final boolean hasAttributes = (bits & 0x20) != 0;
final boolean moreAlternativeMethods = (bits & 0x80) != 0;
-
+
coders[i].decompressionMethodId = new byte[idSize];
header.readFully(coders[i].decompressionMethodId);
if (isSimple) {
@@ -532,7 +563,7 @@ public class SevenZFile implements Closeable {
folder.coders = coders;
folder.totalInputStreams = totalInStreams;
folder.totalOutputStreams = totalOutStreams;
-
+
if (totalOutStreams == 0) {
throw new IOException("Total output streams can't be 0");
}
@@ -544,7 +575,7 @@ public class SevenZFile implements Closeable {
bindPairs[i].outIndex = readUint64(header);
}
folder.bindPairs = bindPairs;
-
+
if (totalInStreams < numBindPairs) {
throw new IOException("Total input streams can't be less than the number of bind pairs");
}
@@ -567,10 +598,10 @@ public class SevenZFile implements Closeable {
}
}
folder.packedStreams = packedStreams;
-
+
return folder;
}
-
+
private BitSet readAllOrBits(final DataInput header, final int size) throws IOException {
final int areAllDefined = header.readUnsignedByte();
final BitSet bits;
@@ -584,7 +615,7 @@ public class SevenZFile implements Closeable {
}
return bits;
}
-
+
private BitSet readBits(final DataInput header, final int size) throws IOException {
final BitSet bits = new BitSet(size);
int mask = 0;
@@ -599,7 +630,7 @@ public class SevenZFile implements Closeable {
}
return bits;
}
-
+
private void readFilesInfo(final DataInput header, final Archive archive) throws IOException {
final long numFiles = readUint64(header);
final SevenZArchiveEntry[] files = new SevenZArchiveEntry[(int)numFiles];
@@ -719,7 +750,7 @@ public class SevenZFile implements Closeable {
case NID.kDummy: {
// 7z 9.20 asserts the content is all zeros and ignores the property
// Compress up to 1.8.1 would throw an exception, now we ignore it (see COMPRESS-287
-
+
if (skipBytesFully(header, size) < size) {
throw new IOException("Incomplete kDummy property");
}
@@ -757,10 +788,10 @@ public class SevenZFile implements Closeable {
archive.files = files;
calculateStreamMap(archive);
}
-
+
private void calculateStreamMap(final Archive archive) throws IOException {
final StreamMap streamMap = new StreamMap();
-
+
int nextFolderPackStreamIndex = 0;
final int numFolders = archive.folders != null ? archive.folders.length : 0;
streamMap.folderFirstPackStreamIndex = new int[numFolders];
@@ -768,7 +799,7 @@ public class SevenZFile implements Closeable {
streamMap.folderFirstPackStreamIndex[i] = nextFolderPackStreamIndex;
nextFolderPackStreamIndex += archive.folders[i].packedStreams.length;
}
-
+
long nextPackStreamOffset = 0;
final int numPackSizes = archive.packSizes != null ? archive.packSizes.length : 0;
streamMap.packStreamOffsets = new long[numPackSizes];
@@ -776,7 +807,7 @@ public class SevenZFile implements Closeable {
streamMap.packStreamOffsets[i] = nextPackStreamOffset;
nextPackStreamOffset += archive.packSizes[i];
}
-
+
streamMap.folderFirstFileIndex = new int[numFolders];
streamMap.fileFolderIndex = new int[archive.files.length];
int nextFolderIndex = 0;
@@ -807,10 +838,10 @@ public class SevenZFile implements Closeable {
nextFolderUnpackStreamIndex = 0;
}
}
-
+
archive.streamMap = streamMap;
}
-
+
private void buildDecodingStream() throws IOException {
final int folderIndex = archive.streamMap.fileFolderIndex[currentEntryIndex];
if (folderIndex < 0) {
@@ -836,7 +867,7 @@ public class SevenZFile implements Closeable {
currentFolderInputStream.close();
currentFolderInputStream = null;
}
-
+
final Folder folder = archive.folders[folderIndex];
final int firstPackStreamIndex = archive.streamMap.folderFirstPackStreamIndex[folderIndex];
final long folderOffset = SIGNATURE_HEADER_SIZE + archive.packPos +
@@ -848,16 +879,16 @@ public class SevenZFile implements Closeable {
if (file.getHasCrc()) {
fileStream = new CRC32VerifyingInputStream(fileStream, file.getSize(), file.getCrcValue());
}
-
+
deferredBlockStreams.add(fileStream);
}
private InputStream buildDecoderStack(final Folder folder, final long folderOffset,
final int firstPackStreamIndex, final SevenZArchiveEntry entry) throws IOException {
- file.seek(folderOffset);
+ channel.position(folderOffset);
InputStream inputStreamStack =
new BufferedInputStream(
- new BoundedRandomAccessFileInputStream(file,
+ new BoundedSeekableByteChannelInputStream(channel,
archive.packSizes[firstPackStreamIndex]));
final LinkedList<SevenZMethodConfiguration> methods = new LinkedList<>();
for (final Coder coder : folder.getOrderedCoders()) {
@@ -877,7 +908,7 @@ public class SevenZFile implements Closeable {
}
return inputStreamStack;
}
-
+
/**
* Reads a byte of data.
*
@@ -888,7 +919,7 @@ public class SevenZFile implements Closeable {
public int read() throws IOException {
return getCurrentStream().read();
}
-
+
private InputStream getCurrentStream() throws IOException {
if (archive.files[currentEntryIndex].getSize() == 0) {
return new ByteArrayInputStream(new byte[0]);
@@ -896,7 +927,7 @@ public class SevenZFile implements Closeable {
if (deferredBlockStreams.isEmpty()) {
throw new IllegalStateException("No current 7z entry (call getNextEntry() first).");
}
-
+
while (deferredBlockStreams.size() > 1) {
// In solid compression mode we need to decompress all leading folder'
// streams to get access to an entry. We defer this until really needed
@@ -920,7 +951,7 @@ public class SevenZFile implements Closeable {
public int read(final byte[] b) throws IOException {
return read(b, 0, b.length);
}
-
+
/**
* Reads data into an array of bytes.
*
@@ -934,7 +965,7 @@ public class SevenZFile implements Closeable {
public int read(final byte[] b, final int off, final int len) throws IOException {
return getCurrentStream().read(b, off, len);
}
-
+
private static long readUint64(final DataInput in) throws IOException {
// long rather than int as it might get shifted beyond the range of an int
final long firstByte = in.readUnsignedByte();
@@ -997,7 +1028,21 @@ public class SevenZFile implements Closeable {
}
return skipped;
}
-
+
+ private int readFully(ByteBuffer buf) throws IOException {
+ final int expectedLength = buf.rewind().remaining();
+ int read = 0;
+ while (read < expectedLength) {
+ int readNow = channel.read(buf);
+ if (readNow <= 0) {
+ break;
+ }
+ read += readNow;
+ }
+ buf.flip();
+ return read;
+ }
+
@Override
public String toString() {
return archive.toString();