| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * 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.android.zipflinger; |
| |
| import com.android.annotations.NonNull; |
| import java.io.IOException; |
| import java.nio.channels.FileChannel; |
| import java.nio.file.Path; |
| import java.nio.file.StandardOpenOption; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.zip.Deflater; |
| |
| public class ZipSource { |
| public static final int COMPRESSION_NO_CHANGE = -2; |
| private FileChannel channel; |
| private ZipMap map; |
| |
| private final List<Source> selectedEntries = new ArrayList<>(); |
| |
| public ZipSource(ZipMap map) { |
| this.map = map; |
| } |
| |
| public ZipSource(@NonNull Path file) throws IOException { |
| this(ZipMap.from(file, false)); |
| } |
| |
| @NonNull |
| public void select(@NonNull String entryName, @NonNull String newName) { |
| select(entryName, newName, COMPRESSION_NO_CHANGE, Source.NO_ALIGNMENT); |
| } |
| |
| /** |
| * Select an entry to be copied to the archive managed by zipflinger. |
| * |
| * <p>An entry will remain unchanged and zero-copy will happen when: - compression level is |
| * COMPRESSION_NO_CHANGE. - compression level is 1-9 and the entry is already compressed. - |
| * compression level is Deflater.NO_COMPRESSION and the entry is already uncompressed. |
| * |
| * <p>Otherwise, the entry is deflated/inflated accordingly via transfer to memory, crc |
| * calculation , and written to the target archive. |
| * |
| * @param entryName Name of the entry in the source zip. |
| * @param newName Name of the entry in the destination zip. |
| * @param compressionLevel The desired compression level. |
| * @return |
| */ |
| @NonNull |
| public void select( |
| @NonNull String entryName, |
| @NonNull String newName, |
| int compressionLevel, |
| long alignment) { |
| Entry entry = map.getEntries().get(entryName); |
| if (entry == null) { |
| throw new IllegalStateException( |
| String.format("Cannot find '%s' in archive '%s'", entryName, map.getPath())); |
| } |
| Source entrySource = newZipSourceEntryFor(newName, entry, compressionLevel); |
| entrySource.align(alignment); |
| entrySource.versionMadeBy = entry.getVersionMadeBy(); |
| entrySource.externalAttributes = entry.getExternalAttributes(); |
| selectedEntries.add(entrySource); |
| } |
| |
| public Map<String, Entry> entries() { |
| return map.getEntries(); |
| } |
| |
| public static ZipSource selectAll(@NonNull Path file) throws IOException { |
| ZipSource source = new ZipSource(file); |
| for (Entry e : source.entries().values()) { |
| source.select(e.getName(), e.getName(), COMPRESSION_NO_CHANGE, Source.NO_ALIGNMENT); |
| } |
| return source; |
| } |
| |
| void open() throws IOException { |
| channel = FileChannel.open(map.getPath(), StandardOpenOption.READ); |
| } |
| |
| void close() throws IOException { |
| if (channel != null) { |
| channel.close(); |
| } |
| } |
| |
| FileChannel getChannel() { |
| return channel; |
| } |
| |
| public List<? extends Source> getSelectedEntries() { |
| return selectedEntries; |
| } |
| |
| @NonNull |
| private Source newZipSourceEntryFor(String newName, Entry entry, int compressionLevel) { |
| |
| // Source Destination |
| // ======================== |
| // X NO_CHANGE -> No changes |
| // INFLATED INFLATED -> No changes |
| // INFLATED DEFLATED -> Deflate |
| // DEFLATED INFLATED -> Inflate |
| // DEFLATED DEFLATED -> Inflate then Deflate |
| |
| if (compressionLevel == COMPRESSION_NO_CHANGE |
| || (!entry.isCompressed() && compressionLevel == Deflater.NO_COMPRESSION)) { |
| return new ZipSourceEntry(newName, entry, this); |
| } |
| |
| return new ZipSourceEntryPipe(newName, entry, this, compressionLevel); |
| } |
| |
| String getName() { |
| return map.getPath().toString(); |
| } |
| |
| @NonNull |
| public byte[] getComment() { |
| return map.getComment(); |
| } |
| } |