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}