Hi,

Committed.

Regards,
Jeroen

2006-02-15  Jeroen Frijters  <[EMAIL PROTECTED]>

        * java/util/zip/ZipFile.java
        (checkZipFile): Inlined readLeInt and rewritten for robustness.
        (readLeShort(DataInput,byte[]), readLeInt(DataInput,byte[],
        readLeShort(byte[],int), readLeInt(byte[],int)): Removed.
        (readEntries): Rewritten to use PartialInputStream.
        (locBuf, checkLocalHeader): Removed.
        (getInputStream): Rewritten to use new PartialInputStream.
        (PartialInputStream): Rewritten to do buffering.
Index: java/util/zip/ZipFile.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/util/zip/ZipFile.java,v
retrieving revision 1.27
diff -u -r1.27 ZipFile.java
--- java/util/zip/ZipFile.java  16 Sep 2005 01:07:21 -0000      1.27
+++ java/util/zip/ZipFile.java  13 Feb 2006 14:59:52 -0000
@@ -1,5 +1,5 @@
 /* ZipFile.java --
-   Copyright (C) 2001, 2002, 2003, 2004, 2005
+   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -41,8 +41,6 @@
 
 import gnu.java.util.EmptyEnumeration;
 
-import java.io.BufferedInputStream;
-import java.io.DataInput;
 import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
@@ -141,23 +139,33 @@
     checkZipFile();
   }
 
-  private void checkZipFile() throws IOException, ZipException
+  private void checkZipFile() throws ZipException
   {
-    byte[] magicBuf = new byte[4];
-    boolean validRead = true;
+    boolean valid = false;
 
     try 
       {
-       raf.readFully(magicBuf);
-      } 
-    catch (EOFException eof) 
+        byte[] buf = new byte[4];
+        raf.readFully(buf);
+        int sig = buf[0] & 0xFF
+                | ((buf[1] & 0xFF) << 8)
+                | ((buf[2] & 0xFF) << 16)
+                | ((buf[3] & 0xFF) << 24);
+        valid = sig == LOCSIG;
+      }
+    catch (IOException _)
       {
-       validRead = false;
       } 
 
-    if (validRead == false || readLeInt(magicBuf, 0) != LOCSIG)
+    if (!valid)
       {
-       raf.close();
+        try
+          {
+           raf.close();
+          }
+        catch (IOException _)
+          {
+          }
        throw new ZipException("Not a valid zip file");
       }
   }
@@ -172,69 +180,6 @@
   }
 
   /**
-   * Read an unsigned short in little endian byte order from the given
-   * DataInput stream using the given byte buffer.
-   *
-   * @param di DataInput stream to read from.
-   * @param b the byte buffer to read in (must be at least 2 bytes long).
-   * @return The value read.
-   *
-   * @exception IOException if a i/o error occured.
-   * @exception EOFException if the file ends prematurely
-   */
-  private int readLeShort(DataInput di, byte[] b) throws IOException
-  {
-    di.readFully(b, 0, 2);
-    return (b[0] & 0xff) | (b[1] & 0xff) << 8;
-  }
-
-  /**
-   * Read an int in little endian byte order from the given
-   * DataInput stream using the given byte buffer.
-   *
-   * @param di DataInput stream to read from.
-   * @param b the byte buffer to read in (must be at least 4 bytes long).
-   * @return The value read.
-   *
-   * @exception IOException if a i/o error occured.
-   * @exception EOFException if the file ends prematurely
-   */
-  private int readLeInt(DataInput di, byte[] b) throws IOException
-  {
-    di.readFully(b, 0, 4);
-    return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
-           | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
-  }
-
-  /**
-   * Read an unsigned short in little endian byte order from the given
-   * byte buffer at the given offset.
-   *
-   * @param b the byte array to read from.
-   * @param off the offset to read from.
-   * @return The value read.
-   */
-  private int readLeShort(byte[] b, int off)
-  {
-    return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
-  }
-
-  /**
-   * Read an int in little endian byte order from the given
-   * byte buffer at the given offset.
-   *
-   * @param b the byte array to read from.
-   * @param off the offset to read from.
-   * @return The value read.
-   */
-  private int readLeInt(byte[] b, int off)
-  {
-    return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
-           | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
-  }
-  
-
-  /**
    * Read the central directory of a zip file and fill the entries
    * array.  This is called exactly once when first needed. It is called
    * while holding the lock on <code>raf</code>.
@@ -246,63 +191,48 @@
   {
     /* Search for the End Of Central Directory.  When a zip comment is 
      * present the directory may start earlier.
-     * FIXME: This searches the whole file in a very slow manner if the
-     * file isn't a zip file.
+     * Note that a comment has a maximum length of 64K, so that is the
+     * maximum we search backwards.
      */
+    PartialInputStream inp = new PartialInputStream(raf, 4096);
     long pos = raf.length() - ENDHDR;
-    byte[] ebs  = new byte[CENHDR];
-    
+    long top = Math.max(0, pos - 65536);
     do
       {
-       if (pos < 0)
+       if (pos < top)
          throw new ZipException
            ("central directory not found, probably not a zip file: " + name);
-       raf.seek(pos--);
+       inp.seek(pos--);
       }
-    while (readLeInt(raf, ebs) != ENDSIG);
+    while (inp.readLeInt() != ENDSIG);
     
-    if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
+    if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
       throw new EOFException(name);
-    int count = readLeShort(raf, ebs);
-    if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
+    int count = inp.readLeShort();
+    if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
       throw new EOFException(name);
-    int centralOffset = readLeInt(raf, ebs);
+    int centralOffset = inp.readLeInt();
 
     entries = new HashMap(count+count/2);
-    raf.seek(centralOffset);
+    inp.seek(centralOffset);
     
-    byte[] buffer = new byte[16];
     for (int i = 0; i < count; i++)
       {
-       raf.readFully(ebs);
-       if (readLeInt(ebs, 0) != CENSIG)
+       if (inp.readLeInt() != CENSIG)
          throw new ZipException("Wrong Central Directory signature: " + name);
 
-       int method = readLeShort(ebs, CENHOW);
-       int dostime = readLeInt(ebs, CENTIM);
-       int crc = readLeInt(ebs, CENCRC);
-       int csize = readLeInt(ebs, CENSIZ);
-       int size = readLeInt(ebs, CENLEN);
-       int nameLen = readLeShort(ebs, CENNAM);
-       int extraLen = readLeShort(ebs, CENEXT);
-       int commentLen = readLeShort(ebs, CENCOM);
-       
-       int offset = readLeInt(ebs, CENOFF);
-
-       int needBuffer = Math.max(nameLen, commentLen);
-       if (buffer.length < needBuffer)
-         buffer = new byte[needBuffer];
-
-       raf.readFully(buffer, 0, nameLen);
-       String name;
-       try
-         {
-           name = new String(buffer, 0, nameLen, "UTF-8");
-         }
-       catch (UnsupportedEncodingException uee)
-         {
-           throw new AssertionError(uee);
-         }
+        inp.skip(6);
+       int method = inp.readLeShort();
+       int dostime = inp.readLeInt();
+       int crc = inp.readLeInt();
+       int csize = inp.readLeInt();
+       int size = inp.readLeInt();
+       int nameLen = inp.readLeShort();
+       int extraLen = inp.readLeShort();
+       int commentLen = inp.readLeShort();
+        inp.skip(8);
+       int offset = inp.readLeInt();
+       String name = inp.readString(nameLen);
 
        ZipEntry entry = new ZipEntry(name);
        entry.setMethod(method);
@@ -313,20 +243,12 @@
        if (extraLen > 0)
          {
            byte[] extra = new byte[extraLen];
-           raf.readFully(extra);
+           inp.readFully(extra);
            entry.setExtra(extra);
          }
        if (commentLen > 0)
          {
-           raf.readFully(buffer, 0, commentLen);
-           try
-             {
-               entry.setComment(new String(buffer, 0, commentLen, "UTF-8"));
-             }
-           catch (UnsupportedEncodingException uee)
-             {
-               throw new AssertionError(uee);
-             }
+            entry.setComment(inp.readString(commentLen));
          }
        entry.offset = offset;
        entries.put(name, entry);
@@ -429,42 +351,6 @@
       }
   }
 
-
-  //access should be protected by synchronized(raf)
-  private byte[] locBuf = new byte[LOCHDR];
-
-  /**
-   * Checks, if the local header of the entry at index i matches the
-   * central directory, and returns the offset to the data.
-   * 
-   * @param entry to check.
-   * @return the start offset of the (compressed) data.
-   *
-   * @exception IOException if a i/o error occured.
-   * @exception ZipException if the local header doesn't match the 
-   * central directory header
-   */
-  private long checkLocalHeader(ZipEntry entry) throws IOException
-  {
-    synchronized (raf)
-      {
-       raf.seek(entry.offset);
-       raf.readFully(locBuf);
-       
-       if (readLeInt(locBuf, 0) != LOCSIG)
-         throw new ZipException("Wrong Local header signature: " + name);
-
-       if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
-         throw new ZipException("Compression method mismatch: " + name);
-
-       if (entry.getName().length() != readLeShort(locBuf, LOCNAM))
-         throw new ZipException("file name length mismatch: " + name);
-
-       int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT);
-       return entry.offset + LOCHDR + extraLen;
-      }
-  }
-
   /**
    * Creates an input stream reading the given zip entry as
    * uncompressed data.  Normally zip entry should be an entry
@@ -497,16 +383,32 @@
     if (zipEntry == null)
       return null;
 
-    long start = checkLocalHeader(zipEntry);
+    PartialInputStream inp = new PartialInputStream(raf, 1024);
+    inp.seek(zipEntry.offset);
+
+    if (inp.readLeInt() != LOCSIG)
+      throw new ZipException("Wrong Local header signature: " + name);
+
+    inp.skip(4);
+
+    if (zipEntry.getMethod() != inp.readLeShort())
+      throw new ZipException("Compression method mismatch: " + name);
+
+    inp.skip(16);
+
+    int nameLen = inp.readLeShort();
+    int extraLen = inp.readLeShort();
+    inp.skip(nameLen + extraLen);
+
+    inp.setLength(zipEntry.getCompressedSize());
+
     int method = zipEntry.getMethod();
-    InputStream is = new BufferedInputStream(new PartialInputStream
-      (raf, start, zipEntry.getCompressedSize()));
     switch (method)
       {
       case ZipOutputStream.STORED:
-       return is;
+       return inp;
       case ZipOutputStream.DEFLATED:
-       return new InflaterInputStream(is, new Inflater(true));
+       return new InflaterInputStream(inp, new Inflater(true));
       default:
        throw new ZipException("Unknown compression method " + method);
       }
@@ -562,21 +464,41 @@
     }
   }
 
-  private static class PartialInputStream extends InputStream
+  private static final class PartialInputStream extends InputStream
   {
     private final RandomAccessFile raf;
-    long filepos, end;
+    private final byte[] buffer;
+    private long bufferOffset;
+    private int pos;
+    private long end;
 
-    public PartialInputStream(RandomAccessFile raf, long start, long len)
+    public PartialInputStream(RandomAccessFile raf, int bufferSize)
+      throws IOException
     {
       this.raf = raf;
-      filepos = start;
-      end = start + len;
+      buffer = new byte[bufferSize];
+      bufferOffset = -buffer.length;
+      pos = buffer.length;
+      end = raf.length();
+    }
+
+    void setLength(long length)
+    {
+      end = bufferOffset + pos + length;
+    }
+
+    private void fillBuffer() throws IOException
+    {
+      synchronized (raf)
+        {
+          raf.seek(bufferOffset);
+          raf.readFully(buffer, 0, (int) Math.min(buffer.length, end - 
bufferOffset));
+        }
     }
     
     public int available()
     {
-      long amount = end - filepos;
+      long amount = end - (bufferOffset + pos);
       if (amount > Integer.MAX_VALUE)
        return Integer.MAX_VALUE;
       return (int) amount;
@@ -584,41 +506,130 @@
     
     public int read() throws IOException
     {
-      if (filepos == end)
+      if (bufferOffset + pos >= end)
        return -1;
-      synchronized (raf)
-       {
-         raf.seek(filepos++);
-         return raf.read();
-       }
+      if (pos == buffer.length)
+        {
+          bufferOffset += buffer.length;
+          pos = 0;
+          fillBuffer();
+        }
+      return buffer[pos++] & 0xFF;
     }
 
     public int read(byte[] b, int off, int len) throws IOException
     {
-      if (len > end - filepos)
+      if (len > end - (bufferOffset + pos))
        {
-         len = (int) (end - filepos);
+         len = (int) (end - (bufferOffset + pos));
          if (len == 0)
            return -1;
        }
-      synchronized (raf)
-       {
-         raf.seek(filepos);
-         int count = raf.read(b, off, len);
-         if (count > 0)
-           filepos += len;
-         return count;
-       }
+
+      int totalBytesRead = Math.min(buffer.length - pos, len);
+      System.arraycopy(buffer, pos, b, off, totalBytesRead);
+      pos += totalBytesRead;
+      off += totalBytesRead;
+      len -= totalBytesRead;
+
+      while (len > 0)
+        {
+          bufferOffset += buffer.length;
+          pos = 0;
+          fillBuffer();
+          int remain = Math.min(buffer.length, len);
+          System.arraycopy(buffer, pos, b, off, remain);
+          pos += remain;
+          off += remain;
+          len -= remain;
+          totalBytesRead += remain;
+        }
+
+      return totalBytesRead;
     }
 
-    public long skip(long amount)
+    public long skip(long amount) throws IOException
     {
       if (amount < 0)
-       throw new IllegalArgumentException();
-      if (amount > end - filepos)
-       amount = end - filepos;
-      filepos += amount;
+       return 0;
+      if (amount > end - (bufferOffset + pos))
+       amount = end - (bufferOffset + pos);
+      seek(bufferOffset + pos + amount);
       return amount;
     }
+
+    void seek(long newpos) throws IOException
+    {
+      long offset = newpos - bufferOffset;
+      if (offset >= 0 && offset <= buffer.length)
+        {
+          pos = (int) offset;
+        }
+      else
+        {
+          bufferOffset = newpos;
+          pos = 0;
+          fillBuffer();
+        }
+    }
+
+    void readFully(byte[] buf) throws IOException
+    {
+      if (read(buf, 0, buf.length) != buf.length)
+        throw new EOFException();
+    }
+
+    void readFully(byte[] buf, int off, int len) throws IOException
+    {
+      if (read(buf, off, len) != len)
+        throw new EOFException();
+    }
+
+    int readLeShort() throws IOException
+    {
+      int b0 = read();
+      int b1 = read();
+      if (b1 == -1)
+        throw new EOFException();
+      return (b0 & 0xff) | (b1 & 0xff) << 8;
+    }
+
+    int readLeInt() throws IOException
+    {
+      int b0 = read();
+      int b1 = read();
+      int b2 = read();
+      int b3 = read();
+      if (b3 == -1)
+        throw new EOFException();
+      return ((b0 & 0xff) | (b1 & 0xff) << 8)
+            | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
+    }
+
+    String readString(int length) throws IOException
+    {
+      if (length > end - (bufferOffset + pos))
+        throw new EOFException();
+
+      try
+        {
+          if (buffer.length - pos >= length)
+            {
+              String s = new String(buffer, pos, length, "UTF-8");
+              pos += length;
+              return s;
+            }
+          else
+            {
+              byte[] b = new byte[length];
+              readFully(b);
+              return new String(b, 0, length, "UTF-8");
+            }
+        }
+      catch (UnsupportedEncodingException uee)
+        {
+          throw new AssertionError(uee);
+        }
+    }
   }
 }

Reply via email to