Skip to content

Commit

Permalink
Merge branch 'casid:main' into generator-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
edward3h committed May 24, 2023
2 parents c06e973 + d74f2f0 commit 1ab7981
Show file tree
Hide file tree
Showing 3 changed files with 15 additions and 249 deletions.
41 changes: 1 addition & 40 deletions jte-runtime/src/main/java/gg/jte/output/StringOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import gg.jte.TemplateOutput;

import java.io.Writer;

public class StringOutput extends Writer implements TemplateOutput {
public class StringOutput implements TemplateOutput {
private final StringBuilder stringBuilder;

public StringOutput() {
Expand Down Expand Up @@ -68,41 +66,4 @@ public String toString() {
public void reset() {
stringBuilder.setLength(0);
}

// Writer interface

@Override
public void write(char[] buffer, int off, int len) {
stringBuilder.append(buffer, off, len);
}

@Override
public void write(int c) {
stringBuilder.append((char)c);
}

@Override
public void write(char[] cbuf) {
stringBuilder.append(cbuf);
}

@Override
public void write(String str) {
stringBuilder.append(str);
}

@Override
public void write(String str, int off, int len) {
stringBuilder.append(str, off, off + len);
}

@Override
public void flush() {
// nothing to do
}

@Override
public void close() {
// nothing to do
}
}
219 changes: 12 additions & 207 deletions jte-runtime/src/main/java/gg/jte/output/Utf8ByteOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,28 @@

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
* Heavily optimized UTF-8 template output, designed to be CPU and memory friendly.
* UTF-8 template output, designed to be CPU and memory friendly.
* You may want to use this class, if you write to a low-level binary output stream, and you need the exact content-size of the output.
*
* CAUTION: You must enable {@link gg.jte.TemplateEngine#setBinaryStaticContent(boolean)}, otherwise this class won't provide any benefits over {@link StringOutput}!
*/
public final class Utf8ByteOutput implements TemplateOutput {

private final int chunkSize;

private final char[] tempBuffer;
private final int tempBufferSize;

private ArrayList<Chunk> chunks;
private byte[] buffer;
private int lastIndex;
private int currentIndex;

private char highSurrogate;

/**
* Constructs an output with sane defaults
*/
public Utf8ByteOutput() {
this(1024, 512);
}

/**
* Constructs an output with custom settings.
* This output maintains a list of binary chunks. Pre-encoded data is passed as is, while internal buffers are created for dynamic data as needed.
* @param chunkSize The size in bytes for chunks of dynamic data.
* @param tempBufferSize The size for the temporary buffer used for intermediate String encoding.
*/
public Utf8ByteOutput(int chunkSize, int tempBufferSize) {
this.chunkSize = chunkSize;
buffer = new byte[this.chunkSize];

this.tempBufferSize = tempBufferSize;
tempBuffer = new char[tempBufferSize];
}
private final List<byte[]> chunks = new ArrayList<>();

/**
* @return The amount of bytes written to this output.
*/
public int getContentLength() {
int contentLength = currentIndex - lastIndex;
int contentLength = 0;

if (chunks != null) {
for (Chunk chunk : chunks) {
contentLength += chunk.length;
}
for (byte[] chunk : chunks) {
contentLength += chunk.length;
}

return contentLength;
Expand All @@ -78,188 +47,24 @@ public void writeTo(OutputStream os) throws IOException {
* @throws IOException in case the consume operation fails
*/
public void writeTo(DataConsumer dataConsumer) throws IOException {
if (chunks != null) {
for (Chunk chunk : chunks) {
dataConsumer.accept(chunk.data, chunk.startIndex, chunk.length);
}
}

int remaining = currentIndex - lastIndex;
if (remaining > 0) {
dataConsumer.accept(buffer, lastIndex, remaining);
for (byte[] chunk : chunks) {
dataConsumer.accept(chunk, 0, chunk.length);
}
}

@Override
public void writeContent(String s) {
int len = s.length();
for (int i = 0; i < len; i += tempBufferSize) {
int size = Math.min(tempBufferSize, len - i);
s.getChars(i, i + size, tempBuffer, 0);
write(tempBuffer, 0, size);
}
chunks.add(s.getBytes(StandardCharsets.UTF_8));
}

@Override
public void writeContent(String s, int beginIndex, int endIndex) {
for (int i = beginIndex; i < endIndex; i += tempBufferSize) {
int size = Math.min(tempBufferSize, endIndex - i);
s.getChars(i, i + size, tempBuffer, 0);
write(tempBuffer, 0, size);
}
chunks.add(s.substring(beginIndex, endIndex).getBytes(StandardCharsets.UTF_8));
}

@Override
public void writeBinaryContent(byte[] value) {
if (value.length < 16) {
doAppend(value); // Don't waste chunks if array is very small.
} else {
if (lastIndex < currentIndex) {
addCurrentChunk();
lastIndex = currentIndex;
}

addChunk(new Chunk(value, 0, value.length));
}
}

@Override
public void writeUserContent(boolean value) {
appendLatin1(String.valueOf(value));
}

@Override
public void writeUserContent(byte value) {
appendLatin1(Byte.toString(value));
}

@Override
public void writeUserContent(char value) {
write(value);
}

@Override
public void writeUserContent(int value) {
appendLatin1(Integer.toString(value));
}

@Override
public void writeUserContent(long value) {
appendLatin1(Long.toString(value));
}

@Override
public void writeUserContent(float value) {
appendLatin1(Float.toString(value));
}

@Override
public void writeUserContent(double value) {
appendLatin1(Double.toString(value));
}

private void write(@SuppressWarnings("NullableProblems") char[] buffer, int off, int len) {
int i = off;
len += off;

while (i < len) {
write(buffer[i++]);
}
}

private void write(char c) {
if (highSurrogate != 0) {
if (Character.isLowSurrogate(c)) {
appendUtf8CodePoint(Character.toCodePoint(highSurrogate, c));
} else {
doAppend((byte)('�'));
appendUtf8Char(c);
}
highSurrogate = 0;
} else if (Character.isHighSurrogate(c)) {
highSurrogate = c;
} else {
appendUtf8Char(c);
}
}

private void appendLatin1(String s) {
int len = s.length();

for (int i = 0; i < len; ++i) {
doAppend((byte) s.charAt(i));
}
}

private void appendUtf8Char(char c) {
if (c < 0x80) {
doAppend((byte) c);
} else if (c < 0x800) {
doAppend((byte) (0xc0 | c >> 6));
doAppend((byte) (0x80 | c & 0x3f));
} else {
doAppend((byte)(0xe0 | (c >> 12)));
doAppend((byte)(0x80 | ((c >> 6) & 0x3f)));
doAppend((byte)(0x80 | (c & 0x3f)));
}
}

private void appendUtf8CodePoint(int c) {
doAppend((byte)(0xf0 | (c >> 18)));
doAppend((byte)(0x80 | ((c >> 12) & 0x3f)));
doAppend((byte)(0x80 | ((c >> 6) & 0x3f)));
doAppend((byte)(0x80 | (c & 0x3f)));
}

private void doAppend(byte[] bytes) {
int length = bytes.length;

if (currentIndex + length < chunkSize) {
System.arraycopy(bytes, 0, buffer, currentIndex, length);
currentIndex += length;
} else {
for (byte b : bytes) {
doAppend(b);
}
}
}

private void doAppend(byte b) {
if (currentIndex == chunkSize) {
createNewChunk();
}
buffer[currentIndex++] = b;
}

private void createNewChunk() {
addCurrentChunk();

buffer = new byte[chunkSize];
lastIndex = 0;
currentIndex = 0;
}

private void addCurrentChunk() {
addChunk(new Chunk(buffer, lastIndex, currentIndex - lastIndex));
}

private void addChunk(Chunk chunk) {
if (chunks == null) {
chunks = new ArrayList<>();
}
chunks.add(chunk);
}

private static final class Chunk {
final byte[] data;
final int startIndex;
final int length;

public Chunk(byte[] data, int startIndex, int length) {
this.data = data;
this.startIndex = startIndex;
this.length = length;
}
chunks.add(value);
}

@FunctionalInterface
Expand Down
4 changes: 2 additions & 2 deletions jte/src/test/java/gg/jte/output/Utf8ByteOutputTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Utf8ByteOutputTest extends AbstractTemplateOutputTest<Utf8ByteOutpu

@Override
Utf8ByteOutput createTemplateOutput() {
return new Utf8ByteOutput(16, 8); // Small chunk size for tests;
return new Utf8ByteOutput();
}

@Test
Expand Down Expand Up @@ -133,7 +133,7 @@ void utf8_missingLowSurrogate() {
output.writeUserContent('f');
output.writeUserContent('o');
output.writeUserContent('o');
thenOutputIs("foo");
thenOutputIs("?foo");
}

protected void thenOutputIs(String expected) {
Expand Down

0 comments on commit 1ab7981

Please sign in to comment.