| /* |
| * 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.dalvik; |
| |
| import com.facebook.buck.java.classes.FileLike; |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.Sets; |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.Files; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Set; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipOutputStream; |
| |
| /** |
| * Helper to write a Zip file used by {@link DalvikAwareZipSplitter}. |
| */ |
| public class DalvikAwareOutputStreamHelper implements ZipOutputStreamHelper { |
| |
| private static final int MAX_METHOD_REFERENCES = 64 * 1024; |
| |
| private final ZipOutputStream outStream; |
| private final Set<String> entryNames = Sets.newHashSet(); |
| private final long linearAllocLimit; |
| private final File reportFile; |
| private final DalvikStatsCache dalvikStatsCache; |
| |
| private final Set<DalvikStatsTool.MethodReference> currentMethodReferences = Sets.newHashSet(); |
| private long currentLinearAllocSize; |
| |
| DalvikAwareOutputStreamHelper( |
| File outputFile, |
| long linearAllocLimit, |
| File reportDir, |
| DalvikStatsCache dalvikStatsCache) |
| throws FileNotFoundException { |
| this.outStream = new ZipOutputStream( |
| new BufferedOutputStream( |
| new FileOutputStream(outputFile))); |
| this.linearAllocLimit = linearAllocLimit; |
| this.reportFile = new File(reportDir, outputFile.getName() + ".txt"); |
| this.dalvikStatsCache = dalvikStatsCache; |
| } |
| |
| private boolean isEntryTooBig(FileLike entry) { |
| DalvikStatsTool.Stats stats = dalvikStatsCache.getStats(entry); |
| if (currentLinearAllocSize + stats.estimatedLinearAllocSize > linearAllocLimit) { |
| return true; |
| } |
| int newReferences = Sets.difference(stats.methodReferences, currentMethodReferences).size(); |
| if (currentMethodReferences.size() + newReferences > MAX_METHOD_REFERENCES) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean canPutEntry(FileLike fileLike) { |
| return !isEntryTooBig(fileLike); |
| } |
| |
| @Override |
| public boolean containsEntry(FileLike fileLike) { |
| return entryNames.contains(fileLike.getRelativePath()); |
| } |
| |
| @Override |
| public void putEntry(FileLike fileLike) throws IOException { |
| String name = fileLike.getRelativePath(); |
| // Tracks unique entry names and avoids duplicates. This is, believe it or not, how |
| // proguard seems to handle merging multiple -injars into a single -outjar. |
| if (!containsEntry(fileLike)) { |
| entryNames.add(name); |
| outStream.putNextEntry(new ZipEntry(name)); |
| try (InputStream in = fileLike.getInput()) { |
| ByteStreams.copy(in, outStream); |
| } |
| |
| // Make sure FileLike#getSize didn't lie (or we forgot to call canPutEntry). |
| DalvikStatsTool.Stats stats = dalvikStatsCache.getStats(fileLike); |
| Preconditions.checkState(!isEntryTooBig(fileLike), |
| "Putting entry %s (%s) exceeded maximum size of %s", |
| name, stats.estimatedLinearAllocSize, linearAllocLimit); |
| currentLinearAllocSize += stats.estimatedLinearAllocSize; |
| currentMethodReferences.addAll(stats.methodReferences); |
| String report = String.format( |
| "%d %d %s\n", |
| stats.estimatedLinearAllocSize, stats.methodReferences.size(), name |
| ); |
| Files.append(report, reportFile, Charsets.UTF_8); |
| } |
| } |
| |
| @Override |
| public void close() throws IOException { |
| outStream.close(); |
| } |
| } |