Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,13 @@
public final class Latin1JsonReader extends JsonReader {
private static final byte[] EMPTY_BYTES = new byte[0];
private static final int INITIAL_STRING_DECODE_BUFFER_SIZE = 1024;

/** The floor the decode buffer is shrunk back to; it is kept until it is twice what is needed. */
private static final int RETAINED_STRING_DECODE_BUFFER_SIZE = 8192;

/** The ceiling on what a single document can keep alive in a pooled reader between parses. */
private static final int MAX_RETAINED_STRING_DECODE_BUFFER_SIZE = 1 << 17;

private static final boolean LITTLE_ENDIAN = NativeByteOrder.IS_LITTLE_ENDIAN;
private static final long BYTE_ONES = 0x0101010101010101L;
private static final int INT_BYTE_ONES = 0x01010101;
Expand All @@ -80,6 +86,13 @@ public final class Latin1JsonReader extends JsonReader {
// Latin1 string content and field-name hashing must keep unsigned byte conversion.
private byte[] input;
private byte[] stringDecodeBuffer = new byte[INITIAL_STRING_DECODE_BUFFER_SIZE];

/**
* Bytes of the longest string decoded since the last {@link #clear()}, so a steady workload keeps
* them.
*/
private int stringDecodeHighWater;

// Keep the cache after hot representation fields; an inherited reference shifts their offsets.
private final FieldNameCache fieldNameCache;
private ZoneIdCache zoneIdCache;
Expand Down Expand Up @@ -275,6 +288,7 @@ protected CharSequence decodeQuotedText(int start, int end) {
outBytes = ensureStringDecodeCapacity(outBytes, out + 2);
out = putUtf16Char(outBytes, out, ch);
}
recordStringDecodeUse(out);
return decodedQuotedText(outBytes, out, true);
}

Expand Down Expand Up @@ -396,8 +410,26 @@ public void clear() {
reset();
input = EMPTY_BYTES;
position = 0;
if (stringDecodeBuffer.length > RETAINED_STRING_DECODE_BUFFER_SIZE) {
stringDecodeBuffer = new byte[RETAINED_STRING_DECODE_BUFFER_SIZE];
// Keep what the last document needed, so consecutive large documents stop re-growing the
// buffer, but never pin more than the ceiling in a pooled reader.
int keep =
Math.max(
RETAINED_STRING_DECODE_BUFFER_SIZE,
Math.min(stringDecodeHighWater, MAX_RETAINED_STRING_DECODE_BUFFER_SIZE));
if (stringDecodeBuffer.length > MAX_RETAINED_STRING_DECODE_BUFFER_SIZE
|| stringDecodeBuffer.length - keep >= keep) {
stringDecodeBuffer = new byte[keep];
}
stringDecodeHighWater = 0;
}

/**
* Both decode paths end here, so a buffer grown for quoted text is kept the same way a String's
* is.
*/
private void recordStringDecodeUse(int length) {
if (length > stringDecodeHighWater) {
stringDecodeHighWater = length;
}
}

Expand Down Expand Up @@ -2759,6 +2791,24 @@ private String readStringLatin1Tail(byte[] bytes, int out, int ch) {
bytes = ensureStringDecodeCapacity(bytes, out + 1);
bytes[out++] = (byte) ch;
}
// Copy the plain characters up to the next stop character in one pass. Without this the whole
// remainder of a string is decoded one character at a time once the first escape is seen.
int runStart = position;
int wordEnd = input.length - Long.BYTES;
while (position <= wordEnd) {
long stopMask = stringStopMask(LittleEndian.getInt64(input, position));
if (stopMask != 0) {
position += Long.numberOfTrailingZeros(stopMask) >>> 3;
break;
}
position += Long.BYTES;
}
int run = position - runStart;
if (run > 0) {
bytes = ensureStringDecodeCapacity(bytes, out + run);
System.arraycopy(input, runStart, bytes, out, run);
out += run;
}
if (position >= input.length) {
throw error("Unterminated string");
}
Expand Down Expand Up @@ -2835,6 +2885,7 @@ private char readLowSurrogateEscape() {
}

private String finishDecodedString(byte[] bytes, int length, boolean utf16) {
recordStringDecodeUse(length);
// The decode buffer is reader-owned reusable storage; returned Strings must own exact bytes.
byte[] result = new byte[length];
System.arraycopy(bytes, 0, result, 0, length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@
*/
public final class Utf16JsonReader extends JsonReader {
private static final int INITIAL_STRING_DECODE_BUFFER_SIZE = 1024;

/** The floor the decode buffer is shrunk back to; it is kept until it is twice what is needed. */
private static final int RETAINED_STRING_DECODE_BUFFER_SIZE = 8192;

/** The ceiling on what a single document can keep alive in a pooled reader between parses. */
private static final int MAX_RETAINED_STRING_DECODE_BUFFER_SIZE = 1 << 17;

private static final boolean LITTLE_ENDIAN = NativeByteOrder.IS_LITTLE_ENDIAN;
private static final long BYTE_ONES = 0x0101010101010101L;
private static final long BYTE_HIGH_BITS = 0x8080808080808080L;
Expand All @@ -76,6 +82,13 @@ public final class Utf16JsonReader extends JsonReader {
private byte[] bytes;
private int length;
private byte[] stringDecodeBuffer = new byte[INITIAL_STRING_DECODE_BUFFER_SIZE];

/**
* Bytes of the longest string decoded since the last {@link #clear()}, so a steady workload keeps
* them.
*/
private int stringDecodeHighWater;

// Keep the cache after hot representation fields; an inherited reference shifts their offsets.
private final FieldNameCache fieldNameCache;
private ZoneIdCache zoneIdCache;
Expand Down Expand Up @@ -271,6 +284,7 @@ protected CharSequence decodeQuotedText(int start, int end) {
outBytes = ensureStringDecodeCapacity(outBytes, out + 2);
out = putUtf16Char(outBytes, out, ch);
}
recordStringDecodeUse(out);
return decodedQuotedText(outBytes, out, true);
}

Expand Down Expand Up @@ -417,8 +431,26 @@ public void clear() {
bytes = null;
length = 0;
position = 0;
if (stringDecodeBuffer.length > RETAINED_STRING_DECODE_BUFFER_SIZE) {
stringDecodeBuffer = new byte[RETAINED_STRING_DECODE_BUFFER_SIZE];
// Keep what the last document needed, so consecutive large documents stop re-growing the
// buffer, but never pin more than the ceiling in a pooled reader.
int keep =
Math.max(
RETAINED_STRING_DECODE_BUFFER_SIZE,
Math.min(stringDecodeHighWater, MAX_RETAINED_STRING_DECODE_BUFFER_SIZE));
if (stringDecodeBuffer.length > MAX_RETAINED_STRING_DECODE_BUFFER_SIZE
|| stringDecodeBuffer.length - keep >= keep) {
stringDecodeBuffer = new byte[keep];
}
stringDecodeHighWater = 0;
}

/**
* Both decode paths end here, so a buffer grown for quoted text is kept the same way a String's
* is.
*/
private void recordStringDecodeUse(int length) {
if (length > stringDecodeHighWater) {
stringDecodeHighWater = length;
}
}

Expand Down Expand Up @@ -2111,6 +2143,31 @@ private String readStringLatin1Tail(byte[] outBytes, int out, int ch) {
out = putUtf16Char(outBytes, out, (char) ch);
return readStringUtf16Tail(outBytes, out, nextStringChar());
}
// Copy the plain characters up to the next stop character in one pass. Without this the whole
// remainder of a string is decoded one character at a time once the first escape is seen.
if (LITTLE_ENDIAN && bytes != null) {
int runStart = position;
int wordEnd = length - 4;
while (position <= wordEnd) {
long word = LittleEndian.getInt64(bytes, position << 1);
long nonLatin = word & UTF16_NON_LATIN_BYTES;
long stopMask = utf16StringStopMask(word, nonLatin) | nonLatin;
if (stopMask != 0) {
position += Long.numberOfTrailingZeros(stopMask) >>> 4;
break;
}
position += 4;
}
int run = position - runStart;
if (run > 0) {
outBytes = ensureStringDecodeCapacity(outBytes, out + run);
byte[] localBytes = bytes;
for (int i = 0, offset = runStart << 1; i < run; i++, offset += 2) {
outBytes[out + i] = localBytes[offset];
}
out += run;
}
}
ch = nextStringChar();
}
}
Expand Down Expand Up @@ -2208,6 +2265,7 @@ private char readLowSurrogateEscape() {
}

private String finishDecodedString(byte[] outBytes, int length, boolean utf16) {
recordStringDecodeUse(length);
// The decode buffer is reader-owned reusable storage; returned Strings must own exact bytes.
byte[] result = new byte[length];
System.arraycopy(outBytes, 0, result, 0, length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ public final class Utf8JsonReader extends JsonReader {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};
private static final int INITIAL_STRING_DECODE_BUFFER_SIZE = 1024;

/** The floor the decode buffer is shrunk back to; it is kept until it is twice what is needed. */
private static final int RETAINED_STRING_DECODE_BUFFER_SIZE = 8192;

/** The ceiling on what a single document can keep alive in a pooled reader between parses. */
private static final int MAX_RETAINED_STRING_DECODE_BUFFER_SIZE = 1 << 17;

private static final boolean LITTLE_ENDIAN = NativeByteOrder.IS_LITTLE_ENDIAN;
private static final long BYTE_ONES = 0x0101010101010101L;
private static final int INT_BYTE_ONES = 0x01010101;
Expand Down Expand Up @@ -126,6 +132,13 @@ public final class Utf8JsonReader extends JsonReader {
private byte[] input;
private int inputLimit;
private byte[] stringDecodeBuffer = new byte[INITIAL_STRING_DECODE_BUFFER_SIZE];

/**
* Bytes of the longest string decoded since the last {@link #clear()}, so a steady workload keeps
* them.
*/
private int stringDecodeHighWater;

// Keep the cache after hot representation fields; an inherited reference shifts their offsets.
private final FieldNameCache fieldNameCache;
private ZoneIdCache zoneIdCache;
Expand Down Expand Up @@ -379,6 +392,7 @@ protected CharSequence decodeQuotedText(int start, int end) {
out = putUtf16Char(outBytes, out, Character.lowSurrogate(codePoint));
}
}
recordStringDecodeUse(out);
return decodedQuotedText(outBytes, out, true);
}

Expand Down Expand Up @@ -540,8 +554,26 @@ public void clear() {
input = EMPTY_BYTES;
inputLimit = 0;
position = 0;
if (stringDecodeBuffer.length > RETAINED_STRING_DECODE_BUFFER_SIZE) {
stringDecodeBuffer = new byte[RETAINED_STRING_DECODE_BUFFER_SIZE];
// Keep what the last document needed, so consecutive large documents stop re-growing the
// buffer, but never pin more than the ceiling in a pooled reader.
int keep =
Math.max(
RETAINED_STRING_DECODE_BUFFER_SIZE,
Math.min(stringDecodeHighWater, MAX_RETAINED_STRING_DECODE_BUFFER_SIZE));
if (stringDecodeBuffer.length > MAX_RETAINED_STRING_DECODE_BUFFER_SIZE
|| stringDecodeBuffer.length - keep >= keep) {
stringDecodeBuffer = new byte[keep];
}
stringDecodeHighWater = 0;
}

/**
* Both decode paths end here, so a buffer grown for quoted text is kept the same way a String's
* is.
*/
private void recordStringDecodeUse(int length) {
if (length > stringDecodeHighWater) {
stringDecodeHighWater = length;
}
}

Expand Down Expand Up @@ -4417,6 +4449,25 @@ private String readStringLatin1Tail(byte[] bytes, int out, int b) {
return readStringUtf16Tail(bytes, out);
}
}
// Copy the plain characters up to the next stop character in one pass. Without this the whole
// remainder of a string is decoded one character at a time once the first escape is seen.
int runStart = position;
int wordEnd = inputLimit - Long.BYTES;
while (position <= wordEnd) {
long word = LittleEndian.getInt64(input, position);
long stopMask = stringStopMask(word);
if (stopMask != 0) {
position += Long.numberOfTrailingZeros(stopMask) >>> 3;
break;
}
position += Long.BYTES;
}
int run = position - runStart;
if (run > 0) {
bytes = ensureStringDecodeCapacity(bytes, out + run);
System.arraycopy(input, runStart, bytes, out, run);
out += run;
}
if (position >= inputLimit) {
throw error("Unterminated string");
}
Expand Down Expand Up @@ -4687,6 +4738,7 @@ private char readLowSurrogateEscape() {
}

private String finishDecodedString(byte[] bytes, int length, boolean utf16) {
recordStringDecodeUse(length);
// Strings must not share the reader-owned decode buffer; the buffer is reused by later reads.
byte[] result = new byte[length];
System.arraycopy(bytes, 0, result, 0, length);
Expand Down
Loading
Loading