| // Copyright 2015 The Bazel Authors. All rights reserved. |
| // |
| // 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.google.devtools.build.android.ziputils; |
| |
| import static java.nio.ByteOrder.LITTLE_ENDIAN; |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import com.google.errorprone.annotations.CanIgnoreReturnValue; |
| import java.nio.ByteBuffer; |
| |
| /** |
| * Provides a view of a zip files "end of central directory" record. |
| */ |
| public class EndOfCentralDirectory extends View<EndOfCentralDirectory> { |
| |
| /** |
| * Returns a {@code EndOfCentralDirectory} of the given buffer. |
| * |
| * @param buffer containing the data of a "end of central directory" record. |
| * @return a {@code EndOfCentralDirectory} of the data at the current position of the given |
| * byte buffer. |
| */ |
| public static EndOfCentralDirectory viewOf(ByteBuffer buffer) { |
| EndOfCentralDirectory view = new EndOfCentralDirectory(buffer.slice()); |
| view.buffer.position(0).limit(view.getSize()); |
| buffer.position(buffer.position() + view.buffer.remaining()); |
| return view; |
| } |
| |
| private EndOfCentralDirectory(ByteBuffer buffer) { |
| super(buffer); |
| } |
| |
| /** |
| * Creates a {@code EndOfCentralDirectory} with a heap allocated buffer. |
| * |
| * @param comment zip file comment, or {@code null}. |
| * @return a {@code EndOfCentralDirectory} with a heap allocated buffer. |
| */ |
| public static EndOfCentralDirectory allocate(String comment) { |
| byte[] commentData = comment != null ? comment.getBytes(UTF_8) : EMPTY; |
| int size = SIZE + commentData.length; |
| ByteBuffer buffer = ByteBuffer.allocate(size).order(LITTLE_ENDIAN); |
| return new EndOfCentralDirectory(buffer).init(commentData); |
| } |
| |
| /** |
| * Creates a {@code EndOfCentralDirectory} over a writable buffer. |
| * |
| * @param buffer buffer to hold data for the "end of central directory" record. |
| * @param comment zip file global comment, or {@code null}. |
| * @return a {@code EndOfCentralDirectory} with a heap allocated buffer. |
| */ |
| public static EndOfCentralDirectory view(ByteBuffer buffer, String comment) { |
| byte[] commentData = comment != null ? comment.getBytes(UTF_8) : EMPTY; |
| int size = SIZE + commentData.length; |
| EndOfCentralDirectory view = new EndOfCentralDirectory(buffer.slice()).init(commentData); |
| buffer.position(buffer.position() + size); |
| return view; |
| } |
| |
| /** |
| * Copies this {@code EndOfCentralDirectory} over a writable buffer. |
| * |
| * @param buffer writable byte buffer to hold the data of the copy. |
| */ |
| public EndOfCentralDirectory copy(ByteBuffer buffer) { |
| EndOfCentralDirectory view = new EndOfCentralDirectory(buffer.slice()); |
| this.buffer.rewind(); |
| view.buffer.put(this.buffer).flip(); |
| this.buffer.rewind(); |
| buffer.position(buffer.position() + this.buffer.remaining()); |
| return view; |
| } |
| |
| @CanIgnoreReturnValue |
| private EndOfCentralDirectory init(byte[] comment) { |
| buffer.putInt(0, SIGNATURE); |
| set(ENDCOM, comment.length); |
| if (comment.length > 0) { |
| buffer.position(SIZE); |
| buffer.put(comment); |
| } |
| buffer.position(0).limit(SIZE + comment.length); |
| return this; |
| } |
| |
| /** |
| * Signature of end of directory record. |
| */ |
| public static final int SIGNATURE = 0x06054b50; //101010256L |
| |
| /** |
| * Size of end of directory record, not including variable data. |
| * Also the offset of the file comment, if any. |
| */ |
| public static final int SIZE = 22; |
| |
| /** |
| * For accessing the end of central directory signature, with the {@link |
| * View#get(com.google.devtools.build.android.ziputils.View.IntFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.IntFieldId, long)} methods. |
| */ |
| public static final IntFieldId<EndOfCentralDirectory> ENDSIG = new IntFieldId<>(0); |
| |
| /** |
| * For accessing the "this disk number" end of central directory field, with the {@link |
| * View#get(com.google.devtools.build.android.ziputils.View.ShortFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.ShortFieldId, int)} methods. |
| */ |
| public static final ShortFieldId<EndOfCentralDirectory> ENDDSK = new ShortFieldId<>(4); |
| |
| /** |
| * For accessing the "central directory start disk" end of central directory field, with the |
| * {@link View#get(com.google.devtools.build.android.ziputils.View.ShortFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.ShortFieldId, int)} methods. |
| */ |
| public static final ShortFieldId<EndOfCentralDirectory> ENDDCD = new ShortFieldId<>(6); |
| |
| /** |
| * For accessing the "central directory local records" end of central directory field, with the |
| * {@link View#get(com.google.devtools.build.android.ziputils.View.ShortFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.ShortFieldId, int)} methods. |
| */ |
| public static final ShortFieldId<EndOfCentralDirectory> ENDSUB = new ShortFieldId<>(8); |
| |
| /** |
| * For accessing the "central directory total records" end of central directory field, with the |
| * {@link View#get(com.google.devtools.build.android.ziputils.View.ShortFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.ShortFieldId, int)} methods. |
| */ |
| public static final ShortFieldId<EndOfCentralDirectory> ENDTOT = new ShortFieldId<>(10); |
| |
| /** |
| * For accessing the "central directory size" end of central directory field, with the {@link |
| * View#get(com.google.devtools.build.android.ziputils.View.IntFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.IntFieldId, long)} methods. |
| */ |
| public static final IntFieldId<EndOfCentralDirectory> ENDSIZ = new IntFieldId<>(12); |
| |
| /** |
| * For accessing the "central directory offset" end of central directory field, with the {@link |
| * View#get(com.google.devtools.build.android.ziputils.View.IntFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.IntFieldId, long)} methods. |
| */ |
| public static final IntFieldId<EndOfCentralDirectory> ENDOFF = new IntFieldId<>(16); |
| |
| /** |
| * For accessing the "file comment length" end of central directory field, with the {@link |
| * View#get(com.google.devtools.build.android.ziputils.View.ShortFieldId)} and {@link |
| * View#set(com.google.devtools.build.android.ziputils.View.ShortFieldId, int)} methods. |
| */ |
| public static final ShortFieldId<EndOfCentralDirectory> ENDCOM = new ShortFieldId<>(20); |
| |
| /** |
| * Returns the file comment. |
| * @return the file comment, or an empty string. |
| */ |
| public final String getComment() { |
| return getString(SIZE, get(ENDCOM)); |
| } |
| |
| /** |
| * Returns the total size of the end of directory record, including file comment, if any. |
| * @return the total size of the end of directory record. |
| */ |
| public int getSize() { |
| return SIZE + get(ENDCOM); |
| } |
| } |