From 8c78b20d76a59a4cfa9aa61a648e46dd103f55cb Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 18 Jan 2025 16:46:31 +0800 Subject: [PATCH 01/15] refactor --- .../jjb/ClientsWriteUTF8BytesTest.java | 2 +- .../alibaba/fastjson2/JSONWriterJSONB.java | 760 +++++------------- .../com/alibaba/fastjson2/JSONWriterUTF8.java | 627 +++++++-------- .../com/alibaba/fastjson2/util/IOUtils.java | 23 + .../fastjson2/JSONWriterJSONBTest.java | 35 +- .../fastjson2/JSONWriterUTF16Test.java | 6 + .../alibaba/fastjson2/util/IOUtilsTest.java | 7 + 7 files changed, 557 insertions(+), 903 deletions(-) diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jjb/ClientsWriteUTF8BytesTest.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jjb/ClientsWriteUTF8BytesTest.java index d70ef21068..93b081e5da 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jjb/ClientsWriteUTF8BytesTest.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/jjb/ClientsWriteUTF8BytesTest.java @@ -40,7 +40,7 @@ public static void fastjson2() { long millis = System.currentTimeMillis() - start; System.out.println("ClientsWriteUTF8Bytes-fastjson2 millis : " + millis); // zulu8.70.0.23 : 1533 1493 1374 1353 - // zulu17.40.19 : 1419 1361 1356 1356 1317 1224 1212 1202 1182 979 + // zulu17.40.19 : 1419 1361 1356 1356 1317 1224 1212 1202 1182 979 949 // zulu17.40.19_vec : 1116 // zulu17.40.19_reflect : 1427 } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java index ba0d1fa76f..1c400651ca 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterJSONB.java @@ -79,28 +79,17 @@ public void writeAny(Object value) { @Override public void startObject() { - if (level >= context.maxLevel) { - throw new JSONException("level too large : " + level); + if (++level > context.maxLevel) { + overflowLevel(); } - level++; - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off] = BC_OBJECT; - this.off = off + 1; + writeRaw(BC_OBJECT); } @Override public void endObject() { level--; - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off] = BC_OBJECT_END; - this.off = off + 1; + writeRaw(BC_OBJECT_END); } @Override @@ -114,216 +103,110 @@ public void startArray(Object array, int size) { writeTypeName(array.getClass().getName()); } - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - boolean tinyInt = size <= ARRAY_FIX_LEN; - bytes[off] = tinyInt ? (byte) (BC_ARRAY_FIX_MIN + size) : BC_ARRAY; - this.off = off + 1; - if (!tinyInt) { - writeInt32(size); - } + startArray(size); } @Override public void startArray(int size) { int off = this.off; - if (off + 1 >= bytes.length) { - grow0(off + 2); + byte[] bytes = this.bytes; + if (off + 6 > bytes.length) { + bytes = grow(off + 6); } boolean tinyInt = size <= ARRAY_FIX_LEN; - bytes[off] = tinyInt ? (byte) (BC_ARRAY_FIX_MIN + size) : BC_ARRAY; - this.off = off + 1; + putByte(bytes, off++, tinyInt ? (byte) (BC_ARRAY_FIX_MIN + size) : BC_ARRAY); if (!tinyInt) { - writeInt32(size); + off = writeInt32(bytes, off, size); } + this.off = off; } @Override public void startArray0() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, BC_ARRAY_FIX_MIN); - this.off = off + 1; + writeRaw(BC_ARRAY_FIX_MIN); } @Override public void startArray1() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 1)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 1)); } @Override public void startArray2() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 2)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 2)); } @Override public void startArray3() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 3)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 3)); } @Override public void startArray4() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 4)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 4)); } @Override public void startArray5() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 5)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 5)); } @Override public void startArray6() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 6)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 6)); } @Override public void startArray7() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 7)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 7)); } @Override public void startArray8() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 8)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 8)); } @Override public void startArray9() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 9)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 9)); } @Override public void startArray10() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 10)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 10)); } @Override public void startArray11() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 11)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 11)); } @Override public void startArray12() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 12)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 12)); } @Override public void startArray13() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 13)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 13)); } @Override public void startArray14() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 14)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 14)); } @Override public void startArray15() { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - - putByte(bytes, off, (byte) (BC_ARRAY_FIX_MIN + 15)); - this.off = off + 1; + writeRaw((byte) (BC_ARRAY_FIX_MIN + 15)); } @Override public void writeRaw(byte b) { - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off++] = b; + int off = this.off; + putByte(grow1(off), off, b); + this.off = off + 1; } @Override @@ -344,18 +227,12 @@ public void writeName(String name) { @Override public void writeNull() { - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off++] = BC_NULL; + writeRaw(BC_NULL); } @Override public void writeStringNull() { - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off++] = BC_NULL; + writeRaw(BC_NULL); } @Override @@ -515,194 +392,82 @@ public void writeString(char[] chars, int coff, int len, boolean quote) { writeString(new String(chars, coff, len)); } - public void writeStringLatin1(final byte[] value) { - if (value == null) { - writeStringNull(); - return; - } - + public void writeStringLatin1(byte[] value) { + byte[] bytes = this.bytes; int off = this.off; - int strlen = value.length; - int minCapacity = value.length - + off - + 5 /*max str len*/ - + 1; - + int minCapacity = off + value.length + 6; if (minCapacity - bytes.length > 0) { - grow0(minCapacity); + bytes = grow(minCapacity); } - final byte[] bytes = this.bytes; + int strlen = value.length; if (strlen <= STR_ASCII_FIX_LEN) { bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); } else if (strlen <= INT32_BYTE_MAX) { - putStringSizeSmall(bytes, off, strlen); - off += 3; + off = putStringSizeSmall(bytes, off, strlen); } else { - off += putStringSizeLarge(bytes, off, strlen); + off = putStringSizeLarge(bytes, off, strlen); } System.arraycopy(value, 0, bytes, off, value.length); this.off = off + strlen; } - private static void putStringSizeSmall(byte[] bytes, int off, int val) { - bytes[off] = BC_STR_ASCII; - bytes[off + 1] = (byte) (BC_INT32_BYTE_ZERO + (val >> 8)); - bytes[off + 2] = (byte) (val); + private static int putStringSizeSmall(byte[] bytes, int off, int val) { + putByte(bytes, off, BC_STR_ASCII); + putShortBE(bytes, off + 1, (short) ((BC_INT32_BYTE_ZERO << 8) + val)); + return off + 3; } private static int putStringSizeLarge(byte[] bytes, int off, int strlen) { if (strlen <= INT32_SHORT_MAX) { - bytes[off] = BC_STR_ASCII; - bytes[off + 1] = (byte) (BC_INT32_SHORT_ZERO + (strlen >> 16)); - bytes[off + 2] = (byte) (strlen >> 8); - bytes[off + 3] = (byte) (strlen); - return 4; - } - - bytes[off] = BC_STR_ASCII; - bytes[off + 1] = BC_INT32; - IOUtils.putIntBE( - bytes, - off + 2, - strlen - ); - return 6; + putIntBE(bytes, off, (BC_STR_ASCII << 24) + (BC_INT32_SHORT_ZERO << 16) + strlen); + return off + 4; + } + + putShortBE(bytes, off, (short) ((BC_STR_ASCII << 8) | BC_INT32)); + putIntBE(bytes, off + 2, strlen); + return off + 6; } @Override - public void writeString(final char[] chars) { + public void writeString(char[] chars) { if (chars == null) { writeNull(); return; } - int off = this.off; - boolean ascii = true; - int strlen = chars.length; - byte[] bytes = this.bytes; - if (chars.length < STR_ASCII_FIX_LEN) { - int minCapacity = off + 1 + strlen; - if (minCapacity > bytes.length) { - bytes = grow(minCapacity); - } - - bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); - for (int i = 0; i < chars.length; i++) { - char ch = chars[i]; - if (ch > 0x00FF) { - ascii = false; - break; - } - bytes[off++] = (byte) ch; - } - - if (ascii) { - this.off = off; - return; - } else { - off = this.off; - } - } - - { - int i = 0; - int upperBound = chars.length & ~3; - for (; i < upperBound; i += 4) { - char c0 = chars[i]; - char c1 = chars[i + 1]; - char c2 = chars[i + 2]; - char c3 = chars[i + 3]; - if (c0 > 0x00FF || c1 > 0x00FF || c2 > 0x00FF || c3 > 0x00FF) { - ascii = false; - break; - } - } - if (ascii) { - for (; i < chars.length; ++i) { - if (chars[i] > 0x00FF) { - ascii = false; - break; - } - } - } - } - - int minCapacity = (ascii ? strlen : strlen * 3) - + off - + 5 /*max str len*/ - + 1; - - if (minCapacity > bytes.length) { - bytes = grow(minCapacity); - } - - if (ascii) { - if (strlen <= STR_ASCII_FIX_LEN) { - bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); - } else if (strlen <= INT32_BYTE_MAX) { - putStringSizeSmall(bytes, off, strlen); - off += 3; - } else { - off += putStringSizeLarge(bytes, off, strlen); - } - for (int i = 0; i < chars.length; i++) { - bytes[off++] = (byte) chars[i]; - } - } else { - int maxSize = chars.length * 3; - int lenByteCnt = sizeOfInt(maxSize); - minCapacity = off + maxSize + lenByteCnt + 1; - if (minCapacity > bytes.length) { - bytes = grow(minCapacity); - } - int result = IOUtils.encodeUTF8(chars, 0, chars.length, bytes, off + lenByteCnt + 1); - - int utf8len = result - off - lenByteCnt - 1; - int utf8lenByteCnt = sizeOfInt(utf8len); - if (lenByteCnt != utf8lenByteCnt) { - System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len); - } - bytes[off++] = BC_STR_UTF8; - if (utf8len >= BC_INT32_NUM_MIN && utf8len <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) utf8len; - } else if (utf8len >= INT32_BYTE_MIN && utf8len <= INT32_BYTE_MAX) { - bytes[off] = (byte) (BC_INT32_BYTE_ZERO + (utf8len >> 8)); - bytes[off + 1] = (byte) (utf8len); - off += 2; - } else { - off = writeInt32(bytes, off, utf8len); - } - off += utf8len; - } - this.off = off; + writeString0(chars, 0, chars.length); } @Override - public void writeString(final char[] chars, final int charsOff, final int len) { + public void writeString(char[] chars, int charsOff, int len) { if (chars == null) { writeNull(); return; } + writeString0(chars, charsOff, len); + } + + private void writeString0(char[] chars, int coff, int strlen) { int off = this.off; byte[] bytes = this.bytes; boolean ascii = true; - if (len < STR_ASCII_FIX_LEN) { - int minCapacity = off + 1 + len; + if (strlen < STR_ASCII_FIX_LEN) { + int minCapacity = off + 1 + strlen; if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN); - for (int i = charsOff; i < len; i++) { + bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); + for (int i = coff, end = coff + strlen; i < end; i++) { char ch = chars[i]; if (ch > 0x00FF) { ascii = false; break; } - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } if (ascii) { @@ -711,82 +476,53 @@ public void writeString(final char[] chars, final int charsOff, final int len) { } off = this.off; + } else { + ascii = isASCII(chars, coff, strlen); } - { - int i = charsOff; - int upperBound = chars.length & ~3; - for (; i < upperBound; i += 4) { - char c0 = chars[i]; - char c1 = chars[i + 1]; - char c2 = chars[i + 2]; - char c3 = chars[i + 3]; - if (c0 > 0x00FF || c1 > 0x00FF || c2 > 0x00FF || c3 > 0x00FF) { - ascii = false; - break; - } - } - if (ascii) { - for (; i < chars.length; ++i) { - if (chars[i] > 0x00FF) { - ascii = false; - break; - } - } - } - } - - int minCapacity = (ascii ? len : len * 3) - + off - + 5 /*max str len*/ - + 1; - + int minCapacity = (ascii ? strlen : strlen * 3) + off + 6; if (minCapacity > bytes.length) { bytes = grow(minCapacity); } if (ascii) { - if (len <= STR_ASCII_FIX_LEN) { - bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN); - } else if (len <= INT32_BYTE_MAX) { - bytes[off] = BC_STR_ASCII; - bytes[off + 1] = (byte) (BC_INT32_BYTE_ZERO + (len >> 8)); - bytes[off + 2] = (byte) (len); - off += 3; - } else { - bytes[off] = BC_STR_ASCII; - off = writeInt32(bytes, off + 1, len); - } - for (int i = 0; i < chars.length; i++) { - bytes[off++] = (byte) chars[i]; - } + off = writeStringLatin1(bytes, off, chars, coff, strlen); } else { - int maxSize = chars.length * 3; - int lenByteCnt = sizeOfInt(maxSize); - minCapacity = off + maxSize + lenByteCnt + 1; - if (minCapacity > bytes.length) { - bytes = grow(minCapacity); - } - int result = IOUtils.encodeUTF8(chars, 0, chars.length, bytes, off + lenByteCnt + 1); + off = writeUTF8(bytes, off, chars, coff, strlen); + } + this.off = off; + } - int utf8len = result - off - lenByteCnt - 1; - int utf8lenByteCnt = sizeOfInt(utf8len); - if (lenByteCnt != utf8lenByteCnt) { - System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len); - } - bytes[off++] = BC_STR_UTF8; - if (utf8len >= BC_INT32_NUM_MIN && utf8len <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) utf8len; - } else if (utf8len >= INT32_BYTE_MIN && utf8len <= INT32_BYTE_MAX) { - bytes[off] = (byte) (BC_INT32_BYTE_ZERO + (utf8len >> 8)); - bytes[off + 1] = (byte) (utf8len); - off += 2; + private static int writeStringLatin1(byte[] bytes, int off, char[] chars, int coff, int strlen) { + if (strlen <= STR_ASCII_FIX_LEN) { + putByte(bytes, off++, (byte) (strlen + BC_STR_ASCII_FIX_MIN)); + } else { + putByte(bytes, off, BC_STR_ASCII); + if (strlen <= INT32_BYTE_MAX) { + putShortBE(bytes, off + 1, (short) ((BC_INT32_BYTE_ZERO << 8) + strlen)); + off += 3; } else { - off = writeInt32(bytes, off, utf8len); + off = writeInt32(bytes, off + 1, strlen); } - off += utf8len; } - this.off = off; + for (int i = 0; i < strlen; i++) { + putByte(bytes, off++, (byte) chars[coff + i]); + } + return off; + } + + private static int writeUTF8(byte[] bytes, int off, char[] chars, int coff, int strlen) { + int maxSize = strlen * 3; + int lenByteCnt = sizeOfInt(maxSize); + int result = IOUtils.encodeUTF8(chars, coff, strlen, bytes, off + lenByteCnt + 1); + + int utf8len = result - off - lenByteCnt - 1; + int utf8lenByteCnt = sizeOfInt(utf8len); + if (lenByteCnt != utf8lenByteCnt) { + System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len); + } + putByte(bytes, off, BC_STR_UTF8); + return writeInt32(bytes, off + 1, utf8len) + utf8len; } public void writeString(String[] strings) { @@ -828,10 +564,7 @@ public void writeSymbol(String str) { @Override public void writeTypeName(String typeName) { int off = this.off; - byte[] bytes = this.bytes; - if (off == bytes.length) { - bytes = grow(off + 1); - } + byte[] bytes = grow1(off); bytes[off++] = BC_TYPED_ANY; long hash = Fnv.hashCode64(typeName); @@ -901,18 +634,18 @@ public boolean writeTypeName(byte[] typeName, long hash) { return false; } + byte[] bytes = this.bytes; int off = this.off; int minCapacity = off + 2 + typeName.length; if (minCapacity > bytes.length) { - ensureCapacity(minCapacity); + bytes = grow(minCapacity); } - final byte[] bytes = this.bytes; - bytes[off++] = BC_TYPED_ANY; - System.arraycopy(typeName, 0, bytes, off, typeName.length); - off += typeName.length; + putByte(bytes, off, BC_TYPED_ANY); + System.arraycopy(typeName, 0, bytes, off + 1, typeName.length); + off += typeName.length + 1; if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) symbol; + putByte(bytes, off++, (byte) symbol); } else { off = writeInt32(bytes, off, symbol); } @@ -927,7 +660,7 @@ private boolean writeTypeNameSymbol(int symbol) { bytes = grow(off + 7); } - bytes[off] = BC_TYPED_ANY; + putByte(bytes, off, BC_TYPED_ANY); this.off = writeInt32(bytes, off + 1, -symbol); return false; } @@ -962,13 +695,13 @@ public void writeString(List list) { byte[] bytes = this.bytes; final int LATIN = 0; boolean latinAll = true; - for (int i = 0; i < list.size(); i++) { + for (int i = 0; i < size; i++) { String str = list.get(i); if (str == null) { if (off == bytes.length) { bytes = grow(off + 1); } - bytes[off++] = BC_NULL; + putByte(bytes, off++, BC_NULL); continue; } int coder = STRING_CODER.applyAsInt(str); @@ -981,15 +714,11 @@ public void writeString(List list) { bytes = grow(off + strlen + 6); } if (strlen <= STR_ASCII_FIX_LEN) { - bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); + putByte(bytes, off++, (byte) (strlen + BC_STR_ASCII_FIX_MIN)); } else if (strlen <= INT32_BYTE_MAX) { - bytes[off] = BC_STR_ASCII; - bytes[off + 1] = (byte) (BC_INT32_BYTE_ZERO + (strlen >> 8)); - bytes[off + 2] = (byte) (strlen); - off += 3; + off = putStringSizeSmall(bytes, off, strlen); } else { - bytes[off] = BC_STR_ASCII; - off = writeInt32(bytes, off + 1, strlen); + off = putStringSizeLarge(bytes, off, strlen); } byte[] value = STRING_VALUE.apply(str); System.arraycopy(value, 0, bytes, off, value.length); @@ -1001,9 +730,10 @@ public void writeString(List list) { } } - for (int i = 0; i < list.size(); i++) { - String str = list.get(i); - writeString(str); + for (int i = 0; i < size; i++) { + writeString( + list.get(i) + ); } } @@ -1015,29 +745,9 @@ public void writeString(String str) { } if (STRING_VALUE != null) { - int coder = STRING_CODER.applyAsInt(str); byte[] value = STRING_VALUE.apply(str); - - if (coder == 0) { - int off = this.off; - int strlen = value.length; - int minCapacity = value.length + off + 6; - - if (minCapacity - bytes.length > 0) { - ensureCapacity(minCapacity); - } - - final byte[] bytes = this.bytes; - if (strlen <= STR_ASCII_FIX_LEN) { - bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); - } else if (strlen <= INT32_BYTE_MAX) { - putStringSizeSmall(bytes, off, strlen); - off += 3; - } else { - off += putStringSizeLarge(bytes, off, strlen); - } - System.arraycopy(value, 0, bytes, off, value.length); - this.off = off + strlen; + if (STRING_CODER.applyAsInt(str) == 0) { + writeStringLatin1(value); return; } else { if (tryWriteStringUTF16(value)) { @@ -1059,13 +769,13 @@ public void writeStringUTF16(byte[] value) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE; + putByte(bytes, off, JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE); off = writeInt32(bytes, off + 1, strlen); System.arraycopy(value, 0, bytes, off, strlen); this.off = off + strlen; } - protected boolean tryWriteStringUTF16(byte[] value) { + boolean tryWriteStringUTF16(byte[] value) { int check_cnt = 128; if (check_cnt > value.length) { check_cnt = value.length; @@ -1134,14 +844,14 @@ private static int writeUTF8( System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len); } int start = off; - bytes[off] = strtype; + putByte(bytes, off, strtype); off = writeInt32(bytes, off + 1, utf8len); return (off - start) + utf8len; } private static int writeUTF16(byte[] bytes, int off, byte[] value) { int start = off; - bytes[off] = JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE; + putByte(bytes, off, JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE); off = writeInt32(bytes, off + 1, value.length); System.arraycopy(value, 0, bytes, off, value.length); return value.length + off - start; @@ -1153,6 +863,14 @@ void ensureCapacity(int minCapacity) { } } + private byte[] grow1(int off) { + byte[] bytes = this.bytes; + if (off == bytes.length) { + bytes = grow(off + 1); + } + return bytes; + } + private byte[] grow(int minCapacity) { grow0(minCapacity); return bytes; @@ -1174,13 +892,8 @@ public void writeMillis(long millis) { long seconds = (millis / 1000); if (seconds >= Integer.MIN_VALUE && seconds <= Integer.MAX_VALUE) { int secondsInt = (int) seconds; - - bytes[off] = BC_TIMESTAMP_SECONDS; - IOUtils.putIntBE( - bytes, - off + 1, - secondsInt - ); + putByte(bytes, off, BC_TIMESTAMP_SECONDS); + putIntBE(bytes, off + 1, secondsInt); this.off = off + 5; return; } @@ -1189,24 +902,16 @@ public void writeMillis(long millis) { long minutes = seconds / 60; if (minutes >= Integer.MIN_VALUE && minutes <= Integer.MAX_VALUE) { int minutesInt = (int) minutes; - bytes[off] = BC_TIMESTAMP_MINUTES; - IOUtils.putIntBE( - bytes, - off + 1, - minutesInt - ); + putByte(bytes, off, BC_TIMESTAMP_MINUTES); + putIntBE(bytes, off + 1, minutesInt); this.off = off + 5; return; } } } - bytes[off] = BC_TIMESTAMP_MILLIS; - IOUtils.putLongBE( - bytes, - off + 1, - millis - ); + putByte(bytes, off, BC_TIMESTAMP_MILLIS); + putLongBE(bytes, off + 1, millis); this.off = off + 9; } @@ -1221,9 +926,10 @@ public void writeInt64(Long i) { } int off = this.off; if (i == null) { - bytes[off++] = (this.context.features & WRITE_NUM_NULL_MASK) == 0 - ? BC_NULL - : (byte) (BC_INT64_NUM_MIN - INT64_NUM_LOW_VALUE); + putByte(bytes, off++, + (this.context.features & WRITE_NUM_NULL_MASK) == 0 + ? BC_NULL + : (byte) (BC_INT64_NUM_MIN - INT64_NUM_LOW_VALUE)); } else { off = writeInt64(bytes, off, i); } @@ -1298,9 +1004,9 @@ public void writeInt64(long[] value) { bytes = grow(minCapacity); } if (size <= ARRAY_FIX_LEN) { - bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size); + putByte(bytes, off++, (byte) (BC_ARRAY_FIX_MIN + size)); } else { - bytes[off] = BC_ARRAY; + putByte(bytes, off, BC_ARRAY); off = writeInt32(bytes, off + 1, size); } @@ -1353,8 +1059,7 @@ public void writeFloat(float value) { } int i = (int) value; if (i == value && value >= BC_INT32_NUM_MIN && value <= BC_INT32_NUM_MAX) { - putByte(bytes, off, BC_FLOAT_INT); - putByte(bytes, off + 1, (byte) i); + putShortLE(bytes, off, (short) ((BC_FLOAT_INT & 0xFF) | (i << 8))); off += 2; } else { putByte(bytes, off, BC_FLOAT); @@ -1385,7 +1090,7 @@ public void writeDouble(double value) { if (off == bytes.length) { bytes = grow(off + 1); } - bytes[off] = value == 0 ? BC_DOUBLE_NUM_0 : BC_DOUBLE_NUM_1; + putByte(bytes, off, value == 0 ? BC_DOUBLE_NUM_0 : BC_DOUBLE_NUM_1); this.off = off + 1; return; } @@ -1396,7 +1101,7 @@ public void writeDouble(double value) { if (off == bytes.length) { bytes = grow(off + 1); } - bytes[off] = BC_DOUBLE_LONG; + putByte(bytes, off, BC_DOUBLE_LONG); this.off = off + 1; writeInt64(longValue); return; @@ -1406,7 +1111,7 @@ public void writeDouble(double value) { if (off + 9 > bytes.length) { bytes = grow(off + 9); } - bytes[off] = BC_DOUBLE; + putByte(bytes, off, BC_DOUBLE); IOUtils.putLongBE(bytes, off + 1, Double.doubleToLongBits(value)); this.off = off + 9; } @@ -1454,9 +1159,9 @@ public void writeInt32(int[] values) { } if (size <= ARRAY_FIX_LEN) { - bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size); + putByte(bytes, off++, (byte) (BC_ARRAY_FIX_MIN + size)); } else { - bytes[off] = BC_ARRAY; + putByte(bytes, off, BC_ARRAY); off = writeInt32(bytes, off + 1, size); } @@ -1482,17 +1187,17 @@ public void writeInt8(byte[] values) { } if (size <= ARRAY_FIX_LEN) { - bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size); + putByte(bytes, off++, (byte) (BC_ARRAY_FIX_MIN + size)); } else { - bytes[off] = BC_ARRAY; + putByte(bytes, off, BC_ARRAY); off = writeInt32(bytes, off + 1, size); } for (int val : values) { if (val < BC_INT32_NUM_MIN || val > BC_INT32_NUM_MAX) { - bytes[off++] = (byte) (BC_INT32_BYTE_ZERO + (val >> 8)); + putByte(bytes, off++, (byte) (BC_INT32_BYTE_ZERO + (val >> 8))); } - bytes[off++] = (byte) val; + putByte(bytes, off++, (byte) val); } this.off = off; } @@ -1504,8 +1209,7 @@ public void writeInt8(byte val) { if (off + 2 > bytes.length) { bytes = grow(off + 2); } - bytes[off] = BC_INT8; - bytes[off + 1] = val; + putShortLE(bytes, off, (short) ((val << 8) | (BC_INT8 & 0xFF))); this.off = off + 2; } @@ -1554,7 +1258,7 @@ public void writeInt32(Integer i) { } int off = this.off; if (i == null) { - bytes[off++] = (this.context.features & (Feature.NullAsDefaultValue.mask | Feature.WriteNullNumberAsZero.mask)) == 0 ? BC_NULL : 0; + putByte(bytes, off++, (this.context.features & (Feature.NullAsDefaultValue.mask | Feature.WriteNullNumberAsZero.mask)) == 0 ? BC_NULL : 0); } else { off = writeInt32(bytes, off, i); } @@ -1588,16 +1292,16 @@ public void writeListInt32(List values) { bytes = grow(minCapacity); } if (size <= ARRAY_FIX_LEN) { - bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size); + putByte(bytes, off++, (byte) (BC_ARRAY_FIX_MIN + size)); } else { - bytes[off] = BC_ARRAY; + putByte(bytes, off, BC_ARRAY); off = writeInt32(bytes, off + 1, size); } for (int i = 0; i < size; i++) { Integer item = values.get(i); if (item == null) { - bytes[off++] = BC_NULL; + putByte(bytes, off++, BC_NULL); continue; } @@ -1608,14 +1312,7 @@ public void writeListInt32(List values) { @Override public void writeArrayNull() { - int off = this.off; - byte[] bytes = this.bytes; - if (off == bytes.length) { - bytes = grow(off + 1); - } - - bytes[off] = (this.context.features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL; - this.off = off + 1; + writeRaw((this.context.features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL); } @Override @@ -1642,13 +1339,12 @@ public void writeSymbol(int symbol) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = BC_SYMBOL; + putByte(bytes, off++, BC_SYMBOL); if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) symbol; + putByte(bytes, off++, (byte) symbol); } else if (symbol >= INT32_BYTE_MIN && symbol <= INT32_BYTE_MAX) { - bytes[off] = (byte) (BC_INT32_BYTE_ZERO + (symbol >> 8)); - bytes[off + 1] = (byte) (symbol); + putShortBE(bytes, off, (short) ((BC_INT32_BYTE_ZERO << 8) + symbol)); off += 2; } else { off = writeInt32(bytes, off, symbol); @@ -1687,12 +1383,12 @@ public void writeNameRaw(byte[] name, long nameHash) { } if (!symbolExists) { - bytes[off++] = BC_SYMBOL; + putByte(bytes, off++, BC_SYMBOL); System.arraycopy(name, 0, bytes, off, name.length); off += name.length; if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) symbol; + putByte(bytes, off++, (byte) symbol); } else { off = writeInt32(bytes, off, symbol); } @@ -1705,7 +1401,7 @@ public void writeNameRaw(byte[] name, long nameHash) { bytes[off++] = BC_SYMBOL; int intValue = -symbol; if (intValue >= BC_INT32_NUM_MIN && intValue <= BC_INT32_NUM_MAX) { - bytes[off++] = (byte) intValue; + putByte(bytes, off++, (byte) intValue); } else { off = writeInt32(bytes, off, intValue); } @@ -1725,12 +1421,9 @@ public void writeLocalDate(LocalDate date) { bytes = grow(off + 5); } - bytes[off] = BC_LOCAL_DATE; + putByte(bytes, off, BC_LOCAL_DATE); int year = date.getYear(); - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) date.getMonthValue(); - bytes[off + 4] = (byte) date.getDayOfMonth(); + putIntBE(bytes, off + 1, (year << 16) | (date.getMonthValue() << 8) | date.getDayOfMonth()); this.off = off + 5; } @@ -1747,10 +1440,9 @@ public void writeLocalTime(LocalTime time) { bytes = grow(off + 9); } - bytes[off] = BC_LOCAL_TIME; - bytes[off + 1] = (byte) time.getHour(); - bytes[off + 2] = (byte) time.getMinute(); - bytes[off + 3] = (byte) time.getSecond(); + putIntBE(bytes, + off, + (BC_LOCAL_TIME << 24) | (time.getHour() << 16) | (time.getMinute() << 8) | time.getSecond()); this.off = writeInt32(bytes, off + 4, time.getNano()); } @@ -1767,15 +1459,15 @@ public void writeLocalDateTime(LocalDateTime dateTime) { bytes = grow(off + 13); } - bytes[off] = BC_LOCAL_DATETIME; - int year = dateTime.getYear(); - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) dateTime.getMonthValue(); - bytes[off + 4] = (byte) dateTime.getDayOfMonth(); - bytes[off + 5] = (byte) dateTime.getHour(); - bytes[off + 6] = (byte) dateTime.getMinute(); - bytes[off + 7] = (byte) dateTime.getSecond(); + putIntBE(bytes, + off, + (BC_LOCAL_DATETIME << 24) | (dateTime.getYear() << 8) | dateTime.getMonthValue()); + putIntBE(bytes, + off + 4, + (dateTime.getDayOfMonth() << 24) + | (dateTime.getHour() << 16) + | (dateTime.getMinute() << 8) + | dateTime.getSecond()); this.off = writeInt32(bytes, off + 8, dateTime.getNano()); } @@ -1792,16 +1484,15 @@ public void writeZonedDateTime(ZonedDateTime dateTime) { bytes = grow(off + 13); } - bytes[off] = BC_TIMESTAMP_WITH_TIMEZONE; - int year = dateTime.getYear(); - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) dateTime.getMonthValue(); - bytes[off + 4] = (byte) dateTime.getDayOfMonth(); - bytes[off + 5] = (byte) dateTime.getHour(); - bytes[off + 6] = (byte) dateTime.getMinute(); - bytes[off + 7] = (byte) dateTime.getSecond(); - + putIntBE(bytes, + off, + (BC_TIMESTAMP_WITH_TIMEZONE << 24) | (dateTime.getYear() << 8) | dateTime.getMonthValue()); + putIntBE(bytes, + off + 4, + (dateTime.getDayOfMonth() << 24) + | (dateTime.getHour() << 16) + | (dateTime.getMinute() << 8) + | dateTime.getSecond()); this.off = writeInt32(bytes, off + 8, dateTime.getNano()); ZoneId zoneId = dateTime.getZone(); @@ -1830,19 +1521,19 @@ public void writeOffsetDateTime(OffsetDateTime dateTime) { bytes = grow(minCapacity); } - bytes[off] = BC_TIMESTAMP_WITH_TIMEZONE; - int year = dateTime.getYear(); - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) dateTime.getMonthValue(); - bytes[off + 4] = (byte) dateTime.getDayOfMonth(); - bytes[off + 5] = (byte) dateTime.getHour(); - bytes[off + 6] = (byte) dateTime.getMinute(); - bytes[off + 7] = (byte) dateTime.getSecond(); + putIntBE(bytes, + off, + (BC_TIMESTAMP_WITH_TIMEZONE << 24) | (dateTime.getYear() << 8) | dateTime.getMonthValue()); + putIntBE(bytes, + off + 4, + (dateTime.getDayOfMonth() << 24) + | (dateTime.getHour() << 16) + | (dateTime.getMinute() << 8) + | dateTime.getSecond()); off = writeInt32(bytes, off + 8, dateTime.getNano()); - bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN); + putByte(bytes, off++, (byte) (strlen + BC_STR_ASCII_FIX_MIN)); zoneIdStr.getBytes(0, strlen, bytes, off); this.off = off + strlen; } @@ -1872,7 +1563,7 @@ public void writeInstant(Instant instant) { bytes = grow(off + 15); } - bytes[off] = BC_TIMESTAMP; + putByte(bytes, off, BC_TIMESTAMP); off = writeInt64(bytes, off + 1, instant.getEpochSecond()); this.off = writeInt32(bytes, off, instant.getNano()); } @@ -1909,7 +1600,7 @@ public void writeBigInt(BigInteger value, long features) { if (off + 10 > bytes.length) { bytes = grow(off + 10); } - bytes[off] = BC_BIGINT_LONG; + putByte(bytes, off, BC_BIGINT_LONG); this.off = writeInt64(bytes, off + 1, value.longValue()); return; } @@ -1920,7 +1611,7 @@ public void writeBigInt(BigInteger value, long features) { bytes = grow(minCapacity); } - bytes[off] = BC_BIGINT; + putByte(bytes, off, BC_BIGINT); off = writeInt32(bytes, off + 1, valueBytes.length); System.arraycopy(valueBytes, 0, bytes, off, valueBytes.length); this.off = off + valueBytes.length; @@ -1940,7 +1631,7 @@ public void writeBinary(byte[] binary) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = BC_BINARY; + putByte(bytes, off, BC_BINARY); off = writeInt32(bytes, off + 1, len); System.arraycopy(binary, 0, bytes, off, len); @@ -1970,7 +1661,7 @@ public void writeDecimal(BigDecimal value, long features, DecimalFormat format) return; } - bytes[off] = BC_DECIMAL; + putByte(bytes, off, BC_DECIMAL); off = writeInt32(bytes, off + 1, scale); if (intCompact >= Integer.MIN_VALUE && intCompact <= Integer.MAX_VALUE) { off = writeInt32(bytes, off, (int) intCompact); @@ -1984,12 +1675,12 @@ public void writeDecimal(BigDecimal value, long features, DecimalFormat format) BigInteger unscaledValue = value.unscaledValue(); if (scale == 0 && isInt64(unscaledValue)) { - bytes[off] = BC_DECIMAL_LONG; + putByte(bytes, off, BC_DECIMAL_LONG); this.off = writeInt64(bytes, off + 1, unscaledValue.longValue()); return; } - bytes[off] = BC_DECIMAL; + putByte(bytes, off, BC_DECIMAL); off = writeInt32(bytes, off + 1, scale); if (isInt32(unscaledValue)) { @@ -2006,12 +1697,7 @@ && isInt64(unscaledValue)) { @Override public void writeBool(boolean value) { - int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - putByte(bytes, off, value ? BC_TRUE : BC_FALSE); - this.off = off + 1; + writeRaw(value ? BC_TRUE : BC_FALSE); } @Override @@ -2031,11 +1717,7 @@ public void writeBool(boolean[] values) { @Override public void writeReference(String path) { int off = this.off; - byte[] bytes = this.bytes; - if (off == bytes.length) { - bytes = grow(off + 1); - } - bytes[off] = BC_REFERENCE; + putByte(grow1(off), off, BC_REFERENCE); this.off = off + 1; writeString(path == this.lastReference ? "#-1" : path); this.lastReference = path; @@ -2056,15 +1738,9 @@ public void writeDateTime14( bytes = grow(off + 9); } - bytes[off] = BC_LOCAL_DATETIME; - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) month; - bytes[off + 4] = (byte) dayOfMonth; - bytes[off + 5] = (byte) hour; - bytes[off + 6] = (byte) minute; - bytes[off + 7] = (byte) second; - bytes[off + 8] = BC_INT32_NUM_0; + putIntBE(bytes, off, (BC_LOCAL_DATETIME << 24) | ((year & 0xFFFF) << 8) | month); + putIntBE(bytes, off + 4, (dayOfMonth << 24) | (hour << 16) | (minute << 8) | second); + putByte(bytes, off + 8, BC_INT32_NUM_0); this.off = off + 9; } @@ -2077,22 +1753,7 @@ public void writeDateTime19( int minute, int second ) { - int off = this.off; - byte[] bytes = this.bytes; - if (off + 9 > bytes.length) { - bytes = grow(off + 9); - } - - bytes[off] = BC_LOCAL_DATETIME; - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) month; - bytes[off + 4] = (byte) dayOfMonth; - bytes[off + 5] = (byte) hour; - bytes[off + 6] = (byte) minute; - bytes[off + 7] = (byte) second; - bytes[off + 8] = BC_INT32_NUM_0; - this.off = off + 9; + writeDateTime14(year, month, dayOfMonth, hour, minute, second); } @Override @@ -2119,10 +1780,7 @@ public void writeDateYYYMMDD8(int year, int month, int dayOfMonth) { } bytes[off] = BC_LOCAL_DATE; - bytes[off + 1] = (byte) (year >>> 8); - bytes[off + 2] = (byte) year; - bytes[off + 3] = (byte) month; - bytes[off + 4] = (byte) dayOfMonth; + putIntBE(bytes, off + 1, (year << 16) | (month << 8) | dayOfMonth); this.off = off + 5; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java index d6c85fa2ff..56ddecc412 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java @@ -87,13 +87,7 @@ public final void writeReference(String path) { UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, REF); this.off = off + 8; writeString(path); - off = this.off; - bytes = this.bytes; - if (off == bytes.length) { - bytes = grow(off + 1); - } - bytes[off] = '}'; - this.off = off + 1; + writeRaw((byte) '}'); } @Override @@ -106,7 +100,7 @@ public final void writeBase64(byte[] value) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); int eLen = (value.length / 3) * 3; // Length of even 24-bits. @@ -115,10 +109,10 @@ public final void writeBase64(byte[] value) { int i = (value[s++] & 0xff) << 16 | (value[s++] & 0xff) << 8 | (value[s++] & 0xff); // Encode the int into four chars - bytes[off] = (byte) CA[(i >>> 18) & 0x3f]; - bytes[off + 1] = (byte) CA[(i >>> 12) & 0x3f]; - bytes[off + 2] = (byte) CA[(i >>> 6) & 0x3f]; - bytes[off + 3] = (byte) CA[i & 0x3f]; + putByte(bytes, off, (byte) CA[(i >>> 18) & 0x3f]); + putByte(bytes, off + 1, (byte) CA[(i >>> 12) & 0x3f]); + putByte(bytes, off + 2, (byte) CA[(i >>> 6) & 0x3f]); + putByte(bytes, off + 3, (byte) CA[i & 0x3f]); off += 4; } @@ -129,14 +123,14 @@ public final void writeBase64(byte[] value) { int i = ((value[eLen] & 0xff) << 10) | (left == 2 ? ((value[value.length - 1] & 0xff) << 2) : 0); // Set last four chars - bytes[off] = (byte) CA[i >> 12]; - bytes[off + 1] = (byte) CA[(i >>> 6) & 0x3f]; - bytes[off + 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; - bytes[off + 3] = '='; + putByte(bytes, off, (byte) CA[i >> 12]); + putByte(bytes, off + 1, (byte) CA[(i >>> 6) & 0x3f]); + putByte(bytes, off + 2, left == 2 ? (byte) CA[i & 0x3f] : (byte) '='); + putByte(bytes, off + 3, (byte) '='); off += 4; } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -155,22 +149,15 @@ public final void writeHex(byte[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = 'x'; - bytes[off + 1] = '\''; + putShortLE(bytes, off, (short) ('x' | ('\'' << 8))); off += 2; for (int i = 0; i < values.length; i++) { - byte b = values[i]; - int a = b & 0xFF; - int b0 = a >> 4; - int b1 = a & 0xf; - - bytes[off] = (byte) (b0 + (b0 < 10 ? 48 : 55)); - bytes[off + 1] = (byte) (b1 + (b1 < 10 ? 48 : 55)); + putShortLE(bytes, off, hex2U(values[i])); off += 2; } - bytes[off] = '\''; + putByte(bytes, off, (byte) '\''); this.off = off + 1; } @@ -219,17 +206,14 @@ protected final void write0(char c) { if (off == bytes.length) { ensureCapacity(off + 1); } - bytes[off] = (byte) c; + putByte(bytes, off, (byte) c); this.off = off + 1; } @Override public final void writeColon() { int off = this.off; - if (off == bytes.length) { - ensureCapacity(off + 1); - } - bytes[off] = ':'; + putByte(grow1(off), off, (byte) ':'); this.off = off + 1; } @@ -247,7 +231,7 @@ public final void startObject() { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) '{'; + putByte(bytes, off++, (byte) '{'); if (pretty != PRETTY_NON) { off = indent(bytes, off); @@ -268,7 +252,7 @@ public final void endObject() { off = indent(bytes, off); } - bytes[off] = (byte) '}'; + putByte(bytes, off, (byte) '}'); this.off = off + 1; startObject = false; } @@ -282,7 +266,7 @@ public final void writeComma() { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -301,7 +285,7 @@ public final void startArray() { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) '['; + putByte(bytes, off++, (byte) '['); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -320,7 +304,7 @@ public final void endArray() { if (pretty != PRETTY_NON) { off = indent(bytes, off); } - bytes[off] = (byte) ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; startObject = false; } @@ -330,21 +314,16 @@ public final void writeString(List list) { super.writeString(list); return; } - // startArray(); int off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off] = '['; + byte[] bytes = grow1(off); + putByte(bytes, off, (byte) '['); this.off = off + 1; for (int i = 0, size = list.size(); i < size; i++) { if (i != 0) { off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off] = ','; + bytes = grow1(off); + putByte(bytes, off, (byte) ','); this.off = off + 1; } @@ -353,19 +332,17 @@ public final void writeString(List list) { } off = this.off; - if (off == bytes.length) { - grow0(off + 1); - } - bytes[off] = ']'; + bytes = grow1(off); + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @Override public final void writeString(boolean value) { byte quote = (byte) this.quote; - bytes[off++] = quote; + putByte(bytes, off++, quote); writeBool(value); - bytes[off++] = quote; + putByte(bytes, off++, quote); } @Override @@ -417,10 +394,7 @@ public final void writeString(long value) { } private void writeQuote() { - if (off == bytes.length) { - grow(off + 1); - } - bytes[off++] = (byte) quote; + writeRaw((byte) quote); } @Override @@ -450,7 +424,7 @@ public void writeString(String str) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); int i = 0; for (; i < chars.length; i++) { @@ -464,11 +438,12 @@ public void writeString(String str) { ) { break; } - bytes[off++] = (byte) c0; + + putByte(bytes, off++, (byte) c0); } if (i == chars.length) { - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; return; } @@ -478,7 +453,7 @@ public void writeString(String str) { writeStringEscapedRest(chars, chars.length, browserSecure, escapeNoneAscii, i); } - this.bytes[this.off++] = (byte) quote; + putByte(this.bytes, this.off++, (byte) quote); } public void writeStringLatin1(byte[] value) { @@ -516,10 +491,10 @@ public void writeStringLatin1(byte[] value) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = quote; + putByte(bytes, off, quote); System.arraycopy(value, 0, bytes, off + 1, value.length); off += value.length + 1; - bytes[off] = quote; + putByte(bytes, off, quote); this.off = off + 1; } @@ -569,10 +544,10 @@ protected final void writeStringLatin1BrowserSecure(byte[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = quote; + putByte(bytes, off, quote); System.arraycopy(values, 0, bytes, off + 1, values.length); off += values.length + 1; - bytes[off] = quote; + putByte(bytes, off, quote); this.off = off + 1; return; } @@ -598,7 +573,7 @@ public final void writeStringUTF16(byte[] value) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); int coff = 0, char_len = value.length >> 1; while (coff < char_len) { @@ -652,24 +627,24 @@ public final void writeStringUTF16(byte[] value) { writeU4HexU(bytes, off, c); off += 6; } else { - bytes[off++] = (byte) c; + putByte(bytes, off++, (byte) c); } break; default: if (c == quote) { - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) quote; + putByte(bytes, off, (byte) '\\'); + putByte(bytes, off + 1, (byte) quote); off += 2; } else { - bytes[off++] = (byte) c; + putByte(bytes, off++, (byte) c); } break; } } else { if (c < 0x800) { // 2 bytes, 11 bits - bytes[off] = (byte) (0xc0 | (c >> 6)); - bytes[off + 1] = (byte) (0x80 | (c & 0x3f)); + putByte(bytes, off, (byte) (0xc0 | (c >> 6))); + putByte(bytes, off + 1, (byte) (0x80 | (c & 0x3f))); off += 2; } else if (escapeNoneAscii) { writeU4HexU(bytes, off, c); @@ -686,37 +661,37 @@ public final void writeStringUTF16(byte[] value) { coff++; uc = ((c << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d) } else { - bytes[off++] = '?'; + putByte(bytes, off++, (byte) '?'); continue; } } } else { // // Character.isLowSurrogate(c) - bytes[off++] = '?'; + putByte(bytes, off++, (byte) '?'); continue; } if (uc < 0) { - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); } else { - bytes[off] = (byte) (0xf0 | ((uc >> 18))); - bytes[off + 1] = (byte) (0x80 | ((uc >> 12) & 0x3f)); - bytes[off + 2] = (byte) (0x80 | ((uc >> 6) & 0x3f)); - bytes[off + 3] = (byte) (0x80 | (uc & 0x3f)); + putByte(bytes, off, (byte) (0xf0 | ((uc >> 18)))); + putByte(bytes, off + 1, (byte) (0x80 | ((uc >> 12) & 0x3f))); + putByte(bytes, off + 2, (byte) (0x80 | ((uc >> 6) & 0x3f))); + putByte(bytes, off + 3, (byte) (0x80 | (uc & 0x3f))); off += 4; } } else { // 3 bytes, 16 bits - bytes[off] = (byte) (0xe0 | ((c >> 12))); - bytes[off + 1] = (byte) (0x80 | ((c >> 6) & 0x3f)); - bytes[off + 2] = (byte) (0x80 | (c & 0x3f)); + putByte(bytes, off, (byte) (0xe0 | ((c >> 12)))); + putByte(bytes, off + 1, (byte) (0x80 | ((c >> 6) & 0x3f))); + putByte(bytes, off + 2, (byte) (0x80 | (c & 0x3f))); off += 3; } } } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -742,7 +717,7 @@ public final void writeString(final char[] chars) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); int i = 0; for (; i < chars.length; i++) { @@ -756,7 +731,7 @@ public final void writeString(final char[] chars) { ) { break; } - bytes[off++] = (byte) c; + putByte(bytes, off++, (byte) c); } this.off = off; @@ -764,7 +739,7 @@ public final void writeString(final char[] chars) { writeStringEscapedRest(chars, chars.length, browserSecure, escapeNoneAscii, i); } - this.bytes[this.off++] = (byte) quote; + putByte(this.bytes, this.off++, (byte) quote); } public final void writeString(final char[] chars, int stroff, int strlen) { @@ -797,7 +772,7 @@ public final void writeString(final char[] chars, int stroff, int strlen) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); int i = stroff; for (; i < end; i++) { @@ -811,14 +786,14 @@ public final void writeString(final char[] chars, int stroff, int strlen) { ) { break; } - bytes[off++] = (byte) c0; + putByte(bytes, off++, (byte) c0); } this.off = off; if (i < end) { writeStringEscapedRest(chars, end, browserSecure, escapeNoneAscii, i); } - this.bytes[this.off++] = (byte) quote; + putByte(this.bytes, this.off++, (byte) quote); } protected final void writeStringEscaped(byte[] values) { @@ -830,7 +805,7 @@ protected final void writeStringEscaped(byte[] values) { final boolean browserSecure = (context.features & BrowserSecure.mask) != 0; int off = this.off; - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); for (int i = 0; i < values.length; i++) { byte ch = values[i]; switch (ch) { @@ -881,27 +856,27 @@ protected final void writeStringEscaped(byte[] values) { writeU4HexU(bytes, off, ch); off += 6; } else { - bytes[off++] = ch; + putByte(bytes, off++, ch); } break; default: if (ch == quote) { - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) quote; + putByte(bytes, off, (byte) '\\'); + putByte(bytes, off + 1, (byte) quote); off += 2; } else if (ch < 0) { // latin int c = ch & 0xFF; - bytes[off] = (byte) (0xc0 | (c >> 6)); - bytes[off + 1] = (byte) (0x80 | (c & 0x3f)); + putByte(bytes, off, (byte) (0xc0 | (c >> 6))); + putByte(bytes, off + 1, (byte) (0x80 | (c & 0x3f))); off += 2; } else { - bytes[off++] = ch; + putByte(bytes, off++, ch); } break; } } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -925,33 +900,12 @@ protected final void writeStringEscapedRest( if (ch <= 0x007F) { switch (ch) { case '\\': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) '\\'; - off += 2; - break; case '\n': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'n'; - off += 2; - break; case '\r': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'r'; - off += 2; - break; case '\f': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'f'; - off += 2; - break; case '\b': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'b'; - off += 2; - break; case '\t': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 't'; + writeEscapedChar(bytes, off, ch); off += 2; break; case 0: @@ -992,16 +946,16 @@ protected final void writeStringEscapedRest( writeU4HexU(bytes, off, ch); off += 6; } else { - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } break; default: if (ch == quote) { - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) quote; + putByte(bytes, off, (byte) '\\'); + putByte(bytes, off + 1, (byte) quote); off += 2; } else { - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } break; } @@ -1020,36 +974,36 @@ protected final void writeStringEscapedRest( uc = ((ch << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d) } else { // throw new JSONException("encodeUTF8 error", new MalformedInputException(1)); - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); continue; } } } else { // // Character.isLowSurrogate(c) - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); continue; // throw new JSONException("encodeUTF8 error", new MalformedInputException(1)); } if (uc < 0) { - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); } else { - bytes[off] = (byte) (0xf0 | ((uc >> 18))); - bytes[off + 1] = (byte) (0x80 | ((uc >> 12) & 0x3f)); - bytes[off + 2] = (byte) (0x80 | ((uc >> 6) & 0x3f)); - bytes[off + 3] = (byte) (0x80 | (uc & 0x3f)); + putByte(bytes, off, (byte) (0xf0 | ((uc >> 18)))); + putByte(bytes, off + 1, (byte) (0x80 | ((uc >> 12) & 0x3f))); + putByte(bytes, off + 2, (byte) (0x80 | ((uc >> 6) & 0x3f))); + putByte(bytes, off + 3, (byte) (0x80 | (uc & 0x3f))); off += 4; i++; // 2 chars } } else if (ch > 0x07FF) { - bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F)); - bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F)); - bytes[off + 2] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xE0 | ((ch >> 12) & 0x0F))); + putByte(bytes, off + 1, (byte) (0x80 | ((ch >> 6) & 0x3F))); + putByte(bytes, off + 2, (byte) (0x80 | (ch & 0x3F))); off += 3; } else { - bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F)); - bytes[off + 1] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xC0 | ((ch >> 6) & 0x1F))); + putByte(bytes, off + 1, (byte) (0x80 | (ch & 0x3F))); off += 2; } } @@ -1075,7 +1029,7 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) } int off = this.off; if (quoted) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } int end = offset + len; @@ -1089,12 +1043,12 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) || c0 > 0x007F) { break; } - bytes[off++] = (byte) c0; + putByte(bytes, off++, (byte) c0); } if (i == end) { if (quoted) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } this.off = off; return; @@ -1105,33 +1059,12 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) if (ch <= 0x007F) { switch (ch) { case '\\': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) '\\'; - off += 2; - break; case '\n': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'n'; - off += 2; - break; case '\r': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'r'; - off += 2; - break; case '\f': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'f'; - off += 2; - break; case '\b': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'b'; - off += 2; - break; case '\t': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 't'; + writeEscapedChar(bytes, off, ch); off += 2; break; case 0: @@ -1166,11 +1099,11 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) break; default: if (ch == quote) { - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) quote; + putByte(bytes, off, (byte) '\\'); + putByte(bytes, off+ 1, (byte) quote); off += 2; } else { - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } break; } @@ -1189,42 +1122,42 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) uc = ((ch << 10) + d) + (0x010000 - ('\uD800' << 10) - '\uDC00'); // Character.toCodePoint(c, d) } else { // throw new JSONException("encodeUTF8 error", new MalformedInputException(1)); - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); continue; } } } else { // // Character.isLowSurrogate(c) - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); continue; // throw new JSONException("encodeUTF8 error", new MalformedInputException(1)); } if (uc < 0) { - bytes[off++] = (byte) '?'; + putByte(bytes, off++, (byte) '?'); } else { - bytes[off] = (byte) (0xf0 | ((uc >> 18))); - bytes[off + 1] = (byte) (0x80 | ((uc >> 12) & 0x3f)); - bytes[off + 2] = (byte) (0x80 | ((uc >> 6) & 0x3f)); - bytes[off + 3] = (byte) (0x80 | (uc & 0x3f)); + putByte(bytes, off, (byte) (0xf0 | ((uc >> 18)))); + putByte(bytes, off + 1, (byte) (0x80 | ((uc >> 12) & 0x3f))); + putByte(bytes, off + 2, (byte) (0x80 | ((uc >> 6) & 0x3f))); + putByte(bytes, off + 3, (byte) (0x80 | (uc & 0x3f))); off += 4; i++; // 2 chars } } else if (ch > 0x07FF) { - bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F)); - bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F)); - bytes[off + 2] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xE0 | ((ch >> 12) & 0x0F))); + putByte(bytes, off + 1, (byte) (0x80 | ((ch >> 6) & 0x3F))); + putByte(bytes, off + 2, (byte) (0x80 | (ch & 0x3F))); off += 3; } else { - bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F)); - bytes[off + 1] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xC0 | ((ch >> 6) & 0x1F))); + putByte(bytes, off + 1, (byte) (0x80 | (ch & 0x3F))); off += 2; } } if (quoted) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } this.off = off; } @@ -1262,37 +1195,16 @@ public final void writeChar(char ch) { if (off + 8 > bytes.length) { bytes = grow(off + 8); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); if (ch <= 0x007F) { switch (ch) { case '\\': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) '\\'; - off += 2; - break; case '\n': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'n'; - off += 2; - break; case '\r': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'r'; - off += 2; - break; case '\f': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'f'; - off += 2; - break; case '\b': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 'b'; - off += 2; - break; case '\t': - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) 't'; + writeEscapedChar(bytes, off, ch); off += 2; break; case 0: @@ -1327,28 +1239,28 @@ public final void writeChar(char ch) { break; default: if (ch == quote) { - bytes[off] = (byte) '\\'; - bytes[off + 1] = (byte) quote; + putByte(bytes, off, (byte) '\\'); + putByte(bytes, off + 1, (byte) quote); off += 2; } else { - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } break; } } else if (ch >= '\uD800' && ch < ('\uDFFF' + 1)) { // //Character.isSurrogate(c) throw new JSONException("illegal char " + ch); } else if (ch > 0x07FF) { - bytes[off] = (byte) (0xE0 | ((ch >> 12) & 0x0F)); - bytes[off + 1] = (byte) (0x80 | ((ch >> 6) & 0x3F)); - bytes[off + 2] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xE0 | ((ch >> 12) & 0x0F))); + putByte(bytes, off + 1, (byte) (0x80 | ((ch >> 6) & 0x3F))); + putByte(bytes, off + 2, (byte) (0x80 | (ch & 0x3F))); off += 3; } else { - bytes[off] = (byte) (0xC0 | ((ch >> 6) & 0x1F)); - bytes[off + 1] = (byte) (0x80 | (ch & 0x3F)); + putByte(bytes, off, (byte) (0xC0 | ((ch >> 6) & 0x1F))); + putByte(bytes, off + 1, (byte) (0x80 | (ch & 0x3F))); off += 2; } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -1392,7 +1304,7 @@ public final void writeUUID(UUID value) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = '"'; + putByte(bytes, off, (byte) '"'); final long base = ARRAY_BYTE_BASE_OFFSET + off; @@ -1401,22 +1313,22 @@ public final void writeUUID(UUID value) { base + 1, packDigits((int) (msb >> 56), (int) (msb >> 48), (int) (msb >> 40), (int) (msb >> 32)) ); - bytes[off + 9] = '-'; + putByte(bytes, off + 9, (byte) '-'); UNSAFE.putLong( bytes, base + 10, packDigits(((int) msb) >> 24, ((int) msb) >> 16)); - bytes[off + 14] = '-'; + putByte(bytes, off + 14, (byte) '-'); UNSAFE.putLong( bytes, base + 15, packDigits(((int) msb) >> 8, (int) msb)); - bytes[off + 19] = '-'; + putByte(bytes, off + 19, (byte) '-'); UNSAFE.putLong( bytes, base + 20, packDigits((int) (lsb >> 56), (int) (lsb >> 48))); - bytes[off + 24] = '-'; + putByte(bytes, off + 24, (byte) '-'); UNSAFE.putLong( bytes, base + 25, @@ -1425,7 +1337,7 @@ public final void writeUUID(UUID value) { bytes, base + 33, packDigits(((int) lsb) >> 8, (int) lsb)); - bytes[off + 37] = '"'; + putByte(bytes, off + 37, (byte) '"'); this.off += 38; } @@ -1443,15 +1355,15 @@ public final void writeRaw(String str) { for (int i = 0; i < chars.length; i++) { char c = chars[i]; if ((c >= 0x0001) && (c <= 0x007F)) { - bytes[off++] = (byte) c; + putByte(bytes, off++, (byte) c); } else if (c > 0x07FF) { - bytes[off] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - bytes[off + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); - bytes[off + 2] = (byte) (0x80 | (c & 0x3F)); + putByte(bytes, off, (byte) (0xE0 | ((c >> 12) & 0x0F))); + putByte(bytes, off + 1, (byte) (0x80 | ((c >> 6) & 0x3F))); + putByte(bytes, off + 2, (byte) (0x80 | (c & 0x3F))); off += 3; } else { - bytes[off] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - bytes[off + 1] = (byte) (0x80 | (c & 0x3F)); + putByte(bytes, off, (byte) (0xC0 | ((c >> 6) & 0x1F))); + putByte(bytes, off + 1, (byte) (0x80 | (c & 0x3F))); off += 2; } } @@ -1480,7 +1392,7 @@ public final void writeNameRaw(byte[] name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1500,7 +1412,7 @@ public final void writeName2Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1511,7 +1423,7 @@ public final void writeName2Raw(long name) { } private int indent(byte[] bytes, int off) { - bytes[off] = '\n'; + putByte(bytes, off, (byte) '\n'); int toIndex = off + 1 + pretty * level; Arrays.fill(bytes, off + 1, toIndex, pretty == PRETTY_TAB ? (byte) '\t' : (byte) ' '); return toIndex; @@ -1528,7 +1440,7 @@ public final void writeName3Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1549,7 +1461,7 @@ public final void writeName4Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1570,7 +1482,7 @@ public final void writeName5Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1591,14 +1503,14 @@ public final void writeName6Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } } UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name); - bytes[off + 8] = ':'; + putByte(bytes, off + 8, (byte) ':'); this.off = off + 9; } @@ -1613,15 +1525,15 @@ public final void writeName7Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } } UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name); - bytes[off + 8] = (byte) quote; - bytes[off + 9] = ':'; + putByte(bytes, off + 8, (byte) quote); + putByte(bytes, off + 9, (byte) ':'); this.off = off + 10; } @@ -1636,13 +1548,13 @@ public final void writeName8Raw(long name) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 1, name); UNSAFE.putShort(bytes, ARRAY_BYTE_BASE_OFFSET + off + 9, useSingleQuote ? QUOTE_COLON : QUOTE2_COLON); this.off = off + 11; @@ -1659,7 +1571,7 @@ public final void writeName9Raw(long name0, int name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1681,7 +1593,7 @@ public final void writeName10Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1703,7 +1615,7 @@ public final void writeName11Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1725,7 +1637,7 @@ public final void writeName12Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1747,7 +1659,7 @@ public final void writeName13Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1769,7 +1681,7 @@ public final void writeName14Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1777,7 +1689,7 @@ public final void writeName14Raw(long name0, long name1) { UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0); UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1); - bytes[off + 16] = ':'; + putByte(bytes, off + 16, (byte) ':'); this.off = off + 17; } @@ -1792,7 +1704,7 @@ public final void writeName15Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1815,19 +1727,25 @@ public final void writeName16Raw(long name0, long name1) { if (startObject) { startObject = false; } else { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0); UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1); UNSAFE.putShort(bytes, ARRAY_BYTE_BASE_OFFSET + off + 16, useSingleQuote ? QUOTE_COLON : QUOTE2_COLON); this.off = off + 18; } + public final void writeRaw(byte b) { + int off = this.off; + putByte(grow1(off), off, b); + this.off = off + 1; + } + @Override public final void writeRaw(char ch) { if (ch > 128) { @@ -1837,7 +1755,7 @@ public final void writeRaw(char ch) { if (off == bytes.length) { ensureCapacity(off + 1); } - bytes[off++] = (byte) ch; + putByte(bytes, off++, (byte) ch); } @Override @@ -1851,8 +1769,8 @@ public final void writeRaw(char c0, char c1) { if (off + 2 > bytes.length) { bytes = grow(off + 2); } - bytes[off] = (byte) c0; - bytes[off + 1] = (byte) c1; + putByte(bytes, off, (byte) c0); + putByte(bytes, off + 1, (byte) c1); this.off = off + 2; } @@ -1866,7 +1784,7 @@ public final void writeNameRaw(byte[] name, int coff, int len) { } if (!startObject) { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); if (pretty != PRETTY_NON) { off = indent(bytes, off); } @@ -1887,6 +1805,14 @@ private byte[] grow(int minCapacity) { return bytes; } + private byte[] grow1(int off) { + byte[] bytes = this.bytes; + if (off == bytes.length) { + bytes = grow(off + 1); + } + return bytes; + } + private void grow0(int minCapacity) { // minCapacity is usually close to size, so this is a win: bytes = Arrays.copyOf(bytes, newCapacity(minCapacity, bytes.length)); @@ -1906,22 +1832,22 @@ public final void writeInt32(int[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < values.length; i++) { if (i != 0) { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt32(bytes, off, values[i]); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -1936,11 +1862,11 @@ public final void writeInt8(byte i) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt8(bytes, off, i); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } this.off = off; } @@ -1960,22 +1886,22 @@ public final void writeInt8(byte[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < values.length; i++) { if (i != 0) { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt8(bytes, off, values[i]); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -1990,11 +1916,11 @@ public final void writeInt16(short i) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt16(bytes, off, i); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } this.off = off; } @@ -2019,11 +1945,11 @@ public final void writeInt32(int i) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt32(bytes, off, i); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } this.off = off; } @@ -2043,11 +1969,11 @@ public final void writeListInt32(List values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < size; i++) { if (i != 0) { - bytes[off++] = (byte) ','; + putByte(bytes, off++, (byte) ','); } Number item = values.get(i); if (item == null) { @@ -2058,15 +1984,15 @@ public final void writeListInt32(List values) { int v = item.intValue(); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt32(bytes, off, v); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -2083,24 +2009,24 @@ public final void writeInt64(long[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < values.length; i++) { if (i != 0) { - bytes[off++] = (byte) ','; + putByte(bytes, off++, (byte) ','); } long v = values[i]; boolean writeAsString = isWriteAsString(v, context.features); if (writeAsString) { - bytes[off++] = (byte) this.quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt64(bytes, off, v); if (writeAsString) { - bytes[off++] = (byte) this.quote; + putByte(bytes, off++, (byte) quote); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -2118,11 +2044,11 @@ public final void writeListInt64(List values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < size; i++) { if (i != 0) { - bytes[off++] = (byte) ','; + putByte(bytes, off++, (byte) ','); } Long item = values.get(i); if (item == null) { @@ -2134,15 +2060,15 @@ public final void writeListInt64(List values) { long v = item; boolean writeAsString = isWriteAsString(v, context.features); if (writeAsString) { - bytes[off++] = (byte) this.quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt64(bytes, off, v); if (writeAsString) { - bytes[off++] = (byte) this.quote; + putByte(bytes, off++, (byte) quote); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -2157,16 +2083,16 @@ public final void writeInt64(long i) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } off = IOUtils.writeInt64(bytes, off, i); if (writeAsString) { - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); } else if ((features & WriteClassName.mask) != 0 && (features & NotWriteNumberClassName.mask) == 0 && i >= Integer.MIN_VALUE && i <= Integer.MAX_VALUE ) { - bytes[off++] = 'L'; + putByte(bytes, off++, (byte) 'L'); } this.off = off; } @@ -2192,14 +2118,14 @@ public final void writeFloat(float value) { } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } int len = DoubleToDecimal.toString(value, bytes, off, true); off += len; if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } this.off = off; } @@ -2215,13 +2141,13 @@ public final void writeDouble(double value) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } off += DoubleToDecimal.toString(value, bytes, off, true); if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } this.off = off; } @@ -2241,14 +2167,15 @@ public final void writeFloat(float[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = '['; + + putByte(bytes, off++, (byte) '['); for (int i = 0; i < values.length; i++) { if (i != 0) { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } float value = values[i]; @@ -2256,10 +2183,10 @@ public final void writeFloat(float[] values) { off += len; if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -2278,14 +2205,14 @@ public final void writeDouble(double[] values) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = '['; + putByte(bytes, off++, (byte) '['); for (int i = 0; i < values.length; i++) { if (i != 0) { - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } double value = values[i]; @@ -2293,10 +2220,10 @@ public final void writeDouble(double[] values) { off += len; if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } } - bytes[off] = ']'; + putByte(bytes, off, (byte) ']'); this.off = off + 1; } @@ -2315,7 +2242,7 @@ public final void writeDateTime14( if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); if (year < 0 || year > 9999) { throw illegalYear(year); } @@ -2328,7 +2255,7 @@ public final void writeDateTime14( writeDigitPair(bytes, off + 9, hour); writeDigitPair(bytes, off + 11, minute); writeDigitPair(bytes, off + 13, second); - bytes[off + 15] = (byte) quote; + putByte(bytes, off + 15, (byte) quote); this.off = off + 16; } @@ -2346,11 +2273,11 @@ public final void writeDateTime19( if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth); - bytes[off] = ' '; + putByte(bytes, off, (byte) ' '); IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second); - bytes[off + 9] = (byte) quote; + putByte(bytes, off + 9, (byte) quote); this.off = off + 10; } @@ -2372,9 +2299,9 @@ && writeLocalDateWithFormat(date)) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); off = IOUtils.writeLocalDate(bytes, off, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2386,12 +2313,12 @@ public final void writeLocalDateTime(LocalDateTime dateTime) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); LocalDate localDate = dateTime.toLocalDate(); off = IOUtils.writeLocalDate(bytes, off, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); - bytes[off++] = ' '; + putByte(bytes, off++, (byte) ' '); off = IOUtils.writeLocalTime(bytes, off, dateTime.toLocalTime()); - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2403,7 +2330,7 @@ public final void writeDateYYYMMDD8(int year, int month, int dayOfMonth) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); if (year < 0 || year > 9999) { throw illegalYear(year); } @@ -2413,7 +2340,7 @@ public final void writeDateYYYMMDD8(int year, int month, int dayOfMonth) { writeDigitPair(bytes, off + 3, y23); writeDigitPair(bytes, off + 5, month); writeDigitPair(bytes, off + 7, dayOfMonth); - bytes[off + 9] = (byte) quote; + putByte(bytes, off + 9, (byte) quote); this.off = off + 10; } @@ -2425,9 +2352,9 @@ public final void writeDateYYYMMDD10(int year, int month, int dayOfMonth) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); off = IOUtils.writeLocalDate(bytes, off, year, month, dayOfMonth); - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2439,9 +2366,9 @@ public final void writeTimeHHMMSS8(int hour, int minute, int second) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second); - bytes[off + 9] = (byte) quote; + putByte(bytes, off + 9, (byte) quote); this.off = off + 10; } @@ -2453,9 +2380,9 @@ public final void writeLocalTime(LocalTime time) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off++] = (byte) quote; + putByte(bytes, off++, (byte) quote); off = IOUtils.writeLocalTime(bytes, off, time); - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2486,23 +2413,23 @@ public final void writeZonedDateTime(ZonedDateTime dateTime) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); LocalDate localDate = dateTime.toLocalDate(); off = IOUtils.writeLocalDate(bytes, off + 1, localDate.getYear(), localDate.getMonthValue(), localDate.getDayOfMonth()); - bytes[off] = 'T'; + putByte(bytes, off, (byte) 'T'); off = IOUtils.writeLocalTime(bytes, off + 1, dateTime.toLocalTime()); if (zoneSize == 1) { - bytes[off++] = 'Z'; + putByte(bytes, off++, (byte) 'Z'); } else if (firstZoneChar == '+' || firstZoneChar == '-') { zoneId.getBytes(0, zoneIdLength, bytes, off); off += zoneIdLength; } else { - bytes[off++] = '['; + putByte(bytes, off++, (byte) '['); zoneId.getBytes(0, zoneIdLength, bytes, off); off += zoneIdLength; - bytes[off++] = ']'; + putByte(bytes, off++, (byte) ']'); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2519,22 +2446,22 @@ public final void writeOffsetDateTime(OffsetDateTime dateTime) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); LocalDateTime ldt = dateTime.toLocalDateTime(); LocalDate date = ldt.toLocalDate(); off = IOUtils.writeLocalDate(bytes, off + 1, date.getYear(), date.getMonthValue(), date.getDayOfMonth()); - bytes[off] = 'T'; + putByte(bytes, off, (byte) 'T'); off = IOUtils.writeLocalTime(bytes, off + 1, ldt.toLocalTime()); ZoneOffset offset = dateTime.getOffset(); if (offset.getTotalSeconds() == 0) { - bytes[off++] = 'Z'; + putByte(bytes, off++, (byte) 'Z'); } else { String zoneId = offset.getId(); zoneId.getBytes(0, zoneId.length(), bytes, off); off += zoneId.length(); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2550,18 +2477,18 @@ public final void writeOffsetTime(OffsetTime time) { if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); off = IOUtils.writeLocalTime(bytes, off + 1, time.toLocalTime()); ZoneOffset offset = time.getOffset(); if (offset.getTotalSeconds() == 0) { - bytes[off++] = 'Z'; + putByte(bytes, off++, (byte) 'Z'); } else { String zoneId = offset.getId(); zoneId.getBytes(0, zoneId.length(), bytes, off); off += zoneId.length(); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2590,12 +2517,12 @@ public final void writeBigInt(BigInteger value, long features) { bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } str.getBytes(0, strlen, bytes, off); off += strlen; if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } this.off = off; } @@ -2625,9 +2552,9 @@ public final void writeDateTimeISO8601( if (minCapacity > bytes.length) { bytes = grow(minCapacity); } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); off = IOUtils.writeLocalDate(bytes, off + 1, year, month, dayOfMonth); - bytes[off] = (byte) (timeZone ? 'T' : ' '); + putByte(bytes, off, (byte) (timeZone ? 'T' : ' ')); IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second); off += 9; @@ -2640,13 +2567,13 @@ public final void writeDateTimeISO8601( putIntLE(bytes, off, DIGITS_K_32[millis & 0x3ff] & 0xffffff00 | '.'); off += 4; } else { - bytes[off++] = '.'; + putByte(bytes, off++, (byte) '.'); final int rem2 = div - div2 * 10; if (rem2 != 0) { writeDigitPair(bytes, off, div); off += 2; } else { - bytes[off++] = (byte) (div2 + '0'); + putByte(bytes, off++, (byte) (div2 + '0')); } } } @@ -2654,12 +2581,12 @@ public final void writeDateTimeISO8601( if (timeZone) { int offset = offsetSeconds / 3600; if (offsetSeconds == 0) { - bytes[off++] = 'Z'; + putByte(bytes, off++, (byte) 'Z'); } else { int offsetAbs = Math.abs(offset); - bytes[off] = offset >= 0 ? (byte) '+' : (byte) '-'; + putByte(bytes, off, offset >= 0 ? (byte) '+' : (byte) '-'); writeDigitPair(bytes, off + 1, offsetAbs); - bytes[off + 3] = ':'; + putByte(bytes, off + 3, (byte) ':'); int offsetMinutes = (offsetSeconds - offset * 3600) / 60; if (offsetMinutes < 0) { offsetMinutes = -offsetMinutes; @@ -2668,7 +2595,7 @@ public final void writeDateTimeISO8601( off += 6; } } - bytes[off] = (byte) quote; + putByte(bytes, off, (byte) quote); this.off = off + 1; } @@ -2697,7 +2624,7 @@ public final void writeDecimal(BigDecimal value, long features, DecimalFormat fo bytes = grow(minCapacity); } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } boolean asPlain = (features & WriteBigDecimalAsPlain.mask) != 0; @@ -2717,7 +2644,7 @@ public final void writeDecimal(BigDecimal value, long features, DecimalFormat fo } if (writeAsString) { - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } this.off = off; } @@ -2753,7 +2680,7 @@ public final void write(Map map) { if (off == bytes.length) { grow(off + 1); } - bytes[off++] = '{'; + putByte(bytes, off++, (byte) '{'); boolean first = true; for (Map.Entry entry : map.entrySet()) { @@ -2766,7 +2693,7 @@ public final void write(Map map) { if (off == bytes.length) { ensureCapacity(off + 1); } - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } first = false; @@ -2780,7 +2707,7 @@ public final void write(Map map) { if (off == bytes.length) { ensureCapacity(off + 1); } - bytes[off++] = ':'; + putByte(bytes, off++, (byte) ':'); if (value == null) { writeNull(); @@ -2830,7 +2757,7 @@ public final void write(Map map) { if (off == bytes.length) { grow(off + 1); } - bytes[off++] = '}'; + putByte(bytes, off++, (byte) '}'); } @Override @@ -2854,7 +2781,7 @@ public final void write(List array) { if (off == bytes.length) { grow(off + 1); } - bytes[off++] = '['; + putByte(bytes, off++, (byte) '['); boolean first = true; for (int i = 0; i < array.size(); i++) { @@ -2863,7 +2790,7 @@ public final void write(List array) { if (off == bytes.length) { grow(off + 1); } - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } first = false; @@ -2914,7 +2841,7 @@ public final void write(List array) { if (off == bytes.length) { grow(off + 1); } - bytes[off++] = ']'; + putByte(bytes, off++, (byte) ']'); } public void writeBool(boolean value) { @@ -2925,7 +2852,7 @@ public void writeBool(boolean value) { } int off = this.off; if ((context.features & WriteBooleanAsNumber.mask) != 0) { - bytes[off++] = (byte) (value ? '1' : '0'); + putByte(bytes, off++, (byte) (value ? '1' : '0')); } else { off = IOUtils.putBoolean(bytes, off, value); } @@ -2996,16 +2923,16 @@ public final int flushTo(OutputStream out, Charset charset) throws IOException { } static void writeEscapedChar(byte[] bytes, int off, int c0) { - IOUtils.putShortLE(bytes, off, ESCAPED_CHARS[c0 & 0x7f]); + putShortLE(bytes, off, ESCAPED_CHARS[c0 & 0x7f]); } static void writeU4Hex2(byte[] bytes, int off, int c) { - IOUtils.putIntUnaligned(bytes, off, U4); - IOUtils.putShortLE(bytes, off + 4, hex2(c)); + putIntUnaligned(bytes, off, U4); + putShortLE(bytes, off + 4, hex2(c)); } static void writeU4HexU(byte[] bytes, int off, int c) { - IOUtils.putShortUnaligned(bytes, off, U2); - IOUtils.putIntLE(bytes, off + 2, hex4U(c)); + putShortUnaligned(bytes, off, U2); + putIntLE(bytes, off + 2, hex4U(c)); } } diff --git a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java index 83f7991478..18b0bf569d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java @@ -1726,6 +1726,13 @@ public static short hex2(int i) { + 0x30303030 + i); } + public static short hex2U(int i) { + i = ((i & 0xF0) >> 4) | ((i & 0xF) << 8); + int m = (i + 0x06060606) & 0x10101010; + return (short) (((m >> 1) - (m >> 4)) + + 0x30303030 + i); + } + public static int utf16Hex2(int i) { // 0x000F000F i = ((i & 0xF0) >> 4) | ((i & 0xF) << 16); @@ -1808,4 +1815,20 @@ static long convEndian(boolean big, long n) { static short convEndian(boolean big, short n) { return big == BIG_ENDIAN ? n : Short.reverseBytes(n); } + + public static boolean isASCII(char[] chars, int coff, int strlen) { + int i = coff; + for (int upperBound = coff + (strlen & ~3); i < upperBound; i += 4) { + if ((getLongLE(chars, i) & 0xFF00FF00FF00FF00L) != 0) { + return false; + } + } + + for (; i < strlen; ++i) { + if (chars[i] > 0x00FF) { + return false; + } + } + return true; + } } diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterJSONBTest.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterJSONBTest.java index 8212bc6f9a..4822ea0842 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterJSONBTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterJSONBTest.java @@ -406,7 +406,40 @@ public void writeChars1() { JSONWriter jsonWriter = JSONWriter.ofJSONB(); jsonWriter.writeString(chars, 256, 512); byte[] jsonbBytes = jsonWriter.getBytes(); - assertEquals(new String(chars), JSONB.parse(jsonbBytes)); + assertEquals( + new String(chars, 256, 512), + JSONB.parse(jsonbBytes)); + } + + @Test + public void writeChars2() { + char[] chars = new char[] {'A', 'A'}; + Arrays.fill(chars, 'A'); + for (int i = 128; i < 256; i++) { + chars[1] = (char) (i - 256); + JSONWriter jsonWriter = JSONWriter.ofJSONB(); + jsonWriter.writeString(chars, 1, 1); + byte[] jsonbBytes = jsonWriter.getBytes(); + assertEquals( + new String(chars, 1, 1), + JSONB.parse(jsonbBytes)); + } + } + + @Test + public void writeChars3() { + char[] chars = new char[128]; + for (int i = 128; i < 256; i++) { + Arrays.fill(chars, (char) (i - 256)); + chars[0] = 'A'; + int strlen = chars.length - 1; + JSONWriter jsonWriter = JSONWriter.ofJSONB(); + jsonWriter.writeString(chars, 1, strlen); + byte[] jsonbBytes = jsonWriter.getBytes(); + assertEquals( + new String(chars, 1, strlen), + JSONB.parse(jsonbBytes)); + } } @Test diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java index 4445bd4c5c..7657df6be2 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java @@ -1237,5 +1237,11 @@ public void writeU4() { IOUtils.putLongUnaligned(chars, 2, IOUtils.utf16Hex4U(1)); assertEquals("\\u0001", new String(chars)); + + chars = new char[] {'0', '1', '2', '3'}; + assertEquals(0, IOUtils.getLongLE(chars, 0) & 0xFF00FF00FF00FF00L); + + chars = new char[] {'中', '1', '2', '3'}; + assertNotEquals(0, IOUtils.getLongLE(chars, 0) & 0xFF00FF00FF00FF00L); } } diff --git a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java index 09a234b59f..9748e43b9d 100644 --- a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java @@ -390,4 +390,11 @@ public void convEndian() throws Throwable { assertEquals(i16, IOUtils.convEndian(false, i16)); assertEquals(Short.reverseBytes(i16), IOUtils.convEndian(true, i16)); } + + @Test + public void test_isASCII() { + char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', 0x80}; + assertTrue(IOUtils.isASCII(chars, 0, 4)); + assertTrue(IOUtils.isASCII(chars, 4, 4)); + } } From 18124b8973fd345a4005e1d0037f317e40cce9da Mon Sep 17 00:00:00 2001 From: wenshao Date: Sat, 18 Jan 2025 23:43:32 +0800 Subject: [PATCH 02/15] codestyle --- .../src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java index 56ddecc412..722933b2a6 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java @@ -871,7 +871,7 @@ protected final void writeStringEscaped(byte[] values) { putByte(bytes, off + 1, (byte) (0x80 | (c & 0x3f))); off += 2; } else { - putByte(bytes, off++, ch); + putByte(bytes, off++, ch); } break; } @@ -1100,7 +1100,7 @@ public final void writeString(char[] chars, int offset, int len, boolean quoted) default: if (ch == quote) { putByte(bytes, off, (byte) '\\'); - putByte(bytes, off+ 1, (byte) quote); + putByte(bytes, off + 1, (byte) quote); off += 2; } else { putByte(bytes, off++, (byte) ch); @@ -1260,7 +1260,7 @@ public final void writeChar(char ch) { off += 2; } - putByte(bytes, off, (byte) quote); + putByte(bytes, off, (byte) quote); this.off = off + 1; } From ee1f670797c77b1130cf2a75fed77b3dd094d77d Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 00:08:39 +0800 Subject: [PATCH 03/15] refactor --- .../eishay/EishayWriteStringTest.java | 2 +- .../com/alibaba/fastjson2/JSONFactory.java | 11 - .../com/alibaba/fastjson2/JSONWriter.java | 16 +- .../alibaba/fastjson2/JSONWriterUTF16.java | 131 +++++++----- .../fastjson2/JSONWriterUTF16JDK9UF.java | 49 +---- .../fastjson2/support/csv/CSVWriterUTF8.java | 18 +- .../fastjson2/JSONWriterUTF16Test.java | 78 +++++-- .../alibaba/fastjson2/util/IOUtilsTest.java | 4 +- .../fastjson2/JSONWriterUTF16Vector.java | 192 ------------------ .../fastjson2/JSONWriterUTF16VectorTest.java | 14 -- 10 files changed, 168 insertions(+), 347 deletions(-) delete mode 100644 incubator-vector/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16Vector.java delete mode 100644 incubator-vector/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16VectorTest.java diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayWriteStringTest.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayWriteStringTest.java index 544045be35..fc773515ef 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayWriteStringTest.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayWriteStringTest.java @@ -16,7 +16,7 @@ public static void fastjson2() { System.out.println("fastjson2 millis : " + millis); // zulu8.70.0.23 : 3001 2795 // zulu11.62.17 : 3288 2549 - // zulu17.32.13 : 3305 2909 2503 + // zulu17.32.13 : 3305 2909 2503 2353 // zulu17.40.91_vec : 2527 2536 } } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java index 38c158f557..cbec8b9c53 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java @@ -19,7 +19,6 @@ import java.time.ZoneId; import java.util.*; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import java.util.function.Function; import java.util.function.Supplier; import static com.alibaba.fastjson2.util.JDKUtils.VECTOR_BIT_LENGTH; @@ -93,7 +92,6 @@ public static String getProperty(String key) { static final NameCacheEntry[] NAME_CACHE = new NameCacheEntry[8192]; static final NameCacheEntry2[] NAME_CACHE2 = new NameCacheEntry2[8192]; - static final Function INCUBATOR_VECTOR_WRITER_CREATOR_UTF16; static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_ASCII; static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_UTF8; static final JSONReaderUTF16Creator INCUBATOR_VECTOR_READER_CREATOR_UTF16; @@ -230,19 +228,11 @@ public NameCacheEntry2(String name, long value0, long value1) { boolean readerVector = getPropertyBool(properties, "fastjson2.readerVector", false); - Function incubatorVectorCreatorUTF16 = null; JSONReaderUTF8Creator readerCreatorASCII = null; JSONReaderUTF8Creator readerCreatorUTF8 = null; JSONReaderUTF16Creator readerCreatorUTF16 = null; if (JDKUtils.VECTOR_SUPPORT) { if (VECTOR_BIT_LENGTH >= 64) { - try { - Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONWriterUTF16Vector$Factory"); - incubatorVectorCreatorUTF16 = (Function) factoryClass.newInstance(); - } catch (Throwable e) { - initErrorLast = e; - } - if (readerVector) { try { Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONReaderASCIIVector$Factory"); @@ -269,7 +259,6 @@ public NameCacheEntry2(String name, long value0, long value1) { } } } - INCUBATOR_VECTOR_WRITER_CREATOR_UTF16 = incubatorVectorCreatorUTF16; INCUBATOR_VECTOR_READER_CREATOR_ASCII = readerCreatorASCII; INCUBATOR_VECTOR_READER_CREATOR_UTF8 = readerCreatorUTF8; INCUBATOR_VECTOR_READER_CREATOR_UTF16 = readerCreatorUTF16; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java index b1f8f2a811..dbc293d195 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java @@ -548,9 +548,7 @@ public static JSONWriter of() { } else if ((defaultWriterFeatures & OptimizedForAscii.mask) != 0) { jsonWriter = ofUTF8(writeContext); } else { - if (INCUBATOR_VECTOR_WRITER_CREATOR_UTF16 != null) { - jsonWriter = INCUBATOR_VECTOR_WRITER_CREATOR_UTF16.apply(writeContext); - } else if (FIELD_STRING_VALUE != null && STRING_CODER != null && STRING_VALUE != null) { + if (FIELD_STRING_VALUE != null && STRING_CODER != null && STRING_VALUE != null) { jsonWriter = new JSONWriterUTF16JDK9UF(writeContext); } else { jsonWriter = new JSONWriterUTF16(writeContext); @@ -584,8 +582,8 @@ public static JSONWriter of(Context context) { jsonWriter = new JSONWriterUTF8(context); } } else { - if (INCUBATOR_VECTOR_WRITER_CREATOR_UTF16 != null) { - jsonWriter = INCUBATOR_VECTOR_WRITER_CREATOR_UTF16.apply(context); + if (FIELD_STRING_VALUE != null && STRING_CODER != null && STRING_VALUE != null) { + jsonWriter = new JSONWriterUTF16JDK9UF(context); } else { jsonWriter = new JSONWriterUTF16(context); } @@ -606,8 +604,8 @@ public static JSONWriter of(Feature... features) { } else if ((writeContext.features & OptimizedForAscii.mask) != 0) { jsonWriter = ofUTF8(writeContext); } else { - if (INCUBATOR_VECTOR_WRITER_CREATOR_UTF16 != null) { - jsonWriter = INCUBATOR_VECTOR_WRITER_CREATOR_UTF16.apply(writeContext); + if (FIELD_STRING_VALUE != null && STRING_CODER != null && STRING_VALUE != null) { + jsonWriter = new JSONWriterUTF16JDK9UF(writeContext); } else { jsonWriter = new JSONWriterUTF16(writeContext); } @@ -626,8 +624,8 @@ public static JSONWriter ofUTF16(Feature... features) { jsonWriter = new JSONWriterUTF16JDK8(writeContext); } } else { - if (INCUBATOR_VECTOR_WRITER_CREATOR_UTF16 != null) { - jsonWriter = INCUBATOR_VECTOR_WRITER_CREATOR_UTF16.apply(writeContext); + if (FIELD_STRING_VALUE != null && STRING_CODER != null && STRING_VALUE != null) { + jsonWriter = new JSONWriterUTF16JDK9UF(writeContext); } else { jsonWriter = new JSONWriterUTF16(writeContext); } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java index ac2bfd56d9..cb47e8f92e 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java @@ -365,21 +365,21 @@ public void writeStringUTF16(final byte[] value) { grow(minCapacity); } - final long vecQuote = this.useSingleQuote ? BYTE_VEC_64_SINGLE_QUOTE : BYTE_VEC_64_DOUBLE_QUOTE; + final long vecQuote = this.byteVectorQuote; final char[] chars = this.chars; chars[off++] = quote; - int i = 0, char_len = value.length >> 1; - - final int upperBound = (char_len - i) & ~3; - for (; i < upperBound; i += 4) { - long v = getLongLE(value, i << 1); - if (containsEscapedUTF16(v, vecQuote)) { - break; + for (int i = 0, char_len = value.length >> 1; i < char_len;) { + if (i + 8 < char_len) { + long v0 = getLongLE(value, i << 1); + long v1 = getLongLE(value, (i + 4) << 1); + if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && !containsEscaped((v0 << 8) | v1, vecQuote)) { + putLongLE(chars, off, v0); + putLongLE(chars, off + 4, v1); + i += 8; + off += 8; + continue; + } } - IOUtils.putLongLE(chars, off, v); - off += 4; - } - for (; i < char_len;) { char c = getChar(value, i++); if (c == '\\' || c == quote || c < ' ') { escape = true; @@ -398,24 +398,43 @@ public void writeStringUTF16(final byte[] value) { writeStringEscapeUTF16(value); } - static boolean containsEscapedUTF16(long v, long quote) { - /* - for (int i = 0; i < 8; ++i) { - byte c = (byte) data; - if (c == quote || c == '\\' || c < ' ') { - return true; + final void writeStringBrowserSecure(char[] value) { + boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0; + + boolean escape = false; + int off = this.off; + int minCapacity = off + value.length + 2; + if (minCapacity >= chars.length) { + grow(minCapacity); + } + + final char[] chars = this.chars; + chars[off++] = quote; + for (int i = 0, char_len = value.length; i < char_len; i++) { + char c = getChar(value, i); + if (c == '\\' + || c == quote + || c < ' ' + || c == '<' + || c == '>' + || c == '(' + || c == ')' + || (escapeNoneAscii && c > 0x007F) + ) { + escape = true; + break; } - data >>>= 8; - } - return false; - */ - long x22 = v ^ quote; // " -> 0x22, ' -> 0x27 - long x5c = v ^ 0x005C005C_005C005CL; - x22 = (x22 - 0x00010001_00010001L) & ~x22; - x5c = (x5c - 0x00010001_00010001L) & ~x5c; + chars[off++] = c; + } - return ((x22 | x5c | (0x007F007F_007F007FL - v + 0x00100010_00100010L) | v) & 0x00800080_00800080L) != 0; + if (!escape) { + chars[off] = quote; + this.off = off + 1; + return; + } + + writeStringEscape(value); } final void writeStringUTF16BrowserSecure(byte[] value) { @@ -2955,45 +2974,55 @@ private void writeQuote() { chars[off++] = quote; } - public final void writeString(final char[] str) { - if (str == null) { + public final void writeString(final char[] value) { + if (value == null) { writeStringNull(); return; } - boolean browserSecure = (context.features & BrowserSecure.mask) != 0; - boolean special = (context.features & EscapeNoneAscii.mask) != 0; - for (int i = 0; i < str.length; i++) { - char c = str[i]; + if ((context.features & (BrowserSecure.mask | EscapeNoneAscii.mask)) != 0) { + writeStringBrowserSecure(value); + return; + } + + boolean escape = false; + int off = this.off; + int minCapacity = off + value.length + 2; + if (minCapacity >= chars.length) { + grow(minCapacity); + } + + final long vecQuote = this.byteVectorQuote; + final char[] chars = this.chars; + chars[off++] = quote; + for (int i = 0, char_len = value.length; i < char_len;) { + if (i + 8 < char_len) { + long v0 = getLongLE(value, i); + long v1 = getLongLE(value, i + 4); + if (((v0 | v1) & 0xFF00FF00FF00FF00L) == 0 && !containsEscaped((v0 << 8) | v1, vecQuote)) { + putLongLE(chars, off, v0); + putLongLE(chars, off + 4, v1); + i += 8; + off += 8; + continue; + } + } + char c = getChar(value, i++); if (c == '\\' || c == quote || c < ' ') { - special = true; + escape = true; break; } - if (browserSecure && (c == '<' || c == '>' || c == '(' || c == ')')) { - special = true; - break; - } + chars[off++] = c; } - if (!special) { - // inline ensureCapacity - int off = this.off; - int minCapacity = off + str.length + 2; - char[] chars = this.chars; - if (minCapacity > chars.length) { - chars = grow(minCapacity); - } - - chars[off++] = quote; - System.arraycopy(str, 0, chars, off, str.length); - off += str.length; + if (!escape) { chars[off] = quote; this.off = off + 1; return; } - writeStringEscape(str); + writeStringEscape(value); } public final void writeString(char[] str, int coff, int len) { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK9UF.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK9UF.java index 52c4ebc8f6..f8abded833 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK9UF.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK9UF.java @@ -1,9 +1,7 @@ package com.alibaba.fastjson2; import com.alibaba.fastjson2.util.IOUtils; -import sun.misc.Unsafe; -import static com.alibaba.fastjson2.JSONWriter.Feature.BrowserSecure; import static com.alibaba.fastjson2.JSONWriter.Feature.WriteBooleanAsNumber; import static com.alibaba.fastjson2.util.JDKUtils.*; @@ -20,51 +18,12 @@ public void writeString(String str) { return; } - boolean escapeNoneAscii = (context.features & Feature.EscapeNoneAscii.mask) != 0; - boolean browserSecure = (context.features & BrowserSecure.mask) != 0; - boolean escape = false; - final char quote = this.quote; - - final int strlen = str.length(); - int minCapacity = off + strlen + 2; - if (minCapacity >= chars.length) { - ensureCapacity(minCapacity); - } - - final int coder = STRING_CODER.applyAsInt(str); final byte[] value = STRING_VALUE.apply(str); - - int off = this.off; - final char[] chars = this.chars; - chars[off++] = quote; - - for (int i = 0; i < strlen; ++i) { - int c; - if (coder == 0) { - c = value[i]; - } else { - c = UNSAFE.getChar(str, (long) Unsafe.ARRAY_CHAR_BASE_OFFSET + i * 2); - } - if (c == '\\' - || c == quote - || c < ' ' - || (browserSecure && (c == '<' || c == '>' || c == '(' || c == ')')) - || (escapeNoneAscii && c > 0x007F) - ) { - escape = true; - break; - } - - chars[off++] = (char) c; - } - - if (!escape) { - chars[off++] = quote; - this.off = off; - return; + if (STRING_CODER.applyAsInt(str) == 0) { + writeStringLatin1(value); + } else { + writeStringUTF16(value); } - - writeStringEscape(str); } public void writeBool(boolean value) { diff --git a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java index 7559fc016e..a4ee2854a1 100644 --- a/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/support/csv/CSVWriterUTF8.java @@ -12,6 +12,8 @@ import java.time.LocalDateTime; import java.time.ZoneId; +import static com.alibaba.fastjson2.util.IOUtils.putByte; + final class CSVWriterUTF8 extends CSVWriter { private static final short DOUBLE_QUOTE_2_LATIN1 = 0x22 | (0x22 << 8); @@ -42,17 +44,17 @@ void writeDirect(byte[] bytes, int off, int len) { public void writeComma() { checkCapacity(1); - bytes[off++] = ','; + putByte(bytes, off++, (byte) ','); } protected void writeQuote() { checkCapacity(1); - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); } public void writeLine() { checkCapacity(1); - bytes[off++] = '\n'; + putByte(bytes, off++, (byte) '\n'); } public void writeBoolean(boolean v) { @@ -83,7 +85,7 @@ public void writeDateTime19( final byte[] bytes = this.bytes; int off = this.off; off = IOUtils.writeLocalDate(bytes, off, year, month, dayOfMonth); - bytes[off] = ' '; + putByte(bytes, off, (byte) ' '); IOUtils.writeLocalTime(bytes, off + 1, hour, minute, second); this.off = off + 9; } @@ -170,20 +172,20 @@ public void writeString(byte[] utf8) { final int max = bytes.length - 2; int off = this.off; - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); for (byte ch : utf8) { if (ch == '"') { IOUtils.putShortUnaligned(bytes, off, DOUBLE_QUOTE_2_LATIN1); off += 2; } else { - bytes[off++] = ch; + putByte(bytes, off++, ch); } if (off >= max) { flush(); off = this.off; } } - bytes[off++] = '"'; + putByte(bytes, off++, (byte) '"'); this.off = off; } @@ -258,7 +260,7 @@ public void writeLocalDateTime(LocalDateTime ldt) { off = 0; } off = IOUtils.writeLocalDate(bytes, off, ldt.getYear(), ldt.getMonthValue(), ldt.getDayOfMonth()); - bytes[off++] = ' '; + putByte(bytes, off++, (byte) ' '); this.off = IOUtils.writeLocalTime(bytes, off, ldt.toLocalTime()); } diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java index 7657df6be2..55d8f0ef08 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16Test.java @@ -1109,6 +1109,26 @@ public void testEscaped_1() { assertEquals("\"" + str + "\"", jsonWriter.toString()); } + static boolean containsEscapedUTF16(long v, long quote) { + /* + for (int i = 0; i < 8; ++i) { + byte c = (byte) data; + if (c == quote || c == '\\' || c < ' ') { + return true; + } + data >>>= 8; + } + return false; + */ + long x22 = v ^ quote; // " -> 0x22, ' -> 0x27 + long x5c = v ^ 0x005C005C_005C005CL; + + x22 = (x22 - 0x00010001_00010001L) & ~x22; + x5c = (x5c - 0x00010001_00010001L) & ~x5c; + + return ((x22 | x5c | (0x007F007F_007F007FL - v + 0x00100010_00100010L) | v) & 0x00800080_00800080L) != 0; + } + @Test public void testEscaped_2() { String str = "abcd"; @@ -1117,7 +1137,7 @@ public void testEscaped_2() { long v = IOUtils.getLongLE(bytes, 0); assertFalse( - JSONWriterUTF16.containsEscapedUTF16( + containsEscapedUTF16( v, BYTE_VEC_64_DOUBLE_QUOTE)); @@ -1137,7 +1157,7 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } { @@ -1145,16 +1165,16 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); - assertTrue(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); + assertTrue(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } { chars[0] = '\''; long v = IOUtils.getLongLE( toBytes(chars), 0); - assertTrue(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertTrue(containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } } { @@ -1163,7 +1183,7 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } } { @@ -1172,7 +1192,7 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertTrue(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertTrue(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } } @@ -1183,14 +1203,14 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertTrue(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertTrue(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } for (char c : specials_single) { chars[i] = c; long v = IOUtils.getLongLE( toBytes(chars), 0); - assertTrue(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); + assertTrue(containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); } } { @@ -1198,16 +1218,16 @@ public void test_containsEscapedUTF16() { long v = IOUtils.getLongLE( toBytes(chars), 0); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } { chars = new char[]{'A', 'B', 'C', 'D'}; long v = IOUtils.getLongLE( toBytes(chars), 0); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); - assertFalse(JSONWriterUTF16.containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_SINGLE_QUOTE)); + assertFalse(containsEscapedUTF16(v, BYTE_VEC_64_DOUBLE_QUOTE)); } } @@ -1244,4 +1264,34 @@ public void writeU4() { chars = new char[] {'中', '1', '2', '3'}; assertNotEquals(0, IOUtils.getLongLE(chars, 0) & 0xFF00FF00FF00FF00L); } + + @Test + public void testUTF16() { + char[] chars = new char[32]; + for (int i = 0; i < chars.length; i++) { + { + Arrays.fill(chars, 'A'); + chars[chars.length - 1] = '中'; + String str = new String(chars); + String json = JSON.toJSONString(str); + assertEquals(str, JSON.parse(json)); + } + { + Arrays.fill(chars, 'A'); + chars[chars.length - 1] = '中'; + chars[i] = '\r'; + String str = new String(chars); + String json = JSON.toJSONString(str); + assertEquals(str, JSON.parse(json)); + } + + { + Arrays.fill(chars, '中'); + chars[i] = '\r'; + String str = new String(chars); + String json = JSON.toJSONString(str); + assertEquals(str, JSON.parse(json)); + } + } + } } diff --git a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java index 9748e43b9d..baa9c97b14 100644 --- a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java @@ -6,8 +6,7 @@ import java.nio.charset.StandardCharsets; import java.util.Random; -import static com.alibaba.fastjson2.util.JDKUtils.ARRAY_BYTE_BASE_OFFSET; -import static com.alibaba.fastjson2.util.JDKUtils.UNSAFE; +import static com.alibaba.fastjson2.util.JDKUtils.*; import static org.junit.jupiter.api.Assertions.*; public class IOUtilsTest { @@ -394,6 +393,7 @@ public void convEndian() throws Throwable { @Test public void test_isASCII() { char[] chars = new char[] {'0', '1', '2', '3', '4', '5', '6', 0x80}; + long v = UNSAFE.getLong(chars, ARRAY_CHAR_BASE_OFFSET); assertTrue(IOUtils.isASCII(chars, 0, 4)); assertTrue(IOUtils.isASCII(chars, 4, 4)); } diff --git a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16Vector.java b/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16Vector.java deleted file mode 100644 index 44b037f2ec..0000000000 --- a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16Vector.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.alibaba.fastjson2; - -import jdk.incubator.vector.ByteVector; -import jdk.incubator.vector.ShortVector; -import jdk.incubator.vector.Vector; -import sun.misc.Unsafe; - -import java.util.function.Function; - -import static com.alibaba.fastjson2.JSONWriter.Feature.BrowserSecure; -import static com.alibaba.fastjson2.util.JDKUtils.STRING_CODER; -import static com.alibaba.fastjson2.util.JDKUtils.STRING_VALUE; -import static com.alibaba.fastjson2.util.JDKUtils.UNSAFE; - -final class JSONWriterUTF16Vector - extends JSONWriterUTF16 { - static final Vector V_BYTE_64_SPACE = ByteVector.SPECIES_64.broadcast(' '); - static final Vector V_BYTE_64_ZERO = ByteVector.SPECIES_64.broadcast(0); - static final Vector V_BYTE_64_SLASH = ByteVector.SPECIES_64.broadcast('\\'); - static final Vector V_BYTE_64_DOUBLE_QUOTE = ByteVector.SPECIES_64.broadcast('"'); - static final Vector V_BYTE_64_SINGLE_QUOTE = ByteVector.SPECIES_64.broadcast('\''); - - static final Vector V_SHORT_128_SLASH = ShortVector.SPECIES_128.broadcast('\\'); - static final Vector V_SHORT_128_DOUBLE_QUOTE = ShortVector.SPECIES_128.broadcast('"'); - static final Vector V_SHORT_128_SINGLE_QUOTE = ShortVector.SPECIES_128.broadcast('\''); - static final Vector V_SHORT_128_SPACE = ShortVector.SPECIES_128.broadcast(' '); - static final Vector V_SHORT_128_LT = ShortVector.SPECIES_128.broadcast('<'); - static final Vector V_SHORT_128_GT = ShortVector.SPECIES_128.broadcast('>'); - static final Vector V_SHORT_128_LB = ShortVector.SPECIES_128.broadcast('('); - static final Vector V_SHORT_128_RB = ShortVector.SPECIES_128.broadcast(')'); - static final Vector V_SHORT_128_7F = ShortVector.SPECIES_128.broadcast(0x7F); - - JSONWriterUTF16Vector(Context ctx) { - super(ctx); - } - - @Override - public void writeString(String str) { - if (str == null) { - writeStringNull(); - return; - } - - int coder = STRING_CODER.applyAsInt(str); - byte[] value = STRING_VALUE.apply(str); - if (coder == 0) { - writeStringLatin1(value); - } else { - writeStringUTF16(value); - } - } - - @Override - public void writeStringLatin1(byte[] value) { - if ((context.features & BrowserSecure.mask) != 0) { - writeStringLatin1BrowserSecure(value); - return; - } - - boolean escape = false; - - int off = this.off; - final int start = off; - int minCapacity = off + value.length + 2; - if (minCapacity >= chars.length) { - ensureCapacity(minCapacity); - } - - char quote = this.quote; - final char[] chars = this.chars; - chars[off++] = quote; - - int i = 0; - final int upperBound = (value.length - i) & ~7; - for (; i < upperBound; i += 8) { - ByteVector v = (ByteVector) ByteVector.SPECIES_64.fromArray(value, i); - if (v.eq(V_BYTE_64_DOUBLE_QUOTE) - .or(v.eq(V_BYTE_64_SLASH)) - .or(v.lt(V_BYTE_64_SPACE)) - .anyTrue() - ) { - escape = true; - break; - } - - ShortVector sv = (ShortVector) v.castShape(ShortVector.SPECIES_128, 0); - sv.intoCharArray(chars, off); - off += 8; - } - - if (!escape) { - for (; i < value.length; i++) { - byte c = value[i]; - if (c == '\\' - || c == quote - || c < ' ') { - escape = true; - break; - } - chars[off++] = (char) c; - } - } - - if (!escape) { - chars[off] = quote; - this.off = off + 1; - return; - } - - this.off = start; - writeStringEscape(value); - } - - public void writeStringUTF16(final byte[] value) { - if (value == null) { - writeStringNull(); - return; - } - - boolean browserSecure = (context.features & BrowserSecure.mask) != 0; - boolean escapeNoneAscii = (context.features & Feature.EscapeNoneAscii.mask) != 0; - - boolean escape = false; - int off = this.off; - int minCapacity = off + value.length + 2; - if (minCapacity >= chars.length) { - ensureCapacity(minCapacity); - } - - final char[] chars = this.chars; - chars[off++] = quote; - - int i = 0; - final int upperBound = (value.length - i) & ~15; - for (; i < upperBound; i += 16) { - ShortVector v = (ShortVector) ByteVector.SPECIES_128.fromArray(value, i) - .castShape(ShortVector.SPECIES_128, 0); - - if (v.eq(V_SHORT_128_DOUBLE_QUOTE) - .or(v.eq(V_SHORT_128_SINGLE_QUOTE)) - .or(v.lt(V_SHORT_128_SPACE)) - .anyTrue() - || (browserSecure - && v.eq(V_SHORT_128_LT) - .or(v.eq(V_SHORT_128_GT)) - .or(v.eq(V_SHORT_128_LB)) - .or(v.eq(V_SHORT_128_RB)) - .anyTrue()) - || (escapeNoneAscii && V_SHORT_128_7F.lt(v).anyTrue()) - ) { - escape = true; - break; - } - - v.intoCharArray(chars, off); - off += 8; - } - - if (!escape) { - for (; i < value.length; i += 2) { - char c = UNSAFE.getChar(value, (long) Unsafe.ARRAY_BYTE_BASE_OFFSET + i); - if (c == '\\' - || c == quote - || c < ' ' - || (browserSecure && (c == '<' || c == '>' || c == '(' || c == ')')) - || (escapeNoneAscii && c > 0x007F) - ) { - escape = true; - break; - } - - chars[off++] = c; - } - } - - if (!escape) { - chars[off] = quote; - this.off = off + 1; - return; - } - - writeStringEscapeUTF16(value); - } - - public static class Factory - implements Function { - @Override - public Object apply(Object context) { - return new JSONWriterUTF16Vector((Context) context); - } - } -} diff --git a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16VectorTest.java b/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16VectorTest.java deleted file mode 100644 index 6018d0aecc..0000000000 --- a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONWriterUTF16VectorTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alibaba.fastjson2; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class JSONWriterUTF16VectorTest { - @Test - public void test() { - JSONWriter jsonWriter = (JSONWriter) new JSONWriterUTF16Vector.Factory().apply(JSONFactory.createWriteContext()); - jsonWriter.writeString("01234567890012345678900123456789001234567890中国"); - assertEquals("\"01234567890012345678900123456789001234567890中国\"", jsonWriter.toString()); - } -} From 9ad731c014935e8c7a5ca9beae0661807148eddd Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 01:29:24 +0800 Subject: [PATCH 04/15] remove JSONReaderASCIIVector --- .../com/alibaba/fastjson2/JSONFactory.java | 10 -- .../com/alibaba/fastjson2/JSONReader.java | 54 +----- .../fastjson2/JSONReaderASCIIVector.java | 163 ------------------ .../fastjson2/JSONReaderASCIIVectorTest.java | 58 ------- 4 files changed, 7 insertions(+), 278 deletions(-) delete mode 100644 incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderASCIIVector.java delete mode 100644 incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderASCIIVectorTest.java diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java index cbec8b9c53..1d28f45b08 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java @@ -92,7 +92,6 @@ public static String getProperty(String key) { static final NameCacheEntry[] NAME_CACHE = new NameCacheEntry[8192]; static final NameCacheEntry2[] NAME_CACHE2 = new NameCacheEntry2[8192]; - static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_ASCII; static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_UTF8; static final JSONReaderUTF16Creator INCUBATOR_VECTOR_READER_CREATOR_UTF16; @@ -228,19 +227,11 @@ public NameCacheEntry2(String name, long value0, long value1) { boolean readerVector = getPropertyBool(properties, "fastjson2.readerVector", false); - JSONReaderUTF8Creator readerCreatorASCII = null; JSONReaderUTF8Creator readerCreatorUTF8 = null; JSONReaderUTF16Creator readerCreatorUTF16 = null; if (JDKUtils.VECTOR_SUPPORT) { if (VECTOR_BIT_LENGTH >= 64) { if (readerVector) { - try { - Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONReaderASCIIVector$Factory"); - readerCreatorASCII = (JSONReaderUTF8Creator) factoryClass.newInstance(); - } catch (Throwable e) { - initErrorLast = e; - } - try { Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONReaderUTF8Vector$Factory"); readerCreatorUTF8 = (JSONReaderUTF8Creator) factoryClass.newInstance(); @@ -259,7 +250,6 @@ public NameCacheEntry2(String name, long value0, long value1) { } } } - INCUBATOR_VECTOR_READER_CREATOR_ASCII = readerCreatorASCII; INCUBATOR_VECTOR_READER_CREATOR_UTF8 = readerCreatorUTF8; INCUBATOR_VECTOR_READER_CREATOR_UTF16 = readerCreatorUTF16; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index 83e7e97625..941edc2fbb 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -3254,10 +3254,6 @@ public static JSONReader of(byte[] utf8Bytes) { Context context = createReadContext(); if (ascii) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } - return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3276,10 +3272,6 @@ public static JSONReader of(Context context, byte[] utf8Bytes) { } if (ascii) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } - return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3297,10 +3289,6 @@ public static JSONReader of(byte[] utf8Bytes, Context context) { } if (ascii) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } - return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3440,11 +3428,7 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse } if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, bytes, offset, length); - } else { - return new JSONReaderASCII(context, null, bytes, offset, length); - } + return new JSONReaderASCII(context, null, bytes, offset, length); } throw new JSONException("not support charset " + charset); @@ -3466,11 +3450,7 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse } if (!hasNegative) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, bytes, offset, length); - } else { - return new JSONReaderASCII(context, null, bytes, offset, length); - } + return new JSONReaderASCII(context, null, bytes, offset, length); } if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { @@ -3485,11 +3465,7 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse } if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, null, bytes, offset, length); - } else { - return new JSONReaderASCII(context, null, bytes, offset, length); - } + return new JSONReaderASCII(context, null, bytes, offset, length); } throw new JSONException("not support charset " + charset); @@ -3621,11 +3597,7 @@ public static JSONReader of(String str) { if (coder == LATIN1) { byte[] bytes = STRING_VALUE.apply(str); if (PREDICATE_IS_ASCII.test(bytes)) { - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, str, bytes, 0, bytes.length); - } else { - return new JSONReaderASCII(context, str, bytes, 0, bytes.length); - } + return new JSONReaderASCII(context, str, bytes, 0, bytes.length); } } } catch (Exception e) { @@ -3662,11 +3634,7 @@ public static JSONReader of(String str, Context context) { int coder = STRING_CODER.applyAsInt(str); if (coder == LATIN1) { byte[] bytes = STRING_VALUE.apply(str); - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, str, bytes, 0, bytes.length); - } else { - return new JSONReaderASCII(context, str, bytes, 0, bytes.length); - } + return new JSONReaderASCII(context, str, bytes, 0, bytes.length); } } catch (Exception e) { throw new JSONException("unsafe get String.coder error"); @@ -3705,11 +3673,7 @@ public static JSONReader of(String str, int offset, int length) { int coder = STRING_CODER.applyAsInt(str); if (coder == LATIN1) { byte[] bytes = STRING_VALUE.apply(str); - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, str, bytes, offset, length); - } else { - return new JSONReaderASCII(context, str, bytes, offset, length); - } + return new JSONReaderASCII(context, str, bytes, offset, length); } } catch (Exception e) { throw new JSONException("unsafe get String.coder error"); @@ -3746,11 +3710,7 @@ public static JSONReader of(String str, int offset, int length, Context context) int coder = STRING_CODER.applyAsInt(str); if (coder == LATIN1) { byte[] bytes = STRING_VALUE.apply(str); - if (INCUBATOR_VECTOR_READER_CREATOR_ASCII != null) { - return INCUBATOR_VECTOR_READER_CREATOR_ASCII.create(context, str, bytes, offset, length); - } else { - return new JSONReaderASCII(context, str, bytes, offset, length); - } + return new JSONReaderASCII(context, str, bytes, offset, length); } } catch (Exception e) { throw new JSONException("unsafe get String.coder error"); diff --git a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderASCIIVector.java b/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderASCIIVector.java deleted file mode 100644 index fe0d86f319..0000000000 --- a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderASCIIVector.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.alibaba.fastjson2; - -import com.alibaba.fastjson2.util.TypeUtils; -import jdk.incubator.vector.ByteVector; -import jdk.incubator.vector.Vector; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import static com.alibaba.fastjson2.JSONWriterUTF16Vector.*; -import static com.alibaba.fastjson2.util.JDKUtils.LATIN1; -import static com.alibaba.fastjson2.util.JDKUtils.STRING_CREATOR_JDK11; - -final class JSONReaderASCIIVector - extends JSONReaderASCII { - public JSONReaderASCIIVector(Context ctx, String str, byte[] bytes, int offset, int length) { - super(ctx, str, bytes, offset, length); - } - - @Override - public String readString() { - if (ch == '"' || ch == '\'') { - final byte quote = (byte) ch; - final byte slash = (byte) '\\'; - - final byte[] bytes = this.bytes; - int offset = this.offset; - final int start = offset, end = this.end; - int valueLength; - boolean valueEscape = false; - - _for: - { - int i = 0; - Vector v_quote = quote == '"' ? V_BYTE_64_DOUBLE_QUOTE : V_BYTE_64_SINGLE_QUOTE; - for (; offset + 8 < end; offset += 8, i += 8) { - ByteVector v = (ByteVector) ByteVector.SPECIES_64.fromArray(bytes, offset); - if (v.eq(V_BYTE_64_SLASH).or(v.eq(v_quote)).anyTrue()) { - break; - } - } - - for (; ; ++i) { - if (offset >= end) { - throw new JSONException("invalid escape character EOI"); - } - - byte c = bytes[offset]; - if (c == slash) { - valueEscape = true; - c = bytes[offset + 1]; - offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); - continue; - } - - if (c == quote) { - valueLength = i; - break _for; - } - offset++; - } - } - - String str; - if (valueEscape) { - char[] chars = new char[valueLength]; - offset = start; - for (int i = 0; ; ++i) { - int c = bytes[offset] & 0xff; - if (c == '\\') { - c = bytes[++offset] & 0xff; - switch (c) { - case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); - offset += 4; - break; - } - case 'x': { - c = char2(bytes[offset + 1], bytes[offset + 2]); - offset += 2; - break; - } - case '\\': - case '"': - break; - case 'b': - c = '\b'; - break; - case 't': - c = '\t'; - break; - case 'n': - c = '\n'; - break; - case 'f': - c = '\f'; - break; - case 'r': - c = '\r'; - break; - default: - c = char1(c); - break; - } - } else if (c == quote) { - break; - } - chars[i] = (char) c; - offset++; - } - - str = new String(chars); - } else { - int strlen = offset - start; - if (strlen == 1) { - str = TypeUtils.toString(bytes[start]); - } else if (strlen == 2) { - str = TypeUtils.toString( - bytes[start], - bytes[start + 1] - ); - } else if (this.str != null) { - str = this.str.substring(start, offset); - } else if (STRING_CREATOR_JDK11 != null) { - byte[] buf = Arrays.copyOfRange(bytes, start, offset); - str = STRING_CREATOR_JDK11.apply(buf, LATIN1); - } else { - str = new String(bytes, start, offset - start, StandardCharsets.ISO_8859_1); - } - } - - if ((context.features & Feature.TrimString.mask) != 0) { - str = str.trim(); - } - - int 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(); - } - - public static class Factory - implements JSONFactory.JSONReaderUTF8Creator { - @Override - public JSONReader create(Context ctx, String str, byte[] bytes, int offset, int length) { - return new JSONReaderASCIIVector(ctx, str, bytes, offset, length); - } - } -} diff --git a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderASCIIVectorTest.java b/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderASCIIVectorTest.java deleted file mode 100644 index 180ae8f4ad..0000000000 --- a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderASCIIVectorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.alibaba.fastjson2; - -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class JSONReaderASCIIVectorTest { - @Test - public void testJSON() { - for (char i = 0; i < 256; i++) { - String s1 = Character.toString(i); - String json = JSON.toJSONString(JSONObject.of(s1, s1)); - byte[] bytes = json.getBytes(StandardCharsets.ISO_8859_1); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderASCIIVector jsonReader = new JSONReaderASCIIVector(ctx, json, bytes, 0, bytes.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s1); - assertEquals(s1, v1, Integer.toString(i)); - } - } - - @Test - public void testJSON2() { - for (char i = 0; i < 256; i++) { - for (char j = 0; j < 256; j++) { - String s2 = new String(new char[]{i, j}); - String json = JSON.toJSONString(JSONObject.of(s2, s2)); - byte[] bytes = json.getBytes(StandardCharsets.ISO_8859_1); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderASCIIVector jsonReader = new JSONReaderASCIIVector(ctx, json, bytes, 0, bytes.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s2); - assertEquals(s2, v1, ((int) i) + ", " + (int) j); - } - } - } - - @Test - public void testJSON3() { - for (char i = 0; i < 256; i += 3) { - for (char j = 0; j < 256; j += 3) { - for (char k = 0; k < 256; k += 3) { - String s3 = new String(new char[]{i, j, k}); - String json = JSON.toJSONString(JSONObject.of(s3, s3)); - byte[] bytes = json.getBytes(StandardCharsets.ISO_8859_1); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderASCIIVector jsonReader = new JSONReaderASCIIVector(ctx, json, bytes, 0, bytes.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s3); - assertEquals(s3, v1, Arrays.toString(new int[]{i, j, k})); - } - } - } - } -} From df68b320fad648bbd84071e3a89eac46a5d41aa1 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 08:29:47 +0800 Subject: [PATCH 05/15] remove JSONReaderUTF16Vector --- .../eishay/EishayParseStringTest.java | 2 +- .../com/alibaba/fastjson2/JSONFactory.java | 12 -- .../com/alibaba/fastjson2/JSONReader.java | 86 +---------- .../alibaba/fastjson2/JSONReaderUTF16.java | 81 ++++------ .../com/alibaba/fastjson2/JSONReaderUTF8.java | 18 +++ .../fastjson2/JSONReaderUTF16Vector.java | 144 ------------------ .../fastjson2/JSONReaderUTF16VectorTest.java | 34 ----- 7 files changed, 49 insertions(+), 328 deletions(-) delete mode 100644 incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16Vector.java delete mode 100644 incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF16VectorTest.java diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseStringTest.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseStringTest.java index 269ee43fc4..309ad8cea6 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseStringTest.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseStringTest.java @@ -14,7 +14,7 @@ public static void fastjson2() { } long millis = System.currentTimeMillis() - start; System.out.println("fastjson2 millis : " + millis); - // zulu8.70.0.23 : 5569 5630 4435 + // zulu8.70.0.23 : 5569 5630 4435 4199 3851 // zulu11.64.19 : 5022 5033 3754 // zulu17.42.19 : 5377 5193 5222 5154 5116 5083 4987 4079 } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java index 1d28f45b08..89d9f2ecfa 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java @@ -93,7 +93,6 @@ public static String getProperty(String key) { static final NameCacheEntry2[] NAME_CACHE2 = new NameCacheEntry2[8192]; static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_UTF8; - static final JSONReaderUTF16Creator INCUBATOR_VECTOR_READER_CREATOR_UTF16; static int defaultDecimalMaxScale = 2048; @@ -228,7 +227,6 @@ public NameCacheEntry2(String name, long value0, long value1) { boolean readerVector = getPropertyBool(properties, "fastjson2.readerVector", false); JSONReaderUTF8Creator readerCreatorUTF8 = null; - JSONReaderUTF16Creator readerCreatorUTF16 = null; if (JDKUtils.VECTOR_SUPPORT) { if (VECTOR_BIT_LENGTH >= 64) { if (readerVector) { @@ -240,18 +238,8 @@ public NameCacheEntry2(String name, long value0, long value1) { } } } - - if (VECTOR_BIT_LENGTH >= 128 && readerVector) { - try { - Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONReaderUTF16Vector$Factory"); - readerCreatorUTF16 = (JSONReaderUTF16Creator) factoryClass.newInstance(); - } catch (Throwable e) { - initErrorLast = e; - } - } } INCUBATOR_VECTOR_READER_CREATOR_UTF8 = readerCreatorUTF8; - INCUBATOR_VECTOR_READER_CREATOR_UTF16 = readerCreatorUTF16; } private static boolean getPropertyBool(Properties properties, String name, boolean defaultValue) { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index 941edc2fbb..76787560f6 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -3300,19 +3300,8 @@ public static JSONReader of(byte[] utf8Bytes, Context context) { } public static JSONReader of(char[] chars) { - Context context = createReadContext(); - - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - null, - chars, - 0, - chars.length); - } - return new JSONReaderUTF16( - context, + createReadContext(), null, chars, 0, @@ -3321,15 +3310,6 @@ public static JSONReader of(char[] chars) { @Deprecated public static JSONReader of(Context context, char[] chars) { - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - null, - chars, - 0, - chars.length); - } - return new JSONReaderUTF16( context, null, @@ -3397,11 +3377,7 @@ public static JSONReader ofJSONB(byte[] bytes, int offset, int length) { } public static JSONReader ofJSONB(byte[] bytes, int offset, int length, Context context) { - return new JSONReaderJSONB( - context, - bytes, - offset, - length); + return new JSONReaderJSONB(context, bytes, offset, length); } public static JSONReader ofJSONB(byte[] bytes, int offset, int length, SymbolTable symbolTable) { @@ -3489,30 +3465,10 @@ public static JSONReader of(byte[] bytes, int offset, int length, Context contex } public static JSONReader of(char[] chars, int offset, int length) { - Context context = createReadContext(); - - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - null, - chars, - offset, - length); - } - - return new JSONReaderUTF16(context, null, chars, offset, length); + return new JSONReaderUTF16(createReadContext(), null, chars, offset, length); } public static JSONReader of(char[] chars, int offset, int length, Context context) { - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - null, - chars, - offset, - length); - } - return new JSONReaderUTF16(context, null, chars, offset, length); } @@ -3611,15 +3567,6 @@ public static JSONReader of(String str) { return new JSONReaderUTF16(context, str, chars, 0, length); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - str, - null, - 0, - length); - } - return new JSONReaderUTF16(context, str, 0, length); } @@ -3649,15 +3596,6 @@ public static JSONReader of(String str, Context context) { chars = str.toCharArray(); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - str, - chars, - 0, - length); - } - return new JSONReaderUTF16(context, str, chars, 0, length); } @@ -3687,15 +3625,6 @@ public static JSONReader of(String str, int offset, int length) { chars = str.toCharArray(); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - str, - chars, - offset, - length); - } - return new JSONReaderUTF16(context, str, chars, offset, length); } @@ -3724,15 +3653,6 @@ public static JSONReader of(String str, int offset, int length, Context context) chars = str.toCharArray(); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF16 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF16.create( - context, - str, - chars, - offset, - length); - } - return new JSONReaderUTF16(context, str, chars, offset, length); } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java index d96c6c8489..915ce080f5 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java @@ -3045,72 +3045,49 @@ protected final void readString0() { @Override public String readString() { + final char[] chars = this.chars; if (ch == '"' || ch == '\'') { - char[] chars = this.chars; final char quote = ch; + final long byteVectorQuote = quote == '\'' ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L; int offset = this.offset; - final int start = offset; + final int start = offset, end = this.end; int valueLength; boolean valueEscape = false; - _for: + int upperBound = offset + ((end - offset) & ~7); { int i = 0; - char c0 = 0, c1 = 0, c2 = 0, c3; - - // vector optimize - boolean quoted = false; - int upperBound = offset + ((end - offset) & ~3); while (offset < upperBound) { - c0 = chars[offset]; - c1 = chars[offset + 1]; - c2 = chars[offset + 2]; - c3 = chars[offset + 3]; - if (c0 == '\\' || c1 == '\\' || c2 == '\\' || c3 == '\\') { - break; - } - if (c0 == quote || c1 == quote || c2 == quote || c3 == quote) { - quoted = true; + long v0 = getLongLE(chars, offset); + long v1 = getLongLE(chars, offset + 4); + if (((v0 | v1) & 0xFF00FF00FF00FF00L) != 0 + || JSONReaderUTF8.containsSlashOrQuote((v0 << 8) | v1, byteVectorQuote) + ) { break; } - offset += 4; - i += 4; + + offset += 8; + i += 8; } - if (quoted) { - if (c0 == quote) { - // skip - } else if (c1 == quote) { - offset++; - i++; - } else if (c2 == quote) { - offset += 2; - i += 2; - } else { - offset += 3; - i += 3; + for (; ; ++i) { + if (offset >= end) { + throw new JSONException(info("invalid escape character EOI")); + } + char c = chars[offset]; + if (c == '\\') { + valueEscape = true; + c = chars[offset + 1]; + offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); + continue; } - valueLength = i; - } else { - for (; ; ++i) { - if (offset >= end) { - throw new JSONException(info("invalid escape character EOI")); - } - char c = chars[offset]; - if (c == '\\') { - valueEscape = true; - c = chars[offset + 1]; - offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); - continue; - } - if (c == quote) { - valueLength = i; - break _for; - } - offset++; + if (c == quote) { + valueLength = i; + break; } + offset++; } } @@ -3162,11 +3139,7 @@ public String readString() { offset++; } - if (STRING_CREATOR_JDK8 != null) { - str = STRING_CREATOR_JDK8.apply(buf, Boolean.TRUE); - } else { - str = new String(buf); - } + str = new String(buf); } else { char c0, c1; int strlen = offset - start; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java index 74aec9400f..d779e384dc 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java @@ -4796,6 +4796,24 @@ public final void skipComment() { } } + static boolean containsSlashOrQuote(long v, long quote) { + /* + for (int i = 0; i < 8; ++i) { + byte c = (byte) v; + if (c == '"' || c == '\\') { + return true; + } + v >>>= 8; + } + return false; + */ + long x22 = v ^ quote; // " -> 0x22 + long x5c = v ^ 0x5C5C5C5C5C5C5C5CL; // \n -> 0x0a + x22 = (x22 - 0x0101010101010101L) & ~x22; + x5c = (x5c - 0x0101010101010101L) & ~x5c; + return ((x22 | x5c) & 0x8080808080808080L) != 0; + } + @Override public String readString() { final byte[] bytes = this.bytes; diff --git a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16Vector.java b/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16Vector.java deleted file mode 100644 index f4dad0ade9..0000000000 --- a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16Vector.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.alibaba.fastjson2; - -import com.alibaba.fastjson2.util.TypeUtils; -import jdk.incubator.vector.ShortVector; -import jdk.incubator.vector.Vector; - -import static com.alibaba.fastjson2.JSONWriterUTF16Vector.*; - -final class JSONReaderUTF16Vector - extends JSONReaderUTF16 { - JSONReaderUTF16Vector(Context ctx, String str, char[] chars, int offset, int length) { - super(ctx, str, chars, offset, length); - } - - JSONReaderUTF16Vector(Context ctx, String str, int offset, int length) { - super(ctx, str, offset, length); - } - - @Override - public String readString() { - char[] chars = this.chars; - if (ch == '"' || ch == '\'') { - final char quote = ch; - - int offset = this.offset; - final int start = offset, end = this.end; - int valueLength; - boolean valueEscape = false; - - { - int i = 0; - final Vector v_quote = quote == '"' ? V_SHORT_128_DOUBLE_QUOTE : V_SHORT_128_SINGLE_QUOTE; - for (; offset + 8 < end; offset += 8, i += 8) { - ShortVector v = ShortVector.fromCharArray(ShortVector.SPECIES_128, chars, offset); - if (v.eq(V_SHORT_128_SLASH).or(v.eq(v_quote)).anyTrue()) { - break; - } - } - - for (; ; ++i) { - if (offset >= end) { - throw new JSONException(info("invalid escape character EOI")); - } - char c = chars[offset]; - if (c == '\\') { - valueEscape = true; - c = chars[offset + 1]; - offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); - continue; - } - - if (c == quote) { - valueLength = i; - break; - } - offset++; - } - } - - String str; - if (valueEscape) { - char[] buf = new char[valueLength]; - offset = start; - for (int i = 0; ; ++i) { - char c = chars[offset]; - if (c == '\\') { - c = chars[++offset]; - switch (c) { - case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); - offset += 4; - break; - } - case 'x': { - c = char2(chars[offset + 1], chars[offset + 2]); - offset += 2; - break; - } - case '\\': - case '"': - break; - default: - c = char1(c); - break; - } - } else if (c == quote) { - break; - } - buf[i] = c; - offset++; - } - - str = new String(buf); - } else { - char c0, c1; - int strlen = offset - start; - if (strlen == 1 && (c0 = chars[start]) < 128) { - str = TypeUtils.toString(c0); - } else if (strlen == 2 - && (c0 = chars[start]) < 128 - && (c1 = chars[start + 1]) < 128 - ) { - str = TypeUtils.toString(c0, c1); - } else { - str = this.str.substring(start, offset); - } - } - - if ((context.features & Feature.TrimString.mask) != 0) { - str = str.trim(); - } - - int ch = ++offset == end ? EOI : chars[offset++]; - while (ch <= ' ' && (1L << ch & SPACE) != 0) { - ch = offset == end ? EOI : chars[offset++]; - } - - if (comma = ch == ',') { - ch = offset == end ? EOI : chars[offset++]; - while (ch <= ' ' && (1L << ch & SPACE) != 0) { - ch = offset == end ? EOI : chars[offset++]; - } - } - - this.ch = (char) ch; - this.offset = offset; - return str; - } - - return readStringNotMatch(); - } - - public static class Factory - implements JSONFactory.JSONReaderUTF16Creator { - @Override - public JSONReader create(Context ctx, String str, char[] chars, int offset, int length) { - if (chars == null) { - return new JSONReaderUTF16Vector(ctx, str, offset, length); - } else { - return new JSONReaderUTF16Vector(ctx, str, chars, offset, length); - } - } - } -} diff --git a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF16VectorTest.java b/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF16VectorTest.java deleted file mode 100644 index 9df159e094..0000000000 --- a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF16VectorTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alibaba.fastjson2; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class JSONReaderUTF16VectorTest { - @Test - public void testJSON() { - for (char i = 0; i < 512; i++) { - String s1 = Character.toString(i); - String json = JSON.toJSONString(JSONObject.of(s1, s1)); - char[] chars = json.toCharArray(); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderUTF16Vector jsonReader = new JSONReaderUTF16Vector(ctx, json, chars, 0, chars.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s1); - assertEquals(s1, v1, Integer.toString(i)); - } - - for (char i = 0; i < 512; i++) { - for (char j = 0; j < 512; j++) { - String s2 = new String(new char[]{i, j}); - String json = JSON.toJSONString(JSONObject.of(s2, s2)); - char[] chars = json.toCharArray(); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderUTF16Vector jsonReader = new JSONReaderUTF16Vector(ctx, json, chars, 0, chars.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s2); - assertEquals(s2, v1); - } - } - } -} From f55355f570d34726a6125810b192cc72d80a59e0 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 09:00:58 +0800 Subject: [PATCH 06/15] remove JSONReaderUTF8Vector and remove incubator-vector module --- benchmark/pom.xml | 7 - .../eishay/EishayParseUTF8BytesTest.java | 4 +- .../com/alibaba/fastjson2/JSONFactory.java | 21 -- .../com/alibaba/fastjson2/JSONReader.java | 43 +--- .../alibaba/fastjson2/JSONReaderASCII.java | 2 +- .../com/alibaba/fastjson2/JSONReaderUTF8.java | 159 +++++++----- docs/vector_optimized.md | 16 +- incubator-vector/pom.xml | 123 --------- .../fastjson2/JSONReaderUTF8Vector.java | 233 ------------------ .../fastjson2/JSONReaderUTF8VectorTest.java | 47 ---- pom.xml | 1 - 11 files changed, 107 insertions(+), 549 deletions(-) delete mode 100644 incubator-vector/pom.xml delete mode 100644 incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8Vector.java delete mode 100644 incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF8VectorTest.java diff --git a/benchmark/pom.xml b/benchmark/pom.xml index a7118f184d..1b47a3e70c 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -32,13 +32,6 @@ fastjson2-extension ${project.version} - com.alibaba fastjson diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java index 62092da451..58de547ce3 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java @@ -63,8 +63,8 @@ public static void jackson() throws Exception { } public static void main(String[] args) throws Exception { -// fastjson2(); - fastjson2_features(); + fastjson2(); +// fastjson2_features(); // dsljson(); // jackson(); } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java index 89d9f2ecfa..dd192a3203 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java @@ -21,8 +21,6 @@ import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import java.util.function.Supplier; -import static com.alibaba.fastjson2.util.JDKUtils.VECTOR_BIT_LENGTH; - public final class JSONFactory { public static final class Conf { static final Properties DEFAULT_PROPERTIES; @@ -92,8 +90,6 @@ public static String getProperty(String key) { static final NameCacheEntry[] NAME_CACHE = new NameCacheEntry[8192]; static final NameCacheEntry2[] NAME_CACHE2 = new NameCacheEntry2[8192]; - static final JSONReaderUTF8Creator INCUBATOR_VECTOR_READER_CREATOR_UTF8; - static int defaultDecimalMaxScale = 2048; interface JSONReaderUTF8Creator { @@ -223,23 +219,6 @@ public NameCacheEntry2(String name, long value0, long value1) { useJacksonAnnotation = getPropertyBool(properties, "fastjson2.useJacksonAnnotation", true); useGsonAnnotation = getPropertyBool(properties, "fastjson2.useGsonAnnotation", true); defaultWriterAlphabetic = getPropertyBool(properties, "fastjson2.writer.alphabetic", true); - - boolean readerVector = getPropertyBool(properties, "fastjson2.readerVector", false); - - JSONReaderUTF8Creator readerCreatorUTF8 = null; - if (JDKUtils.VECTOR_SUPPORT) { - if (VECTOR_BIT_LENGTH >= 64) { - if (readerVector) { - try { - Class factoryClass = Class.forName("com.alibaba.fastjson2.JSONReaderUTF8Vector$Factory"); - readerCreatorUTF8 = (JSONReaderUTF8Creator) factoryClass.newInstance(); - } catch (Throwable e) { - initErrorLast = e; - } - } - } - } - INCUBATOR_VECTOR_READER_CREATOR_UTF8 = readerCreatorUTF8; } private static boolean getPropertyBool(Properties properties, String name, boolean defaultValue) { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index 76787560f6..0ad8627413 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -3257,11 +3257,7 @@ public static JSONReader of(byte[] utf8Bytes) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } else { - return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); - } + return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); } @Deprecated @@ -3275,11 +3271,7 @@ public static JSONReader of(Context context, byte[] utf8Bytes) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } else { - return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); - } + return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); } public static JSONReader of(byte[] utf8Bytes, Context context) { @@ -3292,11 +3284,7 @@ public static JSONReader of(byte[] utf8Bytes, Context context) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, utf8Bytes, 0, utf8Bytes.length); - } else { - return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); - } + return new JSONReaderUTF8(context, null, utf8Bytes, 0, utf8Bytes.length); } public static JSONReader of(char[] chars) { @@ -3392,11 +3380,7 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse Context context = JSONFactory.createReadContext(); if (charset == StandardCharsets.UTF_8) { - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, bytes, offset, length); - } else { - return new JSONReaderUTF8(context, null, bytes, offset, length); - } + return new JSONReaderUTF8(context, null, bytes, offset, length); } if (charset == StandardCharsets.UTF_16) { @@ -3429,11 +3413,7 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse return new JSONReaderASCII(context, null, bytes, offset, length); } - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, bytes, offset, length); - } else { - return new JSONReaderUTF8(context, null, bytes, offset, length); - } + return new JSONReaderUTF8(context, null, bytes, offset, length); } if (charset == StandardCharsets.UTF_16) { @@ -3448,20 +3428,11 @@ public static JSONReader of(byte[] bytes, int offset, int length, Charset charse } public static JSONReader of(byte[] bytes, int offset, int length) { - Context context = createReadContext(); - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, bytes, offset, length); - } else { - return new JSONReaderUTF8(context, null, bytes, offset, length); - } + return new JSONReaderUTF8(createReadContext(), null, bytes, offset, length); } public static JSONReader of(byte[] bytes, int offset, int length, Context context) { - if (INCUBATOR_VECTOR_READER_CREATOR_UTF8 != null) { - return INCUBATOR_VECTOR_READER_CREATOR_UTF8.create(context, null, bytes, offset, length); - } else { - return new JSONReaderUTF8(context, null, bytes, offset, length); - } + return new JSONReaderUTF8(context, null, bytes, offset, length); } public static JSONReader of(char[] chars, int offset, int length) { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java index ea423aaed2..1b09456c46 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java @@ -1425,7 +1425,7 @@ protected final void readString0() { } @Override - public String readString() { + public final String readString() { if (ch == '"' || ch == '\'') { final byte[] bytes = this.bytes; final byte quote = (byte) ch; diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java index d779e384dc..387a12dc32 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java @@ -7,6 +7,7 @@ import java.io.InputStream; import java.math.BigDecimal; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import java.time.*; import java.util.*; @@ -4816,56 +4817,72 @@ static boolean containsSlashOrQuote(long v, long quote) { @Override public String readString() { - final byte[] bytes = this.bytes; if (ch == '"' || ch == '\'') { + final byte[] bytes = this.bytes; char quote = this.ch; int valueLength; int offset = this.offset; final int start = offset, end = this.end; + final long byteVectorQuote = quote == '\'' ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L; boolean ascii = true; valueEscape = false; - for (int i = 0; ; ++i) { - if (offset >= end) { - throw new JSONException("invalid escape character EOI"); - } + { + int i = 0; + int upperBound = offset + ((end - offset) & ~7); + while (offset < upperBound) { + long v = getLongLE(bytes, offset); + if ((v & 0xFF00FF00FF00FF00L) != 0 || JSONReaderUTF8.containsSlashOrQuote(v, byteVectorQuote)) { + break; + } - int ch = bytes[offset]; - if (ch == '\\') { - valueEscape = true; - ch = bytes[offset + 1]; - offset += (ch == 'u' ? 6 : (ch == 'x' ? 4 : 2)); - continue; + offset += 8; + i += 8; } + // ... - if (ch >= 0) { - if (ch == quote) { - valueLength = i; - break; + for (; ; ++i) { + if (offset >= end) { + throw new JSONException("invalid escape character EOI"); } - offset++; - } else { - ascii = false; - switch ((ch & 0xFF) >> 4) { - case 12: - case 13: { - /* 110x xxxx 10xx xxxx*/ - offset += 2; - break; - } - case 14: { - offset += 3; + + int c = bytes[offset]; + if (c == '\\') { + valueEscape = true; + c = bytes[offset + 1]; + offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); + continue; + } + + if (c >= 0) { + if (c == quote) { + valueLength = i; break; } - default: { - /* 10xx xxxx, 1111 xxxx */ - if ((ch >> 3) == -2) { - offset += 4; - i++; + offset++; + } else { + ascii = false; + switch ((c & 0xFF) >> 4) { + case 12: + case 13: { + /* 110x xxxx 10xx xxxx*/ + offset += 2; break; } + case 14: { + offset += 3; + break; + } + default: { + /* 10xx xxxx, 1111 xxxx */ + if ((c >> 3) == -2) { + offset += 4; + i++; + break; + } - throw new JSONException("malformed input around byte " + offset); + throw new JSONException("malformed input around byte " + offset); + } } } } @@ -4881,15 +4898,12 @@ public String readString() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = DIGITS2[bytes[offset + 1]] * 0x1000 - + DIGITS2[bytes[offset + 2]] * 0x100 - + DIGITS2[bytes[offset + 3]] * 0x10 - + DIGITS2[bytes[offset + 4]]; + ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); offset += 4; break; } case 'x': { - ch = DIGITS2[bytes[offset + 1]] * 0x10 + DIGITS2[bytes[offset + 2]]; + ch = char2(bytes[offset + 1], bytes[offset + 2]); offset += 2; break; } @@ -4925,24 +4939,23 @@ public String readString() { offset++; } else { switch ((ch & 0xFF) >> 4) { - case 0b1100: - case 0b1101: { + case 12: + case 13: { /* 110x xxxx 10xx xxxx*/ - int c2 = bytes[offset + 1]; - chars[i] = (char) ( - ((ch & 0x1F) << 6) | (c2 & 0x3F)); + chars[i] = (char) (((ch & 0x1F) << 6) | (bytes[offset + 1] & 0x3F)); offset += 2; break; } - case 0b1110: { + case 14: { chars[i] = (char) (((ch & 0x0F) << 12) | ((bytes[offset + 1] & 0x3F) << 6) | - ((bytes[offset + 2] & 0x3F))); + ((bytes[offset + 2] & 0x3F) << 0)); offset += 3; break; } default: { + /* 10xx xxxx, 1111 xxxx */ char2_utf8(bytes, offset, ch, chars, i); offset += 4; i++; @@ -4962,32 +4975,20 @@ public String readString() { (char) (bytes[start] & 0xff), (char) (bytes[start + 1] & 0xff) ); - } else if (STRING_CREATOR_JDK8 != null) { - char[] chars = new char[strlen]; - for (int i = 0; i < strlen; ++i) { - chars[i] = (char) bytes[start + i]; - } - - str = STRING_CREATOR_JDK8.apply(chars, Boolean.TRUE); } else if (STRING_CREATOR_JDK11 != null) { - byte[] buf = Arrays.copyOfRange(bytes, start, offset); - str = STRING_CREATOR_JDK11.apply(buf, LATIN1); - } else if (ANDROID) { - str = getLatin1String(start, offset - start); + str = STRING_CREATOR_JDK11.apply( + Arrays.copyOfRange(this.bytes, this.offset, offset), + LATIN1); } else { - str = new String(bytes, start, offset - start, ISO_8859_1); + str = new String(bytes, start, offset - start, StandardCharsets.US_ASCII); } } else { - str = new String(bytes, start, offset - start, UTF_8); + str = new String(bytes, start, offset - start, StandardCharsets.UTF_8); } if ((context.features & Feature.TrimString.mask) != 0) { str = str.trim(); } - // empty string to null - if (str.isEmpty() && (context.features & Feature.EmptyStringAsNull.mask) != 0) { - str = null; - } int ch = ++offset == end ? EOI : bytes[offset++]; while (ch <= ' ' && (1L << ch & SPACE) != 0) { @@ -5006,7 +5007,39 @@ public String readString() { return str; } - return readStringNotMatch(); + switch (ch) { + case '[': + return toString( + readArray()); + case '{': + return toString( + readObject()); + case '-': + case '+': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + readNumber0(); + Number number = getNumber(); + return number.toString(); + case 't': + case 'f': + boolValue = readBoolValue(); + return boolValue ? "true" : "false"; + case 'n': { + readNull(); + return null; + } + default: + throw new JSONException("TODO : " + ch); + } } @Override diff --git a/docs/vector_optimized.md b/docs/vector_optimized.md index d73ace82ca..4faae11c26 100644 --- a/docs/vector_optimized.md +++ b/docs/vector_optimized.md @@ -1,17 +1,3 @@ JDK 17中提供了[vector api](https://openjdk.org/jeps/426),可以用SIMD来优化性能。 -fastjson 2.0.51中已经支持vector api,这个优化目前处于incubator状态,需要通过如下方法打开: - -加上依赖 -```xml - - com.alibaba.fastjson2 - fastjson2-incubator-vector - 2.0.54 - -``` - -JVM启动参数加上 -```shell ---add-modules=jdk.incubator.vector -``` +fastjson 2.0.55版本开始全面使用SWAR(SIMD within a register)来做SIMD优化,不再需要使用vector api. diff --git a/incubator-vector/pom.xml b/incubator-vector/pom.xml deleted file mode 100644 index 3babd1e519..0000000000 --- a/incubator-vector/pom.xml +++ /dev/null @@ -1,123 +0,0 @@ - - - 4.0.0 - - com.alibaba.fastjson2 - fastjson2-parent - 2.0.55-SNAPSHOT - ../pom.xml - - - fastjson2-incubator-vector - fastjson2-incubator-vector - vector implementation - jar - https://github.com/alibaba/fastjson2 - 2022 - - - - Apache 2 - https://www.apache.org/licenses/LICENSE-2.0.txt - repo - A business-friendly OSS license - - - - https://github.com/alibaba/fastjson2 - scm:git:https://git@github.com/alibaba/fastjson2.git - - - 11 - 11 - --add-modules=jdk.incubator.vector - - - - Alibaba Group - https://github.com/alibaba - - - - wenshao - wenshao - shaojin.wensj(at)alibaba-inc.com - - Developer - Tech Leader - - +8 - https://github.com/wenshao - - - - - - com.alibaba.fastjson2 - fastjson2 - ${project.version} - - - - - commons-io - commons-io - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - ${add.modules.option} - - - - - maven-surefire-plugin - - - com/alibaba/fastjson2/**/*.java - - - Asia/Shanghai - - ${add.modules.option} - - - - - - - - gen-javadoc - - - performRelease - true - - - - - - maven-javadoc-plugin - - package - - ${add.modules.option} - - - - - - - - diff --git a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8Vector.java b/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8Vector.java deleted file mode 100644 index 97c243436c..0000000000 --- a/incubator-vector/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8Vector.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.alibaba.fastjson2; - -import com.alibaba.fastjson2.util.TypeUtils; -import jdk.incubator.vector.ByteVector; -import jdk.incubator.vector.Vector; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -import static com.alibaba.fastjson2.JSONWriterUTF16Vector.*; -import static com.alibaba.fastjson2.util.JDKUtils.*; - -final class JSONReaderUTF8Vector - extends JSONReaderUTF8 { - JSONReaderUTF8Vector(Context ctx, String str, byte[] bytes, int offset, int length) { - super(ctx, str, bytes, offset, length); - } - - @Override - public String readString() { - if (ch == '"' || ch == '\'') { - final byte[] bytes = this.bytes; - char quote = this.ch; - int valueLength; - int offset = this.offset; - final int start = offset, end = this.end; - boolean ascii = true; - valueEscape = false; - - { - int i = 0; - Vector v_quote = quote == '"' ? V_BYTE_64_DOUBLE_QUOTE : V_BYTE_64_SINGLE_QUOTE; - for (; offset + 8 < end; offset += 8, i += 8) { - ByteVector v = (ByteVector) ByteVector.SPECIES_64.fromArray(bytes, offset); - if (v.eq(V_BYTE_64_SLASH).or(v.eq(v_quote).or(v.lt(V_BYTE_64_ZERO))).anyTrue()) { - break; - } - } - - for (; ; ++i) { - if (offset >= end) { - throw new JSONException("invalid escape character EOI"); - } - - int c = bytes[offset]; - if (c == '\\') { - valueEscape = true; - c = bytes[offset + 1]; - offset += (c == 'u' ? 6 : (c == 'x' ? 4 : 2)); - continue; - } - - if (c >= 0) { - if (c == quote) { - valueLength = i; - break; - } - offset++; - } else { - ascii = false; - switch ((c & 0xFF) >> 4) { - case 12: - case 13: { - /* 110x xxxx 10xx xxxx*/ - offset += 2; - break; - } - case 14: { - offset += 3; - break; - } - default: { - /* 10xx xxxx, 1111 xxxx */ - if ((c >> 3) == -2) { - offset += 4; - i++; - break; - } - - throw new JSONException("malformed input around byte " + offset); - } - } - } - } - } - - String str; - if (valueEscape) { - char[] chars = new char[valueLength]; - offset = start; - for (int i = 0; ; ++i) { - int ch = bytes[offset]; - if (ch == '\\') { - ch = bytes[++offset]; - switch (ch) { - case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); - offset += 4; - break; - } - case 'x': { - ch = char2(bytes[offset + 1], bytes[offset + 2]); - offset += 2; - break; - } - case '\\': - case '"': - break; - default: - ch = char1(ch); - break; - } - chars[i] = (char) ch; - offset++; - } else if (ch == '"') { - break; - } else { - if (ch >= 0) { - chars[i] = (char) ch; - offset++; - } else { - switch ((ch & 0xFF) >> 4) { - case 12: - case 13: { - /* 110x xxxx 10xx xxxx*/ - chars[i] = (char) (((ch & 0x1F) << 6) | (bytes[offset + 1] & 0x3F)); - offset += 2; - break; - } - case 14: { - chars[i] = (char) - (((ch & 0x0F) << 12) | - ((bytes[offset + 1] & 0x3F) << 6) | - ((bytes[offset + 2] & 0x3F) << 0)); - offset += 3; - break; - } - default: { - /* 10xx xxxx, 1111 xxxx */ - char2_utf8(bytes, offset, ch, chars, i); - offset += 4; - i++; - } - } - } - } - } - - str = new String(chars); - } else if (ascii) { - int strlen = offset - start; - if (strlen == 1) { - str = TypeUtils.toString((char) (bytes[start] & 0xff)); - } else if (strlen == 2) { - str = TypeUtils.toString( - (char) (bytes[start] & 0xff), - (char) (bytes[start + 1] & 0xff) - ); - } else if (STRING_CREATOR_JDK11 != null) { - str = STRING_CREATOR_JDK11.apply( - Arrays.copyOfRange(this.bytes, this.offset, offset), - LATIN1); - } else { - str = new String(bytes, start, offset - start, StandardCharsets.US_ASCII); - } - } else { - str = new String(bytes, start, offset - start, StandardCharsets.UTF_8); - } - - if ((context.features & Feature.TrimString.mask) != 0) { - str = str.trim(); - } - - int 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; - this.offset = offset; - return str; - } - - switch (ch) { - case '[': - return toString( - readArray()); - case '{': - return toString( - readObject()); - case '-': - case '+': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - readNumber0(); - Number number = getNumber(); - return number.toString(); - case 't': - case 'f': - boolValue = readBoolValue(); - return boolValue ? "true" : "false"; - case 'n': { - readNull(); - return null; - } - default: - throw new JSONException("TODO : " + ch); - } - } - - public static class Factory - implements JSONFactory.JSONReaderUTF8Creator { - @Override - public JSONReader create(Context ctx, String str, byte[] bytes, int offset, int length) { - return new JSONReaderUTF8Vector(ctx, str, bytes, offset, length); - } - } -} diff --git a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF8VectorTest.java b/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF8VectorTest.java deleted file mode 100644 index 7c553ef289..0000000000 --- a/incubator-vector/src/test/java/com/alibaba/fastjson2/JSONReaderUTF8VectorTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.alibaba.fastjson2; - -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class JSONReaderUTF8VectorTest { - @Test - public void test() { - JSONReader.Context ctx = JSONFactory.createReadContext(); - String str = "abcdef1234567890中国©®£\uD83D\uDE0D\uD83D\uDC81\uD83D\uDC4C\uD83C\uDF8D\uD83D\uDE0D"; - String json = JSON.toJSONString(str); - byte[] bytes = json.getBytes(StandardCharsets.UTF_8); - JSONReaderUTF8Vector jsonReader = new JSONReaderUTF8Vector(ctx, json, bytes, 0, bytes.length); - String parsed = jsonReader.readString(); - assertEquals(str, parsed); - } - - @Test - public void testJSON() { - for (char i = 0; i < 512; i++) { - String s1 = Character.toString(i); - String json = JSON.toJSONString(JSONObject.of(s1, s1)); - byte[] bytes = json.getBytes(StandardCharsets.UTF_8); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderUTF8Vector jsonReader = new JSONReaderUTF8Vector(ctx, s1, bytes, 0, bytes.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s1); - assertEquals(s1, v1); - } - - for (char i = 0; i < 512; i++) { - for (char j = 0; j < 512; j++) { - String s2 = new String(new char[]{i, j}); - String json = JSON.toJSONString(JSONObject.of(s2, s2)); - byte[] bytes = json.getBytes(StandardCharsets.UTF_8); - JSONReader.Context ctx = JSONFactory.createReadContext(); - JSONReaderUTF8Vector jsonReader = new JSONReaderUTF8Vector(ctx, s2, bytes, 0, bytes.length); - JSONObject object = (JSONObject) jsonReader.readObject(); - Object v1 = object.get(s2); - assertEquals(s2, v1); - } - } - } -} diff --git a/pom.xml b/pom.xml index 8151d47811..a155208165 100644 --- a/pom.xml +++ b/pom.xml @@ -1030,7 +1030,6 @@ example-graalvm-native extension-spring6 example-spring6-test - incubator-vector test-jdk17 From fd8c667ece9df8bc70de1dcacccfe18890e5292b Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 09:11:42 +0800 Subject: [PATCH 07/15] optimize JSONWriterUTF16JDK8UF --- .../fastjson2/JSONWriterUTF16JDK8UF.java | 44 ++----------------- 1 file changed, 4 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK8UF.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK8UF.java index 410ca8e964..1a1c8bbecf 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK8UF.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16JDK8UF.java @@ -13,45 +13,9 @@ public final class JSONWriterUTF16JDK8UF @Override public void writeString(String str) { - if (str == null) { - writeStringNull(); - return; - } - - boolean browserSecure = (context.features & BrowserSecure.mask) != 0; - boolean escapeNoneAscii = (context.features & EscapeNoneAscii.mask) != 0; - char[] value = (char[]) UNSAFE.getObject(str, JDKUtils.FIELD_STRING_VALUE_OFFSET); - final int strlen = value.length; - - boolean escape = false; - for (int i = 0; i < value.length; i++) { - char ch = value[i]; - if (ch == quote || ch == '\\' || ch < ' ' - || (browserSecure && (ch == '<' || ch == '>' || ch == '(' || ch == ')')) - || (escapeNoneAscii && ch > 0x007F) - ) { - escape = true; - break; - } - } - - if (!escape) { - int off = this.off; - // inline ensureCapacity(off + strlen + 2); - int minCapacity = off + strlen + 2; - if (minCapacity >= chars.length) { - ensureCapacity(minCapacity); - } - - final char[] chars = this.chars; - chars[off++] = quote; - System.arraycopy(value, 0, chars, off, value.length); - off += strlen; - chars[off] = quote; - this.off = off + 1; - return; - } - - writeStringEscape(str); + writeString( + str == null + ? null + : (char[]) UNSAFE.getObject(str, JDKUtils.FIELD_STRING_VALUE_OFFSET)); } } From e757777f7a81f2336980375d06493555ab992c75 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 09:13:16 +0800 Subject: [PATCH 08/15] fix build error --- benchmark/pom.xml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/benchmark/pom.xml b/benchmark/pom.xml index 1b47a3e70c..91edfe022f 100644 --- a/benchmark/pom.xml +++ b/benchmark/pom.xml @@ -210,20 +210,4 @@ - - - - enable-incubators-for-jdk17+ - - [17,) - - - - com.alibaba.fastjson2 - fastjson2-incubator-vector - ${project.version} - - - - From 4b9181c28dd517246d040ca4a8ff8c7464dd9514 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 10:04:58 +0800 Subject: [PATCH 09/15] bug fix --- .../java/com/alibaba/fastjson2/JSONWriterUTF8.java | 2 +- .../com/alibaba/fastjson2/JSONWriterUTF8Test.java | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java index 722933b2a6..acf11b3e7f 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java @@ -515,7 +515,7 @@ static boolean containsEscaped(long v, long quote) { x22 = (x22 - 0x0101010101010101L) & ~x22; x5c = (x5c - 0x0101010101010101L) & ~x5c; - return ((x22 | x5c | (0x7F7F7F7F7F7F7F7FL - v + 0x1010101010101010L) | v) & 0x8080808080808080L) != 0; + return ((x22 | x5c | (0x7F7F7F7F7F7F7F7FL - v + 0x2020202020202020L) | v) & 0x8080808080808080L) != 0; } protected final void writeStringLatin1BrowserSecure(byte[] values) { diff --git a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java index 82df2fe188..c31a61e9c3 100644 --- a/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java +++ b/core/src/test/java/com/alibaba/fastjson2/JSONWriterUTF8Test.java @@ -15,8 +15,7 @@ import static com.alibaba.fastjson2.JSONWriter.Feature.*; import static com.alibaba.fastjson2.util.JDKUtils.ARRAY_CHAR_BASE_OFFSET; import static com.alibaba.fastjson2.util.JDKUtils.UNSAFE; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.*; public class JSONWriterUTF8Test { @Test @@ -1131,4 +1130,15 @@ public void writeU4() { IOUtils.putIntUnaligned(bytes, 2, IOUtils.hex4U(1)); assertEquals("\\u0001", new String(bytes)); } + + @Test + public void testSpecial1() { + byte[] buf = new byte[] {'a', 'a', 'a', 'a', 'a', 'a', 'a', 31}; + assertEquals("\"aaaaaaa\\u001f\"", new String(JSON.toJSONBytes(new String(buf)))); + + for (int i = 0; i < 32; i++) { + buf[7] = (byte) i; + assertTrue(JSONWriterUTF8.containsEscaped(IOUtils.getLongUnaligned(buf, 0), 0x2222_2222_2222_2222L)); + } + } } From a3cbcbb9d5f58c2085ac53c032472462d2243464 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 10:41:01 +0800 Subject: [PATCH 10/15] refactor --- .../com/alibaba/fastjson2/JSONFactory.java | 2 - .../com/alibaba/fastjson2/JSONReader.java | 7 ---- .../alibaba/fastjson2/JSONReaderASCII.java | 32 ++++++++------- .../alibaba/fastjson2/JSONReaderUTF16.java | 25 +++++------ .../com/alibaba/fastjson2/JSONReaderUTF8.java | 41 +++++++++---------- .../com/alibaba/fastjson2/util/IOUtils.java | 14 ++++--- .../alibaba/fastjson2/util/IOUtilsTest.java | 7 +++- 7 files changed, 63 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java index dd192a3203..e13d3537eb 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONFactory.java @@ -156,8 +156,6 @@ public NameCacheEntry2(String name, long value0, long value1) { 1.0e20, 1.0e21, 1.0e22 }; - static final Double DOUBLE_ZERO = (double) 0; - static { Properties properties = Conf.DEFAULT_PROPERTIES; { diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index 0ad8627413..78ddc0f7e8 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -499,13 +499,6 @@ static char char2(int c1, int c2) { + DIGITS2[c2]); } - static char char4(int c1, int c2, int c3, int c4) { - return (char) (DIGITS2[c1] * 0x1000 - + DIGITS2[c2] * 0x100 - + DIGITS2[c3] * 0x10 - + DIGITS2[c4]); - } - public abstract boolean nextIfObjectStart(); public abstract boolean nextIfNullOrEmptyString(); diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java index 1b09456c46..d7d70d08f7 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java @@ -10,6 +10,8 @@ import java.util.Arrays; import static com.alibaba.fastjson2.JSONFactory.*; +import static com.alibaba.fastjson2.JSONReaderJSONB.check3; +import static com.alibaba.fastjson2.util.IOUtils.hexDigit4; import static com.alibaba.fastjson2.util.JDKUtils.*; class JSONReaderASCII @@ -141,7 +143,7 @@ public final long readFieldNameHashCode() { } final int quote = ch; - + final int end = this.end; this.nameAscii = true; this.nameEscape = false; int offset = this.nameBegin = this.offset; @@ -272,7 +274,7 @@ public final long readFieldNameHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -346,7 +348,7 @@ public final long readFieldNameHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -453,7 +455,7 @@ public final long readFieldNameHashCodeUnquote() { ch = (char) bytes[offset++]; switch (ch) { case 'u': { - ch = char4(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]); + ch = hexDigit4(bytes, check3(offset, end)); offset += 4; break; } @@ -534,7 +536,7 @@ public final long readFieldNameHashCodeUnquote() { ch = bytes[offset++]; switch (ch) { case 'u': { - ch = char4(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]); + ch = hexDigit4(bytes, check3(offset, end)); offset += 4; break; } @@ -665,7 +667,7 @@ public final long readValueHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -732,7 +734,7 @@ public final long readValueHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -787,7 +789,7 @@ public final long readValueHashCode() { @Override public final long getNameHashCodeLCase() { final byte[] bytes = this.bytes; - int offset = nameBegin; + int offset = nameBegin, end = this.end; long nameValue = 0; for (int i = 0; offset < end; offset++) { int c = bytes[offset]; @@ -796,7 +798,7 @@ public final long getNameHashCodeLCase() { c = bytes[++offset]; switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -875,7 +877,7 @@ public final long getNameHashCodeLCase() { c = bytes[++offset]; switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -930,14 +932,14 @@ public final String getFieldName() { if (JDKUtils.STRING_CREATOR_JDK11 != null) { byte[] chars = new byte[nameLength]; forStmt: - for (int i = 0; offset < nameEnd; ++i) { + for (int i = 0, end = this.end; offset < nameEnd; ++i) { byte b = bytes[offset]; if (b == '\\') { b = bytes[++offset]; switch (b) { case 'u': { - char ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + int ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; if (ch > 0xFF) { chars = null; @@ -994,7 +996,7 @@ public final String getFieldName() { ch = (char) bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = (char) hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -1376,7 +1378,7 @@ protected final void readString0() { c = (char) (bytes[++offset]); switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = (char) hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -1525,7 +1527,7 @@ private int readEscaped(byte[] bytes, int offset, byte quote, char[] buf) { c = (char) bytes[++offset]; switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = (char) hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java index 915ce080f5..9dac88e982 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF16.java @@ -14,6 +14,7 @@ import java.util.*; import static com.alibaba.fastjson2.JSONFactory.*; +import static com.alibaba.fastjson2.JSONReaderJSONB.check3; import static com.alibaba.fastjson2.util.IOUtils.*; import static com.alibaba.fastjson2.util.IOUtils.INT_32_MULT_MIN_10; import static com.alibaba.fastjson2.util.JDKUtils.*; @@ -962,7 +963,7 @@ public final long readFieldNameHashCodeUnquote() { ch = chars[offset++]; switch (ch) { case 'u': { - ch = char4(chars[offset], chars[offset + 1], chars[offset + 2], chars[offset + 3]); + ch = (char) hexDigit4(chars, check3(offset, end)); offset += 4; break; } @@ -1043,7 +1044,7 @@ public final long readFieldNameHashCodeUnquote() { ch = chars[offset++]; switch (ch) { case 'u': { - ch = char4(chars[offset], chars[offset + 1], chars[offset + 2], chars[offset + 3]); + ch = (char) hexDigit4(chars, check3(offset, end)); offset += 4; break; } @@ -1293,7 +1294,7 @@ public final long readFieldNameHashCode() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1359,7 +1360,7 @@ public final long readFieldNameHashCode() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1446,7 +1447,7 @@ public final long readValueHashCode() { ch = chars[++offset]; switch (ch) { case 'u': { - ch = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + ch = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1511,7 +1512,7 @@ public final long readValueHashCode() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1590,7 +1591,7 @@ public final long getNameHashCodeLCase() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1669,7 +1670,7 @@ public final long getNameHashCodeLCase() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -1726,7 +1727,7 @@ public final String getFieldName() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -2932,7 +2933,7 @@ public final String getString() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } @@ -2991,7 +2992,7 @@ protected final void readString0() { if (c == '\\') { c = this.chars[++offset]; if (c == 'u') { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; } else if (c == 'x') { c = char2(chars[offset + 1], chars[offset + 2]); @@ -3101,7 +3102,7 @@ public String readString() { c = chars[++offset]; switch (c) { case 'u': { - c = char4(chars[offset + 1], chars[offset + 2], chars[offset + 3], chars[offset + 4]); + c = (char) hexDigit4(chars, check3(offset + 1, end)); offset += 4; break; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java index 387a12dc32..b9b47dfd8e 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java @@ -12,6 +12,7 @@ import java.util.*; import static com.alibaba.fastjson2.JSONFactory.*; +import static com.alibaba.fastjson2.JSONReaderJSONB.check3; import static com.alibaba.fastjson2.util.IOUtils.*; import static com.alibaba.fastjson2.util.JDKUtils.*; import static java.lang.Long.MIN_VALUE; @@ -529,7 +530,7 @@ public long readFieldNameHashCodeUnquote() { ch = (char) bytes[offset++]; switch (ch) { case 'u': { - ch = char4(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]); + ch = hexDigit4(bytes, check3(offset, end)); offset += 4; break; } @@ -642,7 +643,7 @@ public long readFieldNameHashCodeUnquote() { ch = bytes[offset++]; switch (ch) { case 'u': { - ch = char4(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]); + ch = hexDigit4(bytes, check3(offset, end)); offset += 4; break; } @@ -2494,7 +2495,7 @@ public long readFieldNameHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -2568,7 +2569,7 @@ public long readFieldNameHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -2682,7 +2683,7 @@ public long readValueHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -2749,7 +2750,7 @@ public long readValueHashCode() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -2859,7 +2860,7 @@ public long readValueHashCode() { @Override public long getNameHashCodeLCase() { long hashCode = Fnv.MAGIC_HASH_CODE; - int offset = nameBegin; + int offset = nameBegin, end = this.end; final byte[] bytes = this.bytes; long nameValue = 0; @@ -2870,7 +2871,7 @@ public long getNameHashCodeLCase() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -2969,7 +2970,7 @@ public long getNameHashCodeLCase() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -3076,7 +3077,7 @@ public String getFieldName() { char[] chars = new char[nameLength]; - for (int i = 0; offset < nameEnd; ++i) { + for (int i = 0, end = this.end; offset < nameEnd; ++i) { int ch = bytes[offset]; if (ch < 0) { ch &= 0xFF; @@ -3105,7 +3106,7 @@ public String getFieldName() { ch = (char) bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -4069,13 +4070,13 @@ public final void readString(ValueConsumer consumer, boolean quoted) { char[] chars = new char[valueLength]; offset = start; - for (int i = 0; ; ++i) { + for (int i = 0, end = this.end; ; ++i) { int c = bytes[offset]; if (c == '\\') { c = bytes[++offset]; switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -4192,7 +4193,7 @@ protected void readString0() { char quote = this.ch; int valueLength; int offset = this.offset; - int start = offset; + int start = offset, end = this.end; boolean ascii = true; valueEscape = false; final byte[] bytes = this.bytes; @@ -4251,7 +4252,7 @@ protected void readString0() { c = bytes[++offset]; switch (c) { case 'u': { - c = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + c = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -4617,7 +4618,7 @@ public final String getString() { return stringValue; } final byte[] bytes = this.bytes; - int offset = nameBegin; + int offset = nameBegin, end = this.end; int length = nameEnd - offset; if (!nameEscape) { if (ANDROID) { @@ -4695,11 +4696,7 @@ public final String getString() { c = (char) bytes[++offset]; switch (c) { case 'u': { - int c1 = bytes[offset + 1]; - int c2 = bytes[offset + 2]; - int c3 = bytes[offset + 3]; - int c4 = bytes[offset + 4]; - c = char4(c1, c2, c3, c4); + c = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } @@ -4898,7 +4895,7 @@ public String readString() { ch = bytes[++offset]; switch (ch) { case 'u': { - ch = char4(bytes[offset + 1], bytes[offset + 2], bytes[offset + 3], bytes[offset + 4]); + ch = hexDigit4(bytes, check3(offset + 1, end)); offset += 4; break; } diff --git a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java index 18b0bf569d..d1ab0f7bca 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java @@ -1646,13 +1646,15 @@ private static int indexOfChar0(byte[] value, int ch, int fromIndex, int max) { } public static int hexDigit4(byte[] bytes, int offset) { - int v = Integer.reverseBytes(UNSAFE.getInt(bytes, ARRAY_BYTE_BASE_OFFSET + offset)); + int v = getIntLE(bytes, offset); v = (v & 0x0F0F0F0F) + ((((v & 0x40404040) >> 2) | ((v & 0x40404040) << 1)) >>> 4); - v = ((v >>> 12) & 0xF000) - + ((v >>> 8) & 0xF00) - + ((v >>> 4) & 0xF0) - + (v & 0xF); - return v; + return ((v & 0xF000000) >>> 24) + ((v & 0xF0000) >>> 12) + (v & 0xF00) + ((v & 0xF) << 12); + } + + public static int hexDigit4(char[] bytes, int offset) { + long v = getLongLE(bytes, offset); + v = (v & 0x000F_000F_000F_000FL) + ((((v & 0x0004_0004_0004_00040L) >> 2) | ((v & 0x0004_0004_0004_00040L) << 1)) >>> 4); + return (int) (((v & 0xF_0000_0000_0000L) >>> 48) + ((v & 0xF_0000_0000L) >>> 28) + ((v & 0xF_0000) >> 8) + ((v & 0xF) << 12)); } public static boolean isDigit(int ch) { diff --git a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java index baa9c97b14..e623df34b0 100644 --- a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java @@ -342,7 +342,12 @@ public void printDigitHex() { @Test public void hexDigit4() { byte[] bytes = "1234ABcd".getBytes(StandardCharsets.UTF_8); - System.out.println(Integer.toHexString(IOUtils.hexDigit4(bytes, 0))); + assertEquals("1234", Integer.toHexString(IOUtils.hexDigit4(bytes, 0))); + assertEquals("34ab", Integer.toHexString(IOUtils.hexDigit4(bytes, 2))); + + char[] chars = "1234ABcd".toCharArray(); + assertEquals("1234", Integer.toHexString(IOUtils.hexDigit4(chars, 0))); + assertEquals("34ab", Integer.toHexString(IOUtils.hexDigit4(chars, 2))); } static int hexDigit4(byte[] bytes, int offset) { From 45db80066cfe1a04f9b78d56149be15b78ac3150 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 11:29:05 +0800 Subject: [PATCH 11/15] remove unused code --- .../benchmark/eishay/EishayParseUTF8BytesTest.java | 2 +- .../main/java/com/alibaba/fastjson2/JSONReaderASCII.java | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java index 58de547ce3..10c5d47603 100644 --- a/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java +++ b/benchmark/src/test/java/com/alibaba/fastjson2/benchmark/eishay/EishayParseUTF8BytesTest.java @@ -13,7 +13,7 @@ public static void fastjson2() { } long millis = System.currentTimeMillis() - start; System.out.println("fastjson2 millis : " + millis); - // zulu8.62.0.19 : 703 746 710 706 700 682 717 698 526 500 474 + // zulu8.62.0.19 : 703 746 710 706 700 682 717 698 526 500 474 445 // zulu11.52.13 : 579 565 552 541 554 553 554 538 420 424 434 // zulu17.40.19 : 600 604 597 593 578 567 447 420 } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java index d7d70d08f7..0b9634c244 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java @@ -624,13 +624,6 @@ public final long readFieldNameHashCodeUnquote() { return hashCode; } - public static long getLong(byte[] bytes, int off) { - return UNSAFE.getLong( - bytes, - ARRAY_BYTE_BASE_OFFSET + off - ); - } - @Override public final long readValueHashCode() { int ch = this.ch; From a6010b78c32e44cbcbe89ca32950221f74dbeb95 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 12:09:20 +0800 Subject: [PATCH 12/15] remove unused code --- core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java index b9b47dfd8e..d2caf941d5 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderUTF8.java @@ -4733,7 +4733,6 @@ public final void skipComment() { final byte[] bytes = this.bytes; byte ch = bytes[offset++]; - byte start = ch; boolean multi; if (ch == '*') { From 64dd6f94981b317215c1a3a7e5187bf21a738efc Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 13:49:47 +0800 Subject: [PATCH 13/15] remove unused code --- .../alibaba/fastjson2/JSONReaderASCII.java | 4 +- .../com/alibaba/fastjson2/util/IOUtils.java | 43 ++++++++++++------ .../com/alibaba/fastjson2/util/JDKUtils.java | 16 ------- .../alibaba/fastjson2/util/IOUtilsTest.java | 44 +++++++++++++++---- 4 files changed, 67 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java index 0b9634c244..296f8c6381 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java @@ -1431,13 +1431,13 @@ public final String readString() { int valueLength; boolean valueEscape = false; - int index = IOUtils.indexOfChar(bytes, quote, offset, end); + int index = IOUtils.indexOfQuote(bytes, quote, offset, end); if (index == -1) { throw error("invalid escape character EOI"); } int slashIndex = nextEscapeIndex; if (slashIndex == ESCAPE_INDEX_NOT_SET || (slashIndex != -1 && slashIndex < offset)) { - nextEscapeIndex = slashIndex = IOUtils.indexOfChar(bytes, '\\', offset, end); + nextEscapeIndex = slashIndex = IOUtils.indexOfSlash(bytes, offset, end); } if (slashIndex == -1 || slashIndex > index) { valueLength = index - offset; diff --git a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java index d1ab0f7bca..63f7b3ae66 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/IOUtils.java @@ -1621,19 +1621,23 @@ public static int digit1(byte[] bytes, int off) { return d >= 0 && d <= 9 ? d : -1; } - public static int indexOfChar(byte[] value, int ch, int fromIndex) { - return indexOfChar(value, ch, fromIndex, value.length); + public static int indexOfQuote(byte[] value, int quote, int fromIndex, int max) { + int i = fromIndex; + int upperBound = fromIndex + ((max - fromIndex) & ~7); + long vectorQuote = quote == '\'' ? 0x2727_2727_2727_2727L : 0x2222_2222_2222_2222L; + while (i < upperBound && notContains(getLongLE(value, i), vectorQuote)) { + i += 8; + } + return indexOfChar0(value, quote, i, max); } - public static int indexOfChar(byte[] value, int ch, int fromIndex, int max) { - if (INDEX_OF_CHAR_LATIN1 == null) { - return indexOfChar0(value, ch, fromIndex, max); - } - try { - return (int) INDEX_OF_CHAR_LATIN1.invokeExact(value, ch, fromIndex, max); - } catch (Throwable e) { - throw new JSONException(e.getMessage()); + public static int indexOfSlash(byte[] value, int fromIndex, int max) { + int i = fromIndex; + int upperBound = fromIndex + ((max - fromIndex) & ~7); + while (i < upperBound && notContains(getLongLE(value, i), 0x5C5C5C5C5C5C5C5CL)) { + i += 8; } + return indexOfChar0(value, '\\', i, max); } private static int indexOfChar0(byte[] value, int ch, int fromIndex, int max) { @@ -1645,6 +1649,21 @@ private static int indexOfChar0(byte[] value, int ch, int fromIndex, int max) { return -1; } + private static boolean notContains(long v, long quote) { + /* + for (int i = 0; i < 8; ++i) { + byte c = (byte) v; + if (c == quote) { + return true; + } + v >>>= 8; + } + return false; + */ + long x = v ^ quote; + return (((x - 0x0101010101010101L) & ~x) & 0x8080808080808080L) == 0; + } + public static int hexDigit4(byte[] bytes, int offset) { int v = getIntLE(bytes, offset); v = (v & 0x0F0F0F0F) + ((((v & 0x40404040) >> 2) | ((v & 0x40404040) << 1)) >>> 4); @@ -1661,10 +1680,6 @@ public static boolean isDigit(int ch) { return ch >= '0' && ch <= '9'; } - public static short getShortUnaligned(byte[] bytes, int offset) { - return UNSAFE.getShort(bytes, ARRAY_BYTE_BASE_OFFSET + offset); - } - public static short getShortBE(byte[] bytes, int offset) { return convEndian(true, UNSAFE.getShort(bytes, ARRAY_BYTE_BASE_OFFSET + offset)); diff --git a/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java index 8d6f8db5a9..e491e335cc 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java @@ -59,7 +59,6 @@ public class JDKUtils { public static final MethodHandle METHOD_HANDLE_HAS_NEGATIVE; public static final Predicate PREDICATE_IS_ASCII; - public static final MethodHandle INDEX_OF_CHAR_LATIN1; static final MethodHandles.Lookup IMPL_LOOKUP; static volatile MethodHandle CONSTRUCTOR_LOOKUP; @@ -338,21 +337,6 @@ public class JDKUtils { METHOD_HANDLE_HAS_NEGATIVE = handle; } - MethodHandle indexOfCharLatin1 = null; - if (JVM_VERSION > 9) { - try { - Class cStringLatin1 = Class.forName("java.lang.StringLatin1"); - MethodHandles.Lookup lookup = trustedLookup(cStringLatin1); - indexOfCharLatin1 = lookup.findStatic( - cStringLatin1, - "indexOfChar", - MethodType.methodType(int.class, byte[].class, int.class, int.class, int.class)); - } catch (Throwable ignored) { - // ignore - } - } - INDEX_OF_CHAR_LATIN1 = indexOfCharLatin1; - Boolean compact_strings = null; try { if (JVM_VERSION == 8) { diff --git a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java index e623df34b0..2196bd494a 100644 --- a/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/util/IOUtilsTest.java @@ -365,17 +365,45 @@ static int hexDigit4(byte[] bytes, int offset) { } @Test - public void indexOf() throws Throwable { - byte[] bytes = "abcda".getBytes(StandardCharsets.UTF_8); + public void indexOf() { + byte[] bytes = "'b'd'".getBytes(StandardCharsets.UTF_8); assertEquals(2, - IOUtils.indexOfChar( - bytes, 'c', 0)); + IOUtils.indexOfQuote( + bytes, '\'', 1, bytes.length)); assertEquals(0, - IOUtils.indexOfChar( - bytes, 'a', 0)); + IOUtils.indexOfQuote( + bytes, '\'', 0, bytes.length)); assertEquals(4, - IOUtils.indexOfChar( - bytes, 'a', 1)); + IOUtils.indexOfQuote( + bytes, '\'', 3, bytes.length)); + } + + @Test + public void indexOf1() { + byte[] bytes = "\"b\"d\"".getBytes(StandardCharsets.UTF_8); + assertEquals(2, + IOUtils.indexOfQuote( + bytes, '"', 1, bytes.length)); + assertEquals(0, + IOUtils.indexOfQuote( + bytes, '"', 0, bytes.length)); + assertEquals(4, + IOUtils.indexOfQuote( + bytes, '"', 3, bytes.length)); + } + + @Test + public void indexOfSlash() { + byte[] bytes = "\\b\\d\\".getBytes(StandardCharsets.UTF_8); + assertEquals(2, + IOUtils.indexOfSlash( + bytes, 1, bytes.length)); + assertEquals(0, + IOUtils.indexOfSlash( + bytes, 0, bytes.length)); + assertEquals(4, + IOUtils.indexOfSlash( + bytes, 3, bytes.length)); } @Test From 2b50ce9ee46579c1c87852867bd07463e7d9a762 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 14:13:45 +0800 Subject: [PATCH 14/15] PREDICATE_IS_ASCII --- .../com/alibaba/fastjson2/JSONReader.java | 23 ++++--------------- .../com/alibaba/fastjson2/util/JDKUtils.java | 20 ++++++++++++++++ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java index 78ddc0f7e8..efe47656e5 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReader.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReader.java @@ -3240,13 +3240,8 @@ protected final String toString(Map object) { } public static JSONReader of(byte[] utf8Bytes) { - boolean ascii = false; - if (PREDICATE_IS_ASCII != null) { - ascii = PREDICATE_IS_ASCII.test(utf8Bytes); - } - Context context = createReadContext(); - if (ascii) { + if (PREDICATE_IS_ASCII.test(utf8Bytes)) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3255,12 +3250,7 @@ public static JSONReader of(byte[] utf8Bytes) { @Deprecated public static JSONReader of(Context context, byte[] utf8Bytes) { - boolean ascii = false; - if (PREDICATE_IS_ASCII != null) { - ascii = PREDICATE_IS_ASCII.test(utf8Bytes); - } - - if (ascii) { + if (PREDICATE_IS_ASCII.test(utf8Bytes)) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3268,12 +3258,7 @@ public static JSONReader of(Context context, byte[] utf8Bytes) { } public static JSONReader of(byte[] utf8Bytes, Context context) { - boolean ascii = false; - if (PREDICATE_IS_ASCII != null) { - ascii = PREDICATE_IS_ASCII.test(utf8Bytes); - } - - if (ascii) { + if (PREDICATE_IS_ASCII.test(utf8Bytes)) { return new JSONReaderASCII(context, null, utf8Bytes, 0, utf8Bytes.length); } @@ -3510,7 +3495,7 @@ public static JSONReader of(String str) { } Context context = JSONFactory.createReadContext(); - if (STRING_VALUE != null && STRING_CODER != null && PREDICATE_IS_ASCII != null) { + if (STRING_VALUE != null && STRING_CODER != null) { try { final int LATIN1 = 0; int coder = STRING_CODER.applyAsInt(str); diff --git a/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java b/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java index e491e335cc..c600799116 100644 --- a/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java +++ b/core/src/main/java/com/alibaba/fastjson2/util/JDKUtils.java @@ -316,6 +316,9 @@ public class JDKUtils { initErrorLast = e; } } + if (isAscii == null) { + isAscii = JDKUtils::isASCII; + } PREDICATE_IS_ASCII = isAscii; } @@ -506,4 +509,21 @@ public static String latin1StringJDK8(byte[] bytes, int offset, int strlen) { } return STRING_CREATOR_JDK8.apply(chars, Boolean.TRUE); } + + public static boolean isASCII(byte[] chars) { + int i = 0; + int strlen = chars.length; + for (int upperBound = (strlen & ~7); i < upperBound; i += 8) { + if ((UNSAFE.getLong(chars, ARRAY_BYTE_BASE_OFFSET + i) & 0x8080808080808080L) != 0) { + return false; + } + } + + for (; i < strlen; ++i) { + if (UNSAFE.getByte(chars, ARRAY_BYTE_BASE_OFFSET + i) < 0) { + return false; + } + } + return true; + } } From 8fc6ee04fd29febfe8e959001674db723cfb5308 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 19 Jan 2025 15:06:21 +0800 Subject: [PATCH 15/15] simplify code --- .../java/com/alibaba/fastjson2/JSONReaderASCII.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java index 296f8c6381..a5b5cea9b7 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONReaderASCII.java @@ -171,7 +171,7 @@ public final long readFieldNameHashCode() { offset += 3; } else if ((c3 = bytes[offset + 3]) == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' - && c0 >= 0 && c1 >= 0 && c2 > 0 + && (c0 | c1) >= 0 && c2 > 0 ) { nameValue = (c2 << 16) @@ -182,7 +182,7 @@ public final long readFieldNameHashCode() { offset += 4; } else if ((c4 = bytes[offset + 4]) == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' && c3 != '\\' - && c0 >= 0 && c1 >= 0 && c2 >= 0 && c3 > 0 + && (c0 | c1 | c2) >= 0 && c3 > 0 ) { nameValue = (c3 << 24) @@ -194,7 +194,7 @@ public final long readFieldNameHashCode() { offset += 5; } else if ((c5 = bytes[offset + 5]) == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' && c3 != '\\' && c4 != '\\' - && c0 >= 0 && c1 >= 0 && c2 >= 0 && c3 >= 0 && c4 > 0 + && (c0 | c1 | c2 | c3) >= 0 && c4 > 0 ) { nameValue = (((long) c4) << 32) @@ -207,7 +207,7 @@ public final long readFieldNameHashCode() { offset += 6; } else if ((c6 = bytes[offset + 6]) == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' && c3 != '\\' && c4 != '\\' && c5 != '\\' - && c0 >= 0 && c1 >= 0 && c2 >= 0 && c3 >= 0 && c4 >= 0 && c5 > 0 + && (c0 | c1 | c2 | c3 | c4) >= 0 && c5 > 0 ) { nameValue = (((long) c5) << 40) @@ -221,7 +221,7 @@ public final long readFieldNameHashCode() { offset += 7; } else if ((c7 = bytes[offset + 7]) == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' && c3 != '\\' && c4 != '\\' && c5 != '\\' && c6 != '\\' - && c0 >= 0 && c1 >= 0 && c2 >= 0 && c3 >= 0 && c4 >= 0 && c5 >= 0 && c6 > 0 + && (c0 | c1 | c2 | c3 | c4 | c5) >= 0 && c6 > 0 ) { nameValue = (((long) c6) << 48) @@ -236,7 +236,7 @@ public final long readFieldNameHashCode() { offset += 8; } else if (bytes[offset + 8] == quote && c0 != '\\' && c1 != '\\' && c2 != '\\' && c3 != '\\' && c4 != '\\' && c5 != '\\' && c6 != '\\' && c7 != '\\' - && c0 >= 0 && c1 >= 0 && c2 >= 0 && c3 >= 0 && c4 >= 0 && c5 >= 0 && c6 >= 0 && c7 > 0 + && (c0 | c1 | c2 | c3 | c4 | c5 | c6) >= 0 && c7 > 0 ) { nameValue = (((long) c7) << 56)