199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverpackage org.jf.dexlib2.writer.io;
299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverimport com.google.common.io.ByteStreams;
499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverimport javax.annotation.Nonnull;
699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverimport javax.annotation.Nullable;
799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverimport java.io.*;
899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver/**
1099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver * A deferred output stream that uses a file as its backing store, with a in-memory intermediate buffer.
1199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver */
1299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruverpublic class FileDeferredOutputStream extends DeferredOutputStream {
1399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    private static final int DEFAULT_BUFFER_SIZE = 4 * 1024;
1499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
1599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Nonnull private final File backingFile;
1699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Nonnull private final NakedBufferedOutputStream output;
1799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    private int writtenBytes;
1899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
1999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    public FileDeferredOutputStream(@Nonnull File backingFile) throws FileNotFoundException {
2099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        this(backingFile, DEFAULT_BUFFER_SIZE);
2199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
2299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
2399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    public FileDeferredOutputStream(@Nonnull File backingFile, int bufferSize) throws FileNotFoundException {
2499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        this.backingFile = backingFile;
2599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output = new NakedBufferedOutputStream(new FileOutputStream(backingFile), bufferSize);
2699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
2799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
2899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void writeTo(@Nonnull OutputStream dest) throws IOException {
2999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        byte[] outBuf = output.getBuffer();
3099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        int count = output.getCount();
3199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.resetBuffer();
3299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.close();
3399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
3499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        // did we actually write something out to disk?
3599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        if (count != writtenBytes) {
3699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            InputStream fis = new FileInputStream(backingFile);
3799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            ByteStreams.copy(fis, dest);
3899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            backingFile.delete();
3999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
4099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
4199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        dest.write(outBuf, 0, count);
4299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
4399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
4499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void write(int i) throws IOException {
4599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.write(i);
4699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        writtenBytes++;
4799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
4899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
4999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void write(byte[] bytes) throws IOException {
5099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.write(bytes);
5199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        writtenBytes += bytes.length;
5299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
5399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
5499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void write(byte[] bytes, int off, int len) throws IOException {
5599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.write(bytes, off, len);
5699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        writtenBytes += len;
5799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
5899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
5999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void flush() throws IOException {
6099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.flush();
6199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
6299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
6399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Override public void close() throws IOException {
6499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        output.close();
6599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
6699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
6799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    private static class NakedBufferedOutputStream extends BufferedOutputStream {
6899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        public NakedBufferedOutputStream(OutputStream outputStream) {
6999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            super(outputStream);
7099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
7199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
7299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        public NakedBufferedOutputStream(OutputStream outputStream, int i) {
7399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            super(outputStream, i);
7499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
7599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
7699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        public int getCount() {
7799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            return count;
7899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
7999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
8099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        public void resetBuffer() {
8199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            count = 0;
8299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
8399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
8499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        public byte[] getBuffer() {
8599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            return buf;
8699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        }
8799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
8899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
8999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Nonnull
9099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    public static DeferredOutputStreamFactory getFactory(@Nullable File containingDirectory) {
9199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        return getFactory(containingDirectory, DEFAULT_BUFFER_SIZE);
9299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
9399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver
9499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    @Nonnull
9599b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    public static DeferredOutputStreamFactory getFactory(@Nullable final File containingDirectory,
9699b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver                                                         final int bufferSize) {
9799b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        return new DeferredOutputStreamFactory() {
9899b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            @Override public DeferredOutputStream makeDeferredOutputStream() throws IOException {
9999b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver                File tempFile = File.createTempFile("dexlibtmp", null, containingDirectory);
10099b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver                return new FileDeferredOutputStream(tempFile, bufferSize);
10199b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver            }
10299b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver        };
10399b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver    }
10499b46173c5294d186ccf2e647b86346a22b247c8Ben Gruver}
105