-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#225 add Utf8ByteArrayOutput when a byte array containing the output …
…is needed
- Loading branch information
Showing
5 changed files
with
271 additions
and
1 deletion.
There are no files selected for viewing
76 changes: 76 additions & 0 deletions
76
jte-runtime/src/main/java/gg/jte/output/Utf8ByteArrayOutput.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package gg.jte.output; | ||
|
||
import gg.jte.TemplateOutput; | ||
|
||
import java.io.IOException; | ||
import java.io.Writer; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Arrays; | ||
|
||
/** | ||
* UTF-8 template output, that buffers the entire output into a byte array. | ||
* You may want to use this class over {@link Utf8ByteOutput}, if all you need is a byte array containing the output. | ||
* In this case it will be faster than storing chunks of byte arrays, just to convert them to a byte array in the end. | ||
* | ||
* CAUTION: You must enable {@link gg.jte.TemplateEngine#setBinaryStaticContent(boolean)}, otherwise this class won't provide any benefits over {@link StringOutput}! | ||
*/ | ||
public final class Utf8ByteArrayOutput extends Writer implements TemplateOutput { | ||
|
||
private byte[] buffer; | ||
private int position; | ||
|
||
public Utf8ByteArrayOutput() { | ||
this(8 * 1024); | ||
} | ||
|
||
public Utf8ByteArrayOutput(int initialCapacity) { | ||
buffer = new byte[initialCapacity]; | ||
} | ||
|
||
@Override | ||
public Writer getWriter() { | ||
return this; | ||
} | ||
|
||
@Override | ||
public void writeContent(String value) { | ||
writeBinaryContent(value.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
@Override | ||
public void writeBinaryContent(byte[] value) { | ||
ensureCapacity(position + value.length); | ||
System.arraycopy(value, 0, buffer, position, value.length); | ||
position += value.length; | ||
} | ||
|
||
@Override | ||
public void write(String value) { | ||
writeBinaryContent(value.getBytes(StandardCharsets.UTF_8)); | ||
} | ||
|
||
@Override | ||
public void write(char[] cbuf, int off, int len) throws IOException { | ||
writeContent(new String(cbuf, off, len)); | ||
} | ||
|
||
@Override | ||
public void flush() { | ||
// nothing to do | ||
} | ||
|
||
@Override | ||
public void close() { | ||
// nothing to do | ||
} | ||
|
||
public byte[] toByteArray() { | ||
return Arrays.copyOf(buffer, position); | ||
} | ||
|
||
private void ensureCapacity(int minCapacity) { | ||
if (buffer.length < minCapacity) { | ||
buffer = Arrays.copyOf(buffer, Math.max(minCapacity, 2 * buffer.length)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
jte/src/test/java/gg/jte/benchmark/Benchmark_BinaryArray.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package gg.jte.benchmark; | ||
|
||
import gg.jte.ContentType; | ||
import gg.jte.TemplateEngine; | ||
import gg.jte.output.Utf8ByteArrayOutput; | ||
import gg.jte.resolve.DirectoryCodeResolver; | ||
import gg.jte.runtime.Constants; | ||
|
||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
class Benchmark_BinaryArray { | ||
|
||
private final TemplateEngine templateEngine; | ||
|
||
public static void main(String[] args) { | ||
new Benchmark_BinaryArray().run(); | ||
} | ||
|
||
Benchmark_BinaryArray() { | ||
Path classDirectory = Paths.get("jte-classes"); | ||
|
||
TemplateEngine compiler = TemplateEngine.create(new DirectoryCodeResolver(Benchmark.getTemplateDirectory()), classDirectory, ContentType.Html, null, Constants.PACKAGE_NAME_PRECOMPILED); | ||
compiler.setTrimControlStructures(true); | ||
compiler.setBinaryStaticContent(true); | ||
compiler.precompileAll(); | ||
|
||
templateEngine = TemplateEngine.createPrecompiled(classDirectory, ContentType.Html); | ||
} | ||
|
||
public void run() { | ||
System.out.println("Rendering welcome page for the first time, this will cause the template to compile."); | ||
renderWelcomePage(1); | ||
|
||
System.out.println("Rendering welcome page a million times."); | ||
renderWelcomePage(1_000_000); | ||
} | ||
|
||
private void renderWelcomePage(int amount) { | ||
long start = System.nanoTime(); | ||
|
||
for (int i = 0; i < amount; ++i) { | ||
Page page = new WelcomePage(i); | ||
render(page); | ||
} | ||
|
||
long end = System.nanoTime(); | ||
|
||
System.out.println(amount + " pages rendered in " + TimeUnit.NANOSECONDS.toMillis(end - start) + "ms." + " (~ " + ((float)TimeUnit.NANOSECONDS.toMicros(end - start) / amount) + "µs per page)"); | ||
System.out.println(); | ||
} | ||
|
||
byte[] render(Page page) { | ||
Utf8ByteArrayOutput output = new Utf8ByteArrayOutput(); | ||
templateEngine.render(page.getTemplate(), page, output); | ||
return output.toByteArray(); | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
jte/src/test/java/gg/jte/output/Utf8ByteArrayOutputTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package gg.jte.output; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
public class Utf8ByteArrayOutputTest extends AbstractTemplateOutputTest<Utf8ByteArrayOutput> { | ||
|
||
@Override | ||
Utf8ByteArrayOutput createTemplateOutput() { | ||
return new Utf8ByteArrayOutput(16); // Small initial capacity size for tests; | ||
} | ||
|
||
@Test | ||
void empty() { | ||
thenOutputIs(""); | ||
} | ||
|
||
@Test | ||
void string() { | ||
output.writeContent("Hello"); | ||
thenOutputIs("Hello"); | ||
} | ||
|
||
@Test | ||
void longString() { | ||
output.writeContent("The quick brown fox jumps over the lazy dog"); | ||
thenOutputIs("The quick brown fox jumps over the lazy dog"); | ||
} | ||
|
||
@Test | ||
void longStringSpecialChars() { | ||
output.writeContent("\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9"); | ||
thenOutputIs("\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9"); | ||
} | ||
|
||
@Test | ||
void outputs() { | ||
output.writeContent("\uD83D\uDCA9"); | ||
output.writeContent(" says "); | ||
output.writeUserContent(42); | ||
output.writeContent("x "); | ||
output.writeContent("\uD83D\uDCA9!!!"); | ||
|
||
thenOutputIs("\uD83D\uDCA9 says 42x \uD83D\uDCA9!!!"); | ||
} | ||
|
||
@Test | ||
void binary_string() { | ||
output.writeBinaryContent("Hello".getBytes(StandardCharsets.UTF_8)); | ||
thenOutputIs("Hello"); | ||
} | ||
|
||
@Test | ||
void binary_longString() { | ||
output.writeBinaryContent("The quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8)); | ||
thenOutputIs("The quick brown fox jumps over the lazy dog"); | ||
} | ||
|
||
@Test | ||
void binary_longStringSpecialChars() { | ||
output.writeBinaryContent("\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9".getBytes(StandardCharsets.UTF_8)); | ||
thenOutputIs("\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9"); | ||
} | ||
|
||
@Test | ||
void mixed() { | ||
output.writeContent("\uD83D\uDCA9"); | ||
output.writeBinaryContent(" says ".getBytes(StandardCharsets.UTF_8)); | ||
output.writeUserContent(42); | ||
output.writeContent("x "); | ||
output.writeBinaryContent("\uD83D\uDCA9!!!".getBytes(StandardCharsets.UTF_8)); | ||
|
||
thenOutputIs("\uD83D\uDCA9 says 42x \uD83D\uDCA9!!!"); | ||
} | ||
|
||
@Test | ||
void utf8_0x00() throws IOException { | ||
output.write((char)0); | ||
thenOutputIs("\u0000"); | ||
} | ||
|
||
@Test | ||
void utf8_greek() { | ||
output.writeContent("Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο"); | ||
thenOutputIs("Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο"); | ||
} | ||
|
||
@Test | ||
void utf8_thai() { | ||
String str = "๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า กว่าบรรดาฝูงสัตว์เดรัจฉาน\n" + | ||
" จงฝ่าฟันพัฒนาวิชาการ อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร\n" + | ||
" ไม่ถือโทษโกรธแช่งซัดฮึดฮัดด่า หัดอภัยเหมือนกีฬาอัชฌาสัย\n" + | ||
" ปฏิบัติประพฤติกฎกำหนดใจ พูดจาให้จ๊ะๆ จ๋าๆ น่าฟังเอย ฯ"; | ||
output.writeContent(str); | ||
thenOutputIs(str); | ||
} | ||
|
||
@Test | ||
void utf8_japanese_hiragana() { | ||
String str = "いろはにほへとちりぬるを\n" + | ||
" わかよたれそつねならむ\n" + | ||
" うゐのおくやまけふこえて\n" + | ||
" あさきゆめみしゑひもせす"; | ||
|
||
output.writeContent(str); | ||
thenOutputIs(str); | ||
} | ||
|
||
@Test | ||
void utf8_japanese_katakana() { | ||
String str = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム\n" + | ||
" ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン"; | ||
|
||
output.writeContent(str); | ||
thenOutputIs(str); | ||
} | ||
|
||
protected void thenOutputIs(String expected) { | ||
byte[] bytes = output.toByteArray(); | ||
|
||
String actual = new String(bytes, StandardCharsets.UTF_8); | ||
assertThat(actual).isEqualTo(expected); | ||
} | ||
} |