1package org.jf.dexlib2.writer.io;
2
3import com.google.common.collect.Lists;
4
5import javax.annotation.Nonnull;
6import java.io.IOException;
7import java.io.OutputStream;
8import java.util.List;
9
10/**
11 * A deferred output stream that is stored in memory
12 */
13public class MemoryDeferredOutputStream extends DeferredOutputStream {
14    private static final int DEFAULT_BUFFER_SIZE = 16 * 1024;
15
16    private final List<byte[]> buffers = Lists.newArrayList();
17    private byte[] currentBuffer;
18    private int currentPosition;
19
20    public MemoryDeferredOutputStream() {
21        this(DEFAULT_BUFFER_SIZE);
22    }
23
24    public MemoryDeferredOutputStream(int bufferSize) {
25        currentBuffer = new byte[bufferSize];
26    }
27
28    @Override public void writeTo(OutputStream output) throws IOException {
29        for (byte[] buffer: buffers) {
30            output.write(buffer);
31        }
32        if (currentPosition > 0) {
33            output.write(currentBuffer, 0, currentPosition);
34        }
35        buffers.clear();
36        currentPosition = 0;
37    }
38
39    @Override public void write(int i) throws IOException {
40        if (remaining() == 0) {
41            buffers.add(currentBuffer);
42            currentBuffer = new byte[currentBuffer.length];
43            currentPosition = 0;
44        }
45        currentBuffer[currentPosition++] = (byte)i;
46    }
47
48    @Override public void write(byte[] bytes) throws IOException {
49        write(bytes, 0, bytes.length);
50    }
51
52    @Override public void write(byte[] bytes, int offset, int length) throws IOException {
53        int remaining = remaining();
54        int written = 0;
55        while (length - written > 0) {
56            int toWrite = Math.min(remaining, (length - written));
57            System.arraycopy(bytes, offset + written, currentBuffer, currentPosition, toWrite);
58            written += toWrite;
59            currentPosition += toWrite;
60
61            remaining = remaining();
62            if (remaining == 0) {
63                buffers.add(currentBuffer);
64                currentBuffer = new byte[currentBuffer.length];
65                currentPosition = 0;
66                remaining = currentBuffer.length;
67            }
68        }
69    }
70
71    private int remaining() {
72        return currentBuffer.length - currentPosition;
73    }
74
75    @Nonnull
76    public static DeferredOutputStreamFactory getFactory() {
77        return getFactory(DEFAULT_BUFFER_SIZE);
78    }
79
80    @Nonnull
81    public static DeferredOutputStreamFactory getFactory(final int bufferSize) {
82        return new DeferredOutputStreamFactory() {
83            @Override public DeferredOutputStream makeDeferredOutputStream() {
84                return new MemoryDeferredOutputStream(bufferSize);
85            }
86        };
87    }
88}
89