From d46608becd22c14f08ea8b1fca155c6f2583a37f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 6 May 2011 22:58:07 +0000 Subject: [PATCH] I tested it on Linux, and on Windows, and on OS X: on every platform, near as I can tell, it's substantially faster to read resources directly from the jar file rather than unpacking them first. Why would that be? Well, for starters, I'm guessing it takes less time to decompress data than to read more data from the storage device. Also, it's likely to use disk space more efficiently: having all the resources in one jar makes it more likely that they'll occupy adjacent blocks, not to mention the fact that there's less wasted space from partially filled blocks. So, add an option to avoid unpacking files from jars before reading them. This doesn't work with FastImageIO's memory-mapped image reading, but we don't use that in Spiral Knights. git-svn-id: svn+ssh://src.earth.threerings.net/nenya/trunk@1164 ed5b42cb-e716-0410-a449-f6a68f950b19 --- .../resource/FileResourceBundle.java | 46 +++++++++++++++---- .../threerings/resource/ResourceManager.java | 33 +++++++++---- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/threerings/resource/FileResourceBundle.java b/src/main/java/com/threerings/resource/FileResourceBundle.java index cf035bd9..157f4726 100644 --- a/src/main/java/com/threerings/resource/FileResourceBundle.java +++ b/src/main/java/com/threerings/resource/FileResourceBundle.java @@ -60,12 +60,31 @@ public class FileResourceBundle extends ResourceBundle * @param source a file object that references our source jar file. * @param delay if true, the bundle will wait until someone calls {@link #sourceIsReady} * before allowing access to its resources. - * @param unpack if true the bundle will unpack itself into a temporary directory + * @param unpackFully if true the bundle will unpack itself fully. */ - public FileResourceBundle (File source, boolean delay, boolean unpack) + public FileResourceBundle (File source, boolean delay, boolean unpackFully) + { + this(source, delay, unpackFully, true); + } + + /** + * Constructs a resource bundle with the supplied jar file. + * + * @param source a file object that references our source jar file. + * @param delay if true, the bundle will wait until someone calls {@link #sourceIsReady} + * before allowing access to its resources. + * @param unpackFully if true the bundle will unpack itself fully. + * @param unpackEntries if true and unpackFully is false, individual entries will be + * unpacked into a temporary directory as needed (as opposed to being read directly from + * the jar file). + */ + public FileResourceBundle ( + File source, boolean delay, boolean unpackFully, boolean unpackEntries) { _source = source; - if (unpack) { + _unpackEntries = unpackEntries; + + if (unpackFully) { String root = stripSuffix(source.getPath()); _unpacked = new File(root + ".stamp"); _cache = new File(root); @@ -86,17 +105,25 @@ public class FileResourceBundle extends ResourceBundle public InputStream getResource (String path) throws IOException { - // unpack our resources into a temp directory so that we can load - // them quickly and the file system can cache them sensibly - File rfile = getResourceFile(path); - return (rfile == null) ? null : new FileInputStream(rfile); + // if specified, unpack our resources into a temp directory so that + // we can load them quickly and the file system can cache them sensibly + if (_unpackEntries) { + File rfile = getResourceFile(path); + return (rfile == null) ? null : new FileInputStream(rfile); + } + if (_jarSource == null && resolveJarFile()) { + return null; + } + JarEntry entry = _jarSource.getJarEntry(path); + return (entry == null) ? null : _jarSource.getInputStream(entry); } @Override public BufferedImage getImageResource (String path, boolean useFastIO) throws IOException { - return ResourceManager.loadImage(getResourceFile(path), useFastIO); + return _unpackEntries ? ResourceManager.loadImage(getResourceFile(path), useFastIO) : + ResourceManager.loadImage(getResource(path), useFastIO); } /** @@ -400,6 +427,9 @@ public class FileResourceBundle extends ResourceBundle /** The jar file from which we load resources. */ protected JarFile _jarSource; + /** Whether or not to unpack individual entries as needed from the jar file. */ + protected boolean _unpackEntries; + /** A directory in which we temporarily unpack our resource files. */ protected static File _tmpdir; } diff --git a/src/main/java/com/threerings/resource/ResourceManager.java b/src/main/java/com/threerings/resource/ResourceManager.java index b234b71a..b1a7653c 100644 --- a/src/main/java/com/threerings/resource/ResourceManager.java +++ b/src/main/java/com/threerings/resource/ResourceManager.java @@ -211,7 +211,7 @@ public class ResourceManager // check a system property to determine if we should unpack our bundles, but don't freak // out if we fail to read it try { - _unpack = !Boolean.getBoolean("no_unpack_resources"); + _unpackFully = !Boolean.getBoolean("no_unpack_resources"); } catch (SecurityException se) { // no problem, we're in a sandbox so we definitely won't be unpacking } @@ -263,7 +263,16 @@ public class ResourceManager */ public void setUnpackResources (boolean unpackResources) { - _unpack = unpackResources; + _unpackFully = unpackResources; + } + + /** + * Configures whether we unpack individual jar entries as opposed to reading them directly from + * the jar. + */ + public void setUnpackEntries (boolean unpackEntries) + { + _unpackEntries = unpackEntries; } /** @@ -440,7 +449,8 @@ public class ResourceManager public boolean checkBundle (String path) { File bfile = getResourceFile(path); - return (bfile == null) ? false : new FileResourceBundle(bfile, true, _unpack).isUnpacked(); + return (bfile == null) ? false : new FileResourceBundle( + bfile, true, _unpackFully, _unpackEntries).isUnpacked(); } /** @@ -459,7 +469,8 @@ public class ResourceManager return; } - final FileResourceBundle bundle = new FileResourceBundle(bfile, true, _unpack); + final FileResourceBundle bundle = new FileResourceBundle( + bfile, true, _unpackFully, _unpackEntries); if (bundle.isUnpacked()) { if (bundle.sourceIsReady()) { listener.requestCompleted(bundle); @@ -812,7 +823,8 @@ public class ResourceManager { if (setType.equals(FILE_SET_TYPE)) { FileResourceBundle bundle = - createFileResourceBundle(getResourceFile(path), true, _unpack); + createFileResourceBundle(getResourceFile(path), + true, _unpackFully, _unpackEntries); if (!bundle.isUnpacked() || !bundle.sourceIsReady()) { dlist.add(bundle); } @@ -828,9 +840,9 @@ public class ResourceManager * Creates an appropriate bundle for fetching resources from files. */ protected FileResourceBundle createFileResourceBundle (File source, boolean delay, - boolean unpack) + boolean unpackFully, boolean unpackEntries) { - return new FileResourceBundle(source, delay, unpack); + return new FileResourceBundle(source, delay, unpackFully, unpackEntries); } /** @@ -1066,8 +1078,11 @@ public class ResourceManager /** The root path we give to network bundles for all resources they're interested in. */ protected String _networkRootPath; - /** Whether or not to unpack our resource bundles. */ - protected boolean _unpack; + /** Whether or not to unpack our resource bundles fully. */ + protected boolean _unpackFully; + + /** Whether or not to unpack individual jar entries. */ + protected boolean _unpackEntries = true; /** Our default resource set. */ protected ResourceBundle[] _default = new ResourceBundle[0];