JSONWriterJSONB.java
package com.alibaba.fastjson2;
import com.alibaba.fastjson2.internal.trove.map.hash.TLongIntHashMap;
import com.alibaba.fastjson2.util.Fnv;
import com.alibaba.fastjson2.util.IOUtils;
import com.alibaba.fastjson2.util.JDKUtils;
import com.alibaba.fastjson2.writer.ObjectWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.time.*;
import java.util.*;
import static com.alibaba.fastjson2.JSONB.Constants.*;
import static com.alibaba.fastjson2.JSONFactory.*;
import static com.alibaba.fastjson2.JSONWriter.Feature.*;
import static com.alibaba.fastjson2.util.DateUtils.OFFSET_8_ZONE_ID_NAME;
import static com.alibaba.fastjson2.util.DateUtils.SHANGHAI_ZONE_ID_NAME;
import static com.alibaba.fastjson2.util.IOUtils.*;
import static com.alibaba.fastjson2.util.JDKUtils.*;
import static com.alibaba.fastjson2.util.TypeUtils.*;
final class JSONWriterJSONB
extends JSONWriter {
// optimize for write ZonedDateTime
static final byte[] SHANGHAI_ZONE_ID_NAME_BYTES = JSONB.toBytes(SHANGHAI_ZONE_ID_NAME);
static final byte[] OFFSET_8_ZONE_ID_NAME_BYTES = JSONB.toBytes(OFFSET_8_ZONE_ID_NAME);
private final CacheItem cacheItem;
byte[] bytes;
TLongIntHashMap symbols;
int symbolIndex;
private long rootTypeNameHash;
JSONWriterJSONB(Context ctx, SymbolTable symbolTable) {
super(ctx, symbolTable, true, StandardCharsets.UTF_8);
cacheItem = CACHE_ITEMS[System.identityHashCode(Thread.currentThread()) & (CACHE_ITEMS.length - 1)];
byte[] bytes = BYTES_UPDATER.getAndSet(cacheItem, null);
if (bytes == null) {
bytes = new byte[8192];
}
this.bytes = bytes;
}
@Override
public void close() {
final byte[] bytes = this.bytes;
if (bytes.length < CACHE_THRESHOLD) {
BYTES_UPDATER.lazySet(cacheItem, bytes);
}
}
@Override
public void writeAny(Object value) {
if (value == null) {
writeNull();
return;
}
boolean fieldBased = (context.features & Feature.FieldBased.mask) != 0;
Class<?> valueClass = value.getClass();
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
if (isBeanToArray()) {
objectWriter.writeArrayMappingJSONB(this, value, null, null, 0);
} else {
objectWriter.writeJSONB(this, value, null, null, 0);
}
}
@Override
public void startObject() {
if (++level > context.maxLevel) {
overflowLevel();
}
writeRaw(BC_OBJECT);
}
@Override
public void endObject() {
level--;
writeRaw(BC_OBJECT_END);
}
@Override
public void startArray() {
throw new JSONException("unsupported operation");
}
@Override
public void startArray(Object array, int size) {
if (isWriteTypeInfo(array)) {
writeTypeName(array.getClass().getName());
}
startArray(size);
}
@Override
public void startArray(int size) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 6 > bytes.length) {
bytes = grow(off + 6);
}
this.off = JSONB.IO.startArray(bytes, off, size);
}
@Override
public void startArray0() {
writeRaw(BC_ARRAY_FIX_MIN);
}
@Override
public void startArray1() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 1));
}
@Override
public void startArray2() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 2));
}
@Override
public void startArray3() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 3));
}
@Override
public void startArray4() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 4));
}
@Override
public void startArray5() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 5));
}
@Override
public void startArray6() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 6));
}
@Override
public void startArray7() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 7));
}
@Override
public void startArray8() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 8));
}
@Override
public void startArray9() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 9));
}
@Override
public void startArray10() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 10));
}
@Override
public void startArray11() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 11));
}
@Override
public void startArray12() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 12));
}
@Override
public void startArray13() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 13));
}
@Override
public void startArray14() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 14));
}
@Override
public void startArray15() {
writeRaw((byte) (BC_ARRAY_FIX_MIN + 15));
}
@Override
public void writeRaw(byte b) {
int off = this.off;
grow1(off)[off] = b;
this.off = off + 1;
}
@Override
public void writeChar(char ch) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 6 > bytes.length) {
bytes = grow(off + 6);
}
bytes[off] = BC_CHAR;
this.off = JSONB.IO.writeInt32(bytes, off + 1, ch);
}
@Override
public void writeName(String name) {
writeString(name);
}
@Override
public void writeNull() {
writeRaw(BC_NULL);
}
@Override
public void writeStringNull() {
writeRaw(BC_NULL);
}
@Override
public void endArray() {
}
@Override
public void writeComma() {
throw new JSONException("unsupported operation");
}
@Override
protected void write0(char ch) {
throw new JSONException("unsupported operation");
}
@Override
public void writeString(boolean value) {
writeString(Boolean.toString(value));
}
@Override
public void writeString(byte value) {
writeString(Integer.toString(value));
}
@Override
public void writeString(short value) {
writeString(Integer.toString(value));
}
@Override
public void writeString(int value) {
writeString(Integer.toString(value));
}
@Override
public void writeString(long value) {
writeString(Long.toString(value));
}
@Override
public void writeString(boolean[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(byte[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(short[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(int[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(long[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(float[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(double[] value) {
if (value == null) {
writeArrayNull();
return;
}
startArray(value.length);
for (int i = 0; i < value.length; i++) {
writeString(value[i]);
}
}
@Override
public void writeString(char[] chars, int coff, int len, boolean quote) {
if (chars == null) {
writeNull();
return;
}
boolean ascii = true;
for (int i = 0; i < len; ++i) {
if (chars[i + coff] > 0x00FF) {
ascii = false;
break;
}
}
if (ascii) {
int off = this.off;
byte[] bytes = this.bytes;
if (len <= STR_ASCII_FIX_LEN) {
bytes[off++] = (byte) (len + BC_STR_ASCII_FIX_MIN);
} else {
bytes[off] = BC_STR_ASCII;
off = JSONB.IO.writeInt32(bytes, off + 1, len);
}
for (int i = 0; i < len; ++i) {
bytes[off++] = (byte) chars[coff + i];
}
this.off = off;
return;
}
writeString(new String(chars, coff, len));
}
public void writeStringLatin1(byte[] value) {
byte[] bytes = this.bytes;
int off = this.off;
int minCapacity = off + value.length + 6;
if (minCapacity - bytes.length > 0) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeStringLatin1(bytes, off, value);
}
@Override
public void writeString(char[] chars) {
if (chars == null) {
writeNull();
return;
}
writeString0(chars, 0, chars.length);
}
@Override
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 (strlen < 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 = coff, end = coff + strlen; i < end; i++) {
char ch = chars[i];
if (ch > 0x00FF) {
ascii = false;
break;
}
bytes[off++] = (byte) ch;
}
if (ascii) {
this.off = off;
return;
}
off = this.off;
} else {
ascii = isLatin1(chars, coff, strlen);
}
int minCapacity = (ascii ? strlen : strlen * 3) + off + 6;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (ascii) {
off = writeStringLatin1(bytes, off, chars, coff, strlen);
} else {
off = writeUTF8(bytes, off, chars, coff, strlen);
}
this.off = off;
}
private static int writeStringLatin1(byte[] bytes, int off, char[] chars, int coff, int strlen) {
if (strlen <= STR_ASCII_FIX_LEN) {
bytes[off++] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
} else {
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 = JSONB.IO.writeInt32(bytes, off + 1, strlen);
}
}
for (int i = 0; i < strlen; i++) {
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);
}
bytes[off] = BC_STR_UTF8;
return JSONB.IO.writeInt32(bytes, off + 1, utf8len) + utf8len;
}
public void writeString(String[] strings) {
if (strings == null) {
writeArrayNull();
return;
}
startArray(strings.length);
for (int i = 0; i < strings.length; i++) {
String item = strings[i];
if (item == null) {
writeStringNull();
continue;
}
writeString(item);
}
}
@Override
public void writeSymbol(String str) {
if (str == null) {
writeNull();
return;
}
if (symbolTable != null) {
int ordinal = symbolTable.getOrdinal(str);
if (ordinal >= 0) {
writeRaw(BC_SYMBOL);
writeInt32(-ordinal);
return;
}
}
writeString(str);
}
@Override
public void writeTypeName(String typeName) {
int off = this.off;
byte[] bytes = grow1(off);
bytes[off++] = BC_TYPED_ANY;
long hash = Fnv.hashCode64(typeName);
int symbol = -1;
if (symbolTable != null) {
symbol = symbolTable.getOrdinalByHashCode(hash);
if (symbol == -1 && symbols != null) {
symbol = symbols.get(hash);
}
} else if (symbols != null) {
symbol = symbols.get(hash);
}
if (symbol == -1) {
if (symbols == null) {
symbols = new TLongIntHashMap();
}
symbols.put(hash, symbol = symbolIndex++);
} else {
if (off == bytes.length) {
bytes = grow(off + 1);
}
this.off = JSONB.IO.writeInt32(bytes, off, symbol);
return;
}
this.off = off;
writeString(typeName);
writeInt32(symbol);
}
@Override
public boolean writeTypeName(byte[] typeName, long hash) {
if (symbolTable != null) {
int symbol = symbolTable.getOrdinalByHashCode(hash);
if (symbol != -1) {
return writeTypeNameSymbol(symbol);
}
}
boolean symbolExists = false;
int symbol;
if (rootTypeNameHash == hash) {
symbolExists = true;
symbol = 0;
} else if (symbols != null) {
symbol = symbols.putIfAbsent(hash, symbolIndex);
if (symbol != symbolIndex) {
symbolExists = true;
} else {
symbolIndex++;
}
} else {
symbol = symbolIndex++;
if (symbol == 0) {
rootTypeNameHash = hash;
}
if (symbol != 0 || (context.features & WriteNameAsSymbol.mask) != 0) {
symbols = new TLongIntHashMap(hash, symbol);
}
}
if (symbolExists) {
writeTypeNameSymbol(-symbol);
return false;
}
byte[] bytes = this.bytes;
int off = this.off;
int minCapacity = off + 2 + typeName.length;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
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;
} else {
off = JSONB.IO.writeInt32(bytes, off, symbol);
}
this.off = off;
return false;
}
private boolean writeTypeNameSymbol(int symbol) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 7 > bytes.length) {
bytes = grow(off + 7);
}
bytes[off] = BC_TYPED_ANY;
this.off = JSONB.IO.writeInt32(bytes, off + 1, -symbol);
return false;
}
static int sizeOfInt(int i) {
if (i >= BC_INT32_NUM_MIN && i <= BC_INT32_NUM_MAX) {
return 1;
}
if (i >= INT32_BYTE_MIN && i <= INT32_BYTE_MAX) {
return 2;
}
if (i >= INT32_SHORT_MIN && i <= INT32_SHORT_MAX) {
return 3;
}
return 5;
}
public void writeString(List<String> list) {
if (list == null) {
writeArrayNull();
return;
}
final int size = list.size();
startArray(size);
if (STRING_VALUE != null && STRING_CODER != null) {
int off = this.off;
byte[] bytes = this.bytes;
final int LATIN = 0;
boolean latinAll = true;
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;
continue;
}
int coder = STRING_CODER.applyAsInt(str);
if (coder != LATIN) {
latinAll = false;
break;
}
byte[] value = STRING_VALUE.apply(str);
if (off + value.length + 6 >= bytes.length) {
bytes = grow(off + value.length + 6);
}
off = JSONB.IO.writeStringLatin1(bytes, off, value);
}
if (latinAll) {
this.off = off;
return;
}
}
for (int i = 0; i < size; i++) {
writeString(
list.get(i)
);
}
}
@Override
public void writeString(String str) {
if (str == null) {
writeNull();
return;
}
if (STRING_VALUE != null) {
byte[] value = STRING_VALUE.apply(str);
if (STRING_CODER.applyAsInt(str) == 0) {
writeStringLatin1(value);
return;
} else {
if (tryWriteStringUTF16(value)) {
return;
}
}
}
writeString(
JDKUtils.getCharArray(str)
);
}
public void writeStringUTF16(byte[] value) {
int off = this.off;
final int strlen = value.length;
int minCapacity = off + strlen + 6;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeStringUTF16(bytes, off, value);
}
boolean tryWriteStringUTF16(byte[] value) {
int check_cnt = 128;
if (check_cnt > value.length) {
check_cnt = value.length;
}
if ((check_cnt & 1) == 1) {
check_cnt -= 1;
}
int asciiCount = 0;
for (int i = 0; i + 2 <= check_cnt; i += 2) {
byte b0 = value[i];
byte b1 = value[i + 1];
if (b0 == 0 || b1 == 0) {
asciiCount++;
}
}
boolean utf16 = value.length != 0 && (asciiCount == 0 || (check_cnt >> 1) / asciiCount >= 3); // utf16������������>=1/3
int off = this.off;
int minCapacity = off + 6 + value.length * 2 + 1;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (!utf16) {
int maxSize = value.length + (value.length >> 2);
int lenByteCnt = sizeOfInt(maxSize);
int result = IOUtils.encodeUTF8(value, 0, value.length, bytes, off + lenByteCnt + 1);
int utf8len = result - off - lenByteCnt - 1;
if (utf8len > value.length) {
utf16 = true;
} else if (result != -1) {
this.off = off + writeUTF8(bytes, off, value, utf8len, asciiCount, lenByteCnt);
return true;
}
}
if (utf16) {
this.off = off + writeUTF16(bytes, off, value);
return true;
}
return false;
}
private static int writeUTF8(
byte[] bytes,
int off,
byte[] value,
int utf8len,
int asciiCount,
int lenByteCnt
) {
final byte strtype;
if (utf8len * 2 == value.length) {
if (asciiCount <= STR_ASCII_FIX_LEN) {
bytes[off] = (byte) (BC_STR_ASCII_FIX_MIN + utf8len);
System.arraycopy(bytes, off + 1 + lenByteCnt, bytes, off + 1, utf8len);
return utf8len + 1;
}
strtype = BC_STR_ASCII;
} else {
strtype = BC_STR_UTF8;
}
int utf8lenByteCnt = sizeOfInt(utf8len);
if (lenByteCnt != utf8lenByteCnt) {
System.arraycopy(bytes, off + lenByteCnt + 1, bytes, off + utf8lenByteCnt + 1, utf8len);
}
int start = off;
bytes[off] = strtype;
off = JSONB.IO.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;
off = JSONB.IO.writeInt32(bytes, off + 1, value.length);
System.arraycopy(value, 0, bytes, off, value.length);
return value.length + off - start;
}
@Override
public final Object ensureCapacity(int minCapacity) {
byte[] bytes = this.bytes;
if (minCapacity >= bytes.length) {
this.bytes = bytes = Arrays.copyOf(bytes, newCapacity(minCapacity, bytes.length));
}
return bytes;
}
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;
}
private void grow0(int minCapacity) {
bytes = Arrays.copyOf(bytes, newCapacity(minCapacity, bytes.length));
}
@Override
public void writeMillis(long millis) {
int off = this.off;
int minCapacity = off + 9;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (millis % 1000 == 0) {
long seconds = (millis / 1000);
if (seconds >= Integer.MIN_VALUE && seconds <= Integer.MAX_VALUE) {
int secondsInt = (int) seconds;
bytes[off] = BC_TIMESTAMP_SECONDS;
putIntBE(bytes, off + 1, secondsInt);
this.off = off + 5;
return;
}
if (seconds % 60 == 0) {
long minutes = seconds / 60;
if (minutes >= Integer.MIN_VALUE && minutes <= Integer.MAX_VALUE) {
int minutesInt = (int) minutes;
bytes[off] = BC_TIMESTAMP_MINUTES;
putIntBE(bytes, off + 1, minutesInt);
this.off = off + 5;
return;
}
}
}
bytes[off] = BC_TIMESTAMP_MILLIS;
putLongBE(bytes, off + 1, millis);
this.off = off + 9;
}
@Override
public void writeInt64(Long i) {
int minCapacity = off + 9;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeInt64(bytes, off, i, context.features);
}
@Override
public void writeInt64(long val) {
int minCapacity = off + 9;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeInt64(bytes, this.off, val);
}
@Override
public void writeInt64(long[] value) {
if (value == null) {
writeArrayNull();
return;
}
int size = value.length;
int off = this.off;
int minCapacity = off + size * 9 + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (size <= ARRAY_FIX_LEN) {
bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
} else {
bytes[off] = BC_ARRAY;
off = JSONB.IO.writeInt32(bytes, off + 1, size);
}
for (int i = 0; i < value.length; i++) {
off = JSONB.IO.writeInt64(bytes, off, value[i]);
}
this.off = off;
}
@Override
public void writeListInt64(List<Long> values) {
if (values == null) {
writeArrayNull();
return;
}
int size = values.size();
int off = this.off;
int minCapacity = off + size * 9 + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (size <= ARRAY_FIX_LEN) {
bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
} else {
bytes[off] = BC_ARRAY;
off = JSONB.IO.writeInt32(bytes, off + 1, size);
}
for (int i = 0; i < size; i++) {
Long item = values.get(i);
if (item == null) {
bytes[off++] = BC_NULL;
continue;
}
off = JSONB.IO.writeInt64(bytes, off, item);
}
this.off = off;
}
@Override
public void writeFloat(float value) {
int off = this.off;
int minCapacity = off + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeFloat(bytes, off, value);
}
@Override
public void writeFloat(float[] values) {
int off = this.off;
byte[] bytes = this.bytes;
int minCapacity = off + (values == null ? 1 : (5 + values.length * 5));
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeFloat(bytes, off, values);
}
@Override
public void writeDouble(double value) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 9 > bytes.length) {
bytes = grow(off + 9);
}
this.off = JSONB.IO.writeDouble(bytes, off, value);
}
@Override
public void writeDouble(double[] values) {
int off = this.off;
byte[] bytes = this.bytes;
int minCapacity = off + (values == null ? 1 : (5 + values.length * 9));
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeDouble(bytes, off, values);
}
@Override
public void writeInt16(short[] values) {
if (values == null) {
writeNull();
return;
}
startArray(values.length);
for (int i = 0; i < values.length; i++) {
writeInt32(values[i]);
}
endArray();
}
@Override
public void writeInt32(int[] values) {
if (values == null) {
writeArrayNull();
return;
}
int off = this.off;
byte[] bytes = this.bytes;
// inline startArray(value.length);
int size = values.length;
int minCapacity = off + 6 + values.length * 5;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (size <= ARRAY_FIX_LEN) {
bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
} else {
bytes[off] = BC_ARRAY;
off = JSONB.IO.writeInt32(bytes, off + 1, size);
}
for (int i = 0; i < values.length; i++) {
off = JSONB.IO.writeInt32(bytes, off, values[i]);
}
this.off = off;
}
@Override
public void writeInt8(byte[] values) {
if (values == null) {
writeArrayNull();
return;
}
int off = this.off;
int size = values.length;
int minCapacity = off + 6 + values.length * 2;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (size <= ARRAY_FIX_LEN) {
bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
} else {
bytes[off] = BC_ARRAY;
off = JSONB.IO.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));
}
bytes[off++] = (byte) val;
}
this.off = off;
}
@Override
public void writeInt8(byte val) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 2 > bytes.length) {
bytes = grow(off + 2);
}
this.off = JSONB.IO.writeInt8(bytes, off, val);
}
@Override
public void writeInt16(short val) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 3 > bytes.length) {
bytes = grow(off + 3);
}
this.off = JSONB.IO.writeInt16(bytes, off, val);
}
@Override
public void writeEnum(Enum e) {
if (e == null) {
writeNull();
return;
}
if ((context.features & (MASK_WRITE_ENUM_USING_TO_STRING | MASK_WRITE_ENUMS_USING_NAME)) != 0) {
writeString(
(context.features & WriteEnumUsingToString.mask) != 0
? e.toString()
: e.name()
);
} else {
int ordinal = e.ordinal();
byte[] bytes = this.bytes;
int off = this.off;
if (off + 5 > bytes.length) {
bytes = grow(off + 5);
}
this.off = JSONB.IO.writeInt32(bytes, off, ordinal);
}
}
@Override
public void writeInt32(Integer i) {
int off = this.off;
int minCapacity = off + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeInt32(bytes, off, i, context.features);
}
@Override
public void writeInt32(int val) {
int off = this.off;
int minCapacity = off + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeInt32(bytes, off, val);
}
@Override
public void writeListInt32(List<Integer> values) {
if (values == null) {
writeArrayNull();
return;
}
int size = values.size();
int off = this.off;
int minCapacity = off + size * 5 + 5;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
if (size <= ARRAY_FIX_LEN) {
bytes[off++] = (byte) (BC_ARRAY_FIX_MIN + size);
} else {
bytes[off] = BC_ARRAY;
off = JSONB.IO.writeInt32(bytes, off + 1, size);
}
for (int i = 0; i < size; i++) {
Integer item = values.get(i);
if (item == null) {
bytes[off++] = BC_NULL;
continue;
}
off = JSONB.IO.writeInt32(bytes, off, item);
}
this.off = off;
}
@Override
public void writeArrayNull() {
writeRaw((this.context.features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL);
}
public void writeArrayNull(long features) {
writeRaw((features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL);
}
@Override
public void writeRaw(String str) {
throw new JSONException("unsupported operation");
}
@Override
public void writeRaw(byte[] raw) {
int off = this.off;
int minCapacity = off + raw.length;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
System.arraycopy(raw, 0, bytes, off, raw.length);
this.off = off + raw.length;
}
public void writeSymbol(int symbol) {
int off = this.off;
int minCapacity = off + 6;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
bytes[off++] = BC_SYMBOL;
if (symbol >= BC_INT32_NUM_MIN && symbol <= BC_INT32_NUM_MAX) {
bytes[off++] = (byte) symbol;
} else if (symbol >= INT32_BYTE_MIN && symbol <= INT32_BYTE_MAX) {
putShortBE(bytes, off, (short) ((BC_INT32_BYTE_ZERO << 8) + symbol));
off += 2;
} else {
off = JSONB.IO.writeInt32(bytes, off, symbol);
}
this.off = off;
}
@Override
public void writeNameRaw(byte[] name, long nameHash) {
int off = this.off;
int minCapacity = off + 6 + name.length;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
int symbol;
if (symbolTable == null
|| (symbol = symbolTable.getOrdinalByHashCode(nameHash)) == -1
) {
if ((context.features & WriteNameAsSymbol.mask) == 0) {
System.arraycopy(name, 0, bytes, off, name.length);
this.off = off + name.length;
return;
}
boolean symbolExists = false;
if (symbols != null) {
if ((symbol = symbols.putIfAbsent(nameHash, symbolIndex)) != symbolIndex) {
symbolExists = true;
} else {
symbolIndex++;
}
} else {
(symbols = new TLongIntHashMap())
.put(nameHash, symbol = symbolIndex++);
}
if (!symbolExists) {
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;
} else {
off = JSONB.IO.writeInt32(bytes, off, symbol);
}
this.off = off;
return;
}
symbol = -symbol;
}
bytes[off++] = BC_SYMBOL;
int intValue = -symbol;
if (intValue >= BC_INT32_NUM_MIN && intValue <= BC_INT32_NUM_MAX) {
bytes[off++] = (byte) intValue;
} else {
off = JSONB.IO.writeInt32(bytes, off, intValue);
}
this.off = off;
}
@Override
public void writeLocalDate(LocalDate date) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 5 > bytes.length) {
bytes = grow(off + 5);
}
this.off = JSONB.IO.writeLocalDate(bytes, off, date);
}
@Override
public void writeLocalTime(LocalTime time) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 9 > bytes.length) {
bytes = grow(off + 9);
}
this.off = JSONB.IO.writeLocalTime(bytes, off, time);
}
@Override
public void writeLocalDateTime(LocalDateTime dateTime) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 13 > bytes.length) {
bytes = grow(off + 13);
}
this.off = JSONB.IO.writeLocalDateTime(bytes, off, dateTime);
}
@Override
public void writeZonedDateTime(ZonedDateTime dateTime) {
if (dateTime == null) {
writeNull();
return;
}
int off = this.off;
byte[] bytes = this.bytes;
if (off + 13 > bytes.length) {
bytes = grow(off + 13);
}
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 = JSONB.IO.writeInt32(bytes, off + 8, dateTime.getNano());
String zoneIdStr = dateTime.getZone().getId();
if (zoneIdStr.equals(SHANGHAI_ZONE_ID_NAME)) {
writeRaw(SHANGHAI_ZONE_ID_NAME_BYTES);
} else {
writeString(zoneIdStr);
}
}
@Override
public void writeOffsetDateTime(OffsetDateTime dateTime) {
int off = this.off;
byte[] bytes = this.bytes;
int minCapacity = off + 21;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeOffsetDateTime(bytes, off, dateTime);
}
@Override
public void writeOffsetTime(OffsetTime offsetTime) {
int off = this.off;
byte[] bytes = this.bytes;
int minCapacity = off + 21;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeOffsetTime(bytes, off, offsetTime);
}
@Override
public void writeInstant(Instant instant) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 15 > bytes.length) {
bytes = grow(off + 15);
}
this.off = JSONB.IO.writeInstant(bytes, off, instant);
}
@Override
public void writeUUID(UUID value) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 18 > bytes.length) {
bytes = grow(off + 18);
}
this.off = JSONB.IO.writeUUID(bytes, off, value);
}
@Override
public void writeBigInt(BigInteger value, long features) {
if (value == null) {
writeNull();
return;
}
int off = this.off;
byte[] bytes = this.bytes;
if (isInt64(value)) {
if (off + 10 > bytes.length) {
bytes = grow(off + 10);
}
bytes[off] = BC_BIGINT_LONG;
this.off = JSONB.IO.writeInt64(bytes, off + 1, value.longValue());
return;
}
byte[] valueBytes = value.toByteArray();
int minCapacity = off + 5 + valueBytes.length;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
bytes[off] = BC_BIGINT;
off = JSONB.IO.writeInt32(bytes, off + 1, valueBytes.length);
System.arraycopy(valueBytes, 0, bytes, off, valueBytes.length);
this.off = off + valueBytes.length;
}
@Override
public void writeBinary(byte[] binary) {
if (binary == null) {
writeNull();
return;
}
int off = this.off;
int len = binary.length;
int minCapacity = off + 6 + len;
byte[] bytes = this.bytes;
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
bytes[off] = BC_BINARY;
off = JSONB.IO.writeInt32(bytes, off + 1, len);
System.arraycopy(binary, 0, bytes, off, len);
this.off = off + len;
}
@Override
public void writeDecimal(BigDecimal value, long features, DecimalFormat format) {
if (value == null) {
writeNull();
return;
}
int precision = value.precision();
int scale = value.scale();
int off = this.off;
byte[] bytes = this.bytes;
if (off + 15 > bytes.length) {
bytes = grow(off + 15);
}
if (precision < 19 && FIELD_DECIMAL_INT_COMPACT_OFFSET != -1) {
long intCompact = UNSAFE.getLong(value, FIELD_DECIMAL_INT_COMPACT_OFFSET);
if (scale == 0) {
bytes[off] = BC_DECIMAL_LONG;
this.off = JSONB.IO.writeInt64(bytes, off + 1, intCompact);
return;
}
bytes[off] = BC_DECIMAL;
off = JSONB.IO.writeInt32(bytes, off + 1, scale);
if (intCompact >= Integer.MIN_VALUE && intCompact <= Integer.MAX_VALUE) {
off = JSONB.IO.writeInt32(bytes, off, (int) intCompact);
} else {
off = JSONB.IO.writeInt64(bytes, off, intCompact);
}
this.off = off;
return;
}
BigInteger unscaledValue = value.unscaledValue();
if (scale == 0
&& isInt64(unscaledValue)) {
bytes[off] = BC_DECIMAL_LONG;
this.off = JSONB.IO.writeInt64(bytes, off + 1, unscaledValue.longValue());
return;
}
bytes[off] = BC_DECIMAL;
off = JSONB.IO.writeInt32(bytes, off + 1, scale);
if (isInt32(unscaledValue)) {
off = JSONB.IO.writeInt32(bytes, off, unscaledValue.intValue());
} else if (isInt64(unscaledValue)) {
off = JSONB.IO.writeInt64(bytes, off, unscaledValue.longValue());
} else {
this.off = off;
writeBigInt(unscaledValue, 0);
return;
}
this.off = off;
}
@Override
public void writeBool(boolean value) {
writeRaw(value ? BC_TRUE : BC_FALSE);
}
@Override
public void writeBool(boolean[] values) {
int off = this.off;
byte[] bytes = this.bytes;
int minCapacity = off + (values == null ? 1 : (5 + values.length));
if (minCapacity > bytes.length) {
bytes = grow(minCapacity);
}
this.off = JSONB.IO.writeBoolean(bytes, off, values);
}
@Override
public void writeReference(String path) {
int off = this.off;
grow1(off)[off] = BC_REFERENCE;
this.off = off + 1;
writeString(path == this.lastReference ? "#-1" : path);
this.lastReference = path;
}
@Override
public void writeDateTime14(
int year,
int month,
int dayOfMonth,
int hour,
int minute,
int second
) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 9 > bytes.length) {
bytes = grow(off + 9);
}
putIntBE(bytes, off, (BC_LOCAL_DATETIME << 24) | ((year & 0xFFFF) << 8) | month);
putIntBE(bytes, off + 4, (dayOfMonth << 24) | (hour << 16) | (minute << 8) | second);
bytes[off + 8] = BC_INT32_NUM_0;
this.off = off + 9;
}
@Override
public void writeDateTime19(
int year,
int month,
int dayOfMonth,
int hour,
int minute,
int second
) {
writeDateTime14(year, month, dayOfMonth, hour, minute, second);
}
@Override
public void writeDateTimeISO8601(
int year,
int month,
int dayOfMonth,
int hour,
int minute,
int second,
int millis,
int offsetSeconds,
boolean timeZone
) {
throw new JSONException("unsupported operation");
}
@Override
public void writeDateYYYMMDD8(int year, int month, int dayOfMonth) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 5 > bytes.length) {
bytes = grow(off + 5);
}
bytes[off] = BC_LOCAL_DATE;
putIntBE(bytes, off + 1, (year << 16) | (month << 8) | dayOfMonth);
this.off = off + 5;
}
@Override
public void writeDateYYYMMDD10(int year, int month, int dayOfMonth) {
writeDateYYYMMDD8(year, month, dayOfMonth);
}
@Override
public void writeTimeHHMMSS8(int hour, int minute, int second) {
throw new JSONException("unsupported operation");
}
@Override
public void writeBase64(byte[] bytes) {
throw new JSONException("UnsupportedOperation");
}
@Override
public void writeHex(byte[] bytes) {
writeBinary(bytes);
}
@Override
public void writeRaw(char ch) {
throw new JSONException("UnsupportedOperation");
}
@Override
public void writeNameRaw(byte[] bytes) {
writeRaw(bytes);
}
@Override
public void writeName2Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 2;
}
@Override
public void writeName3Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 3;
}
@Override
public void writeName4Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 4;
}
@Override
public void writeName5Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 5;
}
@Override
public void writeName6Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 6;
}
@Override
public void writeName7Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 7;
}
@Override
public void writeName8Raw(long name) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 8 > bytes.length) {
bytes = grow(off + 8);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name);
this.off = off + 8;
}
@Override
public void writeName9Raw(long name0, int name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 12 > bytes.length) {
bytes = grow(off + 12);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putInt(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 9;
}
@Override
public void writeName10Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 10;
}
@Override
public void writeName11Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 11;
}
@Override
public void writeName12Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 12;
}
@Override
public void writeName13Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 13;
}
@Override
public void writeName14Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 14;
}
@Override
public void writeName15Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 15;
}
@Override
public void writeName16Raw(long name0, long name1) {
int off = this.off;
byte[] bytes = this.bytes;
if (off + 16 > bytes.length) {
bytes = grow(off + 16);
}
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off, name0);
UNSAFE.putLong(bytes, ARRAY_BYTE_BASE_OFFSET + off + 8, name1);
this.off = off + 16;
}
@Override
public void writeNameRaw(char[] chars) {
throw new JSONException("UnsupportedOperation");
}
@Override
public void writeNameRaw(char[] bytes, int offset, int len) {
throw new JSONException("UnsupportedOperation");
}
@Override
public void writeColon() {
throw new JSONException("UnsupportedOperation");
}
@Override
public void write(List array) {
if (array == null) {
writeArrayNull();
return;
}
final int size = array.size();
startArray(size);
for (int i = 0; i < array.size(); i++) {
Object item = array.get(i);
writeAny(item);
}
}
@Override
public void write(Map map) {
if (map == null) {
writeNull();
return;
}
startObject();
for (Map.Entry entry : (Iterable<Map.Entry>) map.entrySet()) {
writeAny(entry.getKey());
writeAny(entry.getValue());
}
endObject();
}
@Override
public byte[] getBytes() {
return Arrays.copyOf(bytes, off);
}
@Override
public int size() {
return off;
}
@Override
public byte[] getBytes(Charset charset) {
throw new JSONException("not support operator");
}
@Override
public int flushTo(OutputStream to) throws IOException {
int len = off;
to.write(bytes, 0, off);
off = 0;
return len;
}
@Override
public int flushTo(OutputStream out, Charset charset) {
throw new JSONException("UnsupportedOperation");
}
@Override
public String toString() {
if (off == 0) {
return "<empty>";
}
byte[] jsonbBytes = getBytes();
JSONReader reader = JSONReader.ofJSONB(jsonbBytes);
JSONWriter writer = JSONWriter.of();
try {
Object object = reader.readAny();
writer.writeAny(object);
return writer.toString();
} catch (Exception ex) {
return JSONB.typeName(bytes[0]) + ", bytes length " + off;
}
}
@Override
public void println() {
}
}