Professional Documents
Culture Documents
This example shows two versions of a single application: The original version of
the application, in ZipBrowserBefore, and the polished version, in ZipBrowserAfter.
Each ZipEntry stores certain minimal information about the entry: a path (the last
component of which is used as a name), a location in the archive, the original
uncompressed size, the size as stored compressed, the compression type, and a CRC.
There is an implicit tree structure to the entries in an archive, based on the
paths stored in the zip archive name fields, which is used to store directory
hierarchies. This is represented by having two types of ZipEntry, leaf and non-
leaf, with the non-leaf entries having an array of child entries.
The performance and responsiveness changes consist of (a) adding a FileBuffer class
for file access, and (b) placing most access to this class into the background
using an NSOperationQueue. The FileBuffer class represents a simple wrapper around
NSFileHandle, adding some buffering and byte swapping (the zip format is little-
endian). It is deliberately not thread-safe; instead, the application serializes
it by assigning a single instance to a single-threaded NSOperationQueue. The
ZipDocument instance creates the FileBuffer for a given document, and uses it only
long enough to obtain some basic information and confirm that the file does indeed
look like a zip archive; after that, all access to the FileBuffer goes through the
ZipDocument's NSOperationQueue. An operation is created that calls a specific
ZipDocument method to read the entries, put them on a queue, and periodically send
the collected ZipEntry objects back to the main thread to be added to the document
and the user interface. In addition, NSDocument's concurrent document opening is
turned on, to make opening of multiple documents concurrent with respect to each
other.
The security changes consist of adding checks at each point that a value is read
from the zip archive, to make sure that the value is intelligible and in range.
All offsets within the file are compared both above and below to make sure that
they are reasonable, to make sure that they are not made invalid by arithmetic
overflow.
The usability changes consist of adding drag support both in the browser and in the
preview view. Three pasteboard flavors are used: file promises, for drags to
Finder; filenames, for drags to other attachment-handling applications such as
TextEdit; and strings, for everything else. Creation of the dragged file is always
done lazily. Furthermore, since FileBuffer operations are defined to be on the
NSOperationQueue, an operation is created to do this, which calls back to the
ZipDocument to read and uncompress the entry and write it to disk. Currently the
drag code waits for the background file operation to complete, so an error can be
presented immediately if necessary, but this is not essential.
There are some things that this example does not do, that are left as exercises to
the reader. For example, none of the possible filesystem attributes (permissions,
etc.) that can potentially be stored in the archive are handled; the drag code
retrieves the contents of the entry and nothing else. Double-clicking on entries
is not supported; it might be possible to add support so that this would unarchive
and open that specific entry. More generally, this example serves only as a
viewer, not as an editor; no support is added for dragging files into an archive or
otherwise modifying it in any way.