blob: 4c99859c684b3e4314017286bd160ccf4d1bef81 [file] [log] [blame]
/*
* 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);
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, 0); // external file attributes (unknown)
size += ByteIo.writeInt(out, entry.getOffset()); // Offset of local file header.
out.write(nameBytes);
size += nameBytes.length;
return size;
}
}