Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.fury.Fury Maven / Gradle / Ivy
Go to download
Apache Fury™ is a blazingly fast multi-language serialization framework powered by jit and zero-copy.
Apache Fury (incubating) is an effort undergoing incubation at the Apache
Software Foundation (ASF), sponsored by the Apache Incubator PMC.
Incubation is required of all newly accepted projects until a further review
indicates that the infrastructure, communications, and decision making process
have stabilized in a manner consistent with other successful ASF projects.
While incubation status is not necessarily a reflection of the completeness
or stability of the code, it does indicate that the project has yet to be
fully endorsed by the ASF.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fury;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.concurrent.NotThreadSafe;
import org.apache.fury.builder.JITContext;
import org.apache.fury.collection.IdentityMap;
import org.apache.fury.config.CompatibleMode;
import org.apache.fury.config.Config;
import org.apache.fury.config.FuryBuilder;
import org.apache.fury.config.Language;
import org.apache.fury.config.LongEncoding;
import org.apache.fury.io.FuryInputStream;
import org.apache.fury.io.FuryReadableChannel;
import org.apache.fury.logging.Logger;
import org.apache.fury.logging.LoggerFactory;
import org.apache.fury.memory.MemoryBuffer;
import org.apache.fury.memory.MemoryUtils;
import org.apache.fury.resolver.ClassInfo;
import org.apache.fury.resolver.ClassInfoHolder;
import org.apache.fury.resolver.ClassResolver;
import org.apache.fury.resolver.MapRefResolver;
import org.apache.fury.resolver.MetaContext;
import org.apache.fury.resolver.MetaStringResolver;
import org.apache.fury.resolver.NoRefResolver;
import org.apache.fury.resolver.RefResolver;
import org.apache.fury.resolver.SerializationContext;
import org.apache.fury.serializer.ArraySerializers;
import org.apache.fury.serializer.BufferCallback;
import org.apache.fury.serializer.BufferObject;
import org.apache.fury.serializer.OpaqueObjects;
import org.apache.fury.serializer.PrimitiveSerializers.LongSerializer;
import org.apache.fury.serializer.Serializer;
import org.apache.fury.serializer.SerializerFactory;
import org.apache.fury.serializer.StringSerializer;
import org.apache.fury.serializer.collection.CollectionSerializers.ArrayListSerializer;
import org.apache.fury.serializer.collection.MapSerializers.HashMapSerializer;
import org.apache.fury.type.Generics;
import org.apache.fury.type.Type;
import org.apache.fury.util.ExceptionUtils;
import org.apache.fury.util.Preconditions;
import org.apache.fury.util.StringUtils;
/**
* Cross-Lang Data layout: 1byte mask: 1-bit null: 0->null, 1->not null 1-bit endianness: 0->le,
* 1->be 1-bit target lang: 0->native, 1->x_lang if x_lang, will write current process language as a
* byte into buffer. 1-bit out-of-band serialization enable flag: 0 -> not enabled, 1 -> enabled.
* other bits reserved.
*
* serialize/deserialize is user API for root object serialization, write/read api is for inner
* serialization.
*/
@NotThreadSafe
public final class Fury implements BaseFury {
private static final Logger LOG = LoggerFactory.getLogger(Fury.class);
public static final byte NULL_FLAG = -3;
// This flag indicates that object is a not-null value.
// We don't use another byte to indicate REF, so that we can save one byte.
public static final byte REF_FLAG = -2;
// this flag indicates that the object is a non-null value.
public static final byte NOT_NULL_VALUE_FLAG = -1;
// this flag indicates that the object is a referencable and first write.
public static final byte REF_VALUE_FLAG = 0;
public static final byte NOT_SUPPORT_CROSS_LANGUAGE = 0;
public static final short FURY_TYPE_TAG_ID = Type.FURY_TYPE_TAG.getId();
private static final byte isNilFlag = 1;
private static final byte isLittleEndianFlag = 1 << 1;
private static final byte isCrossLanguageFlag = 1 << 2;
private static final byte isOutOfBandFlag = 1 << 3;
private static final boolean isLittleEndian = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
private static final byte BITMAP = isLittleEndian ? isLittleEndianFlag : 0;
private static final int BUFFER_SIZE_LIMIT = 128 * 1024;
private static final short MAGIC_NUMBER = 0x62D4;
private final Config config;
private final boolean refTracking;
private final boolean shareMeta;
private final RefResolver refResolver;
private final ClassResolver classResolver;
private final MetaStringResolver metaStringResolver;
private final SerializationContext serializationContext;
private final ClassLoader classLoader;
private final JITContext jitContext;
private MemoryBuffer buffer;
private final List nativeObjects;
private final StringSerializer stringSerializer;
private final ArrayListSerializer arrayListSerializer;
private final HashMapSerializer hashMapSerializer;
private final Language language;
private final boolean compressInt;
private final LongEncoding longEncoding;
private final Generics generics;
private Language peerLanguage;
private BufferCallback bufferCallback;
private Iterator outOfBandBuffers;
private boolean peerOutOfBandEnabled;
private int depth;
private int copyDepth;
private final boolean copyRefTracking;
private final IdentityMap originToCopyMap;
private int classDefEndOffset;
public Fury(FuryBuilder builder, ClassLoader classLoader) {
// Avoid set classLoader in `FuryBuilder`, which won't be clear when
// `org.apache.fury.ThreadSafeFury.clearClassLoader` is called.
config = new Config(builder);
this.language = config.getLanguage();
this.refTracking = config.trackingRef();
this.copyRefTracking = config.copyRef();
this.shareMeta = config.isMetaShareEnabled();
compressInt = config.compressInt();
longEncoding = config.longEncoding();
if (refTracking) {
this.refResolver = new MapRefResolver();
} else {
this.refResolver = new NoRefResolver();
}
jitContext = new JITContext(this);
metaStringResolver = new MetaStringResolver();
classResolver = new ClassResolver(this);
classResolver.initialize();
serializationContext = new SerializationContext(config);
this.classLoader = classLoader;
nativeObjects = new ArrayList<>();
generics = new Generics(this);
stringSerializer = new StringSerializer(this);
arrayListSerializer = new ArrayListSerializer(this);
hashMapSerializer = new HashMapSerializer(this);
originToCopyMap = new IdentityMap<>();
classDefEndOffset = -1;
LOG.info("Created new fury {}", this);
}
@Override
public void register(Class> cls) {
classResolver.register(cls);
}
@Override
public void register(Class> cls, boolean createSerializer) {
classResolver.register(cls, createSerializer);
}
@Override
public void register(Class> cls, Short id) {
classResolver.register(cls, id);
}
@Override
public void register(Class> cls, Short id, boolean createSerializer) {
classResolver.register(cls, id, createSerializer);
}
/** register class with given type tag which will be used for cross-language serialization. */
public void register(Class> cls, String typeTag) {
classResolver.register(cls, typeTag);
}
@Override
public void registerSerializer(Class type, Class extends Serializer> serializerClass) {
classResolver.registerSerializer(type, serializerClass);
}
@Override
public void registerSerializer(Class> type, Serializer> serializer) {
classResolver.registerSerializer(type, serializer);
}
@Override
public void registerSerializer(Class> type, Function> serializerCreator) {
classResolver.registerSerializer(type, serializerCreator.apply(this));
}
@Override
public void setSerializerFactory(SerializerFactory serializerFactory) {
classResolver.setSerializerFactory(serializerFactory);
}
public SerializerFactory getSerializerFactory() {
return classResolver.getSerializerFactory();
}
@Override
public MemoryBuffer serialize(Object obj, long address, int size) {
MemoryBuffer buffer = MemoryUtils.buffer(address, size);
serialize(buffer, obj, null);
return buffer;
}
@Override
public byte[] serialize(Object obj) {
MemoryBuffer buf = getBuffer();
buf.writerIndex(0);
serialize(buf, obj, null);
byte[] bytes = buf.getBytes(0, buf.writerIndex());
resetBuffer();
return bytes;
}
@Override
public byte[] serialize(Object obj, BufferCallback callback) {
MemoryBuffer buf = getBuffer();
buf.writerIndex(0);
serialize(buf, obj, callback);
byte[] bytes = buf.getBytes(0, buf.writerIndex());
resetBuffer();
return bytes;
}
@Override
public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) {
return serialize(buffer, obj, null);
}
@Override
public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback) {
if (language == Language.XLANG) {
buffer.writeInt16(MAGIC_NUMBER);
}
byte bitmap = BITMAP;
if (language != Language.JAVA) {
bitmap |= isCrossLanguageFlag;
}
if (obj == null) {
bitmap |= isNilFlag;
buffer.writeByte(bitmap);
return buffer;
}
if (callback != null) {
bitmap |= isOutOfBandFlag;
bufferCallback = callback;
}
buffer.writeByte(bitmap);
try {
jitContext.lock();
if (depth != 0) {
throwDepthSerializationException();
}
if (language == Language.JAVA) {
write(buffer, obj);
} else {
buffer.writeByte((byte) Language.JAVA.ordinal());
xserializeInternal(buffer, obj);
}
return buffer;
} catch (StackOverflowError t) {
throw processStackOverflowError(t);
} finally {
resetWrite();
jitContext.unlock();
}
}
@Override
public void serialize(OutputStream outputStream, Object obj) {
serializeToStream(outputStream, buf -> serialize(buf, obj, null));
}
@Override
public void serialize(OutputStream outputStream, Object obj, BufferCallback callback) {
serializeToStream(outputStream, buf -> serialize(buf, obj, callback));
}
private StackOverflowError processStackOverflowError(StackOverflowError e) {
if (!refTracking) {
String msg =
"Object may contain circular references, please enable ref tracking "
+ "by `FuryBuilder#withRefTracking(true)`";
String rawMessage = e.getMessage();
if (StringUtils.isNotBlank(rawMessage)) {
msg += ": " + rawMessage;
}
StackOverflowError t1 = ExceptionUtils.trySetStackOverflowErrorMessage(e, msg);
if (t1 != null) {
return t1;
}
}
throw e;
}
private StackOverflowError processCopyStackOverflowError(StackOverflowError e) {
if (!copyRefTracking) {
String msg =
"Object may contain circular references, please enable ref tracking "
+ "by `FuryBuilder#withRefCopy(true)`";
StackOverflowError t1 = ExceptionUtils.trySetStackOverflowErrorMessage(e, msg);
if (t1 != null) {
return t1;
}
}
throw e;
}
public MemoryBuffer getBuffer() {
MemoryBuffer buf = buffer;
if (buf == null) {
buf = buffer = MemoryBuffer.newHeapBuffer(64);
}
return buf;
}
public void resetBuffer() {
MemoryBuffer buf = buffer;
if (buf != null && buf.size() > BUFFER_SIZE_LIMIT) {
buffer = MemoryBuffer.newHeapBuffer(BUFFER_SIZE_LIMIT);
}
}
private void write(MemoryBuffer buffer, Object obj) {
int startOffset = buffer.writerIndex();
if (shareMeta) {
buffer.writeInt32(-1); // preserve 4-byte for nativeObjects start offsets.
}
// reduce caller stack
if (!refResolver.writeRefOrNull(buffer, obj)) {
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
classResolver.writeClass(buffer, classInfo);
writeData(buffer, classInfo, obj);
}
MetaContext metaContext = serializationContext.getMetaContext();
if (shareMeta && metaContext != null && !metaContext.writingClassDefs.isEmpty()) {
buffer.putInt32(startOffset, buffer.writerIndex() - startOffset - 4);
classResolver.writeClassDefs(buffer);
}
}
private void xserializeInternal(MemoryBuffer buffer, Object obj) {
int startOffset = buffer.writerIndex();
buffer.writeInt32(-1); // preserve 4-byte for nativeObjects start offsets.
buffer.writeInt32(-1); // preserve 4-byte for nativeObjects size
xwriteRef(buffer, obj);
buffer.putInt32(startOffset, buffer.writerIndex());
buffer.putInt32(startOffset + 4, nativeObjects.size());
refResolver.resetWrite();
// fury write opaque object classname which cause later write of classname only write an id.
classResolver.resetWrite();
metaStringResolver.resetWrite();
for (Object nativeObject : nativeObjects) {
writeRef(buffer, nativeObject);
}
}
/** Serialize a nullable referencable object to buffer
. */
public void writeRef(MemoryBuffer buffer, Object obj) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
classResolver.writeClass(buffer, classInfo);
writeData(buffer, classInfo, obj);
}
}
public void writeRef(MemoryBuffer buffer, Object obj, ClassInfoHolder classInfoHolder) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
ClassInfo classInfo = classResolver.getClassInfo(obj.getClass(), classInfoHolder);
classResolver.writeClass(buffer, classInfo);
writeData(buffer, classInfo, obj);
}
}
public void writeRef(MemoryBuffer buffer, Object obj, ClassInfo classInfo) {
Serializer serializer = classInfo.getSerializer();
if (serializer.needToWriteRef()) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
classResolver.writeClass(buffer, classInfo);
depth++;
serializer.write(buffer, obj);
depth--;
}
} else {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
classResolver.writeClass(buffer, classInfo);
depth++;
serializer.write(buffer, obj);
depth--;
}
}
}
public void writeRef(MemoryBuffer buffer, T obj, Serializer serializer) {
if (serializer.needToWriteRef()) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
depth++;
serializer.write(buffer, obj);
depth--;
}
} else {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
depth++;
serializer.write(buffer, obj);
depth--;
}
}
}
/** Write object class and data without tracking ref. */
public void writeNullable(MemoryBuffer buffer, Object obj) {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
writeNonRef(buffer, obj);
}
}
/** Write object class and data without tracking ref. */
public void writeNullable(MemoryBuffer buffer, Object obj, ClassInfoHolder classInfoHolder) {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
writeNonRef(buffer, obj, classResolver.getClassInfo(obj.getClass(), classInfoHolder));
}
}
public void writeNullable(MemoryBuffer buffer, Object obj, ClassInfo classInfo) {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
writeNonRef(buffer, obj, classInfo);
}
}
/**
* Serialize a not-null and non-reference object to buffer
.
*
* If reference is enabled, this method should be called only the object is first seen in the
* object graph.
*/
public void writeNonRef(MemoryBuffer buffer, Object obj) {
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
classResolver.writeClass(buffer, classInfo);
writeData(buffer, classInfo, obj);
}
public void writeNonRef(MemoryBuffer buffer, Object obj, ClassInfo classInfo) {
classResolver.writeClass(buffer, classInfo);
Serializer serializer = classInfo.getSerializer();
depth++;
serializer.write(buffer, obj);
depth--;
}
public void writeNonRef(MemoryBuffer buffer, T obj, Serializer serializer) {
depth++;
serializer.write(buffer, obj);
depth--;
}
public void xwriteRef(MemoryBuffer buffer, Object obj) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
xwriteNonRef(buffer, obj, null);
}
}
public void xwriteRef(MemoryBuffer buffer, T obj, Serializer serializer) {
if (serializer.needToWriteRef()) {
if (!refResolver.writeRefOrNull(buffer, obj)) {
xwriteNonRef(buffer, obj, serializer);
}
} else {
if (obj == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
xwriteNonRef(buffer, obj, serializer);
}
}
}
public void xwriteRefByNullableSerializer(
MemoryBuffer buffer, T obj, Serializer serializer) {
if (serializer == null) {
xwriteRef(buffer, obj);
} else {
xwriteRef(buffer, obj, serializer);
}
}
public void xwriteNonRef(MemoryBuffer buffer, T obj, Serializer serializer) {
depth++;
@SuppressWarnings("unchecked")
Class cls = (Class) obj.getClass();
if (serializer == null) {
serializer = classResolver.getSerializer(cls);
}
short typeId = serializer.getXtypeId();
buffer.writeInt16(typeId);
if (typeId != NOT_SUPPORT_CROSS_LANGUAGE) {
if (typeId == FURY_TYPE_TAG_ID) {
classResolver.xwriteTypeTag(buffer, cls);
}
if (typeId < NOT_SUPPORT_CROSS_LANGUAGE) {
classResolver.xwriteClass(buffer, cls);
}
serializer.xwrite(buffer, obj);
} else {
// Write classname so it can be used for debugging which object doesn't support
// cross-language.
// TODO add a config to disable this to reduce space cost.
classResolver.xwriteClass(buffer, cls);
// serializer may increase reference id multi times internally, thus peer cross-language later
// fields/objects deserialization will use wrong reference id since we skip opaque objects
// deserialization.
// So we stash native objects and serialize all those object at the last.
buffer.writeVarUint32(nativeObjects.size());
nativeObjects.add(obj);
}
depth--;
}
/** Write not null data to buffer. */
private void writeData(MemoryBuffer buffer, ClassInfo classInfo, Object obj) {
switch (classInfo.getClassId()) {
case ClassResolver.BOOLEAN_CLASS_ID:
buffer.writeBoolean((Boolean) obj);
break;
case ClassResolver.BYTE_CLASS_ID:
buffer.writeByte((Byte) obj);
break;
case ClassResolver.CHAR_CLASS_ID:
buffer.writeChar((Character) obj);
break;
case ClassResolver.SHORT_CLASS_ID:
buffer.writeInt16((Short) obj);
break;
case ClassResolver.INTEGER_CLASS_ID:
if (compressInt) {
buffer.writeVarInt32((Integer) obj);
} else {
buffer.writeInt32((Integer) obj);
}
break;
case ClassResolver.FLOAT_CLASS_ID:
buffer.writeFloat32((Float) obj);
break;
case ClassResolver.LONG_CLASS_ID:
LongSerializer.writeInt64(buffer, (Long) obj, longEncoding);
break;
case ClassResolver.DOUBLE_CLASS_ID:
buffer.writeFloat64((Double) obj);
break;
case ClassResolver.STRING_CLASS_ID:
stringSerializer.writeJavaString(buffer, (String) obj);
break;
// TODO(add fastpath for other types)
default:
depth++;
classInfo.getSerializer().write(buffer, obj);
depth--;
}
}
public void writeBufferObject(MemoryBuffer buffer, BufferObject bufferObject) {
if (bufferCallback == null || bufferCallback.apply(bufferObject)) {
buffer.writeBoolean(true);
// writer length.
int totalBytes = bufferObject.totalBytes();
// write aligned length so that later buffer copy happen on aligned offset, which will be more
// efficient
// TODO(chaokunyang) Remove branch when other languages support aligned varint.
if (language == Language.JAVA) {
buffer.writeVarUint32Aligned(totalBytes);
} else {
buffer.writeVarUint32(totalBytes);
}
int writerIndex = buffer.writerIndex();
buffer.ensure(writerIndex + bufferObject.totalBytes());
bufferObject.writeTo(buffer);
int size = buffer.writerIndex() - writerIndex;
Preconditions.checkArgument(size == totalBytes);
} else {
buffer.writeBoolean(false);
}
}
// duplicate for speed.
public void writeBufferObject(
MemoryBuffer buffer, ArraySerializers.PrimitiveArrayBufferObject bufferObject) {
if (bufferCallback == null || bufferCallback.apply(bufferObject)) {
buffer.writeBoolean(true);
int totalBytes = bufferObject.totalBytes();
// write aligned length so that later buffer copy happen on aligned offset, which will be very
// efficient
// TODO(chaokunyang) Remove branch when other languages support aligned varint.
if (language == Language.JAVA) {
buffer.writeVarUint32Aligned(totalBytes);
} else {
buffer.writeVarUint32(totalBytes);
}
bufferObject.writeTo(buffer);
} else {
buffer.writeBoolean(false);
}
}
public MemoryBuffer readBufferObject(MemoryBuffer buffer) {
boolean inBand = buffer.readBoolean();
if (inBand) {
int size;
// TODO(chaokunyang) Remove branch when other languages support aligned varint.
if (language == Language.JAVA) {
size = buffer.readAlignedVarUint();
} else {
size = buffer.readVarUint32();
}
MemoryBuffer slice = buffer.slice(buffer.readerIndex(), size);
buffer.readerIndex(buffer.readerIndex() + size);
return slice;
} else {
Preconditions.checkArgument(outOfBandBuffers.hasNext());
return outOfBandBuffers.next();
}
}
public void writeString(MemoryBuffer buffer, String str) {
stringSerializer.writeString(buffer, str);
}
public String readString(MemoryBuffer buffer) {
return stringSerializer.readString(buffer);
}
public void writeJavaStringRef(MemoryBuffer buffer, String str) {
if (stringSerializer.needToWriteRef()) {
if (!refResolver.writeRefOrNull(buffer, str)) {
stringSerializer.writeJavaString(buffer, str);
}
} else {
if (str == null) {
buffer.writeByte(Fury.NULL_FLAG);
} else {
buffer.writeByte(Fury.NOT_NULL_VALUE_FLAG);
stringSerializer.write(buffer, str);
}
}
}
public String readJavaStringRef(MemoryBuffer buffer) {
RefResolver refResolver = this.refResolver;
if (stringSerializer.needToWriteRef()) {
String obj;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
obj = stringSerializer.read(buffer);
refResolver.setReadObject(nextReadRefId, obj);
return obj;
} else {
return (String) refResolver.getReadObject();
}
} else {
byte headFlag = buffer.readByte();
if (headFlag == Fury.NULL_FLAG) {
return null;
} else {
return stringSerializer.read(buffer);
}
}
}
public void writeJavaString(MemoryBuffer buffer, String str) {
stringSerializer.writeJavaString(buffer, str);
}
public String readJavaString(MemoryBuffer buffer) {
return stringSerializer.readJavaString(buffer);
}
public void writeInt64(MemoryBuffer buffer, long value) {
LongSerializer.writeInt64(buffer, value, longEncoding);
}
public long readInt64(MemoryBuffer buffer) {
return LongSerializer.readInt64(buffer, longEncoding);
}
@Override
public Object deserialize(byte[] bytes) {
return deserialize(MemoryUtils.wrap(bytes), null);
}
@Override
public Object deserialize(byte[] bytes, Iterable outOfBandBuffers) {
return deserialize(MemoryUtils.wrap(bytes), outOfBandBuffers);
}
@Override
public Object deserialize(long address, int size) {
return deserialize(MemoryUtils.buffer(address, size), null);
}
@Override
public Object deserialize(MemoryBuffer buffer) {
return deserialize(buffer, null);
}
/**
* Deserialize obj
from a buffer
and outOfBandBuffers
.
*
* @param buffer serialized data. If the provided buffer start address is aligned with 4 bytes,
* the bulk read will be more efficient.
* @param outOfBandBuffers If buffers
is not None, it should be an iterable of
* buffer-enabled objects that is consumed each time the pickle stream references an
* out-of-band {@link BufferObject}. Such buffers have been given in order to the
* `bufferCallback` of a Fury object. If outOfBandBuffers
is null (the default),
* then the buffers are taken from the serialized stream, assuming they are serialized there.
* It is an error for outOfBandBuffers
to be null if the serialized stream was
* produced with a non-null `bufferCallback`.
*/
@Override
public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) {
try {
jitContext.lock();
if (depth != 0) {
throwDepthDeserializationException();
}
if (language == Language.XLANG) {
short magicNumber = buffer.readInt16();
assert magicNumber == MAGIC_NUMBER
: String.format(
"The fury xlang serialization must start with magic number 0x%x. Please "
+ "check whether the serialization is based on the xlang protocol and the data didn't corrupt.",
MAGIC_NUMBER);
}
byte bitmap = buffer.readByte();
if ((bitmap & isNilFlag) == isNilFlag) {
return null;
}
Preconditions.checkArgument(
Fury.isLittleEndian,
"Non-Little-Endian format detected. Only Little-Endian is supported.");
boolean isTargetXLang = (bitmap & isCrossLanguageFlag) == isCrossLanguageFlag;
if (isTargetXLang) {
peerLanguage = Language.values()[buffer.readByte()];
} else {
peerLanguage = Language.JAVA;
}
peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag;
if (peerOutOfBandEnabled) {
Preconditions.checkNotNull(
outOfBandBuffers,
"outOfBandBuffers shouldn't be null when the serialized stream is "
+ "produced with bufferCallback not null.");
this.outOfBandBuffers = outOfBandBuffers.iterator();
} else {
Preconditions.checkArgument(
outOfBandBuffers == null,
"outOfBandBuffers should be null when the serialized stream is "
+ "produced with bufferCallback null.");
}
Object obj;
if (isTargetXLang) {
obj = xdeserializeInternal(buffer);
} else {
if (shareMeta) {
readClassDefs(buffer);
}
obj = readRef(buffer);
}
return obj;
} catch (Throwable t) {
throw ExceptionUtils.handleReadFailed(this, t);
} finally {
if (classDefEndOffset != -1) {
buffer.readerIndex(classDefEndOffset);
}
resetRead();
jitContext.unlock();
}
}
@Override
public Object deserialize(FuryInputStream inputStream) {
return deserialize(inputStream, null);
}
@Override
public Object deserialize(FuryInputStream inputStream, Iterable outOfBandBuffers) {
try {
MemoryBuffer buf = inputStream.getBuffer();
return deserialize(buf, outOfBandBuffers);
} finally {
inputStream.shrinkBuffer();
}
}
@Override
public Object deserialize(FuryReadableChannel channel) {
return deserialize(channel, null);
}
@Override
public Object deserialize(FuryReadableChannel channel, Iterable outOfBandBuffers) {
MemoryBuffer buf = channel.getBuffer();
return deserialize(buf, outOfBandBuffers);
}
private Object xdeserializeInternal(MemoryBuffer buffer) {
Object obj;
int nativeObjectsStartOffset = buffer.readInt32();
int nativeObjectsSize = buffer.readInt32();
int endReaderIndex = nativeObjectsStartOffset;
if (peerLanguage == Language.JAVA) {
int readerIndex = buffer.readerIndex();
buffer.readerIndex(nativeObjectsStartOffset);
for (int i = 0; i < nativeObjectsSize; i++) {
nativeObjects.add(readRef(buffer));
}
endReaderIndex = buffer.readerIndex();
buffer.readerIndex(readerIndex);
refResolver.resetRead();
classResolver.resetRead();
metaStringResolver.resetRead();
}
obj = xreadRef(buffer);
buffer.readerIndex(endReaderIndex);
return obj;
}
/** Deserialize nullable referencable object from buffer
. */
public Object readRef(MemoryBuffer buffer) {
RefResolver refResolver = this.refResolver;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
// ref value or not-null value
Object o = readDataInternal(buffer, classResolver.readClassInfo(buffer));
refResolver.setReadObject(nextReadRefId, o);
return o;
} else {
return refResolver.getReadObject();
}
}
public Object readRef(MemoryBuffer buffer, ClassInfoHolder classInfoHolder) {
RefResolver refResolver = this.refResolver;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
// ref value or not-null value
Object o = readDataInternal(buffer, classResolver.readClassInfo(buffer, classInfoHolder));
refResolver.setReadObject(nextReadRefId, o);
return o;
} else {
return refResolver.getReadObject();
}
}
@SuppressWarnings("unchecked")
public T readRef(MemoryBuffer buffer, Serializer serializer) {
if (serializer.needToWriteRef()) {
T obj;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
obj = serializer.read(buffer);
refResolver.setReadObject(nextReadRefId, obj);
return obj;
} else {
return (T) refResolver.getReadObject();
}
} else {
byte headFlag = buffer.readByte();
if (headFlag == Fury.NULL_FLAG) {
return null;
} else {
return serializer.read(buffer);
}
}
}
/** Deserialize not-null and non-reference object from buffer
. */
public Object readNonRef(MemoryBuffer buffer) {
return readDataInternal(buffer, classResolver.readClassInfo(buffer));
}
public Object readNonRef(MemoryBuffer buffer, ClassInfoHolder classInfoHolder) {
return readDataInternal(buffer, classResolver.readClassInfo(buffer, classInfoHolder));
}
/** Read object class and data without tracking ref. */
public Object readNullable(MemoryBuffer buffer) {
byte headFlag = buffer.readByte();
if (headFlag == Fury.NULL_FLAG) {
return null;
} else {
return readNonRef(buffer);
}
}
/** Class should be read already. */
public Object readData(MemoryBuffer buffer, ClassInfo classInfo) {
depth++;
Serializer> serializer = classInfo.getSerializer();
Object read = serializer.read(buffer);
depth--;
return read;
}
private Object readDataInternal(MemoryBuffer buffer, ClassInfo classInfo) {
switch (classInfo.getClassId()) {
case ClassResolver.BOOLEAN_CLASS_ID:
return buffer.readBoolean();
case ClassResolver.BYTE_CLASS_ID:
return buffer.readByte();
case ClassResolver.CHAR_CLASS_ID:
return buffer.readChar();
case ClassResolver.SHORT_CLASS_ID:
return buffer.readInt16();
case ClassResolver.INTEGER_CLASS_ID:
if (compressInt) {
return buffer.readVarInt32();
} else {
return buffer.readInt32();
}
case ClassResolver.FLOAT_CLASS_ID:
return buffer.readFloat32();
case ClassResolver.LONG_CLASS_ID:
return LongSerializer.readInt64(buffer, longEncoding);
case ClassResolver.DOUBLE_CLASS_ID:
return buffer.readFloat64();
case ClassResolver.STRING_CLASS_ID:
return stringSerializer.readJavaString(buffer);
// TODO(add fastpath for other types)
default:
depth++;
Object read = classInfo.getSerializer().read(buffer);
depth--;
return read;
}
}
public Object xreadRef(MemoryBuffer buffer) {
RefResolver refResolver = this.refResolver;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
Object o = xreadNonRef(buffer, null);
refResolver.setReadObject(nextReadRefId, o);
return o;
} else {
return refResolver.getReadObject();
}
}
public Object xreadRef(MemoryBuffer buffer, Serializer> serializer) {
if (serializer.needToWriteRef()) {
RefResolver refResolver = this.refResolver;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
Object o = xreadNonRef(buffer, serializer);
refResolver.setReadObject(nextReadRefId, o);
return o;
} else {
return refResolver.getReadObject();
}
} else {
byte headFlag = buffer.readByte();
if (headFlag == Fury.NULL_FLAG) {
return null;
} else {
return xreadNonRef(buffer, serializer);
}
}
}
public Object xreadRefByNullableSerializer(MemoryBuffer buffer, Serializer> serializer) {
if (serializer == null) {
return xreadRef(buffer);
} else {
return xreadRef(buffer, serializer);
}
}
public Object xreadNonRef(MemoryBuffer buffer, Serializer> serializer) {
depth++;
short typeId = buffer.readInt16();
ClassResolver classResolver = this.classResolver;
if (typeId != NOT_SUPPORT_CROSS_LANGUAGE) {
Class> cls = null;
if (typeId == FURY_TYPE_TAG_ID) {
cls = classResolver.readClassByTypeTag(buffer);
}
if (typeId < NOT_SUPPORT_CROSS_LANGUAGE) {
if (peerLanguage != Language.JAVA) {
classResolver.xreadClassName(buffer);
cls = classResolver.getClassByTypeId((short) -typeId);
} else {
cls = classResolver.xreadClass(buffer);
}
} else {
if (typeId != FURY_TYPE_TAG_ID) {
cls = classResolver.getClassByTypeId(typeId);
}
}
Preconditions.checkNotNull(cls);
if (serializer == null) {
serializer = classResolver.getSerializer(cls);
}
// TODO check serializer consistent with `classResolver.getSerializer(cls)` when serializer
// not null;
Object o = serializer.xread(buffer);
depth--;
return o;
} else {
String className = classResolver.xreadClassName(buffer);
int ordinal = buffer.readVarUint32();
if (peerLanguage != Language.JAVA) {
return OpaqueObjects.of(peerLanguage, className, ordinal);
} else {
return nativeObjects.get(ordinal);
}
}
}
@Override
public byte[] serializeJavaObject(Object obj) {
MemoryBuffer buf = getBuffer();
buf.writerIndex(0);
serializeJavaObject(buf, obj);
byte[] bytes = buf.getBytes(0, buf.writerIndex());
resetBuffer();
return bytes;
}
@Override
public void serializeJavaObject(MemoryBuffer buffer, Object obj) {
try {
jitContext.lock();
if (depth != 0) {
throwDepthSerializationException();
}
if (config.isMetaShareEnabled()) {
int startOffset = buffer.writerIndex();
buffer.writeInt32(-1); // preserve 4-byte for nativeObjects start offsets.
if (!refResolver.writeRefOrNull(buffer, obj)) {
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
writeData(buffer, classInfo, obj);
MetaContext metaContext = serializationContext.getMetaContext();
if (metaContext != null && !metaContext.writingClassDefs.isEmpty()) {
buffer.putInt32(startOffset, buffer.writerIndex() - startOffset - 4);
classResolver.writeClassDefs(buffer);
}
}
} else {
if (!refResolver.writeRefOrNull(buffer, obj)) {
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
writeData(buffer, classInfo, obj);
}
}
} catch (StackOverflowError t) {
throw processStackOverflowError(t);
} finally {
resetWrite();
jitContext.unlock();
}
}
/**
* Serialize java object without class info, deserialization should use {@link
* #deserializeJavaObject}.
*/
@Override
public void serializeJavaObject(OutputStream outputStream, Object obj) {
serializeToStream(outputStream, buf -> serializeJavaObject(buf, obj));
}
@Override
public T deserializeJavaObject(byte[] data, Class cls) {
return deserializeJavaObject(MemoryBuffer.fromByteArray(data), cls);
}
@Override
@SuppressWarnings("unchecked")
public T deserializeJavaObject(MemoryBuffer buffer, Class cls) {
try {
jitContext.lock();
if (depth != 0) {
throwDepthDeserializationException();
}
if (shareMeta) {
readClassDefs(buffer);
}
T obj;
int nextReadRefId = refResolver.tryPreserveRefId(buffer);
if (nextReadRefId >= NOT_NULL_VALUE_FLAG) {
obj = (T) readDataInternal(buffer, classResolver.getClassInfo(cls));
return obj;
} else {
return null;
}
} catch (Throwable t) {
throw ExceptionUtils.handleReadFailed(this, t);
} finally {
if (classDefEndOffset != -1) {
buffer.readerIndex(classDefEndOffset);
}
resetRead();
jitContext.unlock();
}
}
/**
* Deserialize java object from binary by passing class info, serialization should use {@link
* #serializeJavaObject}.
*
* Note that {@link FuryInputStream} will buffer and read more data, do not use the original
* passed stream when constructing {@link FuryInputStream}. If this is not possible, use {@link
* org.apache.fury.io.BlockedStreamUtils} instead for streaming serialization and deserialization.
*/
@Override
public T deserializeJavaObject(FuryInputStream inputStream, Class cls) {
try {
MemoryBuffer buf = inputStream.getBuffer();
return deserializeJavaObject(buf, cls);
} finally {
inputStream.shrinkBuffer();
}
}
/**
* Deserialize java object from binary channel by passing class info, serialization should use
* {@link #serializeJavaObject}.
*
* Note that {@link FuryInputStream} will buffer and read more data, do not use the original
* passed stream when constructing {@link FuryInputStream}. If this is not possible, use {@link
* org.apache.fury.io.BlockedStreamUtils} instead for streaming serialization and deserialization.
*/
@Override
public T deserializeJavaObject(FuryReadableChannel channel, Class cls) {
MemoryBuffer buf = channel.getBuffer();
return deserializeJavaObject(buf, cls);
}
/**
* Deserialize java object from binary by passing class info, serialization should use {@link
* #deserializeJavaObjectAndClass}.
*/
@Override
public byte[] serializeJavaObjectAndClass(Object obj) {
MemoryBuffer buf = getBuffer();
buf.writerIndex(0);
serializeJavaObjectAndClass(buf, obj);
byte[] bytes = buf.getBytes(0, buf.writerIndex());
resetBuffer();
return bytes;
}
/**
* Serialize java object with class info, deserialization should use {@link
* #deserializeJavaObjectAndClass}.
*/
@Override
public void serializeJavaObjectAndClass(MemoryBuffer buffer, Object obj) {
try {
jitContext.lock();
if (depth != 0) {
throwDepthSerializationException();
}
write(buffer, obj);
} catch (StackOverflowError t) {
throw processStackOverflowError(t);
} finally {
resetWrite();
jitContext.unlock();
}
}
/**
* Serialize java object with class info, deserialization should use {@link
* #deserializeJavaObjectAndClass}.
*/
@Override
public void serializeJavaObjectAndClass(OutputStream outputStream, Object obj) {
serializeToStream(outputStream, buf -> serializeJavaObjectAndClass(buf, obj));
}
/**
* Deserialize class info and java object from binary, serialization should use {@link
* #serializeJavaObjectAndClass}.
*/
@Override
public Object deserializeJavaObjectAndClass(byte[] data) {
return deserializeJavaObjectAndClass(MemoryBuffer.fromByteArray(data));
}
/**
* Deserialize class info and java object from binary, serialization should use {@link
* #serializeJavaObjectAndClass}.
*/
@Override
public Object deserializeJavaObjectAndClass(MemoryBuffer buffer) {
try {
jitContext.lock();
if (depth != 0) {
throwDepthDeserializationException();
}
if (shareMeta) {
readClassDefs(buffer);
}
return readRef(buffer);
} catch (Throwable t) {
throw ExceptionUtils.handleReadFailed(this, t);
} finally {
if (classDefEndOffset != -1) {
buffer.readerIndex(classDefEndOffset);
}
resetRead();
jitContext.unlock();
}
}
/**
* Deserialize class info and java object from binary, serialization should use {@link
* #serializeJavaObjectAndClass}.
*/
@Override
public Object deserializeJavaObjectAndClass(FuryInputStream inputStream) {
try {
MemoryBuffer buf = inputStream.getBuffer();
return deserializeJavaObjectAndClass(buf);
} finally {
inputStream.shrinkBuffer();
}
}
/**
* Deserialize class info and java object from binary channel, serialization should use {@link
* #serializeJavaObjectAndClass}.
*/
@Override
public Object deserializeJavaObjectAndClass(FuryReadableChannel channel) {
MemoryBuffer buf = channel.getBuffer();
return deserializeJavaObjectAndClass(buf);
}
@Override
public T copy(T obj) {
try {
return copyObject(obj);
} catch (StackOverflowError e) {
throw processCopyStackOverflowError(e);
} finally {
if (copyRefTracking) {
resetCopy();
}
}
}
/**
* Copy object. This method provides a fast copy of common types.
*
* @param obj object to copy
* @return copied object
*/
public T copyObject(T obj) {
if (obj == null) {
return null;
}
Object copy;
ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
switch (classInfo.getClassId()) {
case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
case ClassResolver.PRIMITIVE_INT_CLASS_ID:
case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
case ClassResolver.BOOLEAN_CLASS_ID:
case ClassResolver.BYTE_CLASS_ID:
case ClassResolver.CHAR_CLASS_ID:
case ClassResolver.SHORT_CLASS_ID:
case ClassResolver.INTEGER_CLASS_ID:
case ClassResolver.FLOAT_CLASS_ID:
case ClassResolver.LONG_CLASS_ID:
case ClassResolver.DOUBLE_CLASS_ID:
case ClassResolver.STRING_CLASS_ID:
return obj;
case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_CLASS_ID:
boolean[] boolArr = (boolean[]) obj;
return (T) Arrays.copyOf(boolArr, boolArr.length);
case ClassResolver.PRIMITIVE_BYTE_ARRAY_CLASS_ID:
byte[] byteArr = (byte[]) obj;
return (T) Arrays.copyOf(byteArr, byteArr.length);
case ClassResolver.PRIMITIVE_CHAR_ARRAY_CLASS_ID:
char[] charArr = (char[]) obj;
return (T) Arrays.copyOf(charArr, charArr.length);
case ClassResolver.PRIMITIVE_SHORT_ARRAY_CLASS_ID:
short[] shortArr = (short[]) obj;
return (T) Arrays.copyOf(shortArr, shortArr.length);
case ClassResolver.PRIMITIVE_INT_ARRAY_CLASS_ID:
int[] intArr = (int[]) obj;
return (T) Arrays.copyOf(intArr, intArr.length);
case ClassResolver.PRIMITIVE_FLOAT_ARRAY_CLASS_ID:
float[] floatArr = (float[]) obj;
return (T) Arrays.copyOf(floatArr, floatArr.length);
case ClassResolver.PRIMITIVE_LONG_ARRAY_CLASS_ID:
long[] longArr = (long[]) obj;
return (T) Arrays.copyOf(longArr, longArr.length);
case ClassResolver.PRIMITIVE_DOUBLE_ARRAY_CLASS_ID:
double[] doubleArr = (double[]) obj;
return (T) Arrays.copyOf(doubleArr, doubleArr.length);
case ClassResolver.STRING_ARRAY_CLASS_ID:
String[] stringArr = (String[]) obj;
return (T) Arrays.copyOf(stringArr, stringArr.length);
case ClassResolver.ARRAYLIST_CLASS_ID:
copy = arrayListSerializer.copy((ArrayList) obj);
break;
case ClassResolver.HASHMAP_CLASS_ID:
copy = hashMapSerializer.copy((HashMap) obj);
break;
// todo: add fastpath for other types.
default:
copy = copyObject(obj, classInfo.getSerializer());
}
return (T) copy;
}
public T copyObject(T obj, int classId) {
if (obj == null) {
return null;
}
// Fast path to avoid cost of query class map.
switch (classId) {
case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
case ClassResolver.PRIMITIVE_INT_CLASS_ID:
case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
case ClassResolver.BOOLEAN_CLASS_ID:
case ClassResolver.BYTE_CLASS_ID:
case ClassResolver.CHAR_CLASS_ID:
case ClassResolver.SHORT_CLASS_ID:
case ClassResolver.INTEGER_CLASS_ID:
case ClassResolver.FLOAT_CLASS_ID:
case ClassResolver.LONG_CLASS_ID:
case ClassResolver.DOUBLE_CLASS_ID:
case ClassResolver.STRING_CLASS_ID:
return obj;
default:
return copyObject(obj, classResolver.getOrUpdateClassInfo(obj.getClass()).getSerializer());
}
}
public T copyObject(T obj, Serializer serializer) {
copyDepth++;
T copyObject;
if (serializer.needToCopyRef()) {
copyObject = getCopyObject(obj);
if (copyObject == null) {
copyObject = serializer.copy(obj);
originToCopyMap.put(obj, copyObject);
}
} else {
copyObject = serializer.copy(obj);
}
copyDepth--;
return copyObject;
}
/**
* Track ref for copy.
*
* Call this method immediately after composited object such as object
* array/map/collection/bean is created so that circular reference can be copy correctly.
*
* @param o1 object before copying
* @param o2 the copied object
*/
public void reference(T o1, T o2) {
if (o1 != null) {
originToCopyMap.put(o1, o2);
}
}
@SuppressWarnings("unchecked")
public T getCopyObject(T originObj) {
return (T) originToCopyMap.get(originObj);
}
private void serializeToStream(OutputStream outputStream, Consumer function) {
MemoryBuffer buf = getBuffer();
if (outputStream.getClass() == ByteArrayOutputStream.class) {
byte[] oldBytes = buf.getHeapMemory(); // Note: This should not be null.
assert oldBytes != null;
MemoryUtils.wrap((ByteArrayOutputStream) outputStream, buf);
function.accept(buf);
MemoryUtils.wrap(buf, (ByteArrayOutputStream) outputStream);
buf.pointTo(oldBytes, 0, oldBytes.length);
} else {
buf.writerIndex(0);
function.accept(buf);
try {
byte[] bytes = buf.getHeapMemory();
if (bytes != null) {
outputStream.write(bytes, 0, buf.writerIndex());
} else {
outputStream.write(buf.getBytes(0, buf.writerIndex()));
}
outputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
resetBuffer();
}
}
}
private void readClassDefs(MemoryBuffer buffer) {
int relativeClassDefOffset = buffer.readInt32();
if (relativeClassDefOffset == -1) {
return;
}
int readerIndex = buffer.readerIndex();
buffer.readerIndex(readerIndex + relativeClassDefOffset);
classResolver.readClassDefs(buffer);
classDefEndOffset = buffer.readerIndex();
buffer.readerIndex(readerIndex);
}
public void reset() {
refResolver.reset();
classResolver.reset();
metaStringResolver.reset();
serializationContext.reset();
nativeObjects.clear();
peerOutOfBandEnabled = false;
bufferCallback = null;
depth = 0;
resetCopy();
}
public void resetWrite() {
refResolver.resetWrite();
classResolver.resetWrite();
metaStringResolver.resetWrite();
serializationContext.resetWrite();
nativeObjects.clear();
bufferCallback = null;
depth = 0;
}
public void resetRead() {
refResolver.resetRead();
classResolver.resetRead();
metaStringResolver.resetRead();
serializationContext.resetRead();
nativeObjects.clear();
peerOutOfBandEnabled = false;
depth = 0;
classDefEndOffset = -1;
}
public void resetCopy() {
originToCopyMap.clear();
copyDepth = 0;
}
private void throwDepthSerializationException() {
String method = "Fury#" + (language != Language.JAVA ? "x" : "") + "writeXXX";
throw new IllegalStateException(
String.format(
"Nested call Fury.serializeXXX is not allowed when serializing, Please use %s instead",
method));
}
private void throwDepthDeserializationException() {
String method = "Fury#" + (language != Language.JAVA ? "x" : "") + "readXXX";
throw new IllegalStateException(
String.format(
"Nested call Fury.deserializeXXX is not allowed when deserializing, Please use %s instead",
method));
}
public JITContext getJITContext() {
return jitContext;
}
public BufferCallback getBufferCallback() {
return bufferCallback;
}
public boolean isPeerOutOfBandEnabled() {
return peerOutOfBandEnabled;
}
public RefResolver getRefResolver() {
return refResolver;
}
public ClassResolver getClassResolver() {
return classResolver;
}
public MetaStringResolver getMetaStringResolver() {
return metaStringResolver;
}
public SerializationContext getSerializationContext() {
return serializationContext;
}
public Generics getGenerics() {
return generics;
}
public int getDepth() {
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
public void incDepth(int diff) {
this.depth += diff;
}
public void incCopyDepth(int diff) {
this.copyDepth += diff;
}
// Invoked by jit
public StringSerializer getStringSerializer() {
return stringSerializer;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public Language getLanguage() {
return language;
}
public boolean trackingRef() {
return refTracking;
}
public boolean copyTrackingRef() {
return copyRefTracking;
}
public boolean isStringRefIgnored() {
return config.isStringRefIgnored();
}
public boolean isBasicTypesRefIgnored() {
return config.isBasicTypesRefIgnored();
}
public boolean checkClassVersion() {
return config.checkClassVersion();
}
public CompatibleMode getCompatibleMode() {
return config.getCompatibleMode();
}
public Config getConfig() {
return config;
}
public Class extends Serializer> getDefaultJDKStreamSerializerType() {
return config.getDefaultJDKStreamSerializerType();
}
public boolean compressString() {
return config.compressString();
}
public boolean compressInt() {
return compressInt;
}
public LongEncoding longEncoding() {
return longEncoding;
}
public boolean compressLong() {
return config.compressLong();
}
public static FuryBuilder builder() {
return new FuryBuilder();
}
}