001package io.jstach.jstachio.output;
002
003import java.nio.channels.ReadableByteChannel;
004import java.nio.charset.Charset;
005import java.util.ArrayList;
006import java.util.List;
007
008/**
009 * Maintains the encoded output in an iterable of chunks of type <code>T</code> that is
010 * optimized for {@link #asReadableByteChannel()}.
011 *
012 * @author agentgt
013 * @param <T> the chunk type
014 */
015public non-sealed interface ChunkEncodedOutput<T> extends BufferedEncodedOutput {
016
017        /**
018         * Gets the internal sequence of chunks.
019         * @return sequence of chunks
020         */
021        public Iterable<T> getChunks();
022
023        /**
024         * A chunk encoded output backed by a list of byte arrays
025         * @param charset the expected charset
026         * @return encoded output ready to be written to.
027         */
028        static ChunkEncodedOutput<byte[]> ofByteArrays(Charset charset) {
029                return new ByteArrayChunkEncodedOutput(charset);
030        }
031
032        /**
033         * For chunk output the buffer hint is usually the size of the largest chunk.
034         * {@inheritDoc}
035         */
036        @Override
037        public int bufferSizeHint();
038
039}
040
041class ByteArrayChunkEncodedOutput implements ChunkEncodedOutput<byte[]> {
042
043        private final List<byte[]> chunks;
044
045        private final Charset charset;
046
047        private int size = 0;
048
049        private int bufferSizeHint = 0;
050
051        public ByteArrayChunkEncodedOutput(Charset charset) {
052                chunks = new ArrayList<>();
053                this.charset = charset;
054        }
055
056        @Override
057        public void write(byte[] bytes) {
058                addChunk(bytes);
059        }
060
061        private void addChunk(byte[] chunk) {
062                chunks.add(chunk);
063                int length = chunk.length;
064                size += length;
065                if (bufferSizeHint < length) {
066                        bufferSizeHint = length;
067                }
068        }
069
070        @Override
071        public byte[] toByteArray() {
072                byte[] result = new byte[size];
073
074                int index = 0;
075                for (byte[] chunk : chunks) {
076                        System.arraycopy(chunk, 0, result, index, chunk.length);
077                        index += chunk.length;
078                }
079
080                return result;
081        }
082
083        @Override
084        public int size() {
085                return size;
086        }
087
088        @Override
089        public int bufferSizeHint() {
090                return bufferSizeHint;
091        }
092
093        @Override
094        public Charset charset() {
095                return charset;
096        }
097
098        @Override
099        public <E extends Exception> void accept(OutputConsumer<E> consumer) throws E {
100                for (byte[] chunk : chunks) {
101                        consumer.accept(chunk);
102                }
103        }
104
105        @Override
106        public ReadableByteChannel asReadableByteChannel() {
107                return BufferedReadableByteChannel.of(this, chunks);
108        }
109
110        @Override
111        public void close() {
112                // We do nothing here as doing something will not really help anything
113                // as this output is not designed for reuse.
114        }
115
116        @Override
117        public Iterable<byte[]> getChunks() {
118                return chunks;
119        }
120
121}