optimize JSONReaderASCIISlash
This commit is contained in:
parent
8c86b8ce0d
commit
0e36eaa183
@ -416,7 +416,7 @@ public abstract class JSONReader
|
||||
}
|
||||
|
||||
public final boolean isInitStringFieldAsEmpty() {
|
||||
return (context.features & Feature.InitStringFieldAsEmpty.mask) != 0;
|
||||
return (context.features & MASK_INIT_STRING_FIELD_AS_EMPTY) != 0;
|
||||
}
|
||||
|
||||
public final boolean isSupportSmartMatch(long features) {
|
||||
@ -3356,7 +3356,7 @@ public abstract class JSONReader
|
||||
}
|
||||
|
||||
if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) {
|
||||
return new JSONReaderASCII(context, null, bytes, offset, length);
|
||||
return JSONReaderASCII.of(context, null, bytes, offset, length);
|
||||
}
|
||||
|
||||
throw new JSONException("not support charset " + charset);
|
||||
@ -3380,7 +3380,7 @@ public abstract class JSONReader
|
||||
}
|
||||
|
||||
if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) {
|
||||
return new JSONReaderASCII(context, null, bytes, offset, length);
|
||||
return JSONReaderASCII.of(context, null, bytes, offset, length);
|
||||
}
|
||||
|
||||
throw new JSONException("not support charset " + charset);
|
||||
@ -3427,7 +3427,7 @@ public abstract class JSONReader
|
||||
}
|
||||
|
||||
if (charset == StandardCharsets.US_ASCII) {
|
||||
return new JSONReaderASCII(context, is);
|
||||
return JSONReaderASCII.of(context, is);
|
||||
}
|
||||
|
||||
return JSONReader.of(new InputStreamReader(is, charset), context);
|
||||
@ -3485,7 +3485,7 @@ public abstract class JSONReader
|
||||
int coder = STRING_CODER.applyAsInt(str);
|
||||
if (coder == LATIN1) {
|
||||
byte[] bytes = STRING_VALUE.apply(str);
|
||||
return new JSONReaderASCII(context, str, bytes, 0, bytes.length);
|
||||
return JSONReaderASCII.of(context, str, bytes, 0, bytes.length);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("unsafe get String.coder error");
|
||||
@ -3518,7 +3518,7 @@ public abstract class JSONReader
|
||||
int coder = STRING_CODER.applyAsInt(str);
|
||||
if (coder == LATIN1) {
|
||||
byte[] bytes = STRING_VALUE.apply(str);
|
||||
return new JSONReaderASCII(context, str, bytes, offset, length);
|
||||
return JSONReaderASCII.of(context, str, bytes, offset, length);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("unsafe get String.coder error");
|
||||
@ -4317,6 +4317,7 @@ public abstract class JSONReader
|
||||
}
|
||||
}
|
||||
|
||||
protected static final long MASK_INIT_STRING_FIELD_AS_EMPTY = 1L << 4;
|
||||
protected static final long MASK_TRIM_STRING = 1L << 14;
|
||||
protected static final long MASK_EMPTY_STRING_AS_NULL = 1L << 27;
|
||||
protected static final long MASK_DISABLE_REFERENCE_DETECT = 1L << 33;
|
||||
@ -4329,7 +4330,7 @@ public abstract class JSONReader
|
||||
*/
|
||||
ErrorOnNoneSerializable(1 << 2),
|
||||
SupportArrayToBean(1 << 3),
|
||||
InitStringFieldAsEmpty(1 << 4),
|
||||
InitStringFieldAsEmpty(MASK_INIT_STRING_FIELD_AS_EMPTY),
|
||||
/**
|
||||
* It is not safe to explicitly turn on autoType, it is recommended to use AutoTypeBeforeHandler
|
||||
*/
|
||||
|
@ -14,19 +14,17 @@ import static com.alibaba.fastjson2.JSONReaderJSONB.check3;
|
||||
import static com.alibaba.fastjson2.util.IOUtils.hexDigit4;
|
||||
import static com.alibaba.fastjson2.util.JDKUtils.*;
|
||||
|
||||
final class JSONReaderASCII
|
||||
abstract class JSONReaderASCII
|
||||
extends JSONReaderUTF8 {
|
||||
final String str;
|
||||
static final int ESCAPE_INDEX_NOT_SET = -2;
|
||||
private int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
|
||||
|
||||
JSONReaderASCII(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
protected JSONReaderASCII(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
super(ctx, bytes, offset, length);
|
||||
this.str = str;
|
||||
nameAscii = true;
|
||||
}
|
||||
|
||||
JSONReaderASCII(Context ctx, InputStream is) {
|
||||
protected JSONReaderASCII(Context ctx, InputStream is) {
|
||||
super(ctx, is);
|
||||
nameAscii = true;
|
||||
str = null;
|
||||
@ -1419,75 +1417,13 @@ final class JSONReaderASCII
|
||||
stringValue = str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String readString() {
|
||||
int ch = this.ch;
|
||||
if (ch == '"' || ch == '\'') {
|
||||
final byte[] bytes = this.bytes;
|
||||
|
||||
int offset = this.offset;
|
||||
final int start = offset, end = this.end;
|
||||
|
||||
int index;
|
||||
if (INDEX_OF_CHAR_LATIN1 == null) {
|
||||
index = IOUtils.indexOfQuoteV(bytes, ch, offset, end);
|
||||
} else {
|
||||
try {
|
||||
index = (int) INDEX_OF_CHAR_LATIN1.invokeExact(bytes, ch, offset, end);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new JSONException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
throw error("invalid escape character EOI");
|
||||
}
|
||||
int slashIndex = indexOfSlash(bytes, offset, end);
|
||||
if (slashIndex == -1 || slashIndex > index) {
|
||||
offset = index + 1;
|
||||
} else {
|
||||
return readEscaped(bytes, slashIndex, start, end, slashIndex - offset, ch);
|
||||
}
|
||||
|
||||
String str = STRING_CREATOR_JDK11 != null
|
||||
? STRING_CREATOR_JDK11.apply(Arrays.copyOfRange(bytes, start, index), LATIN1)
|
||||
: new String(bytes, start, index - start, StandardCharsets.ISO_8859_1);
|
||||
long features = context.features;
|
||||
if ((features & MASK_TRIM_STRING) != 0) {
|
||||
str = str.trim();
|
||||
}
|
||||
str = (features & MASK_EMPTY_STRING_AS_NULL) != 0 && str.isEmpty() ? null : str;
|
||||
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if (comma = ch == ',') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
this.ch = (char) (ch & 0xFF);
|
||||
this.offset = offset;
|
||||
return str;
|
||||
}
|
||||
|
||||
return readStringNotMatch();
|
||||
static String subString(byte[] bytes, int start, int index) {
|
||||
return STRING_CREATOR_JDK11 != null
|
||||
? STRING_CREATOR_JDK11.apply(Arrays.copyOfRange(bytes, start, index), LATIN1)
|
||||
: new String(bytes, start, index - start, StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
private int indexOfSlash(byte[] bytes, int offset, int end) {
|
||||
int slashIndex = nextEscapeIndex;
|
||||
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
|
||||
nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
|
||||
}
|
||||
return slashIndex;
|
||||
}
|
||||
|
||||
private String readEscaped(byte[] bytes, int offset, int start, int end, int valueLength, int quote) {
|
||||
final String readEscaped(byte[] bytes, int offset, int start, int end, int valueLength, int quote) {
|
||||
for (;;) {
|
||||
if (offset >= end) {
|
||||
throw error("invalid escape character EOI");
|
||||
@ -1580,4 +1516,12 @@ final class JSONReaderASCII
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
static JSONReaderASCII of(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
return new JSONReaderASCIISlash(ctx, str, bytes, offset, length);
|
||||
}
|
||||
|
||||
static JSONReaderASCII of(Context ctx, InputStream is) {
|
||||
return new JSONReaderASCIISlash(ctx, is);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package com.alibaba.fastjson2;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.alibaba.fastjson2.util.JDKUtils.*;
|
||||
|
||||
final class JSONReaderASCIINonSlash
|
||||
extends JSONReaderASCII{
|
||||
JSONReaderASCIINonSlash(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
super(ctx, str, bytes, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String readString() {
|
||||
int ch = this.ch;
|
||||
if (ch == '"' || ch == '\'') {
|
||||
final byte[] bytes = this.bytes;
|
||||
|
||||
int offset = this.offset;
|
||||
final int start = offset, end = this.end;
|
||||
|
||||
int index;
|
||||
try {
|
||||
index = (int) INDEX_OF_CHAR_LATIN1.invokeExact(bytes, ch, offset, end);
|
||||
} catch (Throwable e) {
|
||||
throw new JSONException(e.getMessage());
|
||||
}
|
||||
if (index == -1) {
|
||||
throw error("invalid escape character EOI");
|
||||
}
|
||||
offset = index + 1;
|
||||
|
||||
String str = STRING_CREATOR_JDK11 != null
|
||||
? STRING_CREATOR_JDK11.apply(Arrays.copyOfRange(bytes, start, index), LATIN1)
|
||||
: new String(bytes, start, index - start, StandardCharsets.ISO_8859_1);
|
||||
long features = context.features;
|
||||
if ((features & MASK_TRIM_STRING) != 0) {
|
||||
str = str.trim();
|
||||
}
|
||||
str = (features & MASK_EMPTY_STRING_AS_NULL) != 0 && str.isEmpty() ? null : str;
|
||||
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if (comma = ch == ',') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
this.ch = (char) (ch & 0xFF);
|
||||
this.offset = offset;
|
||||
return str;
|
||||
}
|
||||
|
||||
return readStringNotMatch();
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package com.alibaba.fastjson2;
|
||||
|
||||
import com.alibaba.fastjson2.util.IOUtils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.alibaba.fastjson2.util.JDKUtils.*;
|
||||
import static com.alibaba.fastjson2.util.JDKUtils.INDEX_OF_CHAR_LATIN1;
|
||||
|
||||
final class JSONReaderASCIISlash
|
||||
extends JSONReaderASCII {
|
||||
static final int ESCAPE_INDEX_NOT_SET = -2;
|
||||
private int nextEscapeIndex = ESCAPE_INDEX_NOT_SET;
|
||||
|
||||
JSONReaderASCIISlash(Context ctx, String str, byte[] bytes, int offset, int length) {
|
||||
this(ctx, str, bytes, offset, length, ESCAPE_INDEX_NOT_SET);
|
||||
}
|
||||
|
||||
JSONReaderASCIISlash(Context ctx, String str, byte[] bytes, int offset, int length, int nextEscapeIndex) {
|
||||
super(ctx, str, bytes, offset, length);
|
||||
this.nextEscapeIndex = nextEscapeIndex;
|
||||
}
|
||||
|
||||
JSONReaderASCIISlash(Context ctx, InputStream is) {
|
||||
super(ctx, is);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String readString() {
|
||||
int ch = this.ch;
|
||||
if (ch == '"' || ch == '\'') {
|
||||
final byte[] bytes = this.bytes;
|
||||
|
||||
int offset = this.offset;
|
||||
final int start = offset, end = this.end;
|
||||
|
||||
int index;
|
||||
if (INDEX_OF_CHAR_LATIN1 == null) {
|
||||
index = IOUtils.indexOfQuoteV(bytes, ch, offset, end);
|
||||
} else {
|
||||
try {
|
||||
index = (int) INDEX_OF_CHAR_LATIN1.invokeExact(bytes, ch, offset, end);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new JSONException(e.getMessage());
|
||||
}
|
||||
}
|
||||
if (index == -1) {
|
||||
throw error("invalid escape character EOI");
|
||||
}
|
||||
int slashIndex = indexOfSlash(bytes, offset, end);
|
||||
if (slashIndex == -1 || slashIndex > index) {
|
||||
offset = index + 1;
|
||||
} else {
|
||||
return readEscaped(bytes, slashIndex, start, end, slashIndex - offset, ch);
|
||||
}
|
||||
|
||||
String str = STRING_CREATOR_JDK11 != null
|
||||
? STRING_CREATOR_JDK11.apply(Arrays.copyOfRange(bytes, start, index), LATIN1)
|
||||
: new String(bytes, start, index - start, StandardCharsets.ISO_8859_1);
|
||||
long features = context.features;
|
||||
if ((features & MASK_TRIM_STRING) != 0) {
|
||||
str = str.trim();
|
||||
}
|
||||
str = (features & MASK_EMPTY_STRING_AS_NULL) != 0 && str.isEmpty() ? null : str;
|
||||
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
|
||||
if (comma = ch == ',') {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
while (ch <= ' ' && (1L << ch & SPACE) != 0) {
|
||||
ch = offset == end ? EOI : bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
this.ch = (char) (ch & 0xFF);
|
||||
this.offset = offset;
|
||||
return str;
|
||||
}
|
||||
|
||||
return readStringNotMatch();
|
||||
}
|
||||
|
||||
private int indexOfSlash(byte[] bytes, int offset, int end) {
|
||||
int slashIndex = nextEscapeIndex;
|
||||
if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) {
|
||||
nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end);
|
||||
}
|
||||
return slashIndex;
|
||||
}
|
||||
}
|
@ -7191,7 +7191,19 @@ class JSONReaderUTF8
|
||||
ascii = IOUtils.isASCII(bytes, off, len);
|
||||
}
|
||||
if (ascii) {
|
||||
return new JSONReaderASCII(context, null, bytes, off, len);
|
||||
int slashIndex = JSONReaderASCIISlash.ESCAPE_INDEX_NOT_SET;
|
||||
if (INDEX_OF_CHAR_LATIN1 != null) {
|
||||
try {
|
||||
slashIndex = (int) INDEX_OF_CHAR_LATIN1.invokeExact(bytes, (int) '\\', off, off + len);
|
||||
} catch (Throwable ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (slashIndex == -1) {
|
||||
return new JSONReaderASCIINonSlash(context, null, bytes, off, len);
|
||||
} else {
|
||||
return new JSONReaderASCIISlash(context, null, bytes, off, len, slashIndex);
|
||||
}
|
||||
}
|
||||
return new JSONReaderUTF8(context, bytes, off, len);
|
||||
}
|
||||
|
@ -1626,7 +1626,7 @@ public class JSONReaderTest1 {
|
||||
for (String name : names) {
|
||||
String str = "{\"" + name + "\":123456789}";
|
||||
byte[] bytes = str.getBytes();
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(JSONFactory.createReadContext(), str, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(JSONFactory.createReadContext(), str, bytes, 0, bytes.length);
|
||||
assertTrue(jsonReader.nextIfObjectStart());
|
||||
assertEquals(
|
||||
Fnv.hashCode64(name),
|
||||
@ -1646,7 +1646,7 @@ public class JSONReaderTest1 {
|
||||
bytes[2] = (byte) i0;
|
||||
bytes[3] = (byte) i1;
|
||||
String name = new String(new char[]{(char) i0, (char) i1});
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(context, null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(context, null, bytes, 0, bytes.length);
|
||||
assertTrue(jsonReader.nextIfObjectStart());
|
||||
assertEquals(Fnv.hashCode64(name), jsonReader.readFieldNameHashCode());
|
||||
}
|
||||
@ -1665,7 +1665,7 @@ public class JSONReaderTest1 {
|
||||
bytes[3] = (byte) i1;
|
||||
bytes[4] = (byte) i2;
|
||||
String name = new String(new char[]{(char) i0, (char) i1, (char) i2});
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(context, null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(context, null, bytes, 0, bytes.length);
|
||||
assertTrue(jsonReader.nextIfObjectStart());
|
||||
assertEquals(Fnv.hashCode64(name), jsonReader.readFieldNameHashCode());
|
||||
}
|
||||
@ -1680,7 +1680,7 @@ public class JSONReaderTest1 {
|
||||
bytes[3] = (byte) i1;
|
||||
bytes[4] = (byte) i2;
|
||||
String name = new String(new char[]{(char) i0, (char) i1, (char) i2});
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(context, null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(context, null, bytes, 0, bytes.length);
|
||||
assertTrue(jsonReader.nextIfObjectStart());
|
||||
assertEquals(Fnv.hashCode64(name), jsonReader.readFieldNameHashCode());
|
||||
}
|
||||
@ -1703,7 +1703,7 @@ public class JSONReaderTest1 {
|
||||
bytes[5] = (byte) i3;
|
||||
|
||||
String name = new String(new char[]{(char) i0, (char) i1, (char) i2, (char) i3});
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(context, null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(context, null, bytes, 0, bytes.length);
|
||||
assertTrue(jsonReader.nextIfObjectStart());
|
||||
assertEquals(Fnv.hashCode64(name), jsonReader.readFieldNameHashCode());
|
||||
}
|
||||
@ -1728,7 +1728,7 @@ public class JSONReaderTest1 {
|
||||
assertTrue(utf16Reader.nextIfObjectStart());
|
||||
String name0 = utf16Reader.readFieldName();
|
||||
|
||||
JSONReaderASCII asciiReader = new JSONReaderASCII(JSONFactory.createReadContext(), null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII asciiReader = JSONReaderASCII.of(JSONFactory.createReadContext(), null, bytes, 0, bytes.length);
|
||||
assertTrue(asciiReader.nextIfObjectStart());
|
||||
String name1 = asciiReader.readFieldName();
|
||||
|
||||
@ -1756,7 +1756,7 @@ public class JSONReaderTest1 {
|
||||
assertTrue(utf16Reader.nextIfObjectStart());
|
||||
String name0 = utf16Reader.readFieldName();
|
||||
|
||||
JSONReaderASCII asciiReader = new JSONReaderASCII(JSONFactory.createReadContext(), null, bytes, 0, bytes.length);
|
||||
JSONReaderASCII asciiReader = JSONReaderASCII.of(JSONFactory.createReadContext(), null, bytes, 0, bytes.length);
|
||||
assertTrue(asciiReader.nextIfObjectStart());
|
||||
String name1 = asciiReader.readFieldName();
|
||||
|
||||
@ -1996,7 +1996,7 @@ public class JSONReaderTest1 {
|
||||
String json = JSON.toJSONString(JSONObject.of(s1, s1));
|
||||
byte[] bytes = json.getBytes(StandardCharsets.ISO_8859_1);
|
||||
JSONReader.Context ctx = JSONFactory.createReadContext();
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(ctx, json, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(ctx, json, bytes, 0, bytes.length);
|
||||
JSONObject object = (JSONObject) jsonReader.readObject();
|
||||
Object v1 = object.get(s1);
|
||||
assertEquals(s1, v1, Integer.toString(i));
|
||||
@ -2008,7 +2008,7 @@ public class JSONReaderTest1 {
|
||||
String json = JSON.toJSONString(JSONObject.of(s2, s2));
|
||||
byte[] bytes = json.getBytes(StandardCharsets.ISO_8859_1);
|
||||
JSONReader.Context ctx = JSONFactory.createReadContext();
|
||||
JSONReaderASCII jsonReader = new JSONReaderASCII(ctx, json, bytes, 0, bytes.length);
|
||||
JSONReaderASCII jsonReader = JSONReaderASCII.of(ctx, json, bytes, 0, bytes.length);
|
||||
JSONObject object = (JSONObject) jsonReader.readObject();
|
||||
Object v1 = object.get(s2);
|
||||
assertEquals(s2, v1);
|
||||
|
@ -161,7 +161,7 @@ public class TestUtils {
|
||||
return new JSONReader[]{
|
||||
new JSONReaderUTF8(JSONFactory.createReadContext(), utf8Bytes, 0, utf8Bytes.length),
|
||||
new JSONReaderUTF16(JSONFactory.createReadContext(), utf16Bytes, 0, utf16Bytes.length),
|
||||
new JSONReaderASCII(JSONFactory.createReadContext(), null, utf8Bytes, 0, utf8Bytes.length)
|
||||
JSONReaderASCII.of(JSONFactory.createReadContext(), null, utf8Bytes, 0, utf8Bytes.length)
|
||||
};
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user