diff --git a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java index 8d6bdae06..77ecdf95e 100644 --- a/src/main/java/org/redkale/convert/json/JsonBytesWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java @@ -38,6 +38,14 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { } } + private static final byte[][] TENTHOUSAND_BYTES2 = new byte[TENTHOUSAND_MAX][]; + + static { + for (int i = 0; i < TENTHOUSAND_BYTES2.length; i++) { + TENTHOUSAND_BYTES2[i] = String.valueOf(-i).getBytes(); + } + } + private int count; private byte[] content; @@ -186,8 +194,10 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { public void writeFieldShortValue(final byte[] fieldBytes, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) - ? TENTHOUSAND_BYTES[(int) value] - : Utility.latin1ByteArray(String.valueOf(value)); + ? TENTHOUSAND_BYTES[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2); @@ -202,7 +212,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] - : Utility.latin1ByteArray(String.valueOf(value)); + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2); @@ -217,7 +229,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] - : Utility.latin1ByteArray(String.valueOf(value)); + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[(int) -value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2); @@ -246,8 +260,10 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) - ? TENTHOUSAND_BYTES[(int) value] - : Utility.latin1ByteArray(String.valueOf(value)); + ? TENTHOUSAND_BYTES[value] + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2 + 1); @@ -263,7 +279,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] - : Utility.latin1ByteArray(String.valueOf(value)); + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2 + 1); @@ -279,7 +297,9 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { byte[] bs1 = fieldBytes; byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] - : Utility.latin1ByteArray(String.valueOf(value)); + : ((value < 0 && value > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[(int) -value] + : Utility.latin1ByteArray(String.valueOf(value))); int len1 = bs1.length; int len2 = bs2.length; byte[] src = expand(len1 + len2 + 1); @@ -356,11 +376,15 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { byte[] bs1 = firstFieldBytes; byte[] bs2 = (value1 >= 0 && value1 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value1] - : Utility.latin1ByteArray(String.valueOf(value1)); + : ((value1 < 0 && value1 > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value1] + : Utility.latin1ByteArray(String.valueOf(value1))); byte[] bs3 = lastFieldBytes; byte[] bs4 = (value2 >= 0 && value2 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value2] - : Utility.latin1ByteArray(String.valueOf(value2)); + : ((value2 < 0 && value2 > -TENTHOUSAND_MAX) + ? TENTHOUSAND_BYTES2[-value2] + : Utility.latin1ByteArray(String.valueOf(value2))); int len1 = bs1.length; int len2 = bs2.length; int len3 = bs3.length; @@ -405,42 +429,6 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { return this.count; } - private void writeEscapeLatinString(final boolean quote, byte[] value) { - byte[] bytes = expand(value.length * 2 + 2); - int curr = count; - if (quote) { - bytes[curr++] = BYTE_DQUOTE; - } - for (byte b : value) { - if (b == '"') { - bytes[curr++] = '\\'; - bytes[curr++] = '"'; - } else if (b == '\\') { - bytes[curr++] = '\\'; - bytes[curr++] = '\\'; - } else if (b < 32) { - if (b == '\n') { - bytes[curr++] = '\\'; - bytes[curr++] = 'n'; - } else if (b == '\r') { - bytes[curr++] = '\\'; - bytes[curr++] = 'r'; - } else if (b == '\t') { - bytes[curr++] = '\\'; - bytes[curr++] = 't'; - } else { - bytes[curr++] = b; - } - } else { - bytes[curr++] = b; - } - } - if (quote) { - bytes[curr++] = BYTE_DQUOTE; - } - count = curr; - } - @Override public void writeString(String value) { writeString(true, value); @@ -456,12 +444,17 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { writeEscapeLatinString(quote, Utility.latin1ByteArray(value)); return; } - byte[] bytes = expand(value.length() * 4 + 2); + byte[] utf16s = Utility.byteUTF16Array(value); + if (utf16s != null) { // JDK9+ + writeUTF16String(quote, utf16s); + return; + } + int len = value.length(); + byte[] bytes = expand(len * 4 + 2); int curr = count; if (quote) { bytes[curr++] = BYTE_DQUOTE; } - int len = value.length(); for (int i = 0; i < len; i++) { char ch = value.charAt(i); switch (ch) { @@ -493,12 +486,12 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); } else if (Character.isSurrogate(ch)) { // 连取两个 int uc = Character.toCodePoint(ch, value.charAt(++i)); - bytes[curr++] = (byte) (0xf0 | ((uc >> 18))); + bytes[curr++] = (byte) (0xf0 | (uc >> 18)); bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); bytes[curr++] = (byte) (0x80 | (uc & 0x3f)); } else { - bytes[curr++] = (byte) (0xe0 | ((ch >> 12))); + bytes[curr++] = (byte) (0xe0 | (ch >> 12)); bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); } @@ -511,6 +504,127 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { count = curr; } + // see java.lang.StringCoding.encodeUTF8_UTF16 方法 + private void writeUTF16String(final boolean quote, byte[] value) { + int len = value.length; + byte[] bytes = expand(len * 4 + 2); + int curr = count; + if (quote) { + bytes[curr++] = BYTE_DQUOTE; + } + byte[] src = value; + int i = 0; + while (i < len) { + byte b = src[i]; + byte b2 = src[i + 1]; + i += 2; + if (b2 == 0 && b >= 0) { + if (b == '"') { + bytes[curr++] = '\\'; + bytes[curr++] = '"'; + } else if (b == '\\') { + bytes[curr++] = '\\'; + bytes[curr++] = '\\'; + } else if (b < 32) { + if (b == '\n') { + bytes[curr++] = '\\'; + bytes[curr++] = 'n'; + } else if (b == '\r') { + bytes[curr++] = '\\'; + bytes[curr++] = 'r'; + } else if (b == '\f') { + bytes[curr++] = '\\'; + bytes[curr++] = 'f'; + } else if (b == '\b') { + bytes[curr++] = '\\'; + bytes[curr++] = 'b'; + } else if (b == '\t') { + bytes[curr++] = '\\'; + bytes[curr++] = 't'; + } else { + bytes[curr++] = b; + } + } else { + bytes[curr++] = b; + } + } else { + char c = (char) ((b & 0xff) | ((b2 & 0xff) << 8)); + if (c < 0x800) { + bytes[curr++] = (byte) (0xc0 | (c >> 6)); + bytes[curr++] = (byte) (0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + int uc = -1; + if (Character.isHighSurrogate(c) && i < len) { + char c2 = (char) ((src[i] & 0xff) | ((src[i + 1] & 0xff) << 8)); + if (Character.isLowSurrogate(c2)) { + uc = Character.toCodePoint(c, c2); + } + } + if (uc < 0) { + bytes[curr++] = '?'; + } else { + bytes[curr++] = (byte) (0xf0 | (uc >> 18)); + bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); + bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); + bytes[curr++] = (byte) (0x80 | (uc & 0x3f)); + i += 2; // 2 chars + } + } else { + // 3 bytes, 16 bits + bytes[curr++] = (byte) (0xe0 | (c >> 12)); + bytes[curr++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + bytes[curr++] = (byte) (0x80 | (c & 0x3f)); + } + } + } + if (quote) { + bytes[curr++] = BYTE_DQUOTE; + } + count = curr; + } + + private void writeEscapeLatinString(final boolean quote, byte[] value) { + byte[] bytes = expand(value.length * 2 + 2); + int curr = count; + if (quote) { + bytes[curr++] = BYTE_DQUOTE; + } + for (byte b : value) { + if (b == '"') { + bytes[curr++] = '\\'; + bytes[curr++] = '"'; + } else if (b == '\\') { + bytes[curr++] = '\\'; + bytes[curr++] = '\\'; + } else if (b < 32) { + if (b == '\n') { + bytes[curr++] = '\\'; + bytes[curr++] = 'n'; + } else if (b == '\r') { + bytes[curr++] = '\\'; + bytes[curr++] = 'r'; + } else if (b == '\f') { + bytes[curr++] = '\\'; + bytes[curr++] = 'f'; + } else if (b == '\b') { + bytes[curr++] = '\\'; + bytes[curr++] = 'b'; + } else if (b == '\t') { + bytes[curr++] = '\\'; + bytes[curr++] = 't'; + } else { + bytes[curr++] = b; + } + } else { + bytes[curr++] = b; + } + } + if (quote) { + bytes[curr++] = BYTE_DQUOTE; + } + count = curr; + } + @Override public void writeWrapper(StringWrapper wrapper) { if (wrapper == null || wrapper.getValue() == null) { @@ -522,6 +636,11 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { writeTo(Utility.latin1ByteArray(value)); return; } + byte[] utf16s = Utility.byteUTF16Array(value); + if (utf16s != null) { // JDK9+ + writeUTF16String(false, utf16s); + return; + } byte[] bytes = expand(value.length() * 4 + 2); int curr = count; int len = value.length(); @@ -534,12 +653,12 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); } else if (Character.isSurrogate(ch)) { // 连取两个 int uc = Character.toCodePoint(ch, value.charAt(++i)); - bytes[curr++] = (byte) (0xf0 | ((uc >> 18))); + bytes[curr++] = (byte) (0xf0 | (uc >> 18)); bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); bytes[curr++] = (byte) (0x80 | (uc & 0x3f)); } else { - bytes[curr++] = (byte) (0xe0 | ((ch >> 12))); + bytes[curr++] = (byte) (0xe0 | (ch >> 12)); bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); } @@ -570,6 +689,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { count += bs.length; return; } + if (value < 0 && value > -TENTHOUSAND_MAX) { + byte[] bs = TENTHOUSAND_BYTES2[-value]; + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + return; + } final char sign = value >= 0 ? 0 : '-'; if (value < 0) { value = -value; @@ -625,6 +751,13 @@ public class JsonBytesWriter extends JsonWriter implements ByteTuple { count += bs.length; return; } + if (value < 0 && value > -TENTHOUSAND_MAX) { + byte[] bs = TENTHOUSAND_BYTES2[(int) -value]; + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + return; + } final char sign = value >= 0 ? 0 : '-'; if (value < 0) { value = -value; diff --git a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java index 95e4db731..d98dab089 100644 --- a/src/main/java/org/redkale/convert/json/JsonDynEncoder.java +++ b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java @@ -7,6 +7,11 @@ package org.redkale.convert.json; import java.lang.reflect.*; import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.*; import org.redkale.asm.*; import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; @@ -53,6 +58,48 @@ public abstract class JsonDynEncoder implements Encodeable { if (clazz.isEnum()) { return true; } + if (type == Boolean.class) { + return true; + } + if (type == Byte.class) { + return true; + } + if (type == Short.class) { + return true; + } + if (type == Character.class) { + return true; + } + if (type == Integer.class) { + return true; + } + if (type == Float.class) { + return true; + } + if (type == Long.class) { + return true; + } + if (type == Double.class) { + return true; + } + if (type == BigInteger.class) { + return true; + } + if (type == BigDecimal.class) { + return true; + } + if (type == LocalDate.class) { + return true; + } + if (type == LocalTime.class) { + return true; + } + if (type == LocalDateTime.class) { + return true; + } + if (type == java.util.Date.class) { + return true; + } if (type == boolean[].class) { return true; } @@ -121,6 +168,12 @@ public abstract class JsonDynEncoder implements Encodeable { || t == Long.class || t == Double.class || t == String.class + || t == BigInteger.class + || t == BigDecimal.class + || t == LocalDate.class + || t == LocalTime.class + || t == LocalDateTime.class + || t == java.util.Date.class || ((t instanceof Class) && ((Class) t).isEnum())) { return true; } diff --git a/src/main/java/org/redkale/convert/json/JsonWriter.java b/src/main/java/org/redkale/convert/json/JsonWriter.java index b63280a81..28772f45e 100644 --- a/src/main/java/org/redkale/convert/json/JsonWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonWriter.java @@ -127,7 +127,7 @@ public abstract class JsonWriter extends Writer { writeTo(BYTE_COMMA); } if (member != null) { - writeTo(member.getJsonFieldNameChars()); + writeTo(member.getJsonFieldNameBytes()); } else { writeLatin1To(true, fieldName); writeTo(BYTE_COLON); diff --git a/src/main/java/org/redkale/util/Utility.java b/src/main/java/org/redkale/util/Utility.java index e5b03b65f..d0ef93081 100644 --- a/src/main/java/org/redkale/util/Utility.java +++ b/src/main/java/org/redkale/util/Utility.java @@ -114,10 +114,6 @@ public final class Utility { private static final Function sbByteFunction; - private static final Function strCharFunction; - - private static final Function sbCharFunction; - private static final Predicate strLatin1Function; private static final ToLongFunction bufferAddrFunction; @@ -146,8 +142,6 @@ public final class Utility { .setRemoveOnCancelPolicy(true); Unsafe unsafe0 = null; - Function strCharFunction0 = null; - Function sbCharFunction0 = null; Function strByteFunction0 = null; Function sbByteFunction0 = null; Predicate strLatin1Function0 = null; @@ -352,8 +346,6 @@ public final class Utility { } } unsafeInstance = unsafe0; - strCharFunction = strCharFunction0; - sbCharFunction = sbCharFunction0; strByteFunction = strByteFunction0; sbByteFunction = sbByteFunction0; strLatin1Function = strLatin1Function0; @@ -4849,10 +4841,7 @@ public final class Utility { if (value == null) { return new byte[0]; } - if (strCharFunction == null) { - return encodeUTF8(value.toCharArray()); - } - return encodeUTF8((char[]) strCharFunction.apply(value)); + return encodeUTF8(value.toCharArray()); } public static byte[] encodeUTF8(final char[] array) { @@ -4921,24 +4910,25 @@ public final class Utility { return true; } + public static byte[] byteUTF16Array(String value) { + if (value == null || strByteFunction == null) { + return null; + } + return (byte[]) strByteFunction.apply(value); + } + public static char[] charArray(String value) { if (value == null) { return null; } - if (strCharFunction == null) { - return value.toCharArray(); - } - return (char[]) strCharFunction.apply(value); + return value.toCharArray(); } public static char[] charArray(StringBuilder value) { if (value == null) { return null; } - if (sbCharFunction == null) { - return value.toString().toCharArray(); - } - return (char[]) sbCharFunction.apply(value); + return value.toString().toCharArray(); } // 只能是单字节字符串 @@ -4975,10 +4965,7 @@ public final class Utility { if (value == null) { return -1; } - if (strCharFunction == null) { - return encodeUTF8Length(value.toCharArray()); - } - return encodeUTF8Length((char[]) strCharFunction.apply(value)); + return encodeUTF8Length(value.toCharArray()); } public static int encodeUTF8Length(final char[] text) { diff --git a/src/test/java/org/redkale/test/convert/Message.java b/src/test/java/org/redkale/test/convert/Message.java index 9cdf5e7a2..1de29a140 100644 --- a/src/test/java/org/redkale/test/convert/Message.java +++ b/src/test/java/org/redkale/test/convert/Message.java @@ -73,15 +73,15 @@ public final class Message { longs.add(2222L); longs.add(3333L); msg.longs = longs; - msg.ints = new int[] {2, 3, 4}; + msg.ints = new int[] {-2, 3, 4}; JsonConvert convert = JsonFactory.root().getConvert(); Encodeable encoder = JsonFactory.root().loadEncoder(Message.class); System.out.println(encoder); ByteArray array = new ByteArray(); array.put("数据: ".getBytes(StandardCharsets.UTF_8)); - JsonConvert.root().convertToBytes(array, msg); + convert.convertToBytes(array, msg); System.out.println(array); Message[] mss = new Message[] {msg}; - System.out.println(JsonConvert.root().convertTo(mss)); + System.out.println(convert.convertTo(mss)); } } diff --git a/src/test/java/org/redkale/test/convert/StringWrapperTest.java b/src/test/java/org/redkale/test/convert/StringWrapperTest.java index f7a8b2318..91915ba28 100644 --- a/src/test/java/org/redkale/test/convert/StringWrapperTest.java +++ b/src/test/java/org/redkale/test/convert/StringWrapperTest.java @@ -33,7 +33,9 @@ public class StringWrapperTest { System.out.println(new String(convert.convertToBytes(wrapper))); } { - String val = "{id:'带中文'}"; + String emoji = + new String(new byte[] {(byte) 0xF0, (byte) 0x9F, (byte) 0x98, (byte) 0x81}, StandardCharsets.UTF_8); + String val = "{id:'带中文" + emoji + "'}"; StringWrapper wrapper = new StringWrapper(val); if (!main) Assertions.assertEquals(val, convert.convertTo(wrapper)); if (!main)