package com.googlecode.ant_deb_task; import java.io.*; import java.util.zip.*; /** * An enhanced GZIP output stream that allows the compression level to be set. * * This class also allows the original filesystem to be specified. The original * java.util.zip.GZIPOutputStream defaults this to FAT, so we do the same here. * * @author Kevin McGuinness */ class GZipOutputStream extends DeflaterOutputStream { // Much of this is just copied from the source from GZIPOutputStream private final static int GZIP_MAGIC = 0x8b1f; private final static int TRAILER_SIZE = 8; // These values are from RFC-1952 public static final byte FS_FAT = 0; public static final byte FS_AMIGA = 1; public static final byte FS_VMS = 2; public static final byte FS_UNIX = 3; public static final byte FS_VM_CMS = 4; public static final byte FS_ATARI = 5; public static final byte FS_HPFS = 6; public static final byte FS_MAC = 7; public static final byte FS_Z_SYSTEM = 8; public static final byte FS_CPM = 9; public static final byte FS_TOPS_20 = 10; public static final byte FS_NTFS = 11; public static final byte FS_QDOS = 12; public static final byte FS_ACORN_RISC = 11; public static final byte FS_UNKNOWN = (byte) 0xff; protected CRC32 crc = new CRC32 (); private boolean closed = false; private int level; private byte fileSystem = FS_FAT; private boolean headerWritten = false; /** * Creates a new output stream with the specified buffer size. * * @param out * the output stream * @param level * the compression level (1..9) * @throws IOException * If an I/O error has occurred. * @see java.util.zip.Deflater#setLevel(int) */ public GZipOutputStream (OutputStream out, int level) throws IOException { super (out, new Deflater (level, true), 512); this.level = level; this.fileSystem = FS_FAT; crc.reset (); } /** * Set the file system that is used by the archive. This identifies the type * of file system on which compression took place. This may be useful in * determining end-of-line convention for text files. * * @param value * One of the FS_* values (ex. FS_UNIX). */ public void setFileSystem(byte value) { fileSystem = value; } public void close() throws IOException { if (!closed) { super.close(); def.end (); closed = true; } } public synchronized void write (byte[] buf, int off, int len) throws IOException { if (!headerWritten) { writeHeader (); } super.write (buf, off, len); crc.update (buf, off, len); } public void finish () throws IOException { if (!headerWritten) { writeHeader (); } if (!def.finished ()) { def.finish (); while (!def.finished ()) { int len = def.deflate (buf, 0, buf.length); if (def.finished () && len <= buf.length - TRAILER_SIZE) { // last deflater buffer. Fit trailer at the end writeTrailer (buf, len); len = len + TRAILER_SIZE; out.write (buf, 0, len); return; } if (len > 0) out.write (buf, 0, len); } // if we can't fit the trailer at the end of the last // deflater buffer, we write it separately byte[] trailer = new byte[TRAILER_SIZE]; writeTrailer (trailer, 0); out.write (trailer); } } private void writeHeader () throws IOException { byte[] header = new byte[10]; // Magic number header[0] = (byte) GZIP_MAGIC; header[1] = (byte) (GZIP_MAGIC >> 8); // Compression method header[2] = Deflater.DEFLATED; // Flags header[3] = 0; // Modification time (unavailable) header[4] = 0; header[5] = 0; header[6] = 0; header[7] = 0; // Extra flags switch (level) { case Deflater.BEST_COMPRESSION: header[8] = 2; break; case Deflater.BEST_SPEED: header[8] = 4; break; default: header[8] = 0; } // Set OS header[9] = fileSystem; // Write out.write (header); headerWritten = true; } private void writeTrailer (byte[] buf, int offset) throws IOException { writeInt ((int) crc.getValue (), buf, offset); writeInt (def.getTotalIn (), buf, offset + 4); } private static void writeInt (int i, byte[] buf, int offset) throws IOException { writeShort (i & 0xffff, buf, offset); writeShort ((i >> 16) & 0xffff, buf, offset + 2); } private static void writeShort (int s, byte[] buf, int offset) throws IOException { buf[offset] = (byte) (s & 0xff); buf[offset + 1] = (byte) ((s >> 8) & 0xff); } }