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]
Other format: [Raw text]

Lazy instantiation of Jar and Zip files


Hi,

The following patch makes the creation of Jar and Zip files "lazy".
Reading of zip entries or Manifest files is done only when needed. Also
the creation of the Calendar object and conversion from/to dos time is
only done when get/setTime is called on the ZipEntry. The actual
ZipEntries are now stored in a Hashtable to make getEntry(String) more
efficient (especially important when an an entry cannot be found in a
large zip file).

2002-11-23  Mark Wielaard  <mark@klomp.org>

        * java/util/jar/JarFile.java (manifest): Not final.
        (manifestRead): New field.
        (JarFile): Don't read Manifest in constructor.
        (getManifest): New method.
        (JarEnumeration.nextElement): Use new method.
        (getEntry): Likewise.
        * java/util/zip/ZipFile.java (name): Final.
        (raf): Likewsie.
        (entries): Change type to Hashtable.
        (closed): New field.
        (ZipFile): Don't read enties in constructor.
        (readEntries): Use Hashtable.
        (close): Set new close flag and set entries to null inside 
        synchronized block.
        (entries): Contruct enumeration using new getEntries() method and 
        entries Hashtable. 
        (getEntryIndex): Removed.
        (getEntries): New method.
        (getEntry): Use new getEntries() method and entries Hastable.
        (getInputStream): Likewise.
        (size): Return getEntries().size().
        (ZipEntryEnumeration): Wrap entries Hashtable elements.
        * java/util/zip/ZipEntry.java (cal): Don't initialize.
        (time): Removed
        (dostime): New field.
        (zipFileIndex): Removed.
        (ZipEntry(ZipEntry)): Copy dostime.
        (setDOSTime): Now final and doesn't convert dos time.
        (getDOSTime): Likewise.
        (setTime): Convert dos time.
        (getTime): Likewise.
        (getCalendar): New method.
        (setExtra): Use setTime().
        * java/util/zip/ZipInputStream.java (getNextEntry): Format error msg.

OK to commit?

Cheers,

Mark
Index: java/util/jar/JarFile.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/jar/JarFile.java,v
retrieving revision 1.9
diff -u -r1.9 JarFile.java
--- java/util/jar/JarFile.java	22 Jan 2002 22:40:41 -0000	1.9
+++ java/util/jar/JarFile.java	25 Nov 2002 22:23:19 -0000
@@ -67,18 +67,22 @@
 
   /**
    * The manifest of this file, if any, otherwise null.
-   * Read by the constructor.
+   * Read when first needed.
    */
-  private final Manifest manifest;
+  private Manifest manifest;
 
-  /** Wether to verify the manifest and all entries */
+  /** Wether to verify the manifest and all entries. */
   private boolean verify;
 
+  /** Wether the has already been loaded. */
+  private boolean manifestRead = false;
+
   // Constructors
 
   /**
-   * Creates a new JarFile, tries to read the manifest and if the manifest
-   * exists verifies it.
+   * Creates a new JarFile. All jar entries are verified (when a Manifest file
+   * for this JarFile exists). You need to actually open and read the complete
+   * jar entry (with <code>getInputStream()</code>) to check its signature.
    *
    * @param fileName the name of the file to open
    * @exception FileNotFoundException if the fileName cannot be found
@@ -90,8 +94,10 @@
   }
 
   /**
-   * Creates a new JarFile, tries to read the manifest and if the manifest
-   * exists and verify is true verfies it.
+   * Creates a new JarFile. If verify is true then all jar entries are
+   * verified (when a Manifest file for this JarFile exists). You need to
+   * actually open and read the complete jar entry
+   * (with <code>getInputStream()</code>) to check its signature.
    *
    * @param fileName the name of the file to open
    * @param verify checks manifest and entries when true and a manifest
@@ -103,14 +109,12 @@
     FileNotFoundException, IOException
   {
     super(fileName);
-    manifest = readManifest();
-    if (verify)
-      verify();
   }
 
   /**
-   * Creates a new JarFile, tries to read the manifest and if the manifest
-   * exists verifies it.
+   * Creates a new JarFile. All jar entries are verified (when a Manifest file
+   * for this JarFile exists). You need to actually open and read the complete
+   * jar entry (with <code>getInputStream()</code>) to check its signature.
    *
    * @param file the file to open as a jar file
    * @exception FileNotFoundException if the file does not exits
@@ -122,8 +126,10 @@
   }
 
   /**
-   * Creates a new JarFile, tries to read the manifest and if the manifest
-   * exists and verify is true verfies it.
+   * Creates a new JarFile. If verify is true then all jar entries are
+   * verified (when a Manifest file for this JarFile exists). You need to
+   * actually open and read the complete jar entry
+   * (with <code>getInputStream()</code>) to check its signature.
    *
    * @param file the file to open to open as a jar file
    * @param verify checks manifest and entries when true and a manifest
@@ -135,13 +141,13 @@
     IOException
   {
     super(file);
-    manifest = readManifest();
-    if (verify)
-      verify();
   }
 
   /**
-   * Creates a new JarFile with the indicated mode, tries to read the
+   * Creates a new JarFile with the indicated mode. If verify is true then
+   * all jar entries are verified (when a Manifest file for this JarFile
+   * exists). You need to actually open and read the complete jar entry
+   * (with <code>getInputStream()</code>) to check its signature.
    * manifest and if the manifest exists and verify is true verfies it.
    *
    * @param file the file to open to open as a jar file
@@ -159,9 +165,6 @@
     FileNotFoundException, IOException, IllegalArgumentException
   {
     super(file, mode);
-    manifest = readManifest();
-    if (verify)
-      verify();
   }
 
   // Methods
@@ -241,6 +244,7 @@
     {
       ZipEntry zip = (ZipEntry) entries.nextElement();
       JarEntry jar = new JarEntry(zip);
+      Manifest manifest = getManifest();
       if (manifest != null)
 	{
 	  jar.attr = manifest.getAttributes(jar.getName());
@@ -261,6 +265,7 @@
     if (entry != null)
       {
 	JarEntry jarEntry = new JarEntry(entry);
+	Manifest manifest = getManifest();
 	if (manifest != null)
 	  {
 	    jarEntry.attr = manifest.getAttributes(name);
@@ -303,6 +308,9 @@
    */
   public Manifest getManifest()
   {
+    if (!manifestRead)
+      manifest = readManifest();
+
     return manifest;
   }
 }
Index: java/util/zip/ZipEntry.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/ZipEntry.java,v
retrieving revision 1.14
diff -u -r1.14 ZipEntry.java
--- java/util/zip/ZipEntry.java	15 Jun 2002 18:31:13 -0000	1.14
+++ java/util/zip/ZipEntry.java	25 Nov 2002 22:23:19 -0000
@@ -55,19 +55,18 @@
   private static int KNOWN_CRC    = 4;
   private static int KNOWN_TIME   = 8;
 
-  private static Calendar cal = Calendar.getInstance();
+  private static Calendar cal;
 
   private String name;
   private int size;
   private int compressedSize;
   private int crc;
-  private int time;
+  private int dostime;
   private short known = 0;
   private short method = -1;
   private byte[] extra = null;
   private String comment = null;
 
-  int zipFileIndex = -1;  /* used by ZipFile */
   int flags;              /* used by ZipOutputStream */
   int offset;             /* used by ZipFile and ZipOutputStream */
 
@@ -104,53 +103,24 @@
     size = e.size;
     compressedSize = e.compressedSize;
     crc = e.crc;
-    time = e.time;
+    dostime = e.dostime;
     method = e.method;
     extra = e.extra;
     comment = e.comment;
   }
 
-  void setDOSTime(int dostime)
+  final void setDOSTime(int dostime)
   {
-    int sec = 2 * (dostime & 0x1f);
-    int min = (dostime >> 5) & 0x3f;
-    int hrs = (dostime >> 11) & 0x1f;
-    int day = (dostime >> 16) & 0x1f;
-    int mon = ((dostime >> 21) & 0xf) - 1;
-    int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */
-    
-    // Guard against invalid or missing date causing
-    // IndexOutOfBoundsException.
-    try
-      {
-	synchronized (cal)
-	  {
-	    cal.set(year, mon, day, hrs, min, sec);
-	    time = (int) (cal.getTime().getTime() / 1000L);
-	  }
-	known |= KNOWN_TIME;
-      }
-    catch (RuntimeException ex)
-      {
-	/* Ignore illegal time stamp */
-	known &= ~KNOWN_TIME;
-      }
+    this.dostime = dostime;
+    known |= KNOWN_TIME;
   }
 
-  int getDOSTime()
+  final int getDOSTime()
   {
     if ((known & KNOWN_TIME) == 0)
       return 0;
-    synchronized (cal)
-      {
-	cal.setTime(new Date(time*1000L));
-	return (cal.get(cal.YEAR) - 1980 & 0x7f) << 25
-	  | (cal.get(cal.MONTH) + 1) << 21
-	  | (cal.get(cal.DAY_OF_MONTH)) << 16
-	  | (cal.get(cal.HOUR_OF_DAY)) << 11
-	  | (cal.get(cal.MINUTE)) << 5
-	  | (cal.get(cal.SECOND)) >> 1;
-      }
+    else
+      return dostime;
   }
 
   /**
@@ -190,7 +160,18 @@
    */
   public void setTime(long time)
   {
-    this.time = (int) (time / 1000L);
+    Calendar cal = getCalendar();
+    synchronized (cal)
+      {
+	cal.setTime(new Date(time*1000L));
+	dostime = (cal.get(cal.YEAR) - 1980 & 0x7f) << 25
+	  | (cal.get(cal.MONTH) + 1) << 21
+	  | (cal.get(cal.DAY_OF_MONTH)) << 16
+	  | (cal.get(cal.HOUR_OF_DAY)) << 11
+	  | (cal.get(cal.MINUTE)) << 5
+	  | (cal.get(cal.SECOND)) >> 1;
+      }
+    dostime = (int) (dostime / 1000L);
     this.known |= KNOWN_TIME;
   }
 
@@ -200,7 +181,39 @@
    */
   public long getTime()
   {
-    return (known & KNOWN_TIME) != 0 ? time * 1000L : -1;
+    if ((known & KNOWN_TIME) == 0)
+      return -1;
+    
+    int sec = 2 * (dostime & 0x1f);
+    int min = (dostime >> 5) & 0x3f;
+    int hrs = (dostime >> 11) & 0x1f;
+    int day = (dostime >> 16) & 0x1f;
+    int mon = ((dostime >> 21) & 0xf) - 1;
+    int year = ((dostime >> 25) & 0x7f) + 1980; /* since 1900 */
+   
+    try
+      {
+	cal = getCalendar();
+	synchronized (cal)
+	  {
+	    cal.set(year, mon, day, hrs, min, sec);
+	    return cal.getTime().getTime();
+	  }
+      }
+    catch (RuntimeException ex)
+      {
+	/* Ignore illegal time stamp */
+	known &= ~KNOWN_TIME;
+	return -1;
+      }
+  }
+
+  private static synchronized Calendar getCalendar()
+  {
+    if (cal == null)
+      cal = Calendar.getInstance();
+
+    return cal;
   }
 
   /**
@@ -320,11 +333,11 @@
 		int flags = extra[pos];
 		if ((flags & 1) != 0)
 		  {
-		    time = ((extra[pos+1] & 0xff)
+		    long time = ((extra[pos+1] & 0xff)
 			    | (extra[pos+2] & 0xff) << 8
 			    | (extra[pos+3] & 0xff) << 16
 			    | (extra[pos+4] & 0xff) << 24);
-		    known |= KNOWN_TIME;
+		    setTime(time);
 		  }
 	      }
 	    pos += len;
Index: java/util/zip/ZipFile.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/ZipFile.java,v
retrieving revision 1.18
diff -u -r1.18 ZipFile.java
--- java/util/zip/ZipFile.java	31 Oct 2002 21:56:32 -0000	1.18
+++ java/util/zip/ZipFile.java	25 Nov 2002 22:23:19 -0000
@@ -46,6 +46,7 @@
 import java.io.EOFException;
 import java.io.RandomAccessFile;
 import java.util.Enumeration;
+import java.util.Hashtable;
 import java.util.NoSuchElementException;
 
 /**
@@ -61,21 +62,26 @@
 public class ZipFile implements ZipConstants
 {
 
-  /** Mode flag to open a zip file for reading 
-   *
+  /**
+   * Mode flag to open a zip file for reading.
    */
-
   public static final int OPEN_READ = 0x1;
 
-  /** Mode flag to delete a zip file after reading 
-   *
+  /**
+   * Mode flag to delete a zip file after reading.
    */
-
   public static final int OPEN_DELETE = 0x4;
 
-  private String name;
-  RandomAccessFile raf;
-  ZipEntry[] entries;
+  // Name of this zip file.
+  private final String name;
+
+  // File from which zip entries are read.
+  private final RandomAccessFile raf;
+
+  // The entries of this zip file when initialized and not yet closed.
+  private Hashtable entries;
+
+  private boolean closed = false;
 
   /**
    * Opens a Zip file with the given name for reading.
@@ -87,7 +93,6 @@
   {
     this.raf = new RandomAccessFile(name, "r");
     this.name = name;
-    readEntries();
   }
 
   /**
@@ -100,7 +105,6 @@
   {
     this.raf = new RandomAccessFile(file, "r");
     this.name = file.getName();
-    readEntries();
   }
 
   /**
@@ -130,7 +134,6 @@
       }
     this.raf = new RandomAccessFile(file, "r");
     this.name = file.getName();
-    readEntries();
   }
 
   /**
@@ -160,7 +163,7 @@
 
   /**
    * Read the central directory of a zip file and fill the entries
-   * array.  This is called exactly once by the constructors.
+   * array.  This is called exactly once when first needed.
    * @exception IOException if a i/o error occured.
    * @exception ZipException if the central directory is malformed 
    */
@@ -187,7 +190,7 @@
       throw new EOFException(name);
     int centralOffset = readLeInt(raf);
 
-    entries = new ZipEntry[count];
+    entries = new Hashtable(count);
     raf.seek(centralOffset);
     byte[] ebs  = new byte[24];
     ByteArrayInputStream ebais = new ByteArrayInputStream(ebs);
@@ -236,9 +239,8 @@
 	    raf.readFully(buffer, 0, commentLen);
 	    entry.setComment(new String(buffer, 0, commentLen));
 	  }
-	entry.zipFileIndex = i;
 	entry.offset = offset;
-	entries[i] = entry;
+	entries.put(name, entry);
       }
   }
 
@@ -250,9 +252,10 @@
    */
   public void close() throws IOException
   {
-    entries = null;
     synchronized (raf)
       {
+	closed = true;
+	entries = null;
 	raf.close();
       }
   }
@@ -262,17 +265,34 @@
    */
   public Enumeration entries()
   {
-    if (entries == null)
-      throw new IllegalStateException("ZipFile has closed: " + name);
-    return new ZipEntryEnumeration(entries);
+    try
+      {
+	return new ZipEntryEnumeration(getEntries().elements());
+      }
+    catch (IOException ioe)
+      {
+	return null;
+      }
   }
 
-  private int getEntryIndex(String name)
+  /**
+   * Checks that the ZipFile is still open and reads entries when necessary.
+   *
+   * @exception IllegalStateException when the ZipFile has already been closed.
+   * @exception IOEexception when the entries could not be read.
+   */
+  private Hashtable getEntries() throws IOException
   {
-    for (int i = 0; i < entries.length; i++)
-      if (name.equals(entries[i].getName()))
-	return i;
-    return -1;
+    synchronized(raf)
+      {
+	if (closed)
+	  throw new IllegalStateException("ZipFile has closed: " + name);
+
+	if (entries == null)
+	  readEntries();
+
+	return entries;
+      }
   }
 
   /**
@@ -283,10 +303,16 @@
    * @see #entries */
   public ZipEntry getEntry(String name)
   {
-    if (entries == null)
-      throw new IllegalStateException("ZipFile has closed: " + name);
-    int index = getEntryIndex(name);
-    return index >= 0 ? (ZipEntry) entries[index].clone() : null;
+    try
+      {
+	Hashtable entries = getEntries();
+	ZipEntry entry = (ZipEntry) entries.get(name);
+	return entry != null ? (ZipEntry) entry.clone() : null;
+      }
+    catch (IOException ioe)
+      {
+	return null;
+      }
   }
 
   /**
@@ -334,21 +360,16 @@
    */
   public InputStream getInputStream(ZipEntry entry) throws IOException
   {
-    if (entries == null)
-      throw new IllegalStateException("ZipFile has closed");
-    int index = entry.zipFileIndex;
-    if (index < 0 || index >= entries.length
-	|| entries[index].getName() != entry.getName())
-      {
-	index = getEntryIndex(entry.getName());
-	if (index < 0)
-	  throw new NoSuchElementException();
-      }
+    Hashtable entries = getEntries();
+    String name = entry.getName();
+    ZipEntry zipEntry = (ZipEntry) entries.get(name);
+    if (zipEntry == null)
+      throw new NoSuchElementException(name);
 
-    long start = checkLocalHeader(entries[index]);
-    int method = entries[index].getMethod();
+    long start = checkLocalHeader(zipEntry);
+    int method = zipEntry.getMethod();
     InputStream is = new PartialInputStream
-      (raf, start, entries[index].getCompressedSize());
+      (raf, start, zipEntry.getCompressedSize());
     switch (method)
       {
       case ZipOutputStream.STORED:
@@ -375,42 +396,34 @@
   {
     try
       {
-	return entries.length;
+	return getEntries().size();
       }
-    catch (NullPointerException ex)
+    catch (IOException ioe)
       {
-	throw new IllegalStateException("ZipFile has closed");
+	return 0;
       }
   }
   
   private static class ZipEntryEnumeration implements Enumeration
   {
-    ZipEntry[] array;
-    int ptr = 0;
+    private final Enumeration elements;
 
-    public ZipEntryEnumeration(ZipEntry[] arr)
+    public ZipEntryEnumeration(Enumeration elements)
     {
-      array = arr;
+      this.elements = elements;
     }
 
     public boolean hasMoreElements()
     {
-      return ptr < array.length;
+      return elements.hasMoreElements();
     }
 
     public Object nextElement()
     {
-      try
-	{
-	  /* We return a clone, just to be safe that the user doesn't
-	   * change the entry.  
-	   */
-	  return array[ptr++].clone();
-	}
-      catch (ArrayIndexOutOfBoundsException ex)
-	{
-	  throw new NoSuchElementException();
-	}
+      /* We return a clone, just to be safe that the user doesn't
+       * change the entry.  
+       */
+      return ((ZipEntry)elements.nextElement()).clone();
     }
   }
 
Index: java/util/zip/ZipInputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/ZipInputStream.java,v
retrieving revision 1.16
diff -u -r1.16 ZipInputStream.java
--- java/util/zip/ZipInputStream.java	3 Nov 2002 20:27:31 -0000	1.16
+++ java/util/zip/ZipInputStream.java	25 Nov 2002 22:23:19 -0000
@@ -151,7 +151,7 @@
 	return null;
       }
     if (header != LOCSIG)
-      throw new ZipException("Wrong Local header signature"
+      throw new ZipException("Wrong Local header signature: "
 			     + Integer.toHexString(header));
     /* skip version */
     readLeShort();

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