# HG changeset patch # User Jaroslav Tulach # Date 1360494880 -3600 # Node ID a48961ff3e6b6d68e12f704376d24d640574055d # Parent 6de8252246b53c735ca0ad99199f0b591036e4ef Using FastJar to read the content table of JAR files diff -r 6de8252246b5 -r a48961ff3e6b emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java Sun Feb 10 09:51:22 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/zip/FastJar.java Sun Feb 10 12:14:40 2013 +0100 @@ -28,17 +28,12 @@ * * Portions Copyrighted 2007 Sun Microsystems, Inc. */ -package org.netbeans.modules.java.source.parsing; +package org.apidesign.bck2brwsr.emul.zip; -import java.io.File; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.io.RandomAccessFile; -import java.util.Date; -import java.util.LinkedList; -import java.util.List; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** @@ -46,78 +41,28 @@ * @author Tomas Zezula */ public final class FastJar { + private final byte[] arr; - private FastJar() { + public FastJar(byte[] arr) { + this.arr = arr; } private static final int GIVE_UP = 1<<16; - - - private static class RandomAccessFileInputStream extends InputStream { - - private final RandomAccessFile b; - private final long len; - - public RandomAccessFileInputStream (RandomAccessFile b) throws IOException { - assert b != null; - this.b = b; - this.len = b.length(); - } - - public RandomAccessFileInputStream (RandomAccessFile b, long len) throws IOException { - assert b != null; - assert len >=0; - this.b = b; - this.len = b.getFilePointer()+len; - } - - public int read (byte[] data, int offset, int size) throws IOException { - int rem = available(); - if (rem == 0) { - return -1; - } - int rlen; - if (size> 25) & 0x7f) + 80), (int)(((dosTime >> 21) & 0x0f) - 1), @@ -127,120 +72,94 @@ (int)((dosTime << 1) & 0x3e)); return d.getTime(); } + */ } - public static InputStream getInputStream (final File file, final Entry e) throws IOException { - return getInputStream(file, e.offset); + public InputStream getInputStream (final Entry e) throws IOException { + return getInputStream(arr, e.offset); } - static InputStream getInputStream (final File file, final long offset) throws IOException { - RandomAccessFile f = new RandomAccessFile (file, "r"); //NOI18N - f.seek (offset); - ZipInputStream in = new ZipInputStream (new RandomAccessFileInputStream (f)); + private static InputStream getInputStream (byte[] arr, final long offset) throws IOException { + ByteArrayInputStream is = new ByteArrayInputStream(arr); + is.skip(offset); + ZipInputStream in = new ZipInputStream (is); ZipEntry e = in.getNextEntry(); if (e != null && e.getCrc() == 0L && e.getMethod() == ZipEntry.STORED) { - long cp = f.getFilePointer(); - in.close(); - f = new RandomAccessFile (file, "r"); //NOI18N - f.seek (cp); - return new RandomAccessFileInputStream (f, e.getSize()); + int cp = arr.length - is.available(); + return new ByteArrayInputStream(arr, cp, (int)e.getSize()); } return in; } - static ZipEntry getZipEntry (final File file, final long offset) throws IOException { - RandomAccessFile f = new RandomAccessFile (file, "r"); //NOI18N - try { - f.seek (offset); - ZipInputStream in = new ZipInputStream (new RandomAccessFileInputStream (f)); - try { - return in.getNextEntry(); - } finally { - in.close(); + public Entry[] list() throws IOException { + final int size = arr.length; + + int at = size - ZipInputStream.ENDHDR; + + byte[] data = new byte[ZipInputStream.ENDHDR]; + int giveup = 0; + + do { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at--; + giveup++; + if (giveup > GIVE_UP) { + throw new IOException (); } - } finally { - f.close (); + } while (getsig(data) != ZipInputStream.ENDSIG); + + + final long censize = endsiz(data); + final long cenoff = endoff(data); + at = (int) cenoff; + + Entry[] result = new Entry[0]; + int cenread = 0; + data = new byte[ZipInputStream.CENHDR]; + while (cenread < censize) { + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(arr, at, data, 0, data.length); + at += data.length; + if (getsig(data) != ZipInputStream.CENSIG) { + throw new IOException("No central table"); //NOI18N + } + int cennam = cennam(data); + int cenext = cenext(data); + int cencom = cencom(data); + long lhoff = cenoff(data); + long centim = centim(data); + String name = new String(arr, at, cennam, "UTF-8"); + at += cennam; + int seekby = cenext+cencom; + int cendatalen = ZipInputStream.CENHDR + cennam + seekby; + cenread+=cendatalen; + result = addEntry(result, new Entry(name,lhoff, centim)); + at += seekby; } + return result; } - public static Iterable list(File f) throws IOException { - RandomAccessFile b = new RandomAccessFile (f,"r"); //NOI18N - try { - final long size = (int) b.length(); - b.seek (size-ZipFile.ENDHDR); - - byte[] data = new byte[ZipFile.ENDHDR]; - int giveup = 0; - - do { - if (b.read(data, 0, ZipFile.ENDHDR)!=ZipFile.ENDHDR) { - throw new IOException (); - } - b.seek(b.getFilePointer()-(ZipFile.ENDHDR+1)); - giveup++; - if (giveup > GIVE_UP) { - throw new IOException (); - } - } while (getsig(data) != ZipFile.ENDSIG); - - - final long censize = endsiz(data); - final long cenoff = endoff(data); - b.seek (cenoff); - - List result = new LinkedList(); - int cenread = 0; - data = new byte[ZipFile.CENHDR]; - while (cenread < censize) { - if (b.read(data, 0, ZipFile.CENHDR)!=ZipFile.CENHDR) { - throw new IOException ("No central table"); //NOI18N - } - if (getsig(data) != ZipFile.CENSIG) { - throw new IOException("No central table"); //NOI18N - } - int cennam = cennam(data); - int cenext = cenext(data); - int cencom = cencom(data); - long lhoff = cenoff(data); - long centim = centim(data); - String name = name(b, cennam); - int seekby = cenext+cencom; - int cendatalen = ZipFile.CENHDR + cennam + seekby; - cenread+=cendatalen; - result.add(new Entry(name,lhoff, centim)); - seekBy(b,seekby); - } - return result; - } finally { - b.close(); - } - } - - private static final String name(final RandomAccessFile b, final int cennam) throws IOException { - byte[] name = new byte[cennam]; - b.read(name, 0, cennam); - return new String(name, "UTF-8"); //NOI18N + private Entry[] addEntry(Entry[] result, Entry entry) { + Entry[] e = new Entry[result.length + 1]; + e[result.length] = entry; + org.apidesign.bck2brwsr.emul.lang.System.arraycopy(result, 0, e, 0, result.length); + return e; } private static final long getsig(final byte[] b) throws IOException {return get32(b,0);} - private static final long endsiz(final byte[] b) throws IOException {return get32(b,ZipFile.ENDSIZ);} - private static final long endoff(final byte[] b) throws IOException {return get32(b,ZipFile.ENDOFF);} - private static final long cenlen(final byte[] b) throws IOException {return get32(b,ZipFile.CENLEN);} - private static final long censiz(final byte[] b) throws IOException {return get32(b,ZipFile.CENSIZ);} - private static final long centim(final byte[] b) throws IOException {return get32(b,ZipFile.CENTIM);} - private static final int cennam(final byte[] b) throws IOException {return get16(b,ZipFile.CENNAM);} - private static final int cenext(final byte[] b) throws IOException {return get16(b,ZipFile.CENEXT);} - private static final int cencom(final byte[] b) throws IOException {return get16(b,ZipFile.CENCOM);} - private static final long cenoff (final byte[] b) throws IOException {return get32(b,ZipFile.CENOFF);} - private static final int lochow(final byte[] b) throws IOException {return get16(b,ZipFile.LOCHOW);} - private static final int locname(final byte[] b) throws IOException {return get16(b,ZipFile.LOCNAM);} - private static final int locext(final byte[] b) throws IOException {return get16(b,ZipFile.LOCEXT);} - private static final long locsiz(final byte[] b) throws IOException {return get32(b,ZipFile.LOCSIZ);} + private static final long endsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDSIZ);} + private static final long endoff(final byte[] b) throws IOException {return get32(b,ZipInputStream.ENDOFF);} + private static final long cenlen(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENLEN);} + private static final long censiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENSIZ);} + private static final long centim(final byte[] b) throws IOException {return get32(b,ZipInputStream.CENTIM);} + private static final int cennam(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENNAM);} + private static final int cenext(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENEXT);} + private static final int cencom(final byte[] b) throws IOException {return get16(b,ZipInputStream.CENCOM);} + private static final long cenoff (final byte[] b) throws IOException {return get32(b,ZipInputStream.CENOFF);} + private static final int lochow(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCHOW);} + private static final int locname(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCNAM);} + private static final int locext(final byte[] b) throws IOException {return get16(b,ZipInputStream.LOCEXT);} + private static final long locsiz(final byte[] b) throws IOException {return get32(b,ZipInputStream.LOCSIZ);} - private static final void seekBy(final RandomAccessFile b, int offset) throws IOException { - b.seek(b.getFilePointer() + offset); - } - private static final int get16(final byte[] b, int off) throws IOException { final int b1 = b[off]; final int b2 = b[off+1]; diff -r 6de8252246b5 -r a48961ff3e6b vm/src/main/java/org/apidesign/vm4brwsr/Zips.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Sun Feb 10 09:51:22 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Zips.java Sun Feb 10 12:14:40 2013 +0100 @@ -19,17 +19,25 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.emul.zip.FastJar; /** Conversion from classpath to load function. * * @author Jaroslav Tulach */ final class Zips { - private Zips() { + private final FastJar fj; + + private Zips(String path, byte[] zipData) throws IOException { + long bef = currentTimeMillis(); + fj = new FastJar(zipData); + for (FastJar.Entry e : fj.list()) { + putRes(e.name, e); + } + log("Iterating thru " + path + " took " + (currentTimeMillis() - bef) + "ms"); } public static void init() { @@ -64,38 +72,32 @@ } return null; } + + @JavaScriptBody(args = { "msg" }, body = "console.log(msg.toString());") + private static native void log(String msg); + + private byte[] findRes(String res) throws IOException { + Object arr = findResImpl(res); + if (arr instanceof FastJar.Entry) { + long bef = currentTimeMillis(); + InputStream zip = fj.getInputStream((FastJar.Entry)arr); + arr = readFully(new byte[512], zip); + putRes(res, arr); + log("Reading " + res + " took " + (currentTimeMillis() - bef) + "ms"); + } + return (byte[]) arr; + } @JavaScriptBody(args = { "res" }, body = "var r = this[res]; return r ? r : null;") - private native byte[] findRes(String res); + private native Object findResImpl(String res); @JavaScriptBody(args = { "res", "arr" }, body = "this[res] = arr;") - private native void putRes(String res, byte[] arr); + private native void putRes(String res, Object arr); private static Zips toZip(String path) throws IOException { URL u = new URL(path); - ZipInputStream zip = new ZipInputStream(u.openStream()); - Zips z = new Zips(); - for (;;) { - ZipEntry entry = zip.getNextEntry(); - if (entry == null) { - break; - } - byte[] arr = new byte[4096]; - int offset = 0; - for (;;) { - int len = zip.read(arr, offset, arr.length - offset); - if (len == -1) { - break; - } - offset += len; - if (offset == arr.length) { - enlargeArray(arr, arr.length + 4096); - } - } - sliceArray(arr, offset); - z.putRes(entry.getName(), arr); - } - return z; + byte[] zipData = (byte[]) u.getContent(new Class[] { byte[].class }); + return new Zips(path, zipData); } private static String processClassPathAttr(final byte[] man, String url, Object[] classpath) throws IOException { @@ -132,6 +134,28 @@ @JavaScriptBody(args = { "arr", "len" }, body = "arr.splice(len, arr.length - len);") private static native void sliceArray(byte[] arr, int len); + + private static Object readFully(byte[] arr, InputStream zip) throws IOException { + int offset = 0; + for (;;) { + int len = zip.read(arr, offset, arr.length - offset); + if (len == -1) { + break; + } + offset += len; + if (offset == arr.length) { + enlargeArray(arr, arr.length + 4096); + } + } + sliceArray(arr, offset); + return arr; + } + + private static long currentTimeMillis() { + return (long)m(); + } + @JavaScriptBody(args = { }, body = "return window.performance.now();") + private static native double m(); } diff -r 6de8252246b5 -r a48961ff3e6b vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vmtest/src/test/java/org/apidesign/bck2brwsr/vmtest/impl/ZipEntryTest.java Sun Feb 10 12:14:40 2013 +0100 @@ -0,0 +1,67 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vmtest.impl; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apidesign.bck2brwsr.emul.zip.FastJar; +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +/** + * + * @author Jaroslav Tulach + */ +@GenerateZip(name = "five.zip", contents = { + "1.txt", "one", + "2.txt", "duo", + "3.txt", "three", + "4.txt", "four", + "5.txt", "five" +}) +public class ZipEntryTest { + @Test + public void readEntriesEffectively() throws IOException { + InputStream is = ZipEntryTest.class.getResourceAsStream("five.zip"); + byte[] arr = new byte[is.available()]; + int len = is.read(arr); + assertEquals(len, arr.length, "Read fully"); + + FastJar fj = new FastJar(arr); + FastJar.Entry[] entrs = fj.list(); + + assertEquals(5, entrs.length, "Five entries"); + + for (int i = 1; i <= 5; i++) { + FastJar.Entry en = entrs[i - 1]; + assertEquals(en.name, i + ".txt"); +// assertEquals(cis.cnt, 0, "Content of the file should be skipped, not read"); + } + + assertContent("three", fj.getInputStream(entrs[3 - 1]), "read OK"); + assertContent("five", fj.getInputStream(entrs[5 - 1]), "read OK"); + } + + private static void assertContent(String exp, InputStream is, String msg) throws IOException { + byte[] arr = new byte[512]; + int len = is.read(arr); + String s = new String(arr, 0, len); + assertEquals(exp, s, msg); + } +}