| /* |
| * Copyright 2013-present Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may |
| * not use this file except in compliance with the License. You may obtain |
| * a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package com.facebook.buck.zip; |
| |
| import com.google.common.base.Charsets; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.zip.ZipEntry; |
| |
| /** |
| * Each zip file has a "central directory" at the end of the archive, which provides the indexes |
| * required for fast random access to the contents of the zip. This class models that. |
| * <p> |
| * The central directory consists of a series of "file headers", describing each entry in the zip, |
| * and a "end of central directory" signature containing book keeping information. |
| */ |
| class CentralDirectory { |
| |
| /** |
| * Write the entire central directory, including the file headers and the end of central directory |
| * signature. |
| * |
| * @param out The stream to output to. |
| * @param startOffset The number of bytes offset within the zip file that this starts at. |
| * @param entries The entries that are contained within the zip. |
| * @throws IOException Should something go awry. |
| */ |
| public void writeCentralDirectory( |
| OutputStream out, |
| long startOffset, |
| Iterable<EntryAccounting> entries) throws IOException { |
| |
| int entryCount = 0; |
| long size = 0; |
| for (EntryAccounting entry : entries) { |
| entryCount++; |
| size += writeCentralDirectoryFileHeader(out, entry); |
| } |
| |
| // End of central directory |
| |
| ByteIo.writeInt(out, ZipEntry.ENDSIG); |
| |
| ByteIo.writeShort(out, 0); // Number of this disk (with end of central directory) |
| ByteIo.writeShort(out, 0); // Number of disk on which central directory starts. |
| ByteIo.writeShort(out, entryCount); // Number of central directory entries in this disk. |
| ByteIo.writeShort(out, entryCount); // Number of central directory entries. |
| ByteIo.writeInt(out, size); // Size of the central directory in bytes. |
| ByteIo.writeInt(out, startOffset); // Offset of the start of the central directory. |
| ByteIo.writeShort(out, 0); // Size of the comment (we don't have one) |
| } |
| |
| /** |
| * Each entry requires a description of that entry to be contained in the central directory. |
| */ |
| private long writeCentralDirectoryFileHeader( |
| OutputStream out, |
| EntryAccounting entry) throws IOException { |
| long size = 0; |
| size += ByteIo.writeInt(out, ZipEntry.CENSIG); |
| size += ByteIo.writeShort(out, entry.getRequiredExtractVersion()); // version made by. |
| size += ByteIo.writeShort(out, entry.getRequiredExtractVersion()); // version to extract with. |
| size += ByteIo.writeShort(out, entry.getFlags()); |
| size += ByteIo.writeShort(out, entry.getCompressionMethod()); // Compression. |
| size += ByteIo.writeInt(out, entry.getTime()); // Modification time. |
| size += ByteIo.writeInt(out, entry.getCrc()); |
| size += ByteIo.writeInt(out, entry.getCompressedSize()); |
| size += ByteIo.writeInt(out, entry.getSize()); |
| |
| byte[] nameBytes = entry.getName().getBytes(Charsets.UTF_8); |
| long externalAttributes = entry.getExternalAttributes(); |
| size += ByteIo.writeShort(out, nameBytes.length); // Length of name. |
| size += ByteIo.writeShort(out, 0); // Length of extra data. |
| size += ByteIo.writeShort(out, 0); // Length of file comment. |
| size += ByteIo.writeShort(out, 0); // Disk on which file starts. |
| size += ByteIo.writeShort(out, 0); // internal file attributes (unknown) |
| size += ByteIo.writeInt(out, externalAttributes); // external file attributes |
| size += ByteIo.writeInt(out, entry.getOffset()); // Offset of local file header. |
| out.write(nameBytes); |
| size += nameBytes.length; |
| |
| return size; |
| } |
| } |