com.facebook.buck.dalvik.DalvikAwareOutputStreamHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.dalvik.DalvikAwareOutputStreamHelper.java

Source

/*
 * 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();
    }
}