Skip to content

Commit

Permalink
Optimize StringBuilder/StringBuffer serialization (apache#908)
Browse files Browse the repository at this point in the history
* Optimize StringBuilder/StringBuffer serialization

* try to optimize StringBuilder

* first to Check code Style

* hidden

* hidden

* bug fix and check code style

* delete excess code and add buffers to try testing

* fix

* try to fix problem

* fix function

* code fix

* code fix again

* Update java/fury-core/src/main/java/io/fury/serializer/Serializers.java

commit

Co-authored-by: Shawn <[email protected]>

* Update java/fury-core/src/main/java/io/fury/serializer/Serializers.java

commit

Co-authored-by: Shawn <[email protected]>

---------

Co-authored-by: pankoli <[email protected]>
Co-authored-by: Shawn <[email protected]>
  • Loading branch information
3 people authored Sep 27, 2023
1 parent b2b9aef commit e71cf72
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 28 deletions.
102 changes: 82 additions & 20 deletions java/fury-core/src/main/java/io/fury/serializer/Serializers.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@

package io.fury.serializer;

import static io.fury.util.function.Functions.makeGetterFunction;

import com.google.common.base.Preconditions;
import com.google.common.primitives.Primitives;
import io.fury.Fury;
import io.fury.collection.Tuple2;
import io.fury.memory.MemoryBuffer;
import io.fury.resolver.ClassResolver;
import io.fury.type.Type;
import io.fury.util.Platform;
import io.fury.util.Utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
Expand All @@ -37,6 +41,8 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.regex.Pattern;

/**
Expand Down Expand Up @@ -410,46 +416,102 @@ public Double read(MemoryBuffer buffer) {
}
}

public static final class StringBuilderSerializer extends Serializer<StringBuilder> {
private final StringSerializer stringSerializer;
static Tuple2<ToIntFunction, Function> builderCache;

private static synchronized Tuple2<ToIntFunction, Function> getBuilderFunc() {
if (builderCache == null) {
Function getValue =
(Function) makeGetterFunction(StringBuilder.class.getSuperclass(), "getValue");
if (Platform.JAVA_VERSION > 8) {
try {
Method getCoderMethod = StringBuilder.class.getSuperclass().getDeclaredMethod("getCoder");
ToIntFunction<CharSequence> getCoder =
(ToIntFunction<CharSequence>) makeGetterFunction(getCoderMethod, int.class);
builderCache = Tuple2.of(getCoder, getValue);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}

public StringBuilderSerializer(Fury fury) {
super(fury, StringBuilder.class);
} else {
builderCache = Tuple2.of(null, getValue);
}
}
return builderCache;
}

public abstract static class AbstractStringBuilderSerializer<T extends CharSequence>
extends Serializer<T> {
protected final ToIntFunction getCoder;
protected final Function getValue;
protected final StringSerializer stringSerializer;

public AbstractStringBuilderSerializer(Fury fury, Class<T> type) {
super(fury, type);
Tuple2<ToIntFunction, Function> builderFunc = getBuilderFunc();
getCoder = builderFunc.f0;
getValue = builderFunc.f1;
stringSerializer = new StringSerializer(fury);
}

@Override
public void write(MemoryBuffer buffer, StringBuilder value) {
stringSerializer.writeJavaString(buffer, value.toString());
public void xwrite(MemoryBuffer buffer, T value) {
stringSerializer.writeUTF8String(buffer, value.toString());
}

@Override
public StringBuilder read(MemoryBuffer buffer) {
return new StringBuilder(stringSerializer.readJavaString(buffer));
public short getXtypeId() {
return (short) -Type.STRING.getId();
}

@Override
public void write(MemoryBuffer buffer, T value) {
if (Platform.JAVA_VERSION > 8) {
int coder = getCoder.applyAsInt(value);
byte[] v = (byte[]) getValue.apply(value);
buffer.writeByte(coder);
if (coder == 0) {
buffer.writePrimitiveArrayWithSizeEmbedded(v, Platform.BYTE_ARRAY_OFFSET, value.length());
} else {
if (coder != 1) {
throw new UnsupportedOperationException("Unsupported coder " + coder);
}
buffer.writePrimitiveArrayWithSizeEmbedded(
v, Platform.BYTE_ARRAY_OFFSET, value.length() << 1);
}
} else {
char[] v = (char[]) getValue.apply(value);
if (StringSerializer.isAscii(v)) {
stringSerializer.writeJDK8Ascii(buffer, v, value.length());
} else {
stringSerializer.writeJDK8UTF16(buffer, v, value.length());
}
}
}
}

public static final class StringBufferSerializer extends Serializer<StringBuffer> {
private final StringSerializer stringSerializer;
public static final class StringBuilderSerializer
extends AbstractStringBuilderSerializer<StringBuilder> {

public StringBufferSerializer(Fury fury) {
super(fury, StringBuffer.class);
stringSerializer = new StringSerializer(fury);
public StringBuilderSerializer(Fury fury) {
super(fury, StringBuilder.class);
}

@Override
public short getXtypeId() {
return (short) -Type.STRING.getId();
public StringBuilder read(MemoryBuffer buffer) {
return new StringBuilder(stringSerializer.readJavaString(buffer));
}

@Override
public void write(MemoryBuffer buffer, StringBuffer value) {
stringSerializer.writeJavaString(buffer, value.toString());
public StringBuilder xread(MemoryBuffer buffer) {
return new StringBuilder(stringSerializer.readUTF8String(buffer));
}
}

@Override
public void xwrite(MemoryBuffer buffer, StringBuffer value) {
stringSerializer.writeUTF8String(buffer, value.toString());
public static final class StringBufferSerializer
extends AbstractStringBuilderSerializer<StringBuffer> {

public StringBufferSerializer(Fury fury) {
super(fury, StringBuffer.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,9 @@ public Expression writeStringExpr(Expression strSerializer, Expression buffer, E
public void writeJava8StringCompressed(MemoryBuffer buffer, String value) {
final char[] chars = (char[]) Platform.getObject(value, STRING_VALUE_FIELD_OFFSET);
if (isAscii(chars)) {
writeJDK8Ascii(buffer, chars);
writeJDK8Ascii(buffer, chars, chars.length);
} else {
writeJDK8UTF16(buffer, chars);
writeJDK8UTF16(buffer, chars, chars.length);
}
}

Expand Down Expand Up @@ -289,9 +289,9 @@ public void writeJavaString(MemoryBuffer buffer, String value) {
final char[] chars = (char[]) Platform.getObject(value, STRING_VALUE_FIELD_OFFSET);
if (compressString) {
if (isAscii(chars)) {
writeJDK8Ascii(buffer, chars);
writeJDK8Ascii(buffer, chars, chars.length);
} else {
writeJDK8UTF16(buffer, chars);
writeJDK8UTF16(buffer, chars, chars.length);
}
} else {
int numBytes = MathUtils.doubleExact(value.length());
Expand Down Expand Up @@ -382,8 +382,7 @@ public static void writeJDK11String(MemoryBuffer buffer, String value) {
buffer.unsafeWriterIndex(writerIndex);
}

public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars) {
final int strLen = chars.length;
public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars, final int strLen) {
int writerIndex = buffer.writerIndex();
// The `ensure` ensure next operations are safe without bound checks,
// and inner heap buffer doesn't change.
Expand Down Expand Up @@ -413,8 +412,7 @@ public void writeJDK8Ascii(MemoryBuffer buffer, char[] chars) {
}
}

public void writeJDK8UTF16(MemoryBuffer buffer, char[] chars) {
int strLen = chars.length;
public void writeJDK8UTF16(MemoryBuffer buffer, char[] chars, int strLen) {
int numBytes = MathUtils.doubleExact(strLen);
if (Platform.IS_LITTLE_ENDIAN) {
buffer.writeByte(UTF16);
Expand Down
13 changes: 13 additions & 0 deletions java/fury-core/src/main/java/io/fury/util/function/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ public static Object makeGetterFunction(Method method) {
}
}

public static Object makeGetterFunction(Method method, Class<?> returnType) {
MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(method.getDeclaringClass());
try {
// Why `lookup.findGetter` doesn't work?
// MethodHandle handle = lookup.findGetter(field.getDeclaringClass(), field.getName(),
// field.getType());
MethodHandle handle = lookup.unreflect(method);
return _JDKAccess.makeGetterFunction(lookup, handle, returnType);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}

public static Tuple2<Class<?>, String> getterMethodInfo(Class<?> type) {
return _JDKAccess.getterMethodInfo(type);
}
Expand Down

0 comments on commit e71cf72

Please sign in to comment.