package org.jf.dexlib2.writer.io; import com.google.common.io.ByteStreams; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.*; /** * A deferred output stream that uses a file as its backing store, with a in-memory intermediate buffer. */ public class FileDeferredOutputStream extends DeferredOutputStream { private static final int DEFAULT_BUFFER_SIZE = 4 * 1024; @Nonnull private final File backingFile; @Nonnull private final NakedBufferedOutputStream output; private int writtenBytes; public FileDeferredOutputStream(@Nonnull File backingFile) throws FileNotFoundException { this(backingFile, DEFAULT_BUFFER_SIZE); } public FileDeferredOutputStream(@Nonnull File backingFile, int bufferSize) throws FileNotFoundException { this.backingFile = backingFile; output = new NakedBufferedOutputStream(new FileOutputStream(backingFile), bufferSize); } @Override public void writeTo(@Nonnull OutputStream dest) throws IOException { byte[] outBuf = output.getBuffer(); int count = output.getCount(); output.resetBuffer(); output.close(); // did we actually write something out to disk? if (count != writtenBytes) { InputStream fis = new FileInputStream(backingFile); ByteStreams.copy(fis, dest); backingFile.delete(); } dest.write(outBuf, 0, count); } @Override public void write(int i) throws IOException { output.write(i); writtenBytes++; } @Override public void write(byte[] bytes) throws IOException { output.write(bytes); writtenBytes += bytes.length; } @Override public void write(byte[] bytes, int off, int len) throws IOException { output.write(bytes, off, len); writtenBytes += len; } @Override public void flush() throws IOException { output.flush(); } @Override public void close() throws IOException { output.close(); } private static class NakedBufferedOutputStream extends BufferedOutputStream { public NakedBufferedOutputStream(OutputStream outputStream) { super(outputStream); } public NakedBufferedOutputStream(OutputStream outputStream, int i) { super(outputStream, i); } public int getCount() { return count; } public void resetBuffer() { count = 0; } public byte[] getBuffer() { return buf; } } @Nonnull public static DeferredOutputStreamFactory getFactory(@Nullable File containingDirectory) { return getFactory(containingDirectory, DEFAULT_BUFFER_SIZE); } @Nonnull public static DeferredOutputStreamFactory getFactory(@Nullable final File containingDirectory, final int bufferSize) { return new DeferredOutputStreamFactory() { @Override public DeferredOutputStream makeDeferredOutputStream() throws IOException { File tempFile = File.createTempFile("dexlibtmp", null, containingDirectory); return new FileDeferredOutputStream(tempFile, bufferSize); } }; } }