From 54484bc2eb22f475311a66688ffdb98161a1e444 Mon Sep 17 00:00:00 2001 From: Aaron Davis Date: Mon, 8 Jul 2013 14:13:08 -0600 Subject: [PATCH] add zipOutputStream and change DeflaterOutputStream --- .../java/util/zip/DeflaterOutputStream.java | 10 +- classpath/java/util/zip/ZipOutputStream.java | 246 +++++++++++------- 2 files changed, 151 insertions(+), 105 deletions(-) diff --git a/classpath/java/util/zip/DeflaterOutputStream.java b/classpath/java/util/zip/DeflaterOutputStream.java index c0a2a00208..64bbcb9be4 100644 --- a/classpath/java/util/zip/DeflaterOutputStream.java +++ b/classpath/java/util/zip/DeflaterOutputStream.java @@ -11,16 +11,16 @@ package java.util.zip; import java.io.OutputStream; +import java.io.FilterOutputStream; import java.io.IOException; -public class DeflaterOutputStream extends OutputStream { - private final OutputStream out; - private final Deflater deflater; - private final byte[] buffer; +public class DeflaterOutputStream extends FilterOutputStream { + protected final Deflater deflater; + protected final byte[] buffer; public DeflaterOutputStream(OutputStream out, Deflater deflater, int bufferSize) { - this.out = out; + super(out); this.deflater = deflater; this.buffer = new byte[bufferSize]; } diff --git a/classpath/java/util/zip/ZipOutputStream.java b/classpath/java/util/zip/ZipOutputStream.java index aa00705375..02702218da 100644 --- a/classpath/java/util/zip/ZipOutputStream.java +++ b/classpath/java/util/zip/ZipOutputStream.java @@ -1,3 +1,13 @@ +/* Copyright (c) 2008-2013, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + package java.util.zip; import java.io.IOException; @@ -8,7 +18,6 @@ import java.util.List; import java.util.zip.CRC32; import java.util.zip.DeflaterOutputStream; import java.util.zip.Deflater; -import java.io.BufferedOutputStream; /** @@ -16,8 +25,12 @@ import java.io.BufferedOutputStream; * Compression method defaults to DEFLATE * All hardcoded defaults match the defaults for openJDK, * including PKZip version, bit flags set, compression level, etc + * + * @author David Chau + * @author Aaron Davis + * @author Christopher Jordan + * @author Riley Moses * - * @author ReadyTalk Summer 2013 Intern Team */ public class ZipOutputStream extends DeflaterOutputStream { private static final int SIGNATURE = 0x04034b50; @@ -28,30 +41,25 @@ public class ZipOutputStream extends DeflaterOutputStream { private static final int DATA_DESCRIPTER_HEADER = 0x08074b50; private static final int END_OF_CENTRAL_DIRECTORY_SIG = 0x06054b50; private static final int DEFAULT_LEVEL = 6; + + private static final int INPUT_BUFFER_SIZE = 1024; - private final OutputStream out; - private DeflaterOutputStream deflaterStream; - private Deflater deflater; private List entries; private CRC32 crc = new CRC32(); - private ZipEntry currentEntry; - - private int bytesWritten; - private int sizeOfCentralDirectory; - private byte[] buffer; + private ZipEntry currentEntry; // holder for current entry + private int bytesWritten; // a counter for total bytes written + private int sizeOfCentralDirectory; // a counter for central dir size - public ZipOutputStream(OutputStream outStream, int bufferSize) { - super(outStream); - out = outStream; + // these are used for the function write(int b) to provide a speed increase + private byte[] inputBuffer = new byte[INPUT_BUFFER_SIZE]; + private int bufferIndex; + + + public ZipOutputStream(OutputStream outStream) { + super(outStream, new Deflater(DEFAULT_LEVEL, true)); bytesWritten = 0; sizeOfCentralDirectory = 0; entries = new ArrayList(); - buffer = new byte[bufferSize]; - deflater = new Deflater(DEFAULT_LEVEL, true); - } - - public ZipOutputStream(OutputStream outStream) { - this(outStream, 4 * 1024); } public void putNextEntry(ZipEntry e) throws IOException { @@ -62,51 +70,24 @@ public class ZipOutputStream extends DeflaterOutputStream { } public void closeEntry() throws IOException { - deflater.finish(); - while (!deflater.finished()) { - deflate(); + // write remainder of buffer if partially full + if (bufferIndex != 0) { + write(inputBuffer, 0, bufferIndex); + bufferIndex = 0; } - deflater.dispose(); - deflater.reset(); + + finish(); currentEntry.crc = (int) crc.getValue(); crc.reset(); writeDataDescriptor(currentEntry); } - private void writeLocalHeader(ZipEntry e) throws IOException { - writeFourBytes(SIGNATURE); // local header signature - writeTwoBytes(VERSION); // version used - writeTwoBytes(BITFLAG); // flags - writeTwoBytes(METHOD); // compression method - writeTwoBytes(e.modTime); // last modified time - writeTwoBytes(e.modDate); // last modified date - writeFourBytes(0); // CRC is 0 for local header - - // with default flag settings, the compressed and uncompressed size - // is written here as 0 and written correctly in the data descripter - writeFourBytes(0); // compressed size - writeFourBytes(0); // uncompressed size - writeTwoBytes(e.name.length()); // length of file name - - // extra field length, in this implementation extra field in not used - writeTwoBytes(0); - - // write file name, return the number of bytes written - int len = writeVariableByteLength(e.getName()); - - bytesWritten += 30 + len; - } - - private void writeDataDescriptor(ZipEntry currentEntry) throws IOException { - writeFourBytes(DATA_DESCRIPTER_HEADER); // data descripter header - writeFourBytes(currentEntry.crc); // crc value - writeFourBytes(currentEntry.compSize); // compressed size - writeFourBytes(currentEntry.uncompSize); // uncompressed size - bytesWritten += 16; - } - + @Override public void write(byte[] b, int offset, int length) throws IOException { + if (offset < 0 || length < 0 || b.length - (offset + length) < 0) + throw new IndexOutOfBoundsException(); + currentEntry.uncompSize += length; crc.update(b, offset, length); currentEntry.crc = (int) crc.getValue(); @@ -116,10 +97,14 @@ public class ZipOutputStream extends DeflaterOutputStream { deflate(); } + @Override public void write(int b) throws IOException { - byte[] buf = new byte[1]; - buf[0] = (byte)(b & 0xff); - write(buf, 0, 1); + inputBuffer[bufferIndex] = (byte)(b & 0xff); + bufferIndex += 1; + if (bufferIndex == 1024) { + write(inputBuffer, 0, bufferIndex); + bufferIndex = 0; + } } private void deflate() throws IOException { @@ -127,72 +112,133 @@ public class ZipOutputStream extends DeflaterOutputStream { currentEntry.compSize += len; bytesWritten += len; if (len > 0) - out.write(buffer, 0 , len); + out.write(buffer, 0, len); + } + + private void writeLocalHeader(ZipEntry e) throws IOException { + byte[] tmpBuffer = new byte[30]; + + addFourBytes(SIGNATURE, 0, tmpBuffer); // local header signature + addTwoBytes(VERSION, 4, tmpBuffer); // version used + addTwoBytes(BITFLAG, 6, tmpBuffer); // flags + addTwoBytes(METHOD, 8, tmpBuffer); // compression method + addFourBytes(e.modTimeDate, 10, tmpBuffer); // last mod date and time + addFourBytes(0, 14, tmpBuffer); // CRC is 0 for local header + + // with default flag settings (bit 3 set) the compressed and uncompressed size + // is written here as 0 and written correctly in the data descripter + addFourBytes(0, 18, tmpBuffer); // compressed size + addFourBytes(0, 22, tmpBuffer); // uncompressed size + addTwoBytes(e.name.length(), 26, tmpBuffer); // length of file name + + // extra field length, in this implementation extra field in not used + addTwoBytes(0, 28, tmpBuffer); + + out.write(tmpBuffer, 0, 30); + + // write file name, return the number of bytes written + int len = writeUTF8(e.getName()); + + bytesWritten += 30 + len; + } + + private void writeDataDescriptor(ZipEntry currentEntry) throws IOException { + byte[] tmpBuffer = new byte[16]; + + addFourBytes(DATA_DESCRIPTER_HEADER, 0, tmpBuffer); // data descripter header + addFourBytes(currentEntry.crc, 4, tmpBuffer); // crc value + addFourBytes(currentEntry.compSize, 8, tmpBuffer); // compressed size + addFourBytes(currentEntry.uncompSize, 12, tmpBuffer);// uncompressed size + out.write(tmpBuffer, 0, 16); + bytesWritten += 16; } private void writeCentralDirectoryHeader(ZipEntry e) throws IOException { - writeFourBytes(CENTRAL_FILE_HEADER); // central directory header signature - writeTwoBytes(VERSION); // version made by - writeTwoBytes(VERSION); // version needed - writeTwoBytes(BITFLAG); // flags - writeTwoBytes(METHOD); // compression method + byte[] tmpBuffer = new byte[46]; + + addFourBytes(CENTRAL_FILE_HEADER, 0, tmpBuffer); // central directory header signature + addTwoBytes(VERSION, 4, tmpBuffer); // version made by + addTwoBytes(VERSION, 6, tmpBuffer); // version needed + addTwoBytes(BITFLAG, 8, tmpBuffer); // flags + addTwoBytes(METHOD, 10, tmpBuffer); // compression method - writeTwoBytes(e.modTime); // last mod time - writeTwoBytes(e.modDate); // last mod date - writeFourBytes(e.crc); // crc - writeFourBytes(e.compSize); // compressed size - writeFourBytes(e.uncompSize); // uncompressed size + addFourBytes(e.modTimeDate, 12, tmpBuffer); // last mod date and time + addFourBytes(e.crc, 16, tmpBuffer); // crc + addFourBytes(e.compSize, 20, tmpBuffer); // compressed size + addFourBytes(e.uncompSize, 24, tmpBuffer); // uncompressed size - writeTwoBytes(e.getName().length()); // file name length + addTwoBytes(e.getName().length(), 28, tmpBuffer); // file name length // the following 5 fields are all 0 for a simple default compression - writeTwoBytes(0); // extra field length (not used) - writeTwoBytes(0); // comment length (not used) - writeTwoBytes(0); // disk number start - writeTwoBytes(0); // internal file attribute - writeFourBytes(0); // external file attribute + addTwoBytes(0, 30, tmpBuffer); // extra field length (not used) + addTwoBytes(0, 32, tmpBuffer); // comment length (not used) + addTwoBytes(0, 34, tmpBuffer); // disk number start + addTwoBytes(0, 36, tmpBuffer); // internal file attribute + addFourBytes(0, 38, tmpBuffer); // external file attribute - writeFourBytes((int) e.offset); // relative offset of local header + addFourBytes((int) e.offset, 42, tmpBuffer); // relative offset of local header - int len = writeVariableByteLength(e.getName()); + out.write(tmpBuffer, 0, 46); + + int len = writeUTF8(e.getName()); bytesWritten += 46 + len; sizeOfCentralDirectory += 46 + len; } private void writeEndofCentralDirectory(int offset) throws IOException { + byte[] tmpBuffer = new byte[22]; + short numEntries = (short) entries.size(); - writeFourBytes(END_OF_CENTRAL_DIRECTORY_SIG); // end of central directory signature - writeTwoBytes(0); // disk number - writeTwoBytes(0); // disk number where central dir starts - writeTwoBytes(numEntries); // number of entries on this disk - writeTwoBytes(numEntries); // number of entries in central dir - writeFourBytes(sizeOfCentralDirectory); // length of central directory - writeFourBytes(offset); // offset of central directory - writeTwoBytes(0); // length of added comments (not used) + addFourBytes(END_OF_CENTRAL_DIRECTORY_SIG, 0, tmpBuffer); // end of central directory signature + addTwoBytes(0, 4, tmpBuffer); // disk number + addTwoBytes(0, 6, tmpBuffer); // disk number where central dir starts + addTwoBytes(numEntries, 8, tmpBuffer); // number of entries on this disk + addTwoBytes(numEntries, 10, tmpBuffer); // number of entries in central dir + addFourBytes(sizeOfCentralDirectory, 12, tmpBuffer); // length of central directory + addFourBytes(offset, 16, tmpBuffer); // offset of central directory + addTwoBytes(0, 20, tmpBuffer); // length of added comments (not used) + out.write(tmpBuffer, 0, 22); bytesWritten += 22; } - + + @Override public void close() throws IOException { int centralDirOffset = bytesWritten; for (ZipEntry e : entries) writeCentralDirectoryHeader(e); writeEndofCentralDirectory(centralDirOffset); - } - - private void writeTwoBytes(int bytes) throws IOException { - out.write(bytes & 0xff); - out.write((bytes >> 8) & 0xff); - } - - private void writeFourBytes(int bytes) throws IOException { - out.write(bytes & 0xff); - out.write((bytes >> 8) & 0xff); - out.write((bytes >> 16) & 0xff); - out.write((bytes >> 24) & 0xff); + deflater.dispose(); + out.close(); } - private int writeVariableByteLength(String text) throws IOException { + @Override + public void flush() throws IOException { + out.write(inputBuffer, 0, inputBuffer.length); + inputBuffer = new byte[INPUT_BUFFER_SIZE]; + } + + private void finish() throws IOException { + deflater.finish(); + while (!deflater.finished()) { + deflate(); + } + deflater.reset(); + } + + private void addTwoBytes(int bytes, int offset, byte[] buffer) throws IOException { + buffer[offset] = (byte) (bytes & 0xff); + buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff); + } + + private void addFourBytes(int bytes, int offset, byte[] buffer) throws IOException { + buffer[offset] = (byte) (bytes & 0xff); + buffer[offset + 1] = (byte) ((bytes >> 8) & 0xff); + buffer[offset + 2] = (byte) ((bytes >> 16) & 0xff); + buffer[offset + 3] = (byte) ((bytes >> 24) & 0xff); + } + + private int writeUTF8(String text) throws IOException { byte[] bytes = text.getBytes("UTF-8"); out.write(bytes, 0, bytes.length); return bytes.length;