This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

patches for DeflaterOutputStream.java and ZipOutputStream.java


It is clear that there has been zero testing of ZipOutputStream.java,
since it contains numerous errors in the zip file format emitted.
The patches fixes the problems I found.  It seems to work for at
least the case I've tested: a single uncompressed ("STORED") member.

When it comes to deflating with level 0 (no compression), I finally
got it so that the entire input file was written out, but pre-pended
with 5 extra bytes.  My guess this is some zlib header that is
emitted even when doing no compression, but the logic of zlib is
convoluted enough I'm not sure.  I finally concluded that when using
the STORED method we should by-pass zlib entirely - that is more
efficient anyway.

With the patch (plus the String patch I will check in soon), Kawa
builds *and* the entire Kawa testsuite runs correctly!

I'll give people a chance to comment before I check this in.

2001-03-30  Per Bothner  <per@bothner.com>

	* DeflaterOutputStream.java (deflate):  Loop while def.needsInput.
	(finish):  def.deflate needs to be called in a loop.
	(inbuf, inbufLength):  New private fields.
	(write(int)): Use inbuf.
	(write(byte[],int,int):  Check if pending output in inbuf.
	* ZipOutputStream.java:  Don't use Deflater if stored.
	Use a Checksum object directly, not via a CheckedOutputStream.
	(uncompressed_size):  New field,
	(closeEntry):  Only write data_directory if needed.
	(write):  If STORED, write directly.
	Always update crc, and uncompressed_size.
	(write_entry):  Fix lots of protocol erors.

Index: DeflaterOutputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/DeflaterOutputStream.java,v
retrieving revision 1.5
diff -u -r1.5 DeflaterOutputStream.java
--- DeflaterOutputStream.java	2000/11/17 21:42:28	1.5
+++ DeflaterOutputStream.java	2001/03/30 18:12:30
@@ -50,13 +50,13 @@
 
   protected void deflate () throws IOException
   {
-    while (true)
+    do
       {
 	int len = def.deflate(buf, 0, buf.length);
-	if (len == 0 || len == -1)
-	  break;
-	out.write(buf, 0, len);
-      }
+	if (len > 0)
+	  out.write(buf, 0, len);
+       }
+    while (! def.needsInput());
   }
 
   public DeflaterOutputStream (OutputStream out)
@@ -78,22 +78,52 @@
 
   public void finish () throws IOException
   {
+    if (inbufLength > 0)
+      {
+	def.setInput (inbuf, 0, inbufLength);
+	deflate ();
+	inbufLength = 0;
+      }
     def.finish();
-    deflate ();
+    while (! def.finished ())
+      {
+	int len = def.deflate(buf, 0, buf.length);
+	if (len > 0)
+	  out.write(buf, 0, len);
+      }
   }
 
   public void write (int bval) throws IOException
   {
-    byte[] b = new byte[1];
-    b[0] = (byte) bval;
-    write (b, 0, 1);
+    if (inbuf == null)
+      {
+	inbuf = new byte[128];
+      }
+    else if (inbufLength == inbuf.length)
+      {
+	def.setInput (inbuf, 0, inbufLength);
+	deflate ();
+	inbufLength = 0;
+      }
+    inbuf[inbufLength++] = (byte) bval;
   }
 
   public void write (byte[] buf, int off, int len) throws IOException
   {
+    if (inbufLength > 0)
+      {
+	def.setInput (inbuf, 0, inbufLength);
+	deflate ();
+	inbufLength = 0;
+      }
     def.setInput (buf, off, len);
     deflate ();
   }
+
+  // Used, if needed, for write(int).
+  private byte[] inbuf;
+  // Used length of inbuf.
+  private int inbufLength;
 
   // The retrieval buffer.
   protected byte[] buf;
Index: ZipOutputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/ZipOutputStream.java,v
retrieving revision 1.8
diff -u -r1.8 ZipOutputStream.java
--- ZipOutputStream.java	2000/11/17 21:42:28	1.8
+++ ZipOutputStream.java	2001/03/30 18:12:31
@@ -47,37 +47,64 @@
 
   public void closeEntry ()  throws IOException
   {
-    int uncompressed_size = def.getTotalIn();
-    int compressed_size = def.getTotalOut();
-    long crc = filter.getChecksum().getValue();
+    int compressed_size;
+    if (current.method == STORED)
+      {
+	compressed_size = uncompressed_size;
+      }
+    else
+      {
+	super.finish();
+	compressed_size = def.getTotalOut();
+      }
+    long crc = sum.getValue();
 
     bytes_written += compressed_size;
 
-    bytes_written += put4 (0x08074b50);
     if (current.getCrc() == -1 || current.getCompressedSize() == -1
 	|| current.getSize() == -1)
       {
 	current.setCrc(crc);
 	current.compressedSize = compressed_size;
 	current.setSize(uncompressed_size);
+	put4 (0x08074b50);
+	put4 ((int) (current.getCrc()));
+	put4 ((int) (current.getCompressedSize()));
+	put4 ((int) (current.getSize()));
+	bytes_written += 16;
       }
-    else
-      {
-	if (current.getCrc() != crc
-	    || current.getCompressedSize() != compressed_size
-	    || current.getSize() != uncompressed_size)
-	  throw new ZipException ("zip entry field incorrect");
-      }
-    bytes_written += put4 ((int) (current.getCrc()));
-    bytes_written += put4 ((int) (current.getCompressedSize()));
-    bytes_written += put4 ((int) (current.getSize()));
+    else if (current.getCrc() != crc
+	     || current.getCompressedSize() != compressed_size
+	     || current.getSize() != uncompressed_size)
+      throw new ZipException ("zip entry field incorrect");
 
     current.next = chain;
     chain = current;
     current = null;
-    filter = null;
   }
 
+  public void write (int bval) throws IOException
+  {
+    if (current.method == STORED)
+      {
+	out.write(bval);
+      }
+    else
+      super.write(bval);
+    sum.update(bval);
+    uncompressed_size += 1;
+  }
+
+  public void write (byte[] buf, int off, int len) throws IOException
+  {
+    if (current.method == STORED)
+      out.write(buf, off, len);
+    else
+      super.write(buf, off, len);
+    sum.update(buf, off, len);
+    uncompressed_size += len;
+  }
+
   public void finish () throws IOException
   {
     if (current != null)
@@ -101,21 +128,19 @@
     // Another disk number.
     put2 (0);
     put2 (count);
+    put2 (count);
     put4 (bytes);
     put4 ((int) offset);
 
     byte[] c = comment.getBytes("8859_1");
     put2 (c.length);
     out.write(c);
-    out.write((byte) 0);
   }
 
   // Helper for finish and putNextEntry.
   private int write_entry (ZipEntry entry, boolean is_local)
     throws IOException
   {
-    long offset = bytes_written;
-
     int bytes = put4 (is_local ? 0x04034b50 : 0x02014b50);
     if (! is_local)
       bytes += put_version ();
@@ -169,25 +194,22 @@
 	// Internal file attributes.
 	bytes += put2 (0);
 	// External file attributes.
-	bytes += put2 (0);
+	bytes += put4 (0);
 	// Relative offset of local header.
-	bytes += put2 ((int) offset);
+	bytes += put4 ((int) entry.relativeOffset);
       }
 
     out.write (name);
-    out.write ((byte) 0);
-    bytes += name.length + 1;
+    bytes += name.length;
     if (entry.extra != null)
       {
 	out.write(entry.extra);
-	out.write((byte) 0);
-	bytes += entry.extra.length + 1;
+	bytes += entry.extra.length;
       }
     if (comment != null)
       {
 	out.write(comment);
-	out.write((byte) 0);
-	bytes += comment.length + 1;
+	bytes += comment.length;
       }
 
     bytes_written += bytes;
@@ -208,13 +230,13 @@
 	// Just in case.
 	entry.compressedSize = entry.getSize();
       }
+    entry.relativeOffset = bytes_written;
     write_entry (entry, true);
     current = entry;
     int compr = (method == STORED) ? Deflater.NO_COMPRESSION : level;
     def.reset();
     def.setLevel(compr);
-    filter = new CheckedOutputStream (new DeflaterOutputStream (out, def),
-				      new CRC32 ());
+    sum.reset();
   }
 
   public void setLevel (int level)
@@ -240,18 +262,10 @@
     this.comment = comment;
   }
 
-  public synchronized void write (byte[] buf, int off, int len)
-    throws IOException
-  {
-    if (filter == null)
-      throw new ZipException ("no open zip entry");
-    filter.write(buf, off, len);
-  }
-
   public ZipOutputStream (OutputStream out)
   {
-    super (out);
-    def = new Deflater (level, true);
+    super (out, new Deflater (Deflater.DEFAULT_COMPRESSION, true), 8192);
+    sum = new CRC32 ();
   }
 
   private int put2 (int i) throws IOException
@@ -282,14 +296,14 @@
   private ZipEntry current;
   // The chain of entries which have been written to this file.
   private ZipEntry chain;
-  // The output stream to which data should be sent.
-  private CheckedOutputStream filter;
 
   private int method = DEFLATED;
   private int level = Deflater.DEFAULT_COMPRESSION;
   private String comment = "";
   private long bytes_written;
+
+  private int uncompressed_size;
 
-  // The Deflater we use.
-  private Deflater def;
+  /** The checksum object. */
+  private Checksum sum;
 }

-- 
	--Per Bothner
per@bothner.com   http://www.bothner.com/~per/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]