Skip to content

Commit 3336ace

Browse files
authored
Support writing large raw String content without expanding the underlying buffer (#398)
Currently, the underlying buffer will expand when writing raw String content that is larger that the buffer. This change writes that as chunks instead.
1 parent 691ca00 commit 3336ace

File tree

2 files changed

+47
-0
lines changed

2 files changed

+47
-0
lines changed

json-core/src/main/java/io/avaje/json/stream/core/JGenerator.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class JGenerator implements JsonGenerator {
5757

5858
private final Grisu3.FastDtoaBuilder doubleBuilder = new Grisu3.FastDtoaBuilder();
5959
private final int largeStringMax;
60+
private final int largeAsciiMax;
6061
private byte[] buffer;
6162
private JsonOutput target;
6263
private int lastOp;
@@ -80,6 +81,7 @@ class JGenerator implements JsonGenerator {
8081
this.buffer = buffer;
8182
// each char can take up to 6 bytes when Unicode escaped, round down 1/8 number of chars
8283
this.largeStringMax = buffer.length >> 3;
84+
this.largeAsciiMax = buffer.length - 10;
8385
}
8486

8587
@Override
@@ -360,13 +362,33 @@ private void writeStringEscape(final String str, int i, int cur, final int len)
360362
@SuppressWarnings("deprecation")
361363
private void writeAscii(final String value) {
362364
final int len = value.length();
365+
if (len > largeAsciiMax) {
366+
writeLargeAscii(value);
367+
return;
368+
}
363369
if (position + len >= buffer.length) {
364370
enlargeOrFlush(position, len);
365371
}
366372
value.getBytes(0, len, buffer, position);
367373
position += len;
368374
}
369375

376+
/** Break a large ascii into segments and flush when necessary */
377+
private void writeLargeAscii(String value) {
378+
int left = value.length();
379+
int offset = 0;
380+
while (left > 0) {
381+
final int len = Math.min(largeAsciiMax, left);
382+
if (position + len >= buffer.length) {
383+
enlargeOrFlush(position, 0); // just flush
384+
}
385+
value.getBytes(offset, offset + len, buffer, position);
386+
position += len;
387+
offset += len;
388+
left -= len;
389+
}
390+
}
391+
370392
private void writeAscii(final byte[] buf) {
371393
final int len = buf.length;
372394
if (position + len >= buffer.length) {

json-core/src/test/java/io/avaje/json/stream/core/JsonWriterTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,29 @@ void largeStringUnicode() {
192192
.describedAs("internal buffer should not grow")
193193
.isEqualTo(100);
194194
}
195+
196+
@Test
197+
void largeStringAscii() {
198+
ByteArrayOutputStream os = new ByteArrayOutputStream();
199+
JGenerator dJsonWriter = new JGenerator(100);
200+
dJsonWriter.prepare(JsonOutput.ofStream(os));
201+
202+
JsonWriteAdapter fw = new JsonWriteAdapter(dJsonWriter, HybridBufferRecycler.shared(), true, true);
203+
204+
String largeValue = '"' + "_1234567890123456789".repeat(21) + '"';
205+
206+
fw.beginObject();
207+
fw.name("key");
208+
fw.rawValue(largeValue);
209+
fw.endObject();
210+
fw.close();
211+
212+
String jsonResult = new String(os.toByteArray(), 0, os.toByteArray().length);
213+
assertThat(jsonResult).isEqualTo("{\"key\":" + largeValue + "}");
214+
215+
byte[] effectiveBufferSize = dJsonWriter.ensureCapacity(0);
216+
assertThat(effectiveBufferSize.length)
217+
.describedAs("internal buffer should not grow")
218+
.isEqualTo(100);
219+
}
195220
}

0 commit comments

Comments
 (0)