Repository: commons-compress Updated Branches: refs/heads/master 9e32a2ce7 -> 87f0f2ec3
COMPRESS-327 write 7z archives to arbirary 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/87f0f2ec Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/87f0f2ec Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/87f0f2ec Branch: refs/heads/master Commit: 87f0f2ec35c600cae7a17621455c00e24256011a Parents: 9e32a2c Author: Stefan Bodewig <bode...@apache.org> Authored: Wed Oct 12 15:54:07 2016 +0200 Committer: Stefan Bodewig <bode...@apache.org> Committed: Wed Oct 12 15:54:07 2016 +0200 ---------------------------------------------------------------------- .../archivers/sevenz/SevenZOutputFile.java | 144 +++++++++++-------- 1 file changed, 85 insertions(+), 59 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-compress/blob/87f0f2ec/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java index c2c0171..2a13003 100644 --- a/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java +++ b/src/main/java/org/apache/commons/compress/archivers/sevenz/SevenZOutputFile.java @@ -24,11 +24,15 @@ import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; -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.BitSet; import java.util.Collections; import java.util.Date; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.LinkedList; @@ -43,7 +47,7 @@ import org.apache.commons.compress.utils.CountingOutputStream; * @since 1.6 */ public class SevenZOutputFile implements Closeable { - private final RandomAccessFile file; + private final SeekableByteChannel channel; private final List<SevenZArchiveEntry> files = new ArrayList<>(); private int numNonEmptyStreams = 0; private final CRC32 crc32 = new CRC32(); @@ -55,18 +59,30 @@ public class SevenZOutputFile implements Closeable { private Iterable<? extends SevenZMethodConfiguration> contentMethods = Collections.singletonList(new SevenZMethodConfiguration(SevenZMethod.LZMA2)); private final Map<SevenZArchiveEntry, long[]> additionalSizes = new HashMap<>(); - + /** * Opens file to write a 7z archive to. * - * @param filename name of the file to write to + * @param filename the file to write to * @throws IOException if opening the file fails */ public SevenZOutputFile(final File filename) throws IOException { - file = new RandomAccessFile(filename, "rw"); - file.seek(SevenZFile.SIGNATURE_HEADER_SIZE); + this(Files.newByteChannel(filename.toPath(), + EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING))); + } + + /** + * Prepares channel to write a 7z archive to. + * + * @param channel the channel to write to + * @throws IOException if the channel cannot be positioned properly + */ + public SevenZOutputFile(final SeekableByteChannel channel) throws IOException { + this.channel = channel; + channel.position(SevenZFile.SIGNATURE_HEADER_SIZE); } - + /** * Sets the default compression method to use for entry contents - the * default is LZMA2. @@ -111,9 +127,9 @@ public class SevenZOutputFile implements Closeable { if (!finished) { finish(); } - file.close(); + channel.close(); } - + /** * Create an archive entry using the inputFile and entryName provided. * @@ -145,7 +161,7 @@ public class SevenZOutputFile implements Closeable { final SevenZArchiveEntry entry = (SevenZArchiveEntry) archiveEntry; files.add(entry); } - + /** * Closes the archive entry. * @throws IOException on error @@ -193,7 +209,7 @@ public class SevenZOutputFile implements Closeable { public void write(final int b) throws IOException { getCurrentOutputStream().write(b); } - + /** * Writes a byte array to the current archive entry. * @param b The byte array to be written. @@ -202,7 +218,7 @@ public class SevenZOutputFile implements Closeable { public void write(final byte[] b) throws IOException { write(b, 0, b.length); } - + /** * Writes part of a byte array to the current archive entry. * @param b The byte array to be written. @@ -215,7 +231,7 @@ public class SevenZOutputFile implements Closeable { getCurrentOutputStream().write(b, off, len); } } - + /** * Finishes the addition of entries to this archive, without closing it. * @@ -226,26 +242,31 @@ public class SevenZOutputFile implements Closeable { throw new IOException("This archive has already been finished"); } finished = true; - - final long headerPosition = file.getFilePointer(); - + + final long headerPosition = channel.position(); + + // TODO use a properly sized buffer for both write operations + // and re-write writeHeader and the startHeaderStream + // operations to work on ByteBuffer directly. final ByteArrayOutputStream headerBaos = new ByteArrayOutputStream(); final DataOutputStream header = new DataOutputStream(headerBaos); - + writeHeader(header); header.flush(); final byte[] headerBytes = headerBaos.toByteArray(); - file.write(headerBytes); - + channel.write(ByteBuffer.wrap(headerBytes)); + final CRC32 crc32 = new CRC32(); - + // signature header - file.seek(0); - file.write(SevenZFile.sevenZSignature); + channel.position(0); + ByteBuffer bb = ByteBuffer.allocate(SevenZFile.sevenZSignature.length + 2); + bb.put(SevenZFile.sevenZSignature); // version - file.write(0); - file.write(2); - + bb.put((byte) 0).put((byte) 2); + bb.flip(); + channel.write(bb); + // start header final ByteArrayOutputStream startHeaderBaos = new ByteArrayOutputStream(); final DataOutputStream startHeaderStream = new DataOutputStream(startHeaderBaos); @@ -258,10 +279,13 @@ public class SevenZOutputFile implements Closeable { final byte[] startHeaderBytes = startHeaderBaos.toByteArray(); crc32.reset(); crc32.update(startHeaderBytes); - file.writeInt(Integer.reverseBytes((int) crc32.getValue())); - file.write(startHeaderBytes); + bb = ByteBuffer.allocate(startHeaderBytes.length + 4); + bb.putInt(Integer.reverseBytes((int) crc32.getValue())); + bb.put(startHeaderBytes); + bb.flip(); + channel.write(bb); } - + /* * Creation of output stream is deferred until data is actually * written as some codecs might write header information even for @@ -300,13 +324,13 @@ public class SevenZOutputFile implements Closeable { super.write(b); crc32.update(b); } - + @Override public void write(final byte[] b) throws IOException { super.write(b); crc32.update(b); } - + @Override public void write(final byte[] b, final int off, final int len) throws IOException { @@ -323,37 +347,37 @@ public class SevenZOutputFile implements Closeable { private void writeHeader(final DataOutput header) throws IOException { header.write(NID.kHeader); - + header.write(NID.kMainStreamsInfo); writeStreamsInfo(header); writeFilesInfo(header); header.write(NID.kEnd); } - + private void writeStreamsInfo(final DataOutput header) throws IOException { if (numNonEmptyStreams > 0) { writePackInfo(header); writeUnpackInfo(header); } - + writeSubStreamsInfo(header); - + header.write(NID.kEnd); } - + private void writePackInfo(final DataOutput header) throws IOException { header.write(NID.kPackInfo); - + writeUint64(header, 0); writeUint64(header, 0xffffFFFFL & numNonEmptyStreams); - + header.write(NID.kSize); for (final SevenZArchiveEntry entry : files) { if (entry.hasStream()) { writeUint64(header, entry.getCompressedSize()); } } - + header.write(NID.kCRC); header.write(1); // "allAreDefined" == true for (final SevenZArchiveEntry entry : files) { @@ -361,13 +385,13 @@ public class SevenZOutputFile implements Closeable { header.writeInt(Integer.reverseBytes((int) entry.getCompressedCrcValue())); } } - + header.write(NID.kEnd); } - + private void writeUnpackInfo(final DataOutput header) throws IOException { header.write(NID.kUnpackInfo); - + header.write(NID.kFolder); writeUint64(header, numNonEmptyStreams); header.write(0); @@ -389,7 +413,7 @@ public class SevenZOutputFile implements Closeable { writeUint64(header, entry.getSize()); } } - + header.write(NID.kCRC); header.write(1); // "allAreDefined" == true for (final SevenZArchiveEntry entry : files) { @@ -397,10 +421,10 @@ public class SevenZOutputFile implements Closeable { header.writeInt(Integer.reverseBytes((int) entry.getCrcValue())); } } - + header.write(NID.kEnd); } - + private void writeFolder(final DataOutput header, final SevenZArchiveEntry entry) throws IOException { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); int numCoders = 0; @@ -434,10 +458,10 @@ public class SevenZOutputFile implements Closeable { bos.write(properties); } } - + private void writeSubStreamsInfo(final DataOutput header) throws IOException { header.write(NID.kSubStreamsInfo); -// +// // header.write(NID.kCRC); // header.write(1); // for (final SevenZArchiveEntry entry : files) { @@ -445,13 +469,13 @@ public class SevenZOutputFile implements Closeable { // header.writeInt(Integer.reverseBytes(entry.getCrc())); // } // } -// +// header.write(NID.kEnd); } - + private void writeFilesInfo(final DataOutput header) throws IOException { header.write(NID.kFilesInfo); - + writeUint64(header, files.size()); writeFileEmptyStreams(header); @@ -464,7 +488,7 @@ public class SevenZOutputFile implements Closeable { writeFileWindowsAttributes(header); header.write(NID.kEnd); } - + private void writeFileEmptyStreams(final DataOutput header) throws IOException { boolean hasEmptyStreams = false; for (final SevenZArchiveEntry entry : files) { @@ -488,7 +512,7 @@ public class SevenZOutputFile implements Closeable { header.write(contents); } } - + private void writeFileEmptyFiles(final DataOutput header) throws IOException { boolean hasEmptyFiles = false; int emptyStreamCounter = 0; @@ -511,7 +535,7 @@ public class SevenZOutputFile implements Closeable { header.write(contents); } } - + private void writeFileAntiItems(final DataOutput header) throws IOException { boolean hasAntiItems = false; final BitSet antiItems = new BitSet(0); @@ -534,10 +558,10 @@ public class SevenZOutputFile implements Closeable { header.write(contents); } } - + private void writeFileNames(final DataOutput header) throws IOException { header.write(NID.kName); - + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final DataOutputStream out = new DataOutputStream(baos); out.write(0); @@ -740,27 +764,29 @@ public class SevenZOutputFile implements Closeable { private class OutputStreamWrapper extends OutputStream { @Override public void write(final int b) throws IOException { - file.write(b); + // TODO use a cached ByteBuffer + channel.write(ByteBuffer.wrap(new byte[] {(byte) b })); compressedCrc32.update(b); fileBytesWritten++; } - + @Override public void write(final byte[] b) throws IOException { OutputStreamWrapper.this.write(b, 0, b.length); } - + @Override public void write(final byte[] b, final int off, final int len) throws IOException { - file.write(b, off, len); + // TODO use a cached ByteBuffer + channel.write(ByteBuffer.wrap(b, off, len)); compressedCrc32.update(b, off, len); fileBytesWritten += len; } @Override public void flush() throws IOException { - // no reason to flush a RandomAccessFile + // no reason to flush the channel } @Override