633 lines
20 KiB
Java
633 lines
20 KiB
Java
/*
|
||
* To change this license header, choose License Headers in Project Properties.
|
||
* To change this template file, choose Tools | Templates
|
||
* and open the template in the editor.
|
||
*/
|
||
package org.redkale.convert.json;
|
||
|
||
import java.lang.reflect.Type;
|
||
import java.nio.charset.StandardCharsets;
|
||
import java.util.function.Consumer;
|
||
import org.redkale.convert.*;
|
||
import static org.redkale.convert.json.JsonWriter.*;
|
||
import org.redkale.util.*;
|
||
|
||
/**
|
||
*
|
||
* writeTo系列的方法输出的字符不能含特殊字符
|
||
* <p>
|
||
* 详情见: https://redkale.org
|
||
*
|
||
* @author zhangjx
|
||
*
|
||
* @since 2.3.0
|
||
*/
|
||
public class JsonBytesWriter extends JsonWriter implements ByteTuple {
|
||
|
||
private static final byte[] BYTES_TUREVALUE = "true".getBytes();
|
||
|
||
private static final byte[] BYTES_FALSEVALUE = "false".getBytes();
|
||
|
||
private static final int TENTHOUSAND_MAX = 10001;
|
||
|
||
private static final byte[][] TENTHOUSAND_BYTES = new byte[TENTHOUSAND_MAX][];
|
||
|
||
static {
|
||
for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) {
|
||
TENTHOUSAND_BYTES[i] = String.valueOf(i).getBytes();
|
||
}
|
||
}
|
||
|
||
private int count;
|
||
|
||
private byte[] content;
|
||
|
||
public JsonBytesWriter() {
|
||
this(defaultSize);
|
||
}
|
||
|
||
public JsonBytesWriter(int size) {
|
||
this.content = new byte[size > 1024 ? size : 1024];
|
||
}
|
||
|
||
public JsonBytesWriter(ByteArray array) {
|
||
this.content = array.content();
|
||
this.count = array.length();
|
||
}
|
||
|
||
public JsonBytesWriter(boolean tiny, boolean nullable, ByteArray array) {
|
||
this.tiny = tiny;
|
||
this.nullable = nullable;
|
||
this.content = array.content();
|
||
this.count = array.length();
|
||
}
|
||
|
||
protected byte[] expand(int len) {
|
||
int newcount = count + len;
|
||
if (newcount <= content.length) {
|
||
return content;
|
||
}
|
||
byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)];
|
||
System.arraycopy(content, 0, newdata, 0, count);
|
||
this.content = newdata;
|
||
return newdata;
|
||
}
|
||
|
||
@Override
|
||
public boolean recycle() {
|
||
super.recycle();
|
||
this.count = 0;
|
||
if (this.content != null && this.content.length > defaultSize * 100) {
|
||
this.content = new byte[defaultSize];
|
||
}
|
||
return true;
|
||
}
|
||
|
||
@Override
|
||
public byte[] content() {
|
||
return content;
|
||
}
|
||
|
||
@Override
|
||
public int offset() {
|
||
return 0;
|
||
}
|
||
|
||
@Override
|
||
public int length() {
|
||
return count;
|
||
}
|
||
|
||
/**
|
||
* 将本对象的内容引用复制给array
|
||
*
|
||
* @param array ByteArray
|
||
*/
|
||
public void directTo(ByteArray array) {
|
||
array.directFrom(content, count);
|
||
}
|
||
|
||
@Override
|
||
public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) {
|
||
if (this.comma) {
|
||
writeTo(',');
|
||
}
|
||
if (member != null) {
|
||
byte[] bs = member.getJsonFieldNameBytes();
|
||
expand(bs.length);
|
||
System.arraycopy(bs, 0, content, count, bs.length);
|
||
count += bs.length;
|
||
} else {
|
||
writeLatin1To(true, fieldName);
|
||
writeTo(':');
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void writeTo(final char ch) { //只能是 0 - 127 的字符
|
||
expand(1);
|
||
content[count++] = (byte) ch;
|
||
}
|
||
|
||
@Override
|
||
public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符
|
||
expand(len);
|
||
for (int i = 0; i < len; i++) {
|
||
content[count + i] = (byte) chs[start + i];
|
||
}
|
||
count += len;
|
||
}
|
||
|
||
@Override
|
||
public void writeTo(final byte ch) { //只能是 0 - 127 的字符
|
||
expand(1);
|
||
content[count++] = ch;
|
||
}
|
||
|
||
@Override
|
||
public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符
|
||
expand(len);
|
||
System.arraycopy(chs, start, content, count, len);
|
||
count += len;
|
||
}
|
||
|
||
/**
|
||
* <b>注意:</b> 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger、BigDecimal转换的String
|
||
*
|
||
* @param quote 是否加双引号
|
||
* @param value 非null且不含需要转义的字符的String值
|
||
*/
|
||
@Override
|
||
public void writeLatin1To(final boolean quote, final String value) {
|
||
if (value == null) {
|
||
writeNull();
|
||
return;
|
||
}
|
||
byte[] bs = Utility.latin1ByteArray(value);
|
||
int len = bs.length;
|
||
if (quote) {
|
||
byte[] src = expand(len + 2);
|
||
src[count++] = '"';
|
||
System.arraycopy(bs, 0, src, count, bs.length);
|
||
count += len;
|
||
src[count++] = '"';
|
||
} else {
|
||
byte[] src = expand(len);
|
||
System.arraycopy(bs, 0, src, count, bs.length);
|
||
count += len;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
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));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
}
|
||
|
||
@Override
|
||
public void writeFieldIntValue(final byte[] fieldBytes, final int value) {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] : Utility.latin1ByteArray(String.valueOf(value));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
}
|
||
|
||
@Override
|
||
public void writeFieldLongValue(final byte[] fieldBytes, final long value) {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
}
|
||
|
||
@Override
|
||
public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = Utility.latin1ByteArray(value);
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 2);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
src[count++] = '"';
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
src[count++] = '"';
|
||
}
|
||
|
||
@Override
|
||
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));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 1);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
src[count++] = '}';
|
||
}
|
||
|
||
@Override
|
||
public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] : Utility.latin1ByteArray(String.valueOf(value));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 1);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
src[count++] = '}';
|
||
}
|
||
|
||
@Override
|
||
public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 1);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
src[count++] = '}';
|
||
}
|
||
|
||
@Override
|
||
public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) {
|
||
if (value == null && nullable()) {
|
||
writeTo(fieldBytes);
|
||
writeNull();
|
||
writeTo('}');
|
||
return;
|
||
}
|
||
if (value == null || (tiny && value.isEmpty())) {
|
||
expand(1);
|
||
content[count++] = '}';
|
||
} else {
|
||
byte[] bs1 = fieldBytes;
|
||
byte[] bs2 = Utility.latin1ByteArray(value);
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 3);
|
||
int c = count;
|
||
System.arraycopy(bs1, 0, src, c, len1);
|
||
c += len1;
|
||
src[c++] = '"';
|
||
System.arraycopy(bs2, 0, src, c, len2);
|
||
c += len2;
|
||
src[c++] = '"';
|
||
src[c++] = '}';
|
||
count = c;
|
||
}
|
||
}
|
||
|
||
@Override //firstFieldBytes 必须带{开头
|
||
public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) {
|
||
if (value == null && nullable()) {
|
||
writeTo('{');
|
||
writeTo(firstFieldBytes);
|
||
writeNull();
|
||
writeTo('}');
|
||
return;
|
||
}
|
||
if (value == null || (tiny && value.isEmpty())) {
|
||
expand(2);
|
||
content[count++] = '{';
|
||
content[count++] = '}';
|
||
} else {
|
||
byte[] bs1 = firstFieldBytes;
|
||
byte[] bs2 = Utility.latin1ByteArray(value);
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
byte[] src = expand(len1 + len2 + 3);
|
||
int c = count;
|
||
System.arraycopy(bs1, 0, src, c, len1);
|
||
c += len1;
|
||
src[c++] = '"';
|
||
System.arraycopy(bs2, 0, src, c, len2);
|
||
c += len2;
|
||
src[c++] = '"';
|
||
src[c++] = '}';
|
||
count = c;
|
||
}
|
||
}
|
||
|
||
@Override //firstFieldBytes 必须带{开头, lastFieldBytes必须,开头
|
||
public void writeObjectByOnlyTwoIntFieldValue(final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) {
|
||
byte[] bs1 = firstFieldBytes;
|
||
byte[] bs2 = (value1 >= 0 && value1 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value1] : Utility.latin1ByteArray(String.valueOf(value1));
|
||
byte[] bs3 = lastFieldBytes;
|
||
byte[] bs4 = (value2 >= 0 && value2 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value2] : Utility.latin1ByteArray(String.valueOf(value2));
|
||
int len1 = bs1.length;
|
||
int len2 = bs2.length;
|
||
int len3 = bs3.length;
|
||
int len4 = bs4.length;
|
||
byte[] src = expand(len1 + len2 + len3 + len4 + 1);
|
||
System.arraycopy(bs1, 0, src, count, len1);
|
||
count += len1;
|
||
System.arraycopy(bs2, 0, src, count, len2);
|
||
count += len2;
|
||
System.arraycopy(bs3, 0, src, count, len3);
|
||
count += len3;
|
||
System.arraycopy(bs4, 0, src, count, len4);
|
||
count += len4;
|
||
src[count++] = '}';
|
||
}
|
||
|
||
public JsonBytesWriter clear() {
|
||
this.count = 0;
|
||
return this;
|
||
}
|
||
|
||
/**
|
||
* 直接获取全部数据, 实际数据需要根据count长度来截取
|
||
*
|
||
* @return byte[]
|
||
*/
|
||
public byte[] directBytes() {
|
||
return content;
|
||
}
|
||
|
||
public void completed(ConvertBytesHandler handler, Consumer<JsonBytesWriter> callback) {
|
||
handler.completed(content, 0, count, callback, this);
|
||
}
|
||
|
||
public byte[] toBytes() {
|
||
byte[] copy = new byte[count];
|
||
System.arraycopy(content, 0, copy, 0, count);
|
||
return copy;
|
||
}
|
||
|
||
public int count() {
|
||
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++] = '"';
|
||
}
|
||
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++] = '"';
|
||
}
|
||
count = curr;
|
||
}
|
||
|
||
@Override
|
||
public void writeString(String value) {
|
||
writeString(true, value);
|
||
}
|
||
|
||
@Override
|
||
public void writeString(final boolean quote, String value) {
|
||
if (value == null) {
|
||
writeNull();
|
||
return;
|
||
}
|
||
if (Utility.isLatin1(value)) {
|
||
writeEscapeLatinString(quote, Utility.latin1ByteArray(value));
|
||
return;
|
||
}
|
||
byte[] bytes = expand(value.length() * 4 + 2);
|
||
int curr = count;
|
||
if (quote) {
|
||
bytes[curr++] = '"';
|
||
}
|
||
int len = value.length();
|
||
for (int i = 0; i < len; i++) {
|
||
char ch = value.charAt(i);
|
||
switch (ch) {
|
||
case '\n':
|
||
bytes[curr++] = '\\';
|
||
bytes[curr++] = 'n';
|
||
break;
|
||
case '\r':
|
||
bytes[curr++] = '\\';
|
||
bytes[curr++] = 'r';
|
||
break;
|
||
case '\t':
|
||
bytes[curr++] = '\\';
|
||
bytes[curr++] = 't';
|
||
break;
|
||
case '\\':
|
||
bytes[curr++] = '\\';
|
||
bytes[curr++] = '\\';
|
||
break;
|
||
case '"':
|
||
bytes[curr++] = '\\';
|
||
bytes[curr++] = '"';
|
||
break;
|
||
default:
|
||
if (ch < 0x80) {
|
||
bytes[curr++] = (byte) ch;
|
||
} else if (ch < 0x800) {
|
||
bytes[curr++] = (byte) (0xc0 | (ch >> 6));
|
||
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) (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) (0x80 | ((ch >> 6) & 0x3f));
|
||
bytes[curr++] = (byte) (0x80 | (ch & 0x3f));
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (quote) {
|
||
bytes[curr++] = '"';
|
||
}
|
||
count = curr;
|
||
}
|
||
|
||
@Override
|
||
public String toString() {
|
||
return new String(content, 0, count, StandardCharsets.UTF_8);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------------
|
||
@Override
|
||
public void writeBoolean(boolean value) {
|
||
byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE;
|
||
expand(bs.length);
|
||
System.arraycopy(bs, 0, content, count, bs.length);
|
||
count += bs.length;
|
||
}
|
||
|
||
@Override
|
||
public void writeInt(int value) {
|
||
if (value >= 0 && value < TENTHOUSAND_MAX) {
|
||
byte[] bs = TENTHOUSAND_BYTES[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;
|
||
}
|
||
int size;
|
||
for (int i = 0;; i++) {
|
||
if (value <= sizeTable[i]) {
|
||
size = i + 1;
|
||
break;
|
||
}
|
||
}
|
||
if (sign != 0) {
|
||
size++; //负数
|
||
}
|
||
byte[] bytes = expand(size);
|
||
|
||
int q, r;
|
||
int charPos = count + size;
|
||
|
||
// Generate two digits per iteration
|
||
while (value >= 65536) {
|
||
q = value / 100;
|
||
// really: r = i - (q * 100);
|
||
r = value - ((q << 6) + (q << 5) + (q << 2));
|
||
value = q;
|
||
bytes[--charPos] = (byte) DigitOnes[r];
|
||
bytes[--charPos] = (byte) DigitTens[r];
|
||
}
|
||
|
||
// Fall thru to fast mode for smaller numbers
|
||
// assert(i <= 65536, i);
|
||
for (;;) {
|
||
q = (value * 52429) >>> (16 + 3);
|
||
r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ...
|
||
bytes[--charPos] = (byte) digits[r];
|
||
value = q;
|
||
if (value == 0) {
|
||
break;
|
||
}
|
||
}
|
||
if (sign != 0) {
|
||
bytes[--charPos] = (byte) sign;
|
||
}
|
||
count += size;
|
||
}
|
||
|
||
@Override
|
||
public void writeLong(long value) {
|
||
if (value >= 0 && value < TENTHOUSAND_MAX) {
|
||
byte[] bs = TENTHOUSAND_BYTES[(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;
|
||
}
|
||
int size = 19;
|
||
long p = 10;
|
||
for (int i = 1; i < 19; i++) {
|
||
if (value < p) {
|
||
size = i;
|
||
break;
|
||
}
|
||
p = 10 * p;
|
||
}
|
||
if (sign != 0) {
|
||
size++; //负数
|
||
}
|
||
byte[] bytes = expand(size);
|
||
|
||
long q;
|
||
int r;
|
||
int charPos = count + size;
|
||
|
||
// Get 2 digits/iteration using longs until quotient fits into an int
|
||
while (value > Integer.MAX_VALUE) {
|
||
q = value / 100;
|
||
// really: r = i - (q * 100);
|
||
r = (int) (value - ((q << 6) + (q << 5) + (q << 2)));
|
||
value = q;
|
||
content[--charPos] = (byte) DigitOnes[r];
|
||
content[--charPos] = (byte) DigitTens[r];
|
||
}
|
||
|
||
// Get 2 digits/iteration using ints
|
||
int q2;
|
||
int i2 = (int) value;
|
||
while (i2 >= 65536) {
|
||
q2 = i2 / 100;
|
||
// really: r = i2 - (q * 100);
|
||
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
|
||
i2 = q2;
|
||
bytes[--charPos] = (byte) DigitOnes[r];
|
||
bytes[--charPos] = (byte) DigitTens[r];
|
||
}
|
||
|
||
// Fall thru to fast mode for smaller numbers
|
||
// assert(i2 <= 65536, i2);
|
||
for (;;) {
|
||
q2 = (i2 * 52429) >>> (16 + 3);
|
||
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
|
||
bytes[--charPos] = (byte) digits[r];
|
||
i2 = q2;
|
||
if (i2 == 0) {
|
||
break;
|
||
}
|
||
}
|
||
if (sign != 0) {
|
||
bytes[--charPos] = (byte) sign;
|
||
}
|
||
count += size;
|
||
}
|
||
|
||
}
|