JSONB.java
package com.alibaba.fastjson2;
import com.alibaba.fastjson2.filter.Filter;
import com.alibaba.fastjson2.internal.trove.map.hash.TLongIntHashMap;
import com.alibaba.fastjson2.reader.ObjectReader;
import com.alibaba.fastjson2.reader.ObjectReaderBean;
import com.alibaba.fastjson2.reader.ObjectReaderProvider;
import com.alibaba.fastjson2.util.*;
import com.alibaba.fastjson2.writer.ObjectWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
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.*;
import static com.alibaba.fastjson2.JSONWriter.Feature.*;
import static com.alibaba.fastjson2.util.IOUtils.*;
import static com.alibaba.fastjson2.util.JDKUtils.*;
/**
* This is the main entry point for using fastjson2 binary format (JSONB) API.
* JSONB is a high-performance binary serialization format that is more compact and faster than JSON text format.
*
* <p>JSONB binary format specification:
* <pre>
* x90 # type_char int
* x91 # binary len_int32 bytes
* x92 # type [str] symbol_int32 jsonb
* x93 # reference
*
* x94 - xa3 # array_0 - array_15
* xa4 # array len_int32 item*
*
* xa5 # object_end
* xa6 # object_start
*
* xa7 # local time b0 b1 b2
* xa8 # local datetime b0 b1 b2 b3 b4 b5 b6
* xa9 # local date b0 b1 b2 b3
* xab # timestamp millis b0 b1 b2 b3 b4 b5 b6 b7
* xac # timestamp seconds b0 b1 b2 b3
* xad # timestamp minutes b0 b1 b2 b3
* xae # timestamp b0 b1 b2 b3 b4 b5 b6 b7 nano_int32
*
* xaf # null
* xb0 # boolean false
* xb1 # boolean true
* xb2 # double 0
* xb3 # double 1
* xb4 # double_long
* xb5 # double
* xb6 # float_int
* xb7 # float
* xb8 # decimal_long
* xb9 # decimal
* xba # bigint_long
* xbb # bigint
* xbc # short
* xbd # byte
* xbe # long
* xbf # long encoded as 32-bit int
* xc0 - xc7 # three-octet compact long (-x40000 to x3ffff)
* xc8 - xd7 # two-octet compact long (-x800 to x7ff, xd0 is 0)
* xd8 - xef # one-octet compact long (-x8 to xf, e0 is 0)
*
* xf0 - xff # one-octet compact int
* x00 - x2f # one-octet compact int
*
* x30 - x3f # two-octet compact int (-x800 to x7ff)
* x40 - x47 # three-octet compact int (-x40000 to x3ffff)
* x48 # 32-bit signed integer ('I')
*
* x49 - x78 # ascii string length 0-47
* x79 # ascii-8 string variable-length
* x7a # utf-8 string variable-length
* x7b # utf-16 string variable-length
* x7c # utf-16LE string variable-length
* x7d # utf-16BE string variable-length
* x7e # gb18030 string variable-length
* x7f # symbol
* </pre>
*
* <p>Example usage:
* <pre>
* // 1. Convert object to JSONB bytes
* User user = new User(1L, "John", 30);
* byte[] jsonbBytes = JSONB.toBytes(user);
*
* // 2. Parse JSONB bytes to object
* User parsedUser = JSONB.parseObject(jsonbBytes, User.class);
*
* // 3. Convert primitive values to JSONB bytes
* byte[] intBytes = JSONB.toBytes(123);
* byte[] strBytes = JSONB.toBytes("Hello World");
* byte[] boolBytes = JSONB.toBytes(true);
*
* // 4. Parse JSONB bytes with features
* User user = JSONB.parseObject(jsonbBytes, User.class, JSONReader.Feature.FieldBased);
*
* // 5. Convert JSONB bytes to JSON string for debugging
* String jsonString = JSONB.toJSONString(jsonbBytes);
*
* // 6. Parse JSONB bytes to JSONObject
* JSONObject jsonObject = JSONB.parseObject(jsonbBytes);
*
* // 7. Parse JSONB bytes to JSONArray
* JSONArray jsonArray = JSONB.parseArray(jsonbBytes);
*
* // 8. Parse JSONB bytes to List
* List<User> userList = JSONB.parseArray(jsonbBytes, User.class);
* </pre>
*
* @since 2.0.0
*/
public interface JSONB {
/**
* Dumps the JSONB bytes to standard output for debugging purposes
*
* @param jsonbBytes the JSONB bytes to dump
*/
static void dump(byte[] jsonbBytes) {
System.out.println(
JSONB.toJSONString(jsonbBytes, true)
);
}
/**
* Dumps the JSONB bytes to standard output for debugging purposes with a symbol table
*
* @param jsonbBytes the JSONB bytes to dump
* @param symbolTable the symbol table to use
*/
static void dump(byte[] jsonbBytes, SymbolTable symbolTable) {
JSONBDump dump = new JSONBDump(jsonbBytes, symbolTable, true);
String str = dump.toString();
System.out.println(str);
}
/**
* Constants for JSONB binary format specification
*
* @since 2.0.0
*/
interface Constants {
/** Binary character type int */
byte BC_CHAR = -112; // 0x90
/** Binary data with length */
byte BC_BINARY = -111; // 0x91
/** Typed object with symbol */
byte BC_TYPED_ANY = -110; // 0x92
/** Reference to previously serialized object */
byte BC_REFERENCE = -109; // 0x93
/** Fixed array length */
int ARRAY_FIX_LEN = 15;
/** Fixed array with 0 elements */
byte BC_ARRAY_FIX_0 = -108; // 0x94
/** Minimum fixed array marker */
byte BC_ARRAY_FIX_MIN = BC_ARRAY_FIX_0;
/** Maximum fixed array marker */
byte BC_ARRAY_FIX_MAX = BC_ARRAY_FIX_MIN + ARRAY_FIX_LEN; // -105
/** Variable length array */
byte BC_ARRAY = -92; // 0xa4 len_int32 item*
/** Object end marker */
byte BC_OBJECT_END = -91; // 0xa5
/** Object start marker */
byte BC_OBJECT = -90; // 0xa6
/** Local time */
byte BC_LOCAL_TIME = -89; // 0xa7 b0 b1 b2 nano_int32
/** Local datetime */
byte BC_LOCAL_DATETIME = -88; // 0xa8 b0 b1 b2 b3 b4 b5 b6 nano_int32
/** Local date */
byte BC_LOCAL_DATE = -87; // 0xa9 b0 b1 b2 b3
/** Timestamp with timezone */
byte BC_TIMESTAMP_WITH_TIMEZONE = -86; // 0xaa b0 b1 b2 b3 b4 b5 b6 b7 str_zone
/** Timestamp in milliseconds */
byte BC_TIMESTAMP_MILLIS = -85; // 0xab b0 b1 b2 b3 b4 b5 b6 b7
/** Timestamp in seconds */
byte BC_TIMESTAMP_SECONDS = -84; // 0xac b0 b1 b2 b3
/** Timestamp in minutes */
byte BC_TIMESTAMP_MINUTES = -83; // 0xad b0 b1 b2 b3
/** Timestamp */
byte BC_TIMESTAMP = -82; // 0xae millis_8 + nano_int32
/** Null value */
byte BC_NULL = -81; // 0xaf
/** Boolean false */
byte BC_FALSE = -80; // 0xb0
/** Boolean true */
byte BC_TRUE = -79; // 0xb1
/** Double 0 */
byte BC_DOUBLE_NUM_0 = -78; // 0xb2
/** Double 1 */
byte BC_DOUBLE_NUM_1 = -77; // 0xb3
/** Double as long */
byte BC_DOUBLE_LONG = -76; // 0xb4
/** Double */
byte BC_DOUBLE = -75; // 0xb5
/** Float as int */
byte BC_FLOAT_INT = -74; // 0xb6
/** Float */
byte BC_FLOAT = -73; // 0xb7
/** Decimal as long */
byte BC_DECIMAL_LONG = -72; // 0xb8
/** Decimal */
byte BC_DECIMAL = -71; // 0xb9
/** BigInteger as long */
byte BC_BIGINT_LONG = -70; // 0xba
/** BigInteger */
byte BC_BIGINT = -69; // 0xbb
/** Short */
byte BC_INT16 = -68; // 0xbc b0 b1
/** Byte */
byte BC_INT8 = -67; // 0xbd b0
/** Long */
byte BC_INT64 = -66; // 0xbe b0 b1 b2 b3 b4 b5 b6 b7
/** Long as int */
byte BC_INT64_INT = -65; // 0xbf b0 b1 b2 b3
/** Minimum 3-byte compact long */
int INT64_SHORT_MIN = -0x40000; // -262144
/** Maximum 3-byte compact long */
int INT64_SHORT_MAX = 0x3ffff; // 262143
/** Minimum 2-byte compact long */
int INT64_BYTE_MIN = -0x800; // -2048
/** Maximum 2-byte compact long */
int INT64_BYTE_MAX = 0x7ff; // 2047
/** Minimum 3-byte compact long marker */
byte BC_INT64_SHORT_MIN = -64; // 0xc0
/** Zero 3-byte compact long marker */
byte BC_INT64_SHORT_ZERO = -60; //
/** Maximum 3-byte compact long marker */
byte BC_INT64_SHORT_MAX = -57; // 0xc7
/** Minimum 2-byte compact long marker */
byte BC_INT64_BYTE_MIN = -56; // 0xc8
/** Zero 2-byte compact long marker */
byte BC_INT64_BYTE_ZERO = -48;
/** Maximum 2-byte compact long marker */
byte BC_INT64_BYTE_MAX = -41; // 0xd7
/** Minimum 1-byte compact long marker */
byte BC_INT64_NUM_MIN = -40; // 0xd8 -8
/** Maximum 1-byte compact long marker */
byte BC_INT64_NUM_MAX = -17; // 0xef 15
/** Minimum 1-byte compact long value */
int INT64_NUM_LOW_VALUE = -8; // -8
/** Maximum 1-byte compact long value */
int INT64_NUM_HIGH_VALUE = 15; // 15
/** Integer 0 */
byte BC_INT32_NUM_0 = 0;
/** Integer 1 */
byte BC_INT32_NUM_1 = 1;
/** Integer 16 */
byte BC_INT32_NUM_16 = 16;
/** Minimum 1-byte compact int */
byte BC_INT32_NUM_MIN = -16; // 0xf0
/** Maximum 1-byte compact int */
byte BC_INT32_NUM_MAX = 47; // 0x2f
/** Minimum 2-byte compact int marker */
byte BC_INT32_BYTE_MIN = 48; // 0x30
/** Zero 2-byte compact int marker */
byte BC_INT32_BYTE_ZERO = 56; // 0x38
/** Maximum 2-byte compact int marker */
byte BC_INT32_BYTE_MAX = 63; // 0x3f
/** Minimum 3-byte compact int marker */
byte BC_INT32_SHORT_MIN = 64; // 0x40
/** Zero 3-byte compact int marker */
byte BC_INT32_SHORT_ZERO = 68;
/** Maximum 3-byte compact int marker */
byte BC_INT32_SHORT_MAX = 71; // 0x47
/** 32-bit signed integer */
byte BC_INT32 = 72; // 0x48
/** Minimum 2-byte compact int value */
int INT32_BYTE_MIN = -0x800; // -2048
/** Maximum 2-byte compact int value */
int INT32_BYTE_MAX = 0x7ff; // 2047
/** Minimum 3-byte compact int value */
int INT32_SHORT_MIN = -0x40000; // -262144
/** Maximum 3-byte compact int value */
int INT32_SHORT_MAX = 0x3ffff; // 262143
/** ASCII string with 0 characters */
byte BC_STR_ASCII_FIX_0 = 73;
/** ASCII string with 1 character */
byte BC_STR_ASCII_FIX_1 = 74;
/** ASCII string with 4 characters */
byte BC_STR_ASCII_FIX_4 = 77;
/** ASCII string with 5 characters */
byte BC_STR_ASCII_FIX_5 = 78;
/** ASCII string with 32 characters */
byte BC_STR_ASCII_FIX_32 = 105;
/** ASCII string with 36 characters */
byte BC_STR_ASCII_FIX_36 = 109;
/** Fixed ASCII string length */
int STR_ASCII_FIX_LEN = 47;
/** Minimum fixed ASCII string marker */
byte BC_STR_ASCII_FIX_MIN = 73; // 0x49
/** Maximum fixed ASCII string marker */
byte BC_STR_ASCII_FIX_MAX = BC_STR_ASCII_FIX_MIN + STR_ASCII_FIX_LEN; // 120 0x78
/** Variable length ASCII string */
byte BC_STR_ASCII = 121;
/** UTF-8 string */
byte BC_STR_UTF8 = 122;
/** UTF-16 string */
byte BC_STR_UTF16 = 123;
/** UTF-16LE string */
byte BC_STR_UTF16LE = 124;
/** UTF-16BE string */
byte BC_STR_UTF16BE = 125;
/** GB18030 string */
byte BC_STR_GB18030 = 126;
/** Symbol */
byte BC_SYMBOL = 127;
}
/**
* Converts a boolean value to JSONB bytes
*
* @param v the boolean value to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(boolean v) {
return new byte[]{v ? BC_TRUE : BC_FALSE};
}
/**
* Converts an integer value to JSONB bytes
*
* @param i the integer value to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(int i) {
if (i >= BC_INT32_NUM_MIN && i <= BC_INT32_NUM_MAX) {
return new byte[]{(byte) i};
}
try (JSONWriter jsonWriter = JSONWriter.ofJSONB()) {
jsonWriter.writeInt32(i);
return jsonWriter.getBytes();
}
}
/**
* Converts a byte value to JSONB bytes
*
* @param i the byte value to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(byte i) {
try (JSONWriter jsonWriter = JSONWriter.ofJSONB()) {
jsonWriter.writeInt8(i);
return jsonWriter.getBytes();
}
}
/**
* Converts a short value to JSONB bytes
*
* @param i the short value to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(short i) {
try (JSONWriter jsonWriter = JSONWriter.ofJSONB()) {
jsonWriter.writeInt16(i);
return jsonWriter.getBytes();
}
}
/**
* Converts a long value to JSONB bytes
*
* @param i the long value to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(long i) {
if (i >= INT64_NUM_LOW_VALUE && i <= INT64_NUM_HIGH_VALUE) {
return new byte[]{
(byte) (BC_INT64_NUM_MIN + (i - INT64_NUM_LOW_VALUE))
};
}
try (JSONWriter jsonWriter = JSONWriter.ofJSONB()) {
jsonWriter.writeInt64(i);
return jsonWriter.getBytes();
}
}
/**
* Parses JSONB bytes to an object using the specified context
*
* @param jsonbBytes the JSONB bytes to parse
* @param context the JSON reader context
* @return the parsed object
* @since 2.0.46
*/
static Object parse(byte[] jsonbBytes, JSONReader.Context context) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object = reader.readAnyObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to an object with specified features
*
* @param jsonbBytes the JSONB bytes to parse
* @param features the JSON reader features to apply
* @return the parsed object
*/
static Object parse(byte[] jsonbBytes, JSONReader.Feature... features) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(getDefaultObjectReaderProvider(), features),
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object = reader.readAnyObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB from an input stream to an object using the specified context
*
* @param in the input stream to parse from
* @param context the JSON reader context
* @return the parsed object
*/
static Object parse(InputStream in, JSONReader.Context context) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(context, in)) {
Object object = reader.readAny();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to an object with a symbol table and features
*
* @param jsonbBytes the JSONB bytes to parse
* @param symbolTable the symbol table to use
* @param features the JSON reader features to apply
* @return the parsed object
*/
static Object parse(byte[] jsonbBytes, SymbolTable symbolTable, JSONReader.Feature... features) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(getDefaultObjectReaderProvider(), symbolTable, features),
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object = reader.readAnyObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to a JSONObject
*
* @param jsonbBytes the JSONB bytes to parse
* @return the parsed JSONObject
*/
static JSONObject parseObject(byte[] jsonbBytes) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider()),
jsonbBytes,
0,
jsonbBytes.length)
) {
JSONObject object = (JSONObject) reader.readObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to a JSONObject with specified features
*
* @param jsonbBytes the JSONB bytes to parse
* @param features the JSON reader features to apply
* @return the parsed JSONObject
*/
static JSONObject parseObject(byte[] jsonbBytes, JSONReader.Feature... features) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider(), features),
jsonbBytes,
0,
jsonbBytes.length)
) {
JSONObject object = (JSONObject) reader.readObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to a JSONObject using the specified context
*
* @param in the input stream to parse from
* @param context the JSON reader context
* @return the parsed JSONObject
*/
static JSONObject parseObject(InputStream in, JSONReader.Context context) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(context, in)) {
JSONObject object = (JSONObject) reader.readObject();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to a JSONArray
*
* @param jsonbBytes the JSONB bytes to parse
* @return the parsed JSONArray
*/
static JSONArray parseArray(byte[] jsonbBytes) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider()),
jsonbBytes,
0,
jsonbBytes.length)
) {
JSONArray array = (JSONArray) reader.readArray();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(array);
}
return array;
}
}
/**
* Parses JSONB from an input stream to a JSONArray using the specified context
*
* @param in the input stream to parse from
* @param context the JSON reader context
* @return the parsed JSONArray
*/
static JSONArray parseArray(InputStream in, JSONReader.Context context) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(context, in)) {
JSONArray array = (JSONArray) reader.readArray();
if (reader.resolveTasks != null) {
reader.handleResolveTasks(array);
}
return array;
}
}
/**
* Parses JSONB bytes to a list of objects of the specified type
*
* @param <T> the type of objects in the list
* @param jsonbBytes the JSONB bytes to parse
* @param type the type of objects in the list
* @return the parsed list of objects
*/
static <T> List<T> parseArray(byte[] jsonbBytes, Type type) {
if (jsonbBytes == null || jsonbBytes.length == 0) {
return null;
}
Type paramType = new ParameterizedTypeImpl(
new Type[]{type}, null, List.class
);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider()),
jsonbBytes,
0,
jsonbBytes.length)
) {
List<T> list = reader.read(paramType);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(list);
}
return list;
}
}
/**
* Parses JSONB bytes to a list of objects of the specified type with features
*
* @param <T> the type of objects in the list
* @param jsonbBytes the JSONB bytes to parse
* @param type the type of objects in the list
* @param features the JSON reader features to apply
* @return the parsed list of objects
*/
static <T> List<T> parseArray(byte[] jsonbBytes, Type type, JSONReader.Feature... features) {
if (jsonbBytes == null || jsonbBytes.length == 0) {
return null;
}
Type paramType = new ParameterizedTypeImpl(
new Type[]{type}, null, List.class
);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider(), features),
jsonbBytes,
0,
jsonbBytes.length)
) {
List<T> list = reader.read(paramType);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(list);
}
return list;
}
}
/**
* Parses JSONB bytes to a list of objects with specified types
*
* @param <T> the type of objects in the list
* @param jsonbBytes the JSONB bytes to parse
* @param types the types of objects in the list
* @return the parsed list of objects
*/
static <T> List<T> parseArray(byte[] jsonbBytes, Type... types) {
if (jsonbBytes == null || jsonbBytes.length == 0) {
return null;
}
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider()),
jsonbBytes,
0,
jsonbBytes.length)
) {
List<T> list = reader.readList(types);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(list);
}
return list;
}
}
/**
* Parses JSONB bytes to a list of objects with specified types and features
*
* @param <T> the type of objects in the list
* @param jsonbBytes the JSONB bytes to parse
* @param types the types of objects in the list
* @param features the JSON reader features to apply
* @return the parsed list of objects
*/
static <T> List<T> parseArray(byte[] jsonbBytes, Type[] types, JSONReader.Feature... features) {
if (jsonbBytes == null || jsonbBytes.length == 0) {
return null;
}
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(JSONFactory.getDefaultObjectReaderProvider(), features),
jsonbBytes,
0, jsonbBytes.length)
) {
List<T> list = reader.readList(types);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(list);
}
return list;
}
}
/**
* Parses JSONB bytes to an object of the specified class
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectClass the class of the object to parse to
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Class<T> objectClass) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
new JSONReader.Context(provider),
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object;
if (objectClass == Object.class) {
object = jsonReader.readAny();
} else {
ObjectReader objectReader = provider.getObjectReader(
objectClass,
(defaultReaderFeatures & JSONReader.Feature.FieldBased.mask) != 0
);
object = objectReader.readJSONBObject(jsonReader, objectClass, null, 0);
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB bytes to an object of the specified type
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectType the type of the object to parse to
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Type objectType) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
ObjectReader objectReader = provider.getObjectReader(objectType);
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
new JSONReader.Context(provider),
jsonbBytes,
0,
jsonbBytes.length)
) {
T object = (T) objectReader.readJSONBObject(jsonReader, objectType, null, 0);
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to an object with specified types
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param types the types of the object to parse to
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Type... types) {
return parseObject(jsonbBytes, new MultiType(types));
}
/**
* Parses JSONB bytes to an object of the specified type with a symbol table
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectType the type of the object to parse to
* @param symbolTable the symbol table to use
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Type objectType, SymbolTable symbolTable) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
ObjectReader objectReader = provider.getObjectReader(objectType);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
new JSONReader.Context(provider, symbolTable),
jsonbBytes,
0,
jsonbBytes.length)
) {
T object = (T) objectReader.readJSONBObject(reader, objectType, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to an object of the specified type with a symbol table and features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectType the type of the object to parse to
* @param symbolTable the symbol table to use
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
Type objectType,
SymbolTable symbolTable,
JSONReader.Feature... features
) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, symbolTable, features);
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectType, fieldBased);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
T object = (T) objectReader.readJSONBObject(reader, objectType, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes to an object of the specified class with a filter and features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectClass the class of the object to parse to
* @param filter the filter to apply
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
Class<T> objectClass,
Filter filter,
JSONReader.Feature... features
) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, filter, features);
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
for (int i = 0; i < features.length; i++) {
context.features |= features[i].mask;
}
Object object;
if (objectClass == Object.class) {
object = jsonReader.readAnyObject();
} else {
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectClass, fieldBased);
object = objectReader.readJSONBObject(jsonReader, objectClass, null, 0);
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB bytes to an object of the specified type with a symbol table, filters and features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectType the type of the object to parse to
* @param symbolTable the symbol table to use
* @param filters the filters to apply
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
Type objectType,
SymbolTable symbolTable,
Filter[] filters,
JSONReader.Feature... features) {
if (jsonbBytes == null || jsonbBytes.length == 0) {
return null;
}
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, symbolTable, filters, features);
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
for (int i = 0; i < features.length; i++) {
context.features |= features[i].mask;
}
Object object;
if (objectType == Object.class) {
object = jsonReader.readAnyObject();
} else {
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectType, fieldBased);
object = objectReader.readJSONBObject(jsonReader, objectType, null, 0);
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Creates a deep copy of the specified object
*
* @param <T> the type of the object
* @param object the object to copy
* @param features the JSON writer features to apply
* @return a deep copy of the object
* @since 2.0.30
*/
static <T> T copy(T object, JSONWriter.Feature... features) {
return JSON.copy(object, features);
}
/**
* Parses JSONB bytes to an object of the specified type reference
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param typeReference the type reference of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, TypeReference typeReference, JSONReader.Feature... features) {
return parseObject(jsonbBytes, typeReference.getType(), features);
}
/**
* Parses JSONB from an input stream to an object of the specified class
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param objectClass the class of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
* @throws IOException if an I/O error occurs
* @since 2.0.30
*/
static <T> T parseObject(
InputStream in,
Class objectClass,
JSONReader.Feature... features
) throws IOException {
return parseObject(in, objectClass, JSONFactory.createReadContext(features));
}
/**
* Parses JSONB from an input stream to an object of the specified type
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param objectType the type of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
* @throws IOException if an I/O error occurs
* @since 2.0.30
*/
static <T> T parseObject(
InputStream in,
Type objectType,
JSONReader.Feature... features
) throws IOException {
return parseObject(in, objectType, JSONFactory.createReadContext(features));
}
/**
* Parses JSONB from an input stream to an object of the specified type using the specified context
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param objectType the type of the object to parse to
* @param context the JSON reader context
* @return the parsed object
* @since 2.0.30
*/
static <T> T parseObject(
InputStream in,
Type objectType,
JSONReader.Context context
) {
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(context, in)
) {
Object object;
if (objectType == Object.class) {
object = jsonReader.readAny();
} else {
ObjectReader objectReader = context.getObjectReader(objectType);
object = objectReader.readJSONBObject(jsonReader, objectType, null, 0);
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB from an input stream to an object of the specified class using the specified context
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param objectClass the class of the object to parse to
* @param context the JSON reader context
* @return the parsed object
* @since 2.0.30
*/
static <T> T parseObject(
InputStream in,
Class objectClass,
JSONReader.Context context
) {
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(context, in)) {
Object object;
if (objectClass == Object.class) {
object = jsonReader.readAny();
} else {
ObjectReader objectReader = context.getObjectReader(objectClass);
object = objectReader.readJSONBObject(jsonReader, objectClass, null, 0);
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB from an input stream with specified length to an object of the specified type using the specified context
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param length the length of data to read
* @param objectType the type of the object to parse to
* @param context the JSON reader context
* @return the parsed object
* @throws IOException if an I/O error occurs
*/
static <T> T parseObject(
InputStream in,
int length,
Type objectType,
JSONReader.Context context
) throws IOException {
int cacheIndex = System.identityHashCode(Thread.currentThread()) & (CACHE_ITEMS.length - 1);
final CacheItem cacheItem = CACHE_ITEMS[cacheIndex];
byte[] bytes = BYTES_UPDATER.getAndSet(cacheItem, null);
if (bytes == null) {
bytes = new byte[8192];
}
try {
if (bytes.length < length) {
bytes = new byte[length];
}
int read = in.read(bytes, 0, length);
if (read != length) {
throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read);
}
return parseObject(bytes, 0, length, objectType, context);
} finally {
BYTES_UPDATER.lazySet(cacheItem, bytes);
}
}
/**
* Parses JSONB from an input stream with specified length to an object of the specified type with features
*
* @param <T> the type of the object
* @param in the input stream to parse from
* @param length the length of data to read
* @param objectType the type of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
* @throws IOException if an I/O error occurs
*/
static <T> T parseObject(
InputStream in,
int length,
Type objectType,
JSONReader.Feature... features
) throws IOException {
int cacheIndex = System.identityHashCode(Thread.currentThread()) & (CACHE_ITEMS.length - 1);
final CacheItem cacheItem = CACHE_ITEMS[cacheIndex];
byte[] bytes = BYTES_UPDATER.getAndSet(cacheItem, null);
if (bytes == null) {
bytes = new byte[8192];
}
try {
if (bytes.length < length) {
bytes = new byte[length];
}
int read = in.read(bytes, 0, length);
if (read != length) {
throw new IllegalArgumentException("deserialize failed. expected read length: " + length + " but actual read: " + read);
}
return parseObject(bytes, 0, length, objectType, features);
} finally {
BYTES_UPDATER.lazySet(cacheItem, bytes);
}
}
/**
* Parses JSONB bytes to an object of the specified class with features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectClass the class of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Class<T> objectClass, JSONReader.Feature... features) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, features);
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object;
if (objectClass == Object.class) {
object = jsonReader.readAnyObject();
} else {
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectClass, fieldBased);
if ((context.features & JSONReader.Feature.SupportArrayToBean.mask) != 0
&& jsonReader.isArray()
&& objectReader instanceof ObjectReaderBean
) {
object = objectReader.readArrayMappingJSONBObject(jsonReader, objectClass, null, 0);
} else {
object = objectReader.readJSONBObject(jsonReader, objectClass, null, 0);
}
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB bytes to an object of the specified class using the specified context
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectClass the class of the object to parse to
* @param context the JSON reader context
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Class<T> objectClass, JSONReader.Context context) {
try (JSONReaderJSONB jsonReader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)
) {
Object object;
if (objectClass == Object.class) {
object = jsonReader.readAnyObject();
} else {
ObjectReader objectReader = context.provider.getObjectReader(
objectClass,
(context.features & JSONReader.Feature.FieldBased.mask) != 0
);
if ((context.features & JSONReader.Feature.SupportArrayToBean.mask) != 0
&& jsonReader.isArray()
&& objectReader instanceof ObjectReaderBean
) {
object = objectReader.readArrayMappingJSONBObject(jsonReader, objectClass, null, 0);
} else {
object = objectReader.readJSONBObject(jsonReader, objectClass, null, 0);
}
}
if (jsonReader.resolveTasks != null) {
jsonReader.handleResolveTasks(object);
}
return (T) object;
}
}
/**
* Parses JSONB bytes to an object of the specified type with features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param objectClass the type of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, Type objectClass, JSONReader.Feature... features) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, features);
ObjectReader objectReader = provider.getObjectReader(
objectClass,
(context.features & JSONReader.Feature.FieldBased.mask) != 0
);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
0,
jsonbBytes.length)) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified class
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the class of the object to parse to
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, int off, int len, Class<T> objectClass) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider);
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectClass, fieldBased);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified type
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param type the type of the object to parse to
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, int off, int len, Type type) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider);
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(type, fieldBased);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, type, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified class with features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the class of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
int off,
int len,
Class<T> objectClass,
JSONReader.Feature... features
) {
ObjectReaderProvider provider = JSONFactory.getDefaultObjectReaderProvider();
JSONReader.Context context = new JSONReader.Context(provider, features);
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = provider.getObjectReader(objectClass, fieldBased);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified type using the specified context
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectType the type of the object to parse to
* @param context the JSON reader context
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
int off,
int len,
Type objectType,
JSONReader.Context context
) {
try (JSONReaderJSONB reader = new JSONReaderJSONB(context, jsonbBytes, off, len)) {
boolean fieldBased = (context.features & JSONReader.Feature.FieldBased.mask) != 0;
ObjectReader objectReader = context.provider.getObjectReader(objectType, fieldBased);
T object = (T) objectReader.readJSONBObject(reader, objectType, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified type with features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectType the type of the object to parse to
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, int off, int len, Type objectType, JSONReader.Feature... features) {
JSONReader.Context context = createReadContext(features);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
ObjectReader objectReader = reader.getObjectReader(objectType);
T object = (T) objectReader.readJSONBObject(reader, objectType, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified class with a symbol table
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the class of the object to parse to
* @param symbolTable the symbol table to use
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, int off, int len, Class<T> objectClass, SymbolTable symbolTable) {
JSONReader.Context context = createReadContext(symbolTable);
ObjectReader objectReader = context.getObjectReader(objectClass);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified type with a symbol table
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the type of the object to parse to
* @param symbolTable the symbol table to use
* @return the parsed object
*/
static <T> T parseObject(byte[] jsonbBytes, int off, int len, Type objectClass, SymbolTable symbolTable) {
JSONReader.Context context = createReadContext(symbolTable);
ObjectReader objectReader = context.getObjectReader(objectClass);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified class with a symbol table and features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the class of the object to parse to
* @param symbolTable the symbol table to use
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
int off,
int len,
Class<T> objectClass,
SymbolTable symbolTable,
JSONReader.Feature... features
) {
JSONReader.Context context = createReadContext(symbolTable, features);
ObjectReader objectReader = context.getObjectReader(objectClass);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Parses JSONB bytes with offset and length to an object of the specified type with a symbol table and features
*
* @param <T> the type of the object
* @param jsonbBytes the JSONB bytes to parse
* @param off the offset in the byte array
* @param len the length of data to parse
* @param objectClass the type of the object to parse to
* @param symbolTable the symbol table to use
* @param features the JSON reader features to apply
* @return the parsed object
*/
static <T> T parseObject(
byte[] jsonbBytes,
int off,
int len,
Type objectClass,
SymbolTable symbolTable,
JSONReader.Feature... features
) {
JSONReader.Context context = createReadContext(symbolTable, features);
ObjectReader objectReader = context.getObjectReader(objectClass);
try (JSONReaderJSONB reader = new JSONReaderJSONB(
context,
jsonbBytes,
off,
len)
) {
T object = (T) objectReader.readJSONBObject(reader, objectClass, null, 0);
if (reader.resolveTasks != null) {
reader.handleResolveTasks(object);
}
return object;
}
}
/**
* Converts a string to JSONB bytes
*
* @param str the string to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(String str) {
if (str == null) {
return new byte[]{BC_NULL};
}
if (JVM_VERSION == 8) {
char[] chars = JDKUtils.getCharArray(str);
int strlen = chars.length;
if (strlen <= STR_ASCII_FIX_LEN) {
boolean ascii = true;
for (int i = 0; i < strlen; ++i) {
if (chars[i] > 0x007F) {
ascii = false;
break;
}
}
if (ascii) {
byte[] bytes = new byte[chars.length + 1];
bytes[0] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
for (int i = 0; i < strlen; ++i) {
bytes[i + 1] = (byte) chars[i];
}
return bytes;
}
}
} else if (STRING_VALUE != null) {
int coder = STRING_CODER.applyAsInt(str);
if (coder == 0) {
byte[] value = STRING_VALUE.apply(str);
int strlen = value.length;
if (strlen <= STR_ASCII_FIX_LEN) {
byte[] bytes = new byte[value.length + 1];
bytes[0] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
System.arraycopy(value, 0, bytes, 1, value.length);
return bytes;
}
}
}
try (JSONWriterJSONB writer = new JSONWriterJSONB(
new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider),
null
)) {
writer.writeString(str);
return writer.getBytes();
}
}
/**
* Converts a string to JSONB bytes with specified charset
*
* @param str the string to convert
* @param charset the charset to use
* @return the JSONB bytes representation
*/
static byte[] toBytes(String str, Charset charset) {
if (str == null) {
return new byte[]{BC_NULL};
}
final byte type;
if (charset == StandardCharsets.UTF_16) {
type = BC_STR_UTF16;
} else if (charset == StandardCharsets.UTF_16BE) {
type = BC_STR_UTF16BE;
} else if (charset == StandardCharsets.UTF_16LE) {
type = BC_STR_UTF16LE;
} else if (charset == StandardCharsets.UTF_8) {
type = BC_STR_UTF8;
} else if (charset == StandardCharsets.US_ASCII || charset == StandardCharsets.ISO_8859_1) {
type = BC_STR_ASCII;
} else if (charset != null && "GB18030".equals(charset.name())) { // GraalVM support
type = BC_STR_GB18030;
} else {
return toBytes(str);
}
byte[] utf16 = str.getBytes(charset);
int byteslen = 2 + utf16.length;
if (utf16.length <= BC_INT32_NUM_MAX) {
byteslen += 0;
} else if (utf16.length <= INT32_BYTE_MAX) {
byteslen += 1;
} else if (utf16.length <= INT32_SHORT_MAX) {
byteslen += 2;
} else {
byteslen += 4;
}
byte[] bytes = new byte[byteslen];
bytes[0] = type;
int off = IO.writeInt32(bytes, 1, utf16.length);
System.arraycopy(utf16, 0, bytes, off, utf16.length);
return bytes;
}
/**
* Converts an object to JSONB bytes
*
* @param object the object to convert
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object) {
final JSONWriter.Context context = new JSONWriter.Context(defaultObjectWriterProvider);
try (JSONWriterJSONB writer = new JSONWriterJSONB(
context,
null
)) {
if (object == null) {
writer.writeNull();
} else {
Class<?> valueClass = object.getClass();
boolean fieldBased = (context.features & JSONWriter.Feature.FieldBased.mask) != 0;
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
objectWriter.writeJSONB(writer, object, null, null, 0);
}
return writer.getBytes();
}
}
/**
* Converts an object to JSONB bytes with specified context
*
* @param object the object to convert
* @param context the JSON writer context
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, JSONWriter.Context context) {
if (context == null) {
context = JSONFactory.createWriteContext();
}
try (JSONWriterJSONB writer = new JSONWriterJSONB(context, null)) {
if (object == null) {
writer.writeNull();
} else {
writer.rootObject = object;
writer.path = JSONWriter.Path.ROOT;
boolean fieldBased = (context.features & JSONWriter.Feature.FieldBased.mask) != 0;
Class<?> valueClass = object.getClass();
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
if ((context.features & JSONWriter.Feature.BeanToArray.mask) != 0) {
objectWriter.writeArrayMappingJSONB(writer, object, null, null, 0);
} else {
objectWriter.writeJSONB(writer, object, null, null, 0);
}
}
return writer.getBytes();
}
}
/**
* Converts an object to JSONB bytes with a symbol table
*
* @param object the object to convert
* @param symbolTable the symbol table to use
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, SymbolTable symbolTable) {
JSONWriter.Context context = new JSONWriter.Context(defaultObjectWriterProvider);
try (JSONWriterJSONB writer = new JSONWriterJSONB(
context,
symbolTable
)) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
ObjectWriter objectWriter = context.getObjectWriter(valueClass, valueClass);
objectWriter.writeJSONB(writer, object, null, null, 0);
}
return writer.getBytes();
}
}
/**
* Converts an object to JSONB bytes with a symbol table and features
*
* @param object the object to convert
* @param symbolTable the symbol table to use
* @param features the JSON writer features to apply
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, SymbolTable symbolTable, JSONWriter.Feature... features) {
return toBytes(object, new Context(), symbolTable, features);
}
/**
* Converts an object to JSONB bytes with specified context, symbol table and features
*
* @param object the object to convert
* @param context the JSON writer context
* @param symbolTable the symbol table to use
* @param features the JSON writer features to apply
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, JSONWriter.Context context, SymbolTable symbolTable, JSONWriter.Feature... features) {
try (JSONWriterJSONB writer = new JSONWriterJSONB(context, symbolTable)) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
boolean fieldBased = (context.features & JSONWriter.Feature.FieldBased.mask) != 0;
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
if ((context.features & JSONWriter.Feature.BeanToArray.mask) != 0) {
objectWriter.writeArrayMappingJSONB(writer, object, null, null, 0);
} else {
objectWriter.writeJSONB(writer, object, null, null, 0);
}
}
return writer.getBytes();
}
}
/**
* Converts an object to JSONB bytes with a symbol table, filters and features
*
* @param object the object to convert
* @param symbolTable the symbol table to use
* @param filters the filters to apply
* @param features the JSON writer features to apply
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, SymbolTable symbolTable, Filter[] filters, JSONWriter.Feature... features) {
JSONWriter.Context context = new JSONWriter.Context(defaultObjectWriterProvider, features);
context.configFilter(filters);
try (JSONWriterJSONB writer = new JSONWriterJSONB(
context,
symbolTable
)) {
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
boolean fieldBased = (context.features & JSONWriter.Feature.FieldBased.mask) != 0;
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
if ((context.features & JSONWriter.Feature.BeanToArray.mask) != 0) {
objectWriter.writeArrayMappingJSONB(writer, object, null, null, 0);
} else {
objectWriter.writeJSONB(writer, object, null, null, 0);
}
}
return writer.getBytes();
}
}
/**
* Converts an object to JSONB bytes with specified features
*
* @param object the object to convert
* @param features the JSON writer features to apply
* @return the JSONB bytes representation
*/
static byte[] toBytes(Object object, JSONWriter.Feature... features) {
JSONWriter.Context context = new JSONWriter.Context(defaultObjectWriterProvider, features);
try (JSONWriterJSONB writer = new JSONWriterJSONB(
context,
null
)) {
if (object == null) {
writer.writeNull();
} else {
writer.rootObject = object;
writer.path = JSONWriter.Path.ROOT;
boolean fieldBased = (context.features & JSONWriter.Feature.FieldBased.mask) != 0;
Class<?> valueClass = object.getClass();
ObjectWriter objectWriter = context.provider.getObjectWriter(valueClass, valueClass, fieldBased);
if ((context.features & JSONWriter.Feature.BeanToArray.mask) != 0) {
objectWriter.writeArrayMappingJSONB(writer, object, null, null, 0);
} else {
objectWriter.writeJSONB(writer, object, null, null, 0);
}
}
return writer.getBytes();
}
}
/**
* Creates a symbol table with the specified names
*
* @param names the names to include in the symbol table
* @return the created symbol table
*/
static SymbolTable symbolTable(String... names) {
return new SymbolTable(names);
}
/**
* Converts JSONB bytes to a JSON string
*
* @param jsonbBytes the JSONB bytes to convert
* @return the JSON string representation
*/
static String toJSONString(byte[] jsonbBytes) {
return new JSONBDump(jsonbBytes, false)
.toString();
}
/**
* Converts JSONB bytes to a JSON string
*
* @param jsonbBytes the JSONB bytes to convert
* @param raw whether to use raw format
* @return the JSON string representation
* @since 2.0.28
*/
static String toJSONString(byte[] jsonbBytes, boolean raw) {
return new JSONBDump(jsonbBytes, raw)
.toString();
}
/**
* Converts JSONB bytes to a JSON string with a symbol table
*
* @param jsonbBytes the JSONB bytes to convert
* @param symbolTable the symbol table to use
* @return the JSON string representation
*/
static String toJSONString(byte[] jsonbBytes, SymbolTable symbolTable) {
return toJSONString(jsonbBytes, symbolTable, false);
}
/**
* Converts JSONB bytes to a JSON string with a symbol table
*
* @param jsonbBytes the JSONB bytes to convert
* @param symbolTable the symbol table to use
* @param raw whether to use raw format
* @return the JSON string representation
*/
static String toJSONString(byte[] jsonbBytes, SymbolTable symbolTable, boolean raw) {
return new JSONBDump(jsonbBytes, symbolTable, raw)
.toString();
}
/**
* Writes an object to an output stream as JSONB bytes
*
* @param out the output stream to write to
* @param object the object to write
* @param features the JSON writer features to apply
* @return the number of bytes written
*/
static int writeTo(
OutputStream out,
Object object,
JSONWriter.Feature... features
) {
try (JSONWriterJSONB writer = new JSONWriterJSONB(
new JSONWriter.Context(JSONFactory.defaultObjectWriterProvider),
null)
) {
writer.config(features);
if (object == null) {
writer.writeNull();
} else {
writer.setRootObject(object);
Class<?> valueClass = object.getClass();
ObjectWriter objectWriter = writer.getObjectWriter(valueClass, valueClass);
objectWriter.writeJSONB(writer, object, null, null, 0);
}
return writer.flushTo(out);
} catch (IOException e) {
throw new JSONException("writeJSONString error", e);
}
}
/**
* Converts a JSON string to JSONB bytes
*
* @param str the JSON string to convert
* @return the JSONB bytes representation
*/
static byte[] fromJSONString(String str) {
return JSONB.toBytes(JSON.parse(str));
}
/**
* Converts JSON bytes to JSONB bytes
*
* @param jsonUtf8Bytes the JSON bytes to convert
* @return the JSONB bytes representation
*/
static byte[] fromJSONBytes(byte[] jsonUtf8Bytes) {
JSONReader reader = JSONReader.of(jsonUtf8Bytes);
ObjectReader objectReader = reader.getObjectReader(Object.class);
Object object = objectReader.readObject(reader, null, null, 0);
return JSONB.toBytes(object);
}
/**
* Gets the type name for the specified type byte
*
* @param type the type byte
* @return the type name
*/
static String typeName(byte type) {
switch (type) {
case BC_OBJECT:
return "OBJECT " + Integer.toString(type);
case BC_OBJECT_END:
return "OBJECT_END " + Integer.toString(type);
case BC_REFERENCE:
return "REFERENCE " + Integer.toString(type);
case BC_SYMBOL:
return "SYMBOL " + Integer.toString(type);
case BC_NULL:
return "NULL " + Integer.toString(type);
case BC_TRUE:
return "TRUE " + Integer.toString(type);
case BC_FALSE:
return "FALSE " + Integer.toString(type);
case BC_STR_UTF8:
return "STR_UTF8 " + Integer.toString(type);
case BC_STR_UTF16:
return "STR_UTF16 " + Integer.toString(type);
case BC_STR_UTF16LE:
return "STR_UTF16LE " + Integer.toString(type);
case BC_STR_UTF16BE:
return "STR_UTF16BE " + Integer.toString(type);
case BC_INT8:
return "INT8 " + Integer.toString(type);
case BC_INT16:
return "INT16 " + Integer.toString(type);
case BC_INT32:
return "INT32 " + Integer.toString(type);
case BC_INT64:
case BC_INT64_INT:
return "INT64 " + Integer.toString(type);
case BC_FLOAT:
case BC_FLOAT_INT:
return "FLOAT " + Integer.toString(type);
case BC_DOUBLE:
case BC_DOUBLE_LONG:
case BC_DOUBLE_NUM_0:
case BC_DOUBLE_NUM_1:
return "DOUBLE " + Integer.toString(type);
case BC_BIGINT:
case BC_BIGINT_LONG:
return "BIGINT " + Integer.toString(type);
case BC_DECIMAL:
case BC_DECIMAL_LONG:
return "DECIMAL " + Integer.toString(type);
case Constants.BC_LOCAL_TIME:
return "LOCAL_TIME " + Integer.toString(type);
case BC_BINARY:
return "BINARY " + Integer.toString(type);
case Constants.BC_LOCAL_DATETIME:
return "LOCAL_DATETIME " + Integer.toString(type);
case BC_TIMESTAMP:
return "TIMESTAMP " + Integer.toString(type);
case BC_TIMESTAMP_MINUTES:
return "TIMESTAMP_MINUTES " + Integer.toString(type);
case BC_TIMESTAMP_SECONDS:
return "TIMESTAMP_SECONDS " + Integer.toString(type);
case BC_TIMESTAMP_MILLIS:
return "TIMESTAMP_MILLIS " + Integer.toString(type);
case BC_TIMESTAMP_WITH_TIMEZONE:
return "TIMESTAMP_WITH_TIMEZONE " + Integer.toString(type);
case Constants.BC_LOCAL_DATE:
return "LOCAL_DATE " + Integer.toString(type);
case BC_TYPED_ANY:
return "TYPED_ANY " + Integer.toString(type);
default:
if (type >= BC_ARRAY_FIX_MIN && type <= BC_ARRAY) {
return "ARRAY " + Integer.toString(type);
}
if (type >= BC_STR_ASCII_FIX_MIN && type <= BC_STR_ASCII) {
return "STR_ASCII " + Integer.toString(type);
}
if (type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX) {
return "INT32 " + Integer.toString(type);
}
if (type >= BC_INT32_BYTE_MIN && type <= BC_INT32_BYTE_MAX) {
return "INT32 " + Integer.toString(type);
}
if (type >= BC_INT32_SHORT_MIN && type <= BC_INT32_SHORT_MAX) {
return "INT32 " + Integer.toString(type);
}
if (type >= BC_INT64_NUM_MIN && type <= BC_INT64_NUM_MAX) {
return "INT64 " + Integer.toString(type);
}
if (type >= BC_INT64_BYTE_MIN && type <= BC_INT64_BYTE_MAX) {
return "INT64 " + Integer.toString(type);
}
if (type >= BC_INT64_SHORT_MIN && type <= BC_INT64_SHORT_MAX) {
return "INT64 " + Integer.toString(type);
}
return Integer.toString(type);
}
}
/**
* Checks if the specified type is an int32 type
*
* @param type the type to check
* @return true if the type is an int32 type, false otherwise
*/
static boolean isInt32(int type) {
return type >= BC_INT32_NUM_MIN && type <= BC_INT32;
}
/**
* Checks if the specified type is an int32 number type
*
* @param type the type to check
* @return true if the type is an int32 number type, false otherwise
*/
static boolean isInt32Num(int type) {
return type >= BC_INT32_NUM_MIN && type <= BC_INT32_NUM_MAX;
}
/**
* Checks if the specified type is an int32 byte type
*
* @param type the type to check
* @return true if the type is an int32 byte type, false otherwise
*/
static boolean isInt32Byte(int type) {
return (type & 0xF0) == 0x30;
}
/**
* Checks if the specified type is an int32 short type
*
* @param type the type to check
* @return true if the type is an int32 short type, false otherwise
*/
static boolean isInt32Short(int type) {
return (type & 0xF8) == 0x40;
}
/**
* Checks if the specified type is an int64 number type
*
* @param type the type to check
* @return true if the type is an int64 number type, false otherwise
*/
static boolean isInt64Num(int type) {
return type >= BC_INT64_NUM_MIN && type <= BC_INT64_NUM_MAX;
}
/**
* Checks if the specified type is an int64 byte type
*
* @param type the type to check
* @return true if the type is an int64 byte type, false otherwise
*/
static boolean isInt64Byte(int type) {
return ((type - BC_INT64_BYTE_MIN) & 0xF0) == 0;
}
/**
* Checks if the specified type is an int64 short type
*
* @param type the type to check
* @return true if the type is an int64 short type, false otherwise
*/
static boolean isInt64Short(int type) {
return (type & 0xF8) == 0xC0;
}
/**
* Checks if the specified integer value can be represented as an int32 byte value
*
* @param i the integer value to check
* @return true if the value can be represented as an int32 byte value, false otherwise
*/
static boolean isInt32ByteValue(int i) {
return ((i + 2048) & ~0xFFF) != 0;
}
/**
* Checks if the specified integer value is within the int32 byte value range
*
* @param i the integer value to check
* @return true if the value is within the int32 byte value range, false otherwise
*/
static boolean isInt32ByteValue1(int i) {
return i >= INT32_BYTE_MIN && i <= INT32_BYTE_MAX;
}
/**
* IO utility methods for JSONB serialization
*/
interface IO {
/**
* Calculates the capacity needed for an enum
*
* @param e the enum value
* @param features the features to apply
* @return the capacity needed
*/
static int enumCapacity(Enum e, long features) {
if ((features & (MASK_WRITE_ENUM_USING_TO_STRING | MASK_WRITE_ENUMS_USING_NAME)) != 0) {
return stringCapacity((features & WriteEnumUsingToString.mask) != 0
? e.toString()
: e.name());
}
return 5;
}
/**
* Writes an enum value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param e the enum value to write
* @param features the features to apply
* @return the new offset
*/
static int writeEnum(byte[] bytes, int off, Enum e, long features) {
if ((features & (MASK_WRITE_ENUM_USING_TO_STRING | MASK_WRITE_ENUMS_USING_NAME)) != 0) {
return writeString(bytes, off,
(features & WriteEnumUsingToString.mask) != 0
? e.toString()
: e.name()
);
} else {
return JSONB.IO.writeInt32(bytes, off, e.ordinal());
}
}
/**
* Writes a Boolean value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Boolean value to write
* @return the new offset
*/
static int writeBoolean(byte[] bytes, int off, Boolean value) {
bytes[off] = value == null ? BC_NULL : value ? BC_TRUE : BC_FALSE;
return off + 1;
}
/**
* Writes a boolean value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the boolean value to write
* @return the new offset
*/
static int writeBoolean(byte[] bytes, int off, boolean value) {
bytes[off] = value ? BC_TRUE : BC_FALSE;
return off + 1;
}
/**
* Writes a boolean array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param values the boolean array to write
* @return the new offset
*/
static int writeBoolean(byte[] bytes, int off, boolean[] values) {
if (values == null) {
bytes[off] = BC_NULL;
return off + 1;
}
off = startArray(bytes, off, values.length);
for (int i = 0; i < values.length; i++) {
bytes[off + i] = values[i] ? BC_TRUE : BC_FALSE;
}
return off + values.length;
}
/**
* Writes a Float value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Float value to write
* @param features the features to apply
* @return the new offset
*/
static int writeFloat(byte[] bytes, int off, Float value, long features) {
float floatValue;
if (value == null) {
if ((features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0) {
bytes[off] = BC_NULL;
return off + 1;
}
floatValue = 0;
} else {
floatValue = value;
}
return IO.writeFloat(bytes, off, floatValue);
}
/**
* Writes a float array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param values the float array to write
* @return the new offset
*/
static int writeFloat(byte[] bytes, int off, float[] values) {
if (values == null) {
bytes[off] = BC_NULL;
return off + 1;
}
off = startArray(bytes, off, values.length);
for (float value : values) {
off = IO.writeFloat(bytes, off, value);
}
return off;
}
/**
* Writes a float value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the float value to write
* @return the new offset
*/
static int writeFloat(byte[] bytes, int off, float value) {
int intValue = (int) value;
if (intValue == value && ((intValue + 0x40000) & ~0x7ffff) == 0) {
bytes[off] = BC_FLOAT_INT;
return IO.writeInt32(bytes, off + 1, intValue);
}
bytes[off] = BC_FLOAT;
IOUtils.putIntBE(bytes, off + 1, Float.floatToIntBits(value));
return off + 5;
}
/**
* Writes a Double value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Double value to write
* @param features the features to apply
* @return the new offset
*/
static int writeDouble(byte[] bytes, int off, Double value, long features) {
if (value == null) {
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0
? BC_NULL
: BC_DOUBLE_NUM_0;
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0 ? BC_NULL : BC_DOUBLE_NUM_0;
return off + 1;
}
return IO.writeDouble(bytes, off, value);
}
/**
* Writes a double value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the double value to write
* @return the new offset
*/
static int writeDouble(byte[] bytes, int off, double value) {
if (value == 0 || value == 1) {
bytes[off] = value == 0 ? BC_DOUBLE_NUM_0 : BC_DOUBLE_NUM_1;
return off + 1;
}
if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
long longValue = (long) value;
if (longValue == value) {
bytes[off] = BC_DOUBLE_LONG;
return IO.writeInt64(bytes, off + 1, longValue);
}
}
bytes[off] = BC_DOUBLE;
IOUtils.putLongBE(bytes, off + 1, Double.doubleToLongBits(value));
return off + 9;
}
/**
* Writes a double array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param values the double array to write
* @return the new offset
*/
static int writeDouble(byte[] bytes, int off, double[] values) {
if (values == null) {
bytes[off] = BC_NULL;
return off + 1;
}
off = startArray(bytes, off, values.length);
for (double value : values) {
off = writeDouble(bytes, off, value);
}
return off;
}
/**
* Writes a Byte value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param val the Byte value to write
* @param features the features to apply
* @return the new offset
*/
static int writeInt8(byte[] bytes, int off, Byte val, long features) {
if (val == null) {
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0 ? BC_NULL : 0;
return off + 1;
}
putShortLE(bytes, off, (short) ((val << 8) | (BC_INT8 & 0xFF)));
return off + 2;
}
/**
* Writes a byte value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param val the byte value to write
* @return the new offset
*/
static int writeInt8(byte[] bytes, int off, byte val) {
putShortLE(bytes, off, (short) ((val << 8) | (BC_INT8 & 0xFF)));
return off + 2;
}
/**
* Writes a Short value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param val the Short value to write
* @param features the features to apply
* @return the new offset
*/
static int writeInt16(byte[] bytes, int off, Short val, long features) {
if (val == null) {
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0 ? BC_NULL : 0;
return off + 1;
}
bytes[off] = BC_INT16;
putShortBE(bytes, off + 1, val);
return off + 3;
}
/**
* Writes a short value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param val the short value to write
* @return the new offset
*/
static int writeInt16(byte[] bytes, int off, short val) {
bytes[off] = BC_INT16;
putShortBE(bytes, off + 1, val);
return off + 3;
}
/**
* Writes an Integer value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Integer value to write
* @param features the features to apply
* @return the new offset
*/
static int writeInt32(byte[] bytes, int off, Integer value, long features) {
if (value == null) {
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0 ? BC_NULL : 0;
return off + 1;
}
return IO.writeInt32(bytes, off, value);
}
/**
* Writes a string with a symbol table to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param str the string to write
* @param symbolTable the symbol table to use
* @return the new offset
*/
static int writeSymbol(byte[] bytes, int off, String str, SymbolTable symbolTable) {
if (str == null) {
bytes[off] = BC_NULL;
return off + 1;
}
int ordinal = symbolTable.getOrdinal(str);
if (ordinal >= 0) {
bytes[off] = BC_STR_ASCII;
return writeInt32(bytes, off + 1, -ordinal);
}
return writeString(bytes, off, str);
}
/**
* Writes a symbol to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param symbol the symbol to write
* @return the new offset
*/
static int writeSymbol(byte[] bytes, int off, int symbol) {
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);
}
return off;
}
/**
* Checks and writes a type name to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param object the object to check
* @param fieldClass the field class
* @param jsonWriter the JSON writer
* @return the new offset
*/
static int checkAndWriteTypeName(byte[] bytes, int off, Object object, Class<?> fieldClass, JSONWriter jsonWriter) {
long features = jsonWriter.getFeatures();
Class<?> objectClass;
if ((features & WriteClassName.mask) == 0
|| object == null
|| (objectClass = object.getClass()) == fieldClass
|| ((features & NotWriteHashMapArrayListClassName.mask) != 0 && (objectClass == HashMap.class || objectClass == ArrayList.class))
|| ((features & NotWriteRootClassName.mask) != 0 && object == jsonWriter.rootObject)
) {
return off;
}
return writeTypeName(bytes, off, TypeUtils.getTypeName(objectClass), jsonWriter);
}
/**
* Writes a type name to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param typeName the type name to write
* @param jsonWriter the JSON writer
* @return the new offset
*/
static int writeTypeName(byte[] bytes, int off, String typeName, JSONWriter jsonWriter) {
JSONWriterJSONB jsonWriterJSONB = (JSONWriterJSONB) jsonWriter;
SymbolTable symbolTable = jsonWriter.symbolTable;
bytes[off++] = BC_TYPED_ANY;
long hash = Fnv.hashCode64(typeName);
int symbol = -1;
if (symbolTable != null) {
symbol = symbolTable.getOrdinalByHashCode(hash);
if (symbol == -1 && jsonWriterJSONB.symbols != null) {
symbol = jsonWriterJSONB.symbols.get(hash);
}
} else if (jsonWriterJSONB.symbols != null) {
symbol = jsonWriterJSONB.symbols.get(hash);
}
if (symbol == -1) {
if (jsonWriterJSONB.symbols == null) {
jsonWriterJSONB.symbols = new TLongIntHashMap();
}
jsonWriterJSONB.symbols.put(hash, symbol = jsonWriterJSONB.symbolIndex++);
} else {
return JSONB.IO.writeInt32(bytes, off, symbol);
}
off = writeString(bytes, off, typeName);
return writeInt32(bytes, off, symbol);
}
/**
* Writes an integer value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the integer value to write
* @return the new offset
*/
static int writeInt32(byte[] bytes, int off, int value) {
if (((value + 0x10) & ~0x3f) == 0) {
bytes[off++] = (byte) value;
} else if (((value + 0x800) & ~0xfff) == 0) {
putShortBE(bytes, off, (short) ((BC_INT32_BYTE_ZERO << 8) + value));
off += 2;
} else if (((value + 0x40000) & ~0x7ffff) == 0) {
bytes[off] = (byte) (BC_INT32_SHORT_ZERO + (value >> 16));
putShortBE(bytes, off + 1, (short) value);
off += 3;
} else {
bytes[off] = BC_INT32;
putIntBE(bytes, off + 1, value);
off += 5;
}
return off;
}
/**
* Writes a collection of Long values to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param values the collection of Long values to write
* @param features the features to apply
* @return the new offset
*/
static int writeInt64(byte[] bytes, int off, Collection<Long> values, long features) {
if (values == null) {
bytes[off] = (features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL;
return off + 1;
}
int size = values.size();
off = startArray(bytes, off, size);
for (Long value : values) {
off = writeInt64(bytes, off, value, features);
}
return off;
}
/**
* Writes a Long value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Long value to write
* @param features the features to apply
* @return the new offset
*/
static int writeInt64(byte[] bytes, int off, Long value, long features) {
if (value == null) {
bytes[off] = (features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) == 0
? BC_NULL
: (byte) (BC_INT64_NUM_MIN - INT64_NUM_LOW_VALUE);
return off + 1;
}
return IO.writeInt64(bytes, off, value);
}
/**
* Writes a long value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the long value to write
* @return the new offset
*/
static int writeInt64(byte[] bytes, int off, long value) {
if (value >= INT64_NUM_LOW_VALUE && value <= INT64_NUM_HIGH_VALUE) {
bytes[off++] = (byte) (BC_INT64_NUM_MIN + (value - INT64_NUM_LOW_VALUE));
} else if (((value + 0x800) & ~0xfffL) == 0) {
putShortBE(bytes, off, (short) ((BC_INT64_BYTE_ZERO << 8) + value));
off += 2;
} else if (((value + 0x40000) & ~0x7ffffL) == 0) {
bytes[off] = (byte) (BC_INT64_SHORT_ZERO + (value >> 16));
putShortBE(bytes, off + 1, (short) value);
off += 3;
} else if ((((value + 0x80000000L) & ~0xffffffffL) == 0)) {
bytes[off] = BC_INT64_INT;
putIntBE(bytes, off + 1, (int) value);
off += 5;
} else {
bytes[off] = BC_INT64;
putLongBE(bytes, off + 1, value);
off += 9;
}
return off;
}
/**
* Starts an array in a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param size the size of the array
* @return the new offset
*/
static int startArray(byte[] bytes, int off, int size) {
boolean tinyInt = size <= ARRAY_FIX_LEN;
bytes[off++] = tinyInt ? (byte) (BC_ARRAY_FIX_MIN + size) : BC_ARRAY;
if (!tinyInt) {
off = writeInt32(bytes, off, size);
}
return off;
}
/**
* Writes a collection of strings to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param strings the collection of strings to write
* @param features the features to apply
* @return the new offset
*/
static int writeString(byte[] bytes, int off, Collection<String> strings, long features) {
if (strings == null) {
bytes[off] = (features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL;
return off + 1;
}
int size = strings.size();
off = startArray(bytes, off, size);
for (String string : strings) {
off = writeString(bytes, off, string);
}
return off;
}
/**
* Writes a string array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param strings the string array to write
* @param features the features to apply
* @return the new offset
*/
static int writeString(byte[] bytes, int off, String[] strings, long features) {
if (strings == null) {
bytes[off] = (features & WRITE_ARRAY_NULL_MASK) != 0 ? BC_ARRAY_FIX_MIN : BC_NULL;
return off + 1;
}
int size = strings.length;
off = startArray(bytes, off, size);
for (String string : strings) {
off = writeString(bytes, off, string);
}
return off;
}
/**
* Writes a string to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param str the string to write
* @return the new offset
*/
static int writeString(byte[] bytes, int off, String str) {
if (str == null) {
bytes[off] = BC_NULL;
return off + 1;
}
if (STRING_CODER != null && STRING_VALUE != null) {
int coder = STRING_CODER.applyAsInt(str);
byte[] value = STRING_VALUE.apply(str);
if (coder == 0) {
return writeStringLatin1(bytes, off, value);
} else {
return writeStringUTF16(bytes, off, value);
}
} else {
return writeString(bytes, off, JDKUtils.getCharArray(str));
}
}
/**
* Writes a UTF-16 string to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the UTF-16 byte array to write
* @return the new offset
*/
static int writeStringUTF16(byte[] bytes, int off, byte[] value) {
final int strlen = value.length;
bytes[off] = JDKUtils.BIG_ENDIAN ? BC_STR_UTF16BE : BC_STR_UTF16LE;
off = JSONB.IO.writeInt32(bytes, off + 1, strlen);
System.arraycopy(value, 0, bytes, off, strlen);
return off + strlen;
}
/**
* Writes a Latin-1 string to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Latin-1 byte array to write
* @return the new offset
*/
static int writeStringLatin1(byte[] bytes, int off, byte[] value) {
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) {
off = putStringSizeSmall(bytes, off, strlen);
} else {
off = putStringSizeLarge(bytes, off, strlen);
}
System.arraycopy(value, 0, bytes, off, value.length);
return off + strlen;
}
/**
* Calculates the capacity needed for a collection of strings
*
* @param strings the collection of strings
* @return the capacity needed
*/
static int stringCapacity(Collection<String> strings) {
if (strings == null) {
return 1;
}
int size = stringCapacity(strings.getClass().getName()) + 7;
for (String string : strings) {
size += stringCapacity(string);
}
return size;
}
/**
* Calculates the capacity needed for a string array
*
* @param strings the string array
* @return the capacity needed
*/
static int stringCapacity(String[] strings) {
if (strings == null) {
return 1;
}
int size = 6;
for (String string : strings) {
size += stringCapacity(string);
}
return size;
}
/**
* Calculates the capacity needed for a collection of Long values
*
* @param values the collection of Long values
* @return the capacity needed
*/
static int int64Capacity(Collection<Long> values) {
if (values == null) {
return 1;
}
return stringCapacity(values.getClass().getName())
+ 7
+ values.size() * 9;
}
/**
* Calculates the capacity needed for a string
*
* @param str the string
* @return the capacity needed
*/
static int stringCapacity(String str) {
if (str == null) {
return 0;
}
int strlen = str.length();
if (STRING_CODER != null && STRING_VALUE != null) {
return (strlen << STRING_CODER.applyAsInt(str)) + 6;
}
return strlen * 3 + 6;
}
/**
* Puts a small string size to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param val the size value to write
* @return the new offset
*/
static int putStringSizeSmall(byte[] bytes, int off, int val) {
bytes[off] = BC_STR_ASCII;
putShortBE(bytes, off + 1, (short) ((BC_INT32_BYTE_ZERO << 8) + val));
return off + 3;
}
/**
* Puts a large string size to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param strlen the size value to write
* @return the new offset
*/
static int putStringSizeLarge(byte[] bytes, int off, int strlen) {
if (strlen <= INT32_SHORT_MAX) {
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;
}
/**
* Writes a character array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param chars the character array to write
* @return the new offset
*/
static int writeString(byte[] bytes, int off, char[] chars) {
return writeString(bytes, off, chars, 0, chars.length);
}
/**
* Writes a character array with offset and length to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param chars the character array to write
* @param coff the offset in the character array
* @param strlen the length of characters to write
* @return the new offset
*/
static int writeString(byte[] bytes, int off, char[] chars, int coff, int strlen) {
int start = off;
boolean ascii = true;
if (strlen < STR_ASCII_FIX_LEN) {
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) {
return off;
}
off = start;
} else {
ascii = isLatin1(chars, coff, strlen);
}
if (ascii) {
off = writeStringLatin1(bytes, off, chars, coff, strlen);
} else {
off = writeUTF8(bytes, off, chars, coff, strlen);
}
return off;
}
/**
* Writes a Latin-1 character array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param chars the character array to write
* @param coff the offset in the character array
* @param strlen the length of characters to write
* @return the new offset
*/
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;
}
/**
* Writes a UTF-8 character array to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param chars the character array to write
* @param coff the offset in the character array
* @param strlen the length of characters to write
* @return the new offset
*/
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;
}
/**
* Calculates the size needed for an integer value
*
* @param i the integer value
* @return the size needed
*/
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;
}
/**
* Writes a UUID value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the UUID value to write
* @return the new offset
*/
static int writeUUID(byte[] bytes, int off, UUID value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
putShortLE(bytes, off, (short) ((BC_BINARY & 0xFF) | ((BC_INT32_NUM_16 & 0xFF) << 8)));
putLongBE(bytes, off + 2, value.getMostSignificantBits());
putLongBE(bytes, off + 10, value.getLeastSignificantBits());
return off + 18;
}
/**
* Writes an Instant value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the Instant value to write
* @return the new offset
*/
static int writeInstant(byte[] bytes, int off, Instant value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
bytes[off] = BC_TIMESTAMP;
off = JSONB.IO.writeInt64(bytes, off + 1, value.getEpochSecond());
return JSONB.IO.writeInt32(bytes, off, value.getNano());
}
/**
* Writes a LocalDate value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the LocalDate value to write
* @return the new offset
*/
static int writeLocalDate(byte[] bytes, int off, LocalDate value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
bytes[off] = BC_LOCAL_DATE;
int year = value.getYear();
putIntBE(bytes, off + 1, (year << 16) | (value.getMonthValue() << 8) | value.getDayOfMonth());
return off + 5;
}
/**
* Writes a LocalTime value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the LocalTime value to write
* @return the new offset
*/
static int writeLocalTime(byte[] bytes, int off, LocalTime value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
putIntBE(bytes,
off,
(BC_LOCAL_TIME << 24) | (value.getHour() << 16) | (value.getMinute() << 8) | value.getSecond());
return JSONB.IO.writeInt32(bytes, off + 4, value.getNano());
}
/**
* Writes a LocalDateTime value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the LocalDateTime value to write
* @return the new offset
*/
static int writeLocalDateTime(byte[] bytes, int off, LocalDateTime value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
putIntBE(bytes,
off,
(BC_LOCAL_DATETIME << 24) | (value.getYear() << 8) | value.getMonthValue());
putIntBE(bytes,
off + 4,
(value.getDayOfMonth() << 24)
| (value.getHour() << 16)
| (value.getMinute() << 8)
| value.getSecond());
return writeInt32(bytes, off + 8, value.getNano());
}
/**
* Writes an OffsetDateTime value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the OffsetDateTime value to write
* @return the new offset
*/
static int writeOffsetDateTime(byte[] bytes, int off, OffsetDateTime value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
putIntBE(bytes,
off,
(BC_TIMESTAMP_WITH_TIMEZONE << 24) | (value.getYear() << 8) | value.getMonthValue());
putIntBE(bytes,
off + 4,
(value.getDayOfMonth() << 24)
| (value.getHour() << 16)
| (value.getMinute() << 8)
| value.getSecond());
off = writeInt32(bytes, off + 8, value.getNano());
String zoneIdStr = value.getOffset().getId();
int strlen = zoneIdStr.length();
bytes[off] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
zoneIdStr.getBytes(0, strlen, bytes, off + 1);
return off + strlen + 1;
}
/**
* Writes an OffsetTime value to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param value the OffsetTime value to write
* @return the new offset
*/
static int writeOffsetTime(byte[] bytes, int off, OffsetTime value) {
if (value == null) {
bytes[off] = BC_NULL;
return off + 1;
}
int year = 1970, month = 1, dayOfMonth = 1;
putIntBE(bytes,
off,
(BC_TIMESTAMP_WITH_TIMEZONE << 24) | (year << 8) | month);
putIntBE(bytes,
off + 4,
(dayOfMonth << 24)
| (value.getHour() << 16)
| (value.getMinute() << 8)
| value.getSecond());
off = writeInt32(bytes, off + 8, value.getNano());
String zoneIdStr = value.getOffset().getId();
int strlen = zoneIdStr.length();
bytes[off] = (byte) (strlen + BC_STR_ASCII_FIX_MIN);
zoneIdStr.getBytes(0, strlen, bytes, off + 1);
return off + strlen + 1;
}
/**
* Writes a reference to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param path the reference path
* @param jsonWriter the JSON writer
* @return the new offset
*/
static int writeReference(byte[] bytes, int off, String path, JSONWriter jsonWriter) {
if (jsonWriter.lastReference == path) {
path = "#-1";
} else {
jsonWriter.lastReference = path;
}
bytes[off] = BC_REFERENCE;
return writeString(bytes, off + 1, path);
}
/**
* Writes a raw name to a byte array
*
* @param bytes the byte array to write to
* @param off the offset in the byte array
* @param name the name byte array to write
* @param nameHash the name hash
* @param jsonWriter the JSON writer
* @return the new offset
*/
static int writeNameRaw(byte[] bytes, int off, byte[] name, long nameHash, JSONWriter jsonWriter) {
SymbolTable symbolTable = jsonWriter.symbolTable;
JSONWriterJSONB jsonWriterJSONB = (JSONWriterJSONB) jsonWriter;
int symbol;
if (symbolTable == null
|| (symbol = symbolTable.getOrdinalByHashCode(nameHash)) == -1
) {
if ((jsonWriter.context.features & WriteNameAsSymbol.mask) == 0) {
System.arraycopy(name, 0, bytes, off, name.length);
return off + name.length;
}
boolean symbolExists = false;
if (jsonWriterJSONB.symbols != null) {
if ((symbol = jsonWriterJSONB.symbols.putIfAbsent(nameHash, jsonWriterJSONB.symbolIndex)) != jsonWriterJSONB.symbolIndex) {
symbolExists = true;
} else {
jsonWriterJSONB.symbolIndex++;
}
} else {
(jsonWriterJSONB.symbols = new TLongIntHashMap())
.put(nameHash, symbol = jsonWriterJSONB.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);
}
return off;
}
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);
}
return off;
}
}
}