diff --git a/java/benchmark/src/main/java/org/apache/fury/benchmark/MemorySuite.java b/java/benchmark/src/main/java/org/apache/fury/benchmark/MemorySuite.java index b89c591055..e29b714568 100644 --- a/java/benchmark/src/main/java/org/apache/fury/benchmark/MemorySuite.java +++ b/java/benchmark/src/main/java/org/apache/fury/benchmark/MemorySuite.java @@ -101,7 +101,7 @@ public Object bytesCopyUnaligned(MemoryState state) { // @Benchmark public Object charsCopyAligned(MemoryState state) { state.heapBuffer.writerIndex(0); - state.heapBuffer.writePrimitiveArrayWithSizeEmbedded( + state.heapBuffer.writePrimitiveArrayWithSize( state.chars, Platform.CHAR_ARRAY_OFFSET, state.chars.length * 2); return state.heapBuffer; } @@ -110,7 +110,7 @@ public Object charsCopyAligned(MemoryState state) { public Object charsCopyUnaligned(MemoryState state) { state.heapBuffer.writerIndex(0); state.heapBuffer.writeBoolean(false); - state.heapBuffer.writePrimitiveArrayWithSizeEmbedded( + state.heapBuffer.writePrimitiveArrayWithSize( state.chars, Platform.CHAR_ARRAY_OFFSET, state.chars.length * 2); return state.heapBuffer; } @@ -118,7 +118,7 @@ public Object charsCopyUnaligned(MemoryState state) { // @Benchmark public Object longsCopyAligned(MemoryState state) { state.heapBuffer.writerIndex(0); - state.heapBuffer.writePrimitiveArrayWithSizeEmbedded( + state.heapBuffer.writePrimitiveArrayWithSize( state.longs, Platform.LONG_ARRAY_OFFSET, state.longs.length * 8); return state.heapBuffer; } @@ -127,7 +127,7 @@ public Object longsCopyAligned(MemoryState state) { public Object longsCopyUnaligned(MemoryState state) { state.heapBuffer.writerIndex(0); state.heapBuffer.writeBoolean(false); - state.heapBuffer.writePrimitiveArrayWithSizeEmbedded( + state.heapBuffer.writePrimitiveArrayWithSize( state.longs, Platform.LONG_ARRAY_OFFSET, state.longs.length * 8); return state.heapBuffer; } diff --git a/java/fury-core/src/main/java/org/apache/fury/BaseFury.java b/java/fury-core/src/main/java/org/apache/fury/BaseFury.java index e268dfb51a..1bcb31ee97 100644 --- a/java/fury-core/src/main/java/org/apache/fury/BaseFury.java +++ b/java/fury-core/src/main/java/org/apache/fury/BaseFury.java @@ -19,8 +19,8 @@ package org.apache.fury; -import java.io.InputStream; import java.io.OutputStream; +import org.apache.fury.io.FuryInputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.serializer.BufferCallback; import org.apache.fury.serializer.Serializer; @@ -110,9 +110,9 @@ public interface BaseFury { Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers); - Object deserialize(InputStream inputStream); + Object deserialize(FuryInputStream inputStream); - Object deserialize(InputStream inputStream, Iterable outOfBandBuffers); + Object deserialize(FuryInputStream inputStream, Iterable outOfBandBuffers); /** * Serialize java object without class info, deserialization should use {@link @@ -140,7 +140,7 @@ public interface BaseFury { */ T deserializeJavaObject(MemoryBuffer buffer, Class cls); - T deserializeJavaObject(InputStream inputStream, Class cls); + T deserializeJavaObject(FuryInputStream inputStream, Class cls); byte[] serializeJavaObjectAndClass(Object obj); @@ -152,5 +152,5 @@ public interface BaseFury { Object deserializeJavaObjectAndClass(MemoryBuffer buffer); - Object deserializeJavaObjectAndClass(InputStream inputStream); + Object deserializeJavaObjectAndClass(FuryInputStream inputStream); } diff --git a/java/fury-core/src/main/java/org/apache/fury/Fury.java b/java/fury-core/src/main/java/org/apache/fury/Fury.java index 202c2f3a09..b259cd64e1 100644 --- a/java/fury-core/src/main/java/org/apache/fury/Fury.java +++ b/java/fury-core/src/main/java/org/apache/fury/Fury.java @@ -19,10 +19,8 @@ package org.apache.fury; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteOrder; import java.util.ArrayList; @@ -30,7 +28,6 @@ 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.ObjectArray; @@ -40,6 +37,7 @@ import org.apache.fury.config.Language; import org.apache.fury.config.LongEncoding; import org.apache.fury.exception.DeserializationException; +import org.apache.fury.io.FuryInputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.resolver.ClassInfo; @@ -248,7 +246,9 @@ public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback ca buffer.put(maskIndex, bitmap); try { jitContext.lock(); - checkDepthForSerialization(); + if (depth != 0) { + throwDepthSerializationException(); + } if (language == Language.JAVA) { write(buffer, obj); } else { @@ -265,23 +265,12 @@ public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback ca @Override public void serialize(OutputStream outputStream, Object obj) { - serialize(outputStream, obj, null); + serializeToStream(outputStream, buf -> serialize(buf, obj, null)); } @Override public void serialize(OutputStream outputStream, Object obj, BufferCallback callback) { - MemoryBuffer buf = getBuffer(); - buf.writerIndex(0); - buf.writeInt(-1); - serialize(buf, obj, callback); - buf.putInt(0, buf.writerIndex() - 4); - try { - outputStream.write(buf.getBytes(0, buf.writerIndex())); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - resetBuffer(); - } + serializeToStream(outputStream, buf -> serialize(buf, obj, callback)); } private StackOverflowError processStackOverflowError(StackOverflowError e) { @@ -721,7 +710,9 @@ public Object deserialize(MemoryBuffer buffer) { public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { try { jitContext.lock(); - checkDepthForDeserialization(); + if (depth != 0) { + throwDepthDeserializationException(); + } byte bitmap = buffer.readByte(); if ((bitmap & isNilFlag) == isNilFlag) { return null; @@ -758,8 +749,7 @@ public Object deserialize(MemoryBuffer buffer, Iterable outOfBandB } return obj; } catch (Throwable t) { - handleReadFailed(t); - throw new IllegalStateException("unreachable"); + throw handleReadFailed(t); } finally { resetRead(); jitContext.unlock(); @@ -767,24 +757,21 @@ public Object deserialize(MemoryBuffer buffer, Iterable outOfBandB } @Override - public Object deserialize(InputStream inputStream) { + public Object deserialize(FuryInputStream inputStream) { return deserialize(inputStream, null); } @Override - public Object deserialize(InputStream inputStream, Iterable outOfBandBuffers) { + public Object deserialize(FuryInputStream inputStream, Iterable outOfBandBuffers) { try { - MemoryBuffer buf = getBuffer(); - readToBufferFromStream(inputStream, buf); + MemoryBuffer buf = inputStream.getBuffer(); return deserialize(buf, outOfBandBuffers); - } catch (IOException e) { - throw new RuntimeException(e); } finally { - resetBuffer(); + inputStream.shrinkBuffer(); } } - private void handleReadFailed(Throwable t) { + private RuntimeException handleReadFailed(Throwable t) { if (refResolver instanceof MapRefResolver) { ObjectArray readObjects = ((MapRefResolver) refResolver).getReadObjects(); // carry with read objects for better trouble shooting. @@ -792,6 +779,7 @@ private void handleReadFailed(Throwable t) { throw new DeserializationException(objects, t); } else { Platform.throwException(t); + throw new IllegalStateException("unreachable"); } } @@ -1023,7 +1011,9 @@ public byte[] serializeJavaObject(Object obj) { public void serializeJavaObject(MemoryBuffer buffer, Object obj) { try { jitContext.lock(); - checkDepthForSerialization(); + if (depth != 0) { + throwDepthSerializationException(); + } if (config.shareMetaContext()) { int startOffset = buffer.writerIndex(); buffer.writeInt(-1); // preserve 4-byte for nativeObjects start offsets. @@ -1066,7 +1056,9 @@ public T deserializeJavaObject(byte[] data, Class cls) { public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { try { jitContext.lock(); - checkDepthForDeserialization(); + if (depth != 0) { + throwDepthDeserializationException(); + } if (config.shareMetaContext()) { classResolver.readClassDefs(buffer); } @@ -1079,8 +1071,7 @@ public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { return null; } } catch (Throwable t) { - handleReadFailed(t); - throw new IllegalStateException("unreachable"); + throw handleReadFailed(t); } finally { resetRead(); jitContext.unlock(); @@ -1092,9 +1083,13 @@ public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { * #serializeJavaObject}. */ @Override - @SuppressWarnings("unchecked") - public T deserializeJavaObject(InputStream inputStream, Class cls) { - return (T) deserializeFromStream(inputStream, buf -> this.deserializeJavaObject(buf, cls)); + public T deserializeJavaObject(FuryInputStream inputStream, Class cls) { + try { + MemoryBuffer buf = inputStream.getBuffer(); + return deserializeJavaObject(buf, cls); + } finally { + inputStream.shrinkBuffer(); + } } /** @@ -1119,7 +1114,9 @@ public byte[] serializeJavaObjectAndClass(Object obj) { public void serializeJavaObjectAndClass(MemoryBuffer buffer, Object obj) { try { jitContext.lock(); - checkDepthForSerialization(); + if (depth != 0) { + throwDepthSerializationException(); + } write(buffer, obj); } catch (StackOverflowError t) { throw processStackOverflowError(t); @@ -1155,14 +1152,15 @@ public Object deserializeJavaObjectAndClass(byte[] data) { public Object deserializeJavaObjectAndClass(MemoryBuffer buffer) { try { jitContext.lock(); - checkDepthForDeserialization(); + if (depth != 0) { + throwDepthDeserializationException(); + } if (config.shareMetaContext()) { classResolver.readClassDefs(buffer); } return readRef(buffer); } catch (Throwable t) { - handleReadFailed(t); - throw new IllegalStateException("unreachable"); + throw handleReadFailed(t); } finally { resetRead(); jitContext.unlock(); @@ -1174,26 +1172,27 @@ public Object deserializeJavaObjectAndClass(MemoryBuffer buffer) { * #serializeJavaObjectAndClass}. */ @Override - public Object deserializeJavaObjectAndClass(InputStream inputStream) { - return deserializeFromStream(inputStream, this::deserializeJavaObjectAndClass); + public Object deserializeJavaObjectAndClass(FuryInputStream inputStream) { + try { + MemoryBuffer buf = inputStream.getBuffer(); + return deserializeJavaObjectAndClass(buf); + } finally { + inputStream.shrinkBuffer(); + } } 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); - int writerIndex = buf.writerIndex(); - buf.writeInt(-1); function.accept(buf); - buf.putInt(writerIndex, buf.writerIndex() - writerIndex); MemoryUtils.wrap(buf, (ByteArrayOutputStream) outputStream); buf.pointTo(oldBytes, 0, oldBytes.length); } else { buf.writerIndex(0); - buf.writeInt(-1); function.accept(buf); - buf.putInt(0, buf.writerIndex() - 4); try { byte[] bytes = buf.getHeapMemory(); if (bytes != null) { @@ -1210,58 +1209,6 @@ private void serializeToStream(OutputStream outputStream, Consumer } } - private Object deserializeFromStream( - InputStream inputStream, Function function) { - MemoryBuffer buf = getBuffer(); - try { - boolean isBis = inputStream.getClass() == ByteArrayInputStream.class; - byte[] oldBytes = null; - if (isBis) { - buf.readerIndex(0); - oldBytes = buf.getHeapMemory(); // Note: This should not be null. - MemoryUtils.wrap((ByteArrayInputStream) inputStream, buf); - buf.increaseReaderIndex(4); // skip size. - } else { - readToBufferFromStream(inputStream, buf); - } - Object o = function.apply(buf); - if (isBis) { - inputStream.skip(buf.readerIndex()); - buf.pointTo(oldBytes, 0, oldBytes.length); - } - return o; - } catch (Throwable t) { - handleReadFailed(t); - throw new IllegalStateException("unreachable"); - } finally { - resetBuffer(); - } - } - - private static void readToBufferFromStream(InputStream inputStream, MemoryBuffer buffer) - throws IOException { - buffer.readerIndex(0); - int read = readBytes(inputStream, buffer.getHeapMemory(), 0, 4); - Preconditions.checkArgument(read == 4); - int size = buffer.readInt(); - buffer.ensure(4 + size); - read = readBytes(inputStream, buffer.getHeapMemory(), 4, size); - Preconditions.checkArgument(read == size); - } - - private static int readBytes(InputStream inputStream, byte[] buffer, int offset, int size) - throws IOException { - int read = 0; - int count = 0; - while (read < size) { - if ((count = inputStream.read(buffer, offset + read, size - read)) == -1) { - break; - } - read += count; - } - return (read == 0 && count == -1) ? -1 : read; - } - public void reset() { refResolver.reset(); classResolver.reset(); @@ -1293,24 +1240,20 @@ public void resetRead() { depth = 0; } - private void checkDepthForSerialization() { - if (depth != 0) { - 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 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 checkDepthForDeserialization() { - if (depth != 0) { - 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)); - } + 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() { diff --git a/java/fury-core/src/main/java/org/apache/fury/ThreadLocalFury.java b/java/fury-core/src/main/java/org/apache/fury/ThreadLocalFury.java index af705fc93a..cf6e45df92 100644 --- a/java/fury-core/src/main/java/org/apache/fury/ThreadLocalFury.java +++ b/java/fury-core/src/main/java/org/apache/fury/ThreadLocalFury.java @@ -19,13 +19,13 @@ package org.apache.fury; -import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.WeakHashMap; import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.concurrent.ThreadSafe; +import org.apache.fury.io.FuryInputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.resolver.ClassResolver; @@ -155,12 +155,12 @@ public Object deserialize(MemoryBuffer buffer, Iterable outOfBandB } @Override - public Object deserialize(InputStream inputStream) { + public Object deserialize(FuryInputStream inputStream) { return bindingThreadLocal.get().get().deserialize(inputStream); } @Override - public Object deserialize(InputStream inputStream, Iterable outOfBandBuffers) { + public Object deserialize(FuryInputStream inputStream, Iterable outOfBandBuffers) { return bindingThreadLocal.get().get().deserialize(inputStream, outOfBandBuffers); } @@ -190,7 +190,7 @@ public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { } @Override - public T deserializeJavaObject(InputStream inputStream, Class cls) { + public T deserializeJavaObject(FuryInputStream inputStream, Class cls) { return bindingThreadLocal.get().get().deserializeJavaObject(inputStream, cls); } @@ -220,7 +220,7 @@ public Object deserializeJavaObjectAndClass(MemoryBuffer buffer) { } @Override - public Object deserializeJavaObjectAndClass(InputStream inputStream) { + public Object deserializeJavaObjectAndClass(FuryInputStream inputStream) { return bindingThreadLocal.get().get().deserializeJavaObjectAndClass(inputStream); } diff --git a/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java b/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java index f92e076e3a..0acb7ebd0c 100644 --- a/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java +++ b/java/fury-core/src/main/java/org/apache/fury/builder/BaseObjectCodecBuilder.java @@ -25,6 +25,7 @@ import static org.apache.fury.codegen.ExpressionOptimizer.invokeGenerated; import static org.apache.fury.codegen.ExpressionUtils.eq; import static org.apache.fury.codegen.ExpressionUtils.gt; +import static org.apache.fury.codegen.ExpressionUtils.inline; import static org.apache.fury.codegen.ExpressionUtils.neq; import static org.apache.fury.codegen.ExpressionUtils.not; import static org.apache.fury.codegen.ExpressionUtils.nullValue; @@ -37,8 +38,6 @@ import static org.apache.fury.type.TypeUtils.OBJECT_TYPE; import static org.apache.fury.type.TypeUtils.PRIMITIVE_BOOLEAN_TYPE; import static org.apache.fury.type.TypeUtils.PRIMITIVE_BYTE_TYPE; -import static org.apache.fury.type.TypeUtils.PRIMITIVE_DOUBLE_TYPE; -import static org.apache.fury.type.TypeUtils.PRIMITIVE_FLOAT_TYPE; import static org.apache.fury.type.TypeUtils.PRIMITIVE_INT_TYPE; import static org.apache.fury.type.TypeUtils.PRIMITIVE_VOID_TYPE; import static org.apache.fury.type.TypeUtils.SET_TYPE; @@ -95,6 +94,7 @@ import org.apache.fury.serializer.collection.CollectionFlags; import org.apache.fury.type.TypeUtils; import org.apache.fury.util.GraalvmSupport; +import org.apache.fury.util.Platform; import org.apache.fury.util.Preconditions; import org.apache.fury.util.ReflectionUtils; import org.apache.fury.util.StringUtils; @@ -276,8 +276,8 @@ protected void registerJITNotifyCallback() { * @see CodeGenerator#getClassUniqueId */ protected void addCommonImports() { - ctx.addImports(List.class, Map.class, Set.class); - ctx.addImports(Fury.class, MemoryBuffer.class, fury.getRefResolver().getClass()); + ctx.addImports( + Fury.class, MemoryBuffer.class, fury.getRefResolver().getClass(), Platform.class); ctx.addImports(ClassInfo.class, ClassInfoHolder.class, ClassResolver.class); ctx.addImport(Generated.class); ctx.addImports(LazyInitBeanSerializer.class, Serializers.EnumSerializer.class); @@ -1138,9 +1138,9 @@ protected Expression deserializeForNotNull( } else if (cls == long.class || cls == Long.class) { return LongSerializer.readLong(buffer, fury.longEncoding()); } else if (cls == float.class || cls == Float.class) { - return new Invoke(buffer, "readFloat", PRIMITIVE_FLOAT_TYPE); + return readFloat(buffer); } else if (cls == double.class || cls == Double.class) { - return new Invoke(buffer, "readDouble", PRIMITIVE_DOUBLE_TYPE); + return readDouble(buffer); } else { throw new IllegalStateException("impossible"); } @@ -1242,7 +1242,7 @@ protected Expression readCollectionCodegen( builder.add(readContainerElements(elementType, true, null, null, buffer, collection, size)); } else { Literal hasNullFlag = Literal.ofInt(CollectionFlags.HAS_NULL); - Expression hasNull = eq(new BitAnd(flags, hasNullFlag), hasNullFlag, "hasNull"); + Expression hasNull = eq(new BitAnd(flags.inline(), hasNullFlag), hasNullFlag, "hasNull"); builder.add( hasNull, readContainerElements(elementType, false, null, hasNull, buffer, collection, size)); @@ -1353,7 +1353,7 @@ private Expression readContainerElements( trackingRef, exprHolder.get("hasNull"), exprHolder.get("serializer"), - v -> new Invoke(exprHolder.get("collection"), "add", v))); + v -> new Invoke(exprHolder.get("collection"), "add", inline(v)))); } private Expression readContainerElement( diff --git a/java/fury-core/src/main/java/org/apache/fury/builder/CodecBuilder.java b/java/fury-core/src/main/java/org/apache/fury/builder/CodecBuilder.java index 9c69757e17..64b7e8272d 100644 --- a/java/fury-core/src/main/java/org/apache/fury/builder/CodecBuilder.java +++ b/java/fury-core/src/main/java/org/apache/fury/builder/CodecBuilder.java @@ -645,6 +645,10 @@ protected Expression readInt(Expression buffer) { return new Invoke(buffer, func, PRIMITIVE_INT_TYPE); } + public static String readIntFunc() { + return Platform.IS_LITTLE_ENDIAN ? "readIntOnLE" : "readIntOnBE"; + } + protected Expression readVarInt(Expression buffer) { String func = Platform.IS_LITTLE_ENDIAN ? "readVarIntOnLE" : "readVarIntOnBE"; return new Invoke(buffer, func, PRIMITIVE_INT_TYPE); diff --git a/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java b/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java index 02be76218b..cb13f72264 100644 --- a/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java +++ b/java/fury-core/src/main/java/org/apache/fury/builder/CompatibleCodecBuilder.java @@ -393,12 +393,13 @@ public Expression buildRecordDecodeExpression() { new StaticInvoke( Platform.class, "copyObjectArray", OBJECT_ARRAY_TYPE, recordComponentDefaultValues); ListExpression readAndSetFieldsExpr = new ListExpression(); - Expression partFieldInfo = new Invoke(buffer, "readInt", "partFieldInfo", PRIMITIVE_LONG_TYPE); + Expression partFieldInfo = + new Invoke(buffer, readIntFunc(), "partFieldInfo", PRIMITIVE_LONG_TYPE); readAndSetFieldsExpr.add(partFieldInfo); readEmbedTypes4Fields(buffer, readAndSetFieldsExpr, components, partFieldInfo); BitOr newPartFieldInfo = new BitOr( - new BitShift("<<", new Invoke(buffer, "readInt", PRIMITIVE_LONG_TYPE), 32), + new BitShift("<<", new Invoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE), 32), new BitAnd(partFieldInfo, new Literal(0x00000000ffffffffL, PRIMITIVE_LONG_TYPE))); readAndSetFieldsExpr.add(new Assign(partFieldInfo, newPartFieldInfo)); readEmbedTypes9Fields(buffer, readAndSetFieldsExpr, components, partFieldInfo); @@ -434,12 +435,13 @@ protected Expression setFieldValue(Expression bean, Descriptor d, Expression val private ListExpression readAndSetFields(Reference buffer, Expression bean) { ListExpression readAndSetFieldsExpr = new ListExpression(); - Expression partFieldInfo = new Invoke(buffer, "readInt", "partFieldInfo", PRIMITIVE_LONG_TYPE); + Expression partFieldInfo = + new Invoke(buffer, readIntFunc(), "partFieldInfo", PRIMITIVE_LONG_TYPE); readAndSetFieldsExpr.add(partFieldInfo); readEmbedTypes4Fields(buffer, readAndSetFieldsExpr, bean, partFieldInfo); BitOr newPartFieldInfo = new BitOr( - new BitShift("<<", new Invoke(buffer, "readInt", PRIMITIVE_LONG_TYPE), 32), + new BitShift("<<", new Invoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE), 32), new BitAnd(partFieldInfo, new Literal(0x00000000ffffffffL, PRIMITIVE_LONG_TYPE))); readAndSetFieldsExpr.add(new Assign(partFieldInfo, newPartFieldInfo)); readEmbedTypes9Fields(buffer, readAndSetFieldsExpr, bean, partFieldInfo); @@ -514,7 +516,7 @@ private Expression readEmbedTypes4( setFieldValue(bean, descriptor, tryInlineCast(expr, descriptor.getTypeToken()))); return new ListExpression( deserializeAction, - new Assign(partFieldInfo, inlineInvoke(buffer, "readInt", PRIMITIVE_LONG_TYPE))); + new Assign(partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE))); } /** @@ -557,7 +559,8 @@ private Expression skipDataBy4Until( cast(partFieldInfo, PRIMITIVE_INT_TYPE)), endTagLiteral), returnEndTag ? new Return(endTagLiteral) : new Return(bean)), - new Assign(partFieldInfo, inlineInvoke(buffer, "readInt", PRIMITIVE_LONG_TYPE)))); + new Assign( + partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE)))); } /** @@ -589,7 +592,8 @@ private Expression skipField4End(Expression bean, Expression buffer, Expression cast(partFieldInfo, PRIMITIVE_INT_TYPE)), endTagLiteral), new Return(bean)), - new Assign(partFieldInfo, inlineInvoke(buffer, "readInt", PRIMITIVE_LONG_TYPE)))); + new Assign( + partFieldInfo, inlineInvoke(buffer, readIntFunc(), PRIMITIVE_LONG_TYPE)))); } private void readEmbedTypes9Fields( diff --git a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java index 6731c3130c..fc50c7f010 100644 --- a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java +++ b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecBuilder.java @@ -560,7 +560,7 @@ private Expression checkClassVersion(Expression buffer) { PRIMITIVE_VOID_TYPE, false, furyRef, - inlineInvoke(buffer, "readInt", PRIMITIVE_INT_TYPE), + inlineInvoke(buffer, readIntFunc(), PRIMITIVE_INT_TYPE), Objects.requireNonNull(classVersionHash)); } @@ -642,7 +642,7 @@ private List deserializeUnCompressedPrimitives( } Expression increaseReaderIndex = new Invoke( - buffer, "increaseReaderIndexUnsafe", new Literal(totalSizeLiteral, PRIMITIVE_INT_TYPE)); + buffer, "increaseReaderIndex", new Literal(totalSizeLiteral, PRIMITIVE_INT_TYPE)); expressions.add(increaseReaderIndex); return expressions; } @@ -734,7 +734,7 @@ private List deserializeCompressedPrimitives( private void addIncReaderIndexExpr(ListExpression expressions, Expression buffer, int diff) { if (diff != 0) { - expressions.add(new Invoke(buffer, "increaseReaderIndexUnsafe", Literal.ofInt(diff))); + expressions.add(new Invoke(buffer, "increaseReaderIndex", Literal.ofInt(diff))); } } diff --git a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecOptimizer.java b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecOptimizer.java index bceac14eec..21cd26e099 100644 --- a/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecOptimizer.java +++ b/java/fury-core/src/main/java/org/apache/fury/builder/ObjectCodecOptimizer.java @@ -96,7 +96,7 @@ private void buildGroups() { List primitiveDescriptorsList = new ArrayList<>(descriptorGrouper.getPrimitiveDescriptors()); while (!primitiveDescriptorsList.isEmpty()) { - int endIndex = Math.min(20, primitiveDescriptorsList.size()); + int endIndex = Math.min(18, primitiveDescriptorsList.size()); primitiveGroups.add(primitiveDescriptorsList.subList(0, endIndex)); primitiveDescriptorsList = primitiveDescriptorsList.subList(endIndex, primitiveDescriptorsList.size()); @@ -125,7 +125,7 @@ private void buildGroups() { MutableTuple3.of( new ArrayList<>(descriptorGrouper.getOtherDescriptors()), 9, otherWriteGroups)); for (MutableTuple3, Integer, List>> decs : groups) { - while (decs.f0.size() > 0) { + while (!decs.f0.isEmpty()) { int endIndex = Math.min(decs.f1, decs.f0.size()); decs.f2.add(decs.f0.subList(0, endIndex)); decs.f0 = decs.f0.subList(endIndex, decs.f0.size()); diff --git a/java/fury-core/src/main/java/org/apache/fury/codegen/ExpressionUtils.java b/java/fury-core/src/main/java/org/apache/fury/codegen/ExpressionUtils.java index b977eb83d7..f1dbda5d7a 100644 --- a/java/fury-core/src/main/java/org/apache/fury/codegen/ExpressionUtils.java +++ b/java/fury-core/src/main/java/org/apache/fury/codegen/ExpressionUtils.java @@ -144,13 +144,21 @@ public static Cast cast(Expression value, TypeToken typeToken) { return new Cast(value, typeToken); } - public static Expression uninline(Expression expression) { + public static Expression inline(Expression expression) { + return inline(expression, true); + } + + private static Expression inline(Expression expression, boolean inline) { if (expression instanceof Expression.Inlineable) { - ((Expression.Inlineable) (expression)).inline(false); + ((Expression.Inlineable) (expression)).inline(inline); } return expression; } + public static Expression uninline(Expression expression) { + return inline(expression, false); + } + public static StaticInvoke invokeStaticInline( Class staticObject, String functionName, TypeToken type, Expression... arguments) { return new StaticInvoke(staticObject, functionName, "", type, false, true, arguments); diff --git a/java/fury-core/src/main/java/org/apache/fury/io/AbstractStreamReader.java b/java/fury-core/src/main/java/org/apache/fury/io/AbstractStreamReader.java new file mode 100644 index 0000000000..4df3e2bf5c --- /dev/null +++ b/java/fury-core/src/main/java/org/apache/fury/io/AbstractStreamReader.java @@ -0,0 +1,50 @@ +/* + * 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.io; + +import java.nio.ByteBuffer; +import org.apache.fury.memory.MemoryBuffer; + +/** An abstract {@link FuryStreamReader} for subclass implementation convenience. */ +public abstract class AbstractStreamReader implements FuryStreamReader { + @Override + public int fillBuffer(int minFillSize) { + return 0; + } + + @Override + public void readTo(byte[] dst, int dstIndex, int length) {} + + @Override + public void readToUnsafe(Object target, long targetPointer, int numBytes) {} + + @Override + public void readToByteBuffer(ByteBuffer dst, int length) {} + + @Override + public int readToByteBuffer(ByteBuffer dst) { + return 0; + } + + @Override + public MemoryBuffer getBuffer() { + return null; + } +} diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryInputStream.java b/java/fury-core/src/main/java/org/apache/fury/io/FuryInputStream.java index e30ecaea21..4fc321528b 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryInputStream.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/FuryInputStream.java @@ -21,46 +21,222 @@ import java.io.IOException; import java.io.InputStream; +import java.nio.ByteBuffer; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.fury.memory.MemoryBuffer; -import org.apache.fury.util.Preconditions; +import org.apache.fury.util.Platform; -/** InputStream based on {@link MemoryBuffer}. */ -public class FuryInputStream extends InputStream { +/** + * A buffered stream by fury. Do not use original {@link InputStream} when this stream object + * created. This stream will try to buffer data inside, the date read from original stream won't be + * the data you expected. Use this stream as a wrapper instead. + */ +@NotThreadSafe +public class FuryInputStream extends InputStream implements FuryStreamReader { + private static final int BUFFER_GROW_STEP_THRESHOLD = 100 * 1024 * 1024; + private final InputStream stream; + private final int bufferSize; private final MemoryBuffer buffer; - public FuryInputStream(MemoryBuffer buffer) { - this.buffer = buffer; + public FuryInputStream(InputStream stream) { + this(stream, 4096); + } + + public FuryInputStream(InputStream stream, int bufferSize) { + this.stream = stream; + this.bufferSize = bufferSize; + byte[] bytes = new byte[bufferSize]; + this.buffer = MemoryBuffer.fromByteArray(bytes, 0, 0, this); + } + + @Override + public int fillBuffer(int minFillSize) { + MemoryBuffer buffer = this.buffer; + byte[] heapMemory = buffer.getHeapMemory(); + int offset = buffer.size(); + int targetSize = offset + minFillSize; + if (targetSize > heapMemory.length) { + int newSize; + if (targetSize < BUFFER_GROW_STEP_THRESHOLD) { + newSize = targetSize << 2; + } else { + newSize = (int) (targetSize * 1.5); + } + byte[] newBuffer = new byte[newSize]; + System.arraycopy(heapMemory, 0, newBuffer, 0, buffer.size()); + buffer.initHeapBuffer(newBuffer, 0, buffer.size()); + heapMemory = newBuffer; + } + try { + int read; + read = stream.read(heapMemory, offset, Math.min(stream.available(), heapMemory.length)); + while (read < minFillSize) { + int newRead = stream.read(heapMemory, offset + read, minFillSize - read); + if (newRead < 0) { + throw new IndexOutOfBoundsException("No enough data in the stream " + stream); + } + read += newRead; + } + buffer.increaseSize(read); + return read; + } catch (IOException e) { + throw new RuntimeException(e); + } } - public int read() { - if (buffer.remaining() == 0) { - return -1; + @Override + public void readTo(byte[] dst, int dstIndex, int len) { + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + if (remaining >= len) { + buf.readBytes(dst, dstIndex, len); } else { - return buffer.readByte() & 0xFF; + buf.readBytes(dst, dstIndex, remaining); + len -= remaining; + dstIndex += remaining; + try { + int read = stream.read(dst, dstIndex, len); + while (read < len) { + int newRead = stream.read(dst, dstIndex + read, len - read); + if (newRead < 0) { + throw new IndexOutOfBoundsException("No enough data in the stream " + stream); + } + read += newRead; + } + } catch (IOException e) { + throw new RuntimeException(e); + } } } - public int read(byte[] bytes, int offset, int length) throws IOException { - if (length == 0) { - return 0; + @Override + public void readToUnsafe(Object target, long targetPointer, int numBytes) { + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + if (remaining < numBytes) { + fillBuffer(numBytes - remaining); } - int size = Math.min(buffer.remaining(), length); - if (size == 0) { - return -1; + byte[] heapMemory = buf.getHeapMemory(); + long address = buf.getUnsafeReaderAddress(); + Platform.copyMemory(heapMemory, address, target, targetPointer, numBytes); + buf.increaseReaderIndex(numBytes); + } + + @Override + public void readToByteBuffer(ByteBuffer dst, int length) { + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + if (remaining < length) { + fillBuffer(length - remaining); + } + byte[] heapMemory = buf.getHeapMemory(); + dst.put(heapMemory, buf.unsafeHeapReaderIndex(), length); + buf.increaseReaderIndex(length); + } + + @Override + public int readToByteBuffer(ByteBuffer dst) { + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + int len = dst.remaining(); + if (remaining >= len) { + buf.read(dst, len); + return len; + } else { + try { + buf.read(dst, remaining); + int available = stream.available(); + if (available > 0) { + fillBuffer(available); + int newRemaining = buf.remaining(); + buf.read(dst, newRemaining); + return newRemaining + remaining; + } else { + return remaining; + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public MemoryBuffer getBuffer() { + return buffer; + } + + public InputStream getStream() { + return stream; + } + + /** + * Shrink buffer to release memory, do not invoke this method is the deserialization for an object + * didn't finish. + */ + public void shrinkBuffer() { + int remaining = buffer.remaining(); + int bufferSize = this.bufferSize; + if (remaining > bufferSize || buffer.size() > bufferSize) { + byte[] heapMemory = buffer.getHeapMemory(); + byte[] newBuffer = new byte[Math.max(bufferSize, remaining)]; + System.arraycopy(heapMemory, buffer.readerIndex(), newBuffer, 0, remaining); + buffer.initHeapBuffer(newBuffer, 0, remaining); + buffer.readerIndex(0); + } + } + + @Override + public int read() throws IOException { + MemoryBuffer buf = buffer; + if (buf.remaining() > 0) { + return buf.readByte() & 0xFF; + } + int available = stream.available(); + if (available > 0) { + fillBuffer(1); + return buf.readByte() & 0xFF; + } + return stream.read(); + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + if (remaining >= len) { + buf.readBytes(b, off, len); + return len; + } else { + buf.readBytes(b, off, remaining); + return stream.read(b, off + remaining, len - remaining) + remaining; } - buffer.readBytes(bytes, offset, size); - return size; } @Override public long skip(long n) throws IOException { - Preconditions.checkArgument(n < Integer.MAX_VALUE); - int nbytes = (int) Math.min(n, buffer.remaining()); - buffer.increaseReaderIndex(nbytes); - return nbytes; + MemoryBuffer buf = buffer; + int remaining = buf.remaining(); + if (remaining >= n) { + buf.increaseReaderIndex((int) n); + return n; + } else { + buf.increaseReaderIndex(remaining); + return stream.skip(n - remaining) + remaining; + } } + @Override public int available() throws IOException { - return buffer.remaining(); + return buffer.remaining() + stream.available(); + } + + @Override + public void close() throws IOException { + stream.close(); } } diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryReadableChannel.java b/java/fury-core/src/main/java/org/apache/fury/io/FuryReadableChannel.java new file mode 100644 index 0000000000..2d5716da83 --- /dev/null +++ b/java/fury-core/src/main/java/org/apache/fury/io/FuryReadableChannel.java @@ -0,0 +1,63 @@ +/* + * 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.io; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import org.apache.fury.memory.MemoryBuffer; + +// TODO support zero-copy channel reading. +public class FuryReadableChannel extends AbstractStreamReader implements ReadableByteChannel { + private final ReadableByteChannel channel; + private final ByteBuffer byteBuffer; + private final MemoryBuffer buffer; + + public FuryReadableChannel(ReadableByteChannel channel) { + this(channel, ByteBuffer.allocate(4096)); + } + + private FuryReadableChannel(ReadableByteChannel channel, ByteBuffer directBuffer) { + this.channel = channel; + this.byteBuffer = directBuffer; + this.buffer = MemoryBuffer.fromByteBuffer(directBuffer); + } + + @Override + public int read(ByteBuffer dst) throws IOException { + throw new UnsupportedEncodingException(); + } + + @Override + public boolean isOpen() { + return channel.isOpen(); + } + + @Override + public void close() throws IOException { + channel.close(); + } + + @Override + public MemoryBuffer getBuffer() { + return buffer; + } +} diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryStreamReader.java b/java/fury-core/src/main/java/org/apache/fury/io/FuryStreamReader.java new file mode 100644 index 0000000000..a40a4b807f --- /dev/null +++ b/java/fury-core/src/main/java/org/apache/fury/io/FuryStreamReader.java @@ -0,0 +1,66 @@ +/* + * 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.io; + +import java.io.InputStream; +import java.nio.ByteBuffer; +import org.apache.fury.memory.MemoryBuffer; + +/** A streaming reader to make {@link MemoryBuffer} to support streaming reading. */ +public interface FuryStreamReader { + /** + * Read stream and fill the data to underlying {@link MemoryBuffer}, which is also the buffer + * returned by {@link #getBuffer}. + */ + int fillBuffer(int minFillSize); + + /** + * Read data into `dst`. This method will block until the enough data are written into the `dst`. + */ + void readTo(byte[] dst, int dstIndex, int length); + + /** + * Read data into `target`. This method will block until the enough data are written into the + * `target`. + */ + void readToUnsafe(Object target, long targetPointer, int numBytes); + + /** + * Read data into `dst`. This method will block until the enough data are written into the `dst`. + */ + void readToByteBuffer(ByteBuffer dst, int length); + + int readToByteBuffer(ByteBuffer dst); + + /** + * Returns the underlying {@link MemoryBuffer}. This method will return same instance of buffer + * for same {@link FuryStreamReader} instance. + */ + MemoryBuffer getBuffer(); + + /** + * Create a {@link FuryInputStream} from the provided {@link InputStream}. Note that the provided + * stream will be owned by the returned {@link FuryInputStream}, do not read the + * provided {@link InputStream} anymore, read the returned stream instead. + */ + static FuryInputStream of(InputStream stream) { + return new FuryInputStream(stream); + } +} diff --git a/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferInputStream.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferInputStream.java new file mode 100644 index 0000000000..6589333fd6 --- /dev/null +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferInputStream.java @@ -0,0 +1,66 @@ +/* + * 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.io; + +import java.io.IOException; +import java.io.InputStream; +import org.apache.fury.memory.MemoryBuffer; +import org.apache.fury.util.Preconditions; + +/** InputStream based on {@link MemoryBuffer}. */ +public class MemoryBufferInputStream extends InputStream { + private final MemoryBuffer buffer; + + public MemoryBufferInputStream(MemoryBuffer buffer) { + this.buffer = buffer; + } + + public int read() { + if (buffer.remaining() == 0) { + return -1; + } else { + return buffer.readByte() & 0xFF; + } + } + + public int read(byte[] bytes, int offset, int length) throws IOException { + if (length == 0) { + return 0; + } + int size = Math.min(buffer.remaining(), length); + if (size == 0) { + return -1; + } + buffer.readBytes(bytes, offset, size); + return size; + } + + @Override + public long skip(long n) throws IOException { + Preconditions.checkArgument(n < Integer.MAX_VALUE); + int nbytes = (int) Math.min(n, buffer.remaining()); + buffer.increaseReaderIndex(nbytes); + return nbytes; + } + + public int available() throws IOException { + return buffer.remaining(); + } +} diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryObjectInput.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectInput.java similarity index 96% rename from java/fury-core/src/main/java/org/apache/fury/io/FuryObjectInput.java rename to java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectInput.java index ed9de748fb..0ad98ac3f6 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryObjectInput.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectInput.java @@ -28,12 +28,12 @@ import org.apache.fury.util.Preconditions; /** ObjectInput based on {@link Fury} and {@link MemoryBuffer}. */ -public class FuryObjectInput extends InputStream implements ObjectInput { +public class MemoryBufferObjectInput extends InputStream implements ObjectInput { private final Fury fury; private MemoryBuffer buffer; private final StringSerializer stringSerializer; - public FuryObjectInput(Fury fury, MemoryBuffer buffer) { + public MemoryBufferObjectInput(Fury fury, MemoryBuffer buffer) { this.fury = fury; this.buffer = buffer; this.stringSerializer = new StringSerializer(fury); diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryObjectOutput.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectOutput.java similarity index 95% rename from java/fury-core/src/main/java/org/apache/fury/io/FuryObjectOutput.java rename to java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectOutput.java index 9addc528fb..be305a05c7 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryObjectOutput.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferObjectOutput.java @@ -29,13 +29,13 @@ import org.apache.fury.util.Preconditions; /** ObjectOutput based on {@link Fury} and {@link MemoryBuffer}. */ -public class FuryObjectOutput extends OutputStream implements ObjectOutput { +public class MemoryBufferObjectOutput extends OutputStream implements ObjectOutput { private final Fury fury; private final DataOutputStream utf8out = new DataOutputStream(this); private final StringSerializer stringSerializer; private MemoryBuffer buffer; - public FuryObjectOutput(Fury fury, MemoryBuffer buffer) { + public MemoryBufferObjectOutput(Fury fury, MemoryBuffer buffer) { this.fury = fury; this.buffer = buffer; this.stringSerializer = new StringSerializer(fury); diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryOutputStream.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferOutputStream.java similarity index 91% rename from java/fury-core/src/main/java/org/apache/fury/io/FuryOutputStream.java rename to java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferOutputStream.java index 86fe199019..b115a84db8 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryOutputStream.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferOutputStream.java @@ -24,10 +24,10 @@ import org.apache.fury.memory.MemoryBuffer; /** OutputStream based on {@link MemoryBuffer}. */ -public class FuryOutputStream extends OutputStream { +public class MemoryBufferOutputStream extends OutputStream { private final MemoryBuffer buffer; - public FuryOutputStream(MemoryBuffer buffer) { + public MemoryBufferOutputStream(MemoryBuffer buffer) { this.buffer = buffer; } diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryReadableByteChannel.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferReadableChannel.java similarity index 85% rename from java/fury-core/src/main/java/org/apache/fury/io/FuryReadableByteChannel.java rename to java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferReadableChannel.java index 905179e78a..13b4d39d7c 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryReadableByteChannel.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferReadableChannel.java @@ -24,19 +24,17 @@ import org.apache.fury.memory.MemoryBuffer; /** {@link ReadableByteChannel} based on fury {@link MemoryBuffer}. */ -public class FuryReadableByteChannel implements ReadableByteChannel { +public class MemoryBufferReadableChannel implements ReadableByteChannel { private boolean open = true; private final MemoryBuffer buffer; - public FuryReadableByteChannel(MemoryBuffer buffer) { + public MemoryBufferReadableChannel(MemoryBuffer buffer) { this.buffer = buffer; } @Override public int read(ByteBuffer dst) { - int position = dst.position(); - buffer.read(dst); - return dst.position() - position; + return buffer.read(dst); } @Override diff --git a/java/fury-core/src/main/java/org/apache/fury/io/FuryWritableByteChannel.java b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferWritableChannel.java similarity index 91% rename from java/fury-core/src/main/java/org/apache/fury/io/FuryWritableByteChannel.java rename to java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferWritableChannel.java index eb044e6132..9e41b07b2c 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/FuryWritableByteChannel.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MemoryBufferWritableChannel.java @@ -24,11 +24,11 @@ import org.apache.fury.memory.MemoryBuffer; /** {@link WritableByteChannel} based on fury {@link MemoryBuffer}. */ -public class FuryWritableByteChannel implements WritableByteChannel { +public class MemoryBufferWritableChannel implements WritableByteChannel { private boolean open = true; private final MemoryBuffer buffer; - public FuryWritableByteChannel(MemoryBuffer buffer) { + public MemoryBufferWritableChannel(MemoryBuffer buffer) { this.buffer = buffer; } diff --git a/java/fury-core/src/main/java/org/apache/fury/io/MockOutputStream.java b/java/fury-core/src/main/java/org/apache/fury/io/MockOutputStream.java index 595a426674..389ca89afc 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/MockOutputStream.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MockOutputStream.java @@ -28,7 +28,7 @@ * to be allocated for actual writing. * *

Note that {@link OutputStream} doesn't support {@link ByteBuffer}, which may incur extra copy. - * See also {@link MockWritableByteChannel}. + * See also {@link MockWritableChannel}. */ public class MockOutputStream extends OutputStream { private int totalBytes; diff --git a/java/fury-core/src/main/java/org/apache/fury/io/MockWritableByteChannel.java b/java/fury-core/src/main/java/org/apache/fury/io/MockWritableChannel.java similarity index 95% rename from java/fury-core/src/main/java/org/apache/fury/io/MockWritableByteChannel.java rename to java/fury-core/src/main/java/org/apache/fury/io/MockWritableChannel.java index 46c742ee80..f50112597a 100644 --- a/java/fury-core/src/main/java/org/apache/fury/io/MockWritableByteChannel.java +++ b/java/fury-core/src/main/java/org/apache/fury/io/MockWritableChannel.java @@ -27,7 +27,7 @@ * data, they just bump a size counter that can be later used to know exactly which data size needs * to be allocated for actual writing. */ -public class MockWritableByteChannel implements WritableByteChannel { +public class MockWritableChannel implements WritableByteChannel { private boolean open = true; private int totalBytes; diff --git a/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java b/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java index 011b31a7b4..cd4b10dc8b 100644 --- a/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java +++ b/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java @@ -27,8 +27,10 @@ import java.nio.ReadOnlyBufferException; import java.util.Arrays; import org.apache.fury.annotation.CodegenInvoke; +import org.apache.fury.io.AbstractStreamReader; +import org.apache.fury.io.FuryStreamReader; import org.apache.fury.util.Platform; -import org.apache.fury.util.Preconditions; +import sun.misc.Unsafe; /** * A class for operations on memory managed by Fury. The buffer may be backed by heap memory (byte @@ -55,14 +57,16 @@ * *

Warning: The instance of this class should not be hold on graalvm build time, the heap unsafe * offset are not correct in runtime since graalvm will change array base offset. + * + *

Note(chaokunyang): Buffer operations are very common, and jvm inline and branch elimination is + * not reliable even in c2 compiler, so we try to inline and avoid checks as we can manually. jvm + * jit may stop inline for some reasons: NodeCountInliningCutoff, + * DesiredMethodLimit,MaxRecursiveInlineLevel,FreqInlineSize,MaxInlineSize */ -// FIXME Buffer operations is most common, and jvm inline and branch elimination -// is not reliable even in c2 compiler, so we try to inline and avoid checks as we can manually. -// Note: This class is based on org.apache.flink.core.memory.MemorySegment and -// org.apache.arrow.memory.ArrowBuf. +// DesiredMethodLimit,MaxRecursiveInlineLevel,FreqInlineSize,MaxInlineSize public final class MemoryBuffer { // The unsafe handle for transparent memory copied (heap/off-heap). - private static final sun.misc.Unsafe UNSAFE = Platform.UNSAFE; + private static final Unsafe UNSAFE = Platform.UNSAFE; // Constant that flags the byte order. Because this is a boolean constant, the JIT compiler can // use this well to aggressively eliminate the non-applicable code paths. private static final boolean LITTLE_ENDIAN = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN); @@ -90,6 +94,7 @@ public final class MemoryBuffer { private int size; private int readerIndex; private int writerIndex; + private final FuryStreamReader streamReader; /** * Creates a new memory buffer that represents the memory of the byte array. @@ -100,24 +105,30 @@ public final class MemoryBuffer { * @param length buffer size */ private MemoryBuffer(byte[] buffer, int offset, int length) { - Preconditions.checkArgument(offset >= 0 && length >= 0); + this(buffer, offset, length, null); + } + + /** + * Creates a new memory buffer that represents the memory of the byte array. + * + * @param buffer The byte array whose memory is represented by this memory buffer. + * @param offset The offset of the sub array to be used; must be non-negative and no larger than + * array.length. + * @param length buffer size + * @param streamReader a reader for reading from a stream. + */ + private MemoryBuffer(byte[] buffer, int offset, int length, FuryStreamReader streamReader) { + checkArgument(offset >= 0 && length >= 0); if (offset + length > buffer.length) { throw new IllegalArgumentException( String.format("%d exceeds buffer size %d", offset + length, buffer.length)); } initHeapBuffer(buffer, offset, length); - } - - private void initHeapBuffer(byte[] buffer, int offset, int length) { - if (buffer == null) { - throw new NullPointerException("buffer"); + if (streamReader != null) { + this.streamReader = streamReader; + } else { + this.streamReader = new BoundChecker(); } - this.heapMemory = buffer; - this.heapOffset = offset; - final long startPos = Platform.BYTE_ARRAY_OFFSET + offset; - this.address = startPos; - this.size = length; - this.addressLimit = startPos + length; } /** @@ -131,6 +142,22 @@ private void initHeapBuffer(byte[] buffer, int offset, int length) { * the memory being released. */ private MemoryBuffer(long offHeapAddress, int size, ByteBuffer offHeapBuffer) { + this(offHeapAddress, size, offHeapBuffer, null); + } + + /** + * Creates a new memory buffer that represents the native memory at the absolute address given by + * the pointer. + * + * @param offHeapAddress The address of the memory represented by this memory buffer. + * @param size The size of this memory buffer. + * @param offHeapBuffer The byte buffer whose memory is represented by this memory buffer which + * may be null if the memory is not allocated by `DirectByteBuffer`. Hold this buffer to avoid + * the memory being released. + * @param streamReader a reader for reading from a stream. + */ + private MemoryBuffer( + long offHeapAddress, int size, ByteBuffer offHeapBuffer, FuryStreamReader streamReader) { this.offHeapBuffer = offHeapBuffer; if (offHeapAddress <= 0) { throw new IllegalArgumentException("negative pointer or size"); @@ -148,6 +175,38 @@ private MemoryBuffer(long offHeapAddress, int size, ByteBuffer offHeapBuffer) { this.address = offHeapAddress; this.addressLimit = this.address + size; this.size = size; + if (streamReader != null) { + this.streamReader = streamReader; + } else { + this.streamReader = new BoundChecker(); + } + } + + private class BoundChecker extends AbstractStreamReader { + @Override + public int fillBuffer(int minFillSize) { + throw new IndexOutOfBoundsException( + String.format( + "readerIndex(%d) + length(%d) exceeds size(%d): %s", + readerIndex, minFillSize, size, this)); + } + + @Override + public MemoryBuffer getBuffer() { + return MemoryBuffer.this; + } + } + + public void initHeapBuffer(byte[] buffer, int offset, int length) { + if (buffer == null) { + throw new NullPointerException("buffer"); + } + this.heapMemory = buffer; + this.heapOffset = offset; + final long startPos = Platform.BYTE_ARRAY_OFFSET + offset; + this.address = startPos; + this.size = length; + this.addressLimit = startPos + length; } // ------------------------------------------------------------------------ @@ -163,6 +222,10 @@ public int size() { return size; } + public void increaseSize(int diff) { + this.addressLimit = address + (size += diff); + } + /** * Checks whether this memory buffer is backed by off-heap memory. * @@ -247,7 +310,7 @@ private void checkPosition(long index, long pos, long length) { if (BoundsChecking.BOUNDS_CHECKING_ENABLED) { if (index < 0 || pos > addressLimit - length) { // index is in fact invalid - throw new IndexOutOfBoundsException(); + throwOOBException(); } } } @@ -282,7 +345,7 @@ public void get(int index, byte[] dst) { public void get(int index, byte[] dst, int offset, int length) { // check the byte array offset and length and the status if ((offset | length | (offset + length) | (dst.length - (offset + length))) < 0) { - throw new IndexOutOfBoundsException(); + throwOOBException(); } final long pos = address + index; if (index >= 0 && pos <= addressLimit - length) { @@ -290,7 +353,7 @@ public void get(int index, byte[] dst, int offset, int length) { Platform.copyMemory(heapMemory, pos, dst, arrayAddress, length); } else { // index is in fact invalid - throw new IndexOutOfBoundsException(); + throwOOBException(); } } @@ -312,16 +375,16 @@ public void get(int index, byte[] dst, int offset, int length) { public void get(int offset, ByteBuffer target, int numBytes) { // check the byte array offset and length if ((offset | numBytes | (offset + numBytes)) < 0) { - throw new IndexOutOfBoundsException(); + throwOOBException(); } final int targetOffset = target.position(); final int remaining = target.remaining(); if (remaining < numBytes) { - throw new BufferOverflowException(); + throwOOBException(); } if (target.isDirect()) { if (target.isReadOnly()) { - throw new ReadOnlyBufferException(); + throwOOBException(); } // copy to the target memory directly final long targetPointer = Platform.getAddress(target) + targetOffset; @@ -330,7 +393,7 @@ public void get(int offset, ByteBuffer target, int numBytes) { Platform.copyMemory(heapMemory, sourcePointer, null, targetPointer, numBytes); target.position(targetOffset + numBytes); } else { - throw new IndexOutOfBoundsException(); + throwOOBException(); } } else if (target.hasArray()) { // move directly into the byte array @@ -372,12 +435,12 @@ public void unsafePut(int index, byte b) { public void put(int offset, ByteBuffer source, int numBytes) { // check the byte array offset and length if ((offset | numBytes | (offset + numBytes)) < 0) { - throw new IndexOutOfBoundsException(); + throwOOBException(); } final int sourceOffset = source.position(); final int remaining = source.remaining(); if (remaining < numBytes) { - throw new BufferUnderflowException(); + throwOOBException(); } if (source.isDirect()) { // copy to the target memory directly @@ -387,7 +450,7 @@ public void put(int offset, ByteBuffer source, int numBytes) { Platform.copyMemory(null, sourcePointer, heapMemory, targetPointer, numBytes); source.position(sourceOffset + numBytes); } else { - throw new IndexOutOfBoundsException(); + throwOOBException(); } } else if (source.hasArray()) { // move directly into the byte array @@ -428,7 +491,7 @@ public void put(int index, byte[] src) { public void put(int index, byte[] src, int offset, int length) { // check the byte array offset and length if ((offset | length | (offset + length) | (src.length - (offset + length))) < 0) { - throw new IndexOutOfBoundsException(); + throwOOBException(); } final long pos = address + index; if (index >= 0 && pos <= addressLimit - length) { @@ -436,7 +499,7 @@ public void put(int index, byte[] src, int offset, int length) { Platform.copyMemory(src, arrayAddress, heapMemory, pos, length); } else { // index is in fact invalid - throw new IndexOutOfBoundsException(); + throwOOBException(); } } @@ -801,14 +864,18 @@ public int readerIndex() { * @throws IndexOutOfBoundsException if the specified {@code readerIndex} is less than {@code 0} * or greater than {@code this.size} */ - public MemoryBuffer readerIndex(int readerIndex) { - if (readerIndex < 0 || readerIndex > size) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex: %d (expected: 0 <= readerIndex <= size(%d))", readerIndex, size)); + public void readerIndex(int readerIndex) { + if (readerIndex < 0) { + throwIndexOOBExceptionForRead(); + } else if (readerIndex > size) { + // in this case, diff must be greater than 0. + streamReader.fillBuffer(readerIndex - size); } this.readerIndex = readerIndex; - return this; + } + + public void readerIndexUnsafe(int readerIndex) { + this.readerIndex = readerIndex; } /** Returns array index for reader index if buffer is a heap buffer. */ @@ -821,13 +888,14 @@ public void increaseReaderIndexUnsafe(int diff) { } public void increaseReaderIndex(int diff) { - int readerIdx = readerIndex + diff; - if (readerIdx < 0 || readerIdx > size) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex: %d (expected: 0 <= readerIndex <= size(%d))", readerIdx, size)); + int readerIdx = readerIndex; + readerIndex = readerIdx += diff; + if (readerIdx < 0) { + throwIndexOOBExceptionForRead(); + } else if (readerIdx > size) { + // in this case, diff must be greater than 0. + streamReader.fillBuffer(readerIdx - size); } - this.readerIndex = readerIdx; } public long getUnsafeReaderAddress() { @@ -851,13 +919,38 @@ public int writerIndex() { */ public void writerIndex(int writerIndex) { if (writerIndex < 0 || writerIndex > size) { - throw new IndexOutOfBoundsException( - String.format( - "writerIndex: %d (expected: 0 <= writerIndex <= size(%d))", writerIndex, size)); + throwOOBExceptionForWriteIndex(writerIndex); } this.writerIndex = writerIndex; } + private void throwOOBExceptionForWriteIndex(int writerIndex) { + throw new IndexOutOfBoundsException( + String.format( + "writerIndex: %d (expected: 0 <= writerIndex <= size(%d))", writerIndex, size)); + } + + // Check should be done outside to avoid this method got into the critical path. + private void throwOOBException() { + throw new IndexOutOfBoundsException( + String.format("size: %d, address %s, addressLimit %d", size, address, addressLimit)); + } + + // Check should be done outside to avoid this method got into the critical path. + private void throwIndexOOBExceptionForRead() { + throw new IndexOutOfBoundsException( + String.format( + "readerIndex: %d (expected: 0 <= readerIndex <= size(%d))", readerIndex, size)); + } + + // Check should be done outside to avoid this method got into the critical path. + private void throwIndexOOBExceptionForRead(int length) { + throw new IndexOutOfBoundsException( + String.format( + "readerIndex: %d (expected: 0 <= readerIndex <= size(%d)), length %d", + readerIndex, size, length)); + } + public void unsafeWriterIndex(int writerIndex) { this.writerIndex = writerIndex; } @@ -1052,6 +1145,7 @@ public int writePositiveVarInt(int v) { * For implementation efficiency, this method needs at most 8 bytes for writing 5 bytes using long * to avoid using two memory operations. */ + @CodegenInvoke public int unsafeWriteVarInt(int v) { // Ensure negatives close to zero is encode in little bytes. v = (v << 1) ^ (v >> 31); @@ -1238,6 +1332,33 @@ public int readPositiveVarInt() { return result; } + /** + * Fast path for read a unsigned varint which is mostly a smaller value in [0, 16384). When the + * value is equal or greater than 16384, the read will be a little slower. + */ + public int readVarUintSmall() { + int readIdx = readerIndex; + if (size - readIdx >= 5) { + int fourByteValue = unsafeGetInt(readIdx++); + int binarySize = fourByteValue & 0x7F; + // Duplicate and manual inline for performance. + // noinspection Duplicates + if ((fourByteValue & 0x80) != 0) { + readIdx++; + binarySize |= (fourByteValue >>> 1) & 0x3f80; + if ((fourByteValue & 0x8000) != 0) { + // merely executed path, make it as a separate method to reduce + // code size of current method for better jvm inline + return continueRead(readIdx, fourByteValue, binarySize); + } + } + readerIndex = readIdx; + return binarySize; + } else { + return readPositiveVarIntSlow(); + } + } + private int readPositiveVarIntSlow() { // Note: // Loop are not used here to improve performance, @@ -1460,10 +1581,8 @@ private int writePositiveVarIntAligned6(int value) { public int readPositiveAlignedVarInt() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (readerIdx > size - 1) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 1, size, this)); + if (readerIdx < size - 10) { + return slowReadPositiveAlignedVarInt(); } long pos = address + readerIdx; long startPos = pos; @@ -1499,6 +1618,45 @@ public int readPositiveAlignedVarInt() { return result; } + public int slowReadPositiveAlignedVarInt() { + int b = readByte(); + // Mask first 6 bits, + // bit 8 `set` indicates have next data bytes. + int result = b & 0x3F; + if ((b & 0x80) != 0) { // has 2nd byte + b = readByte(); + result |= (b & 0x3F) << 6; + if ((b & 0x80) != 0) { // has 3rd byte + b = readByte(); + result |= (b & 0x3F) << 12; + if ((b & 0x80) != 0) { // has 4th byte + b = readByte(); + result |= (b & 0x3F) << 18; + if ((b & 0x80) != 0) { // has 5th byte + b = readByte(); + result |= (b & 0x3F) << 24; + if ((b & 0x80) != 0) { // has 6th byte + b = readByte(); + result |= (b & 0x3F) << 30; + } + } + } + } + } + // bit 7 `unset` indicates have next padding bytes, + if ((b & 0x40) == 0) { // has first padding bytes + b = readByte(); + if ((b & 0x40) == 0) { // has 2nd padding bytes + b = readByte(); + if ((b & 0x40) == 0) { // has 3rd padding bytes + b = readByte(); + checkArgument((b & 0x40) != 0, "At most 3 padding bytes."); + } + } + } + return result; + } + private long skipPadding(long pos, int b) { // bit 7 `unset` indicates have next padding bytes, if ((b & 0x40) == 0) { // has first padding bytes @@ -1507,7 +1665,7 @@ private long skipPadding(long pos, int b) { b = UNSAFE.getByte(heapMemory, pos++); if ((b & 0x40) == 0) { // has 3rd padding bytes b = UNSAFE.getByte(heapMemory, pos++); - Preconditions.checkArgument((b & 0x40) != 0, "At most 3 padding bytes."); + checkArgument((b & 0x40) != 0, "At most 3 padding bytes."); } } } @@ -1606,18 +1764,20 @@ public int unsafeWritePositiveVarLong(long value) { /** Reads the 1-9 byte int part of a var long. */ public long readVarLong() { - long result = readPositiveVarLong(); - return ((result >>> 1) ^ -(result & 1)); + return LITTLE_ENDIAN ? readVarLongOnLE() : readVarLongOnBE(); } + @CodegenInvoke public long readVarLongOnLE() { + // Duplicate and manual inline for performance. + // noinspection Duplicates int readIdx = readerIndex; long result; if (size - readIdx < 9) { result = readPositiveVarLongSlow(); } else { long address = this.address; - long value = Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx)); + long value = UNSAFE.getLong(heapMemory, address + readIdx); // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -1664,6 +1824,7 @@ public long readVarLongOnLE() { return ((result >>> 1) ^ -(result & 1)); } + @CodegenInvoke public long readVarLongOnBE() { int readIdx = readerIndex; long result; @@ -1671,7 +1832,7 @@ public long readVarLongOnBE() { result = readPositiveVarLongSlow(); } else { long address = this.address; - long value = UNSAFE.getLong(heapMemory, address + readIdx); + long value = Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx)); // Duplicate and manual inline for performance. // noinspection Duplicates readIdx++; @@ -1859,12 +2020,6 @@ public int unsafeWriteSliLong(long value) { } } - private void throwIndexOutOfBoundsException(int readIdx, int size, int need) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readIdx, need, size, this)); - } - public void writeBytes(byte[] bytes) { writeBytes(bytes, 0, bytes.length); } @@ -1889,12 +2044,12 @@ public void write(ByteBuffer source, int numBytes) { writerIndex = newIdx; } - public void writeBytesWithSizeEmbedded(byte[] arr) { - writePrimitiveArrayWithSizeEmbedded(arr, Platform.BYTE_ARRAY_OFFSET, arr.length); + public void writeBytesWithSize(byte[] arr) { + writePrimitiveArrayWithSize(arr, Platform.BYTE_ARRAY_OFFSET, arr.length); } /** Write a primitive array into buffer with size varint encoded into the buffer. */ - public void writePrimitiveArrayWithSizeEmbedded(Object arr, int offset, int numBytes) { + public void writePrimitiveArrayWithSize(Object arr, int offset, int numBytes) { int idx = writerIndex; ensure(idx + 5 + numBytes); idx += unsafeWritePositiveVarInt(numBytes); @@ -1903,7 +2058,7 @@ public void writePrimitiveArrayWithSizeEmbedded(Object arr, int offset, int numB writerIndex = idx + numBytes; } - public void writePrimitiveArrayAlignedSizeEmbedded(Object arr, int offset, int numBytes) { + public void writePrimitiveArrayAlignedSize(Object arr, int offset, int numBytes) { writePositiveVarIntAligned(numBytes); final int writerIdx = writerIndex; final int newIdx = writerIdx + numBytes; @@ -1939,10 +2094,8 @@ public void ensure(int length) { public boolean readBoolean() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 1) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 1, size, this)); + if (readerIdx > size - 1) { + streamReader.fillBuffer(1); } readerIndex = readerIdx + 1; return UNSAFE.getByte(heapMemory, address + readerIdx) != 0; @@ -1950,10 +2103,8 @@ public boolean readBoolean() { public byte readByte() { int readerIdx = readerIndex; - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 1) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 1, size, this)); + if (readerIdx > size - 1) { + streamReader.fillBuffer(1); } readerIndex = readerIdx + 1; return UNSAFE.getByte(heapMemory, address + readerIdx); @@ -1962,10 +2113,9 @@ public byte readByte() { public char readChar() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + int remaining = size - readerIdx; + if (remaining < 2) { + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; final long pos = address + readerIdx; @@ -1983,9 +2133,7 @@ public char readCharOnLE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; return UNSAFE.getChar(heapMemory, address + readerIdx); @@ -1998,9 +2146,7 @@ public char readCharOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; return Character.reverseBytes(UNSAFE.getChar(heapMemory, address + readerIdx)); @@ -2009,10 +2155,9 @@ public char readCharOnBE() { public short readShort() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + int remaining = size - readerIdx; + if (remaining < 2) { + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; final long pos = address + readerIdx; @@ -2030,9 +2175,7 @@ public short readShortOnLE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; return UNSAFE.getShort(heapMemory, address + readerIdx); @@ -2045,9 +2188,7 @@ public short readShortOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 2) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 2, size, this)); + streamReader.fillBuffer(2 - remaining); } readerIndex = readerIdx + 2; return Short.reverseBytes(UNSAFE.getShort(heapMemory, address + readerIdx)); @@ -2056,10 +2197,9 @@ public short readShortOnBE() { public int readInt() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + int remaining = size - readerIdx; + if (remaining < 4) { + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; final long pos = address + readerIdx; @@ -2070,15 +2210,14 @@ public int readInt() { } } + // Reduce method body for better inline in the caller. @CodegenInvoke public int readIntOnLE() { int readerIdx = readerIndex; // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; return UNSAFE.getInt(heapMemory, address + readerIdx); @@ -2091,9 +2230,7 @@ public int readIntOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; return Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readerIdx)); @@ -2104,9 +2241,7 @@ public long readLong() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; final long pos = address + readerIdx; @@ -2124,9 +2259,7 @@ public long readLongOnLE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; return UNSAFE.getLong(heapMemory, address + readerIdx); @@ -2139,9 +2272,7 @@ public long readLongOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readerIdx)); @@ -2163,7 +2294,7 @@ public long readSliLongOnLE() { final int readIdx = readerIndex; int diff = size - readIdx; if (diff < 4) { - throwIndexOutOfBoundsException(readIdx, size, 4 - diff); + streamReader.fillBuffer(4 - diff); } int i = UNSAFE.getInt(heapMemory, address + readIdx); if ((i & 0b1) != 0b1) { @@ -2171,7 +2302,7 @@ public long readSliLongOnLE() { return i >> 1; } if (diff < 9) { - throwIndexOutOfBoundsException(readIdx, size, 9 - diff); + streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; return UNSAFE.getLong(heapMemory, address + readIdx + 1); @@ -2183,7 +2314,7 @@ public long readSliLongOnBE() { final int readIdx = readerIndex; int diff = size - readIdx; if (diff < 4) { - throwIndexOutOfBoundsException(readIdx, size, 4 - diff); + streamReader.fillBuffer(4 - diff); } int i = Integer.reverseBytes(UNSAFE.getInt(heapMemory, address + readIdx)); if ((i & 0b1) != 0b1) { @@ -2191,7 +2322,7 @@ public long readSliLongOnBE() { return i >> 1; } if (diff < 9) { - throwIndexOutOfBoundsException(readIdx, size, 9 - diff); + streamReader.fillBuffer(9 - diff); } readerIndex = readIdx + 9; return Long.reverseBytes(UNSAFE.getLong(heapMemory, address + readIdx + 1)); @@ -2200,10 +2331,9 @@ public long readSliLongOnBE() { public float readFloat() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + int remaining = size - readerIdx; + if (remaining < 4) { + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; final long pos = address + readerIdx; @@ -2221,9 +2351,7 @@ public float readFloatOnLE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; return Float.intBitsToFloat(UNSAFE.getInt(heapMemory, address + readerIdx)); @@ -2236,9 +2364,7 @@ public float readFloatOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 4) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 4, size, this)); + streamReader.fillBuffer(4 - remaining); } readerIndex = readerIdx + 4; return Float.intBitsToFloat( @@ -2248,10 +2374,9 @@ public float readFloatOnBE() { public double readDouble() { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + int remaining = size - readerIdx; + if (remaining < 8) { + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; final long pos = address + readerIdx; @@ -2269,9 +2394,7 @@ public double readDoubleOnLE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; return Double.longBitsToDouble(UNSAFE.getLong(heapMemory, address + readerIdx)); @@ -2284,9 +2407,7 @@ public double readDoubleOnBE() { // use subtract to avoid overflow int remaining = size - readerIdx; if (remaining < 8) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, 8, size, this)); + streamReader.fillBuffer(8 - remaining); } readerIndex = readerIdx + 8; return Double.longBitsToDouble( @@ -2295,14 +2416,13 @@ public double readDoubleOnBE() { public byte[] readBytes(int length) { int readerIdx = readerIndex; + byte[] bytes = new byte[length]; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - length) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, length, size, this)); + if (length > size - readerIdx) { + streamReader.readTo(bytes, 0, length); + return bytes; } byte[] heapMemory = this.heapMemory; - final byte[] bytes = new byte[length]; if (heapMemory != null) { // System.arraycopy faster for some jdk than Unsafe. System.arraycopy(heapMemory, heapOffset + readerIdx, bytes, 0, length); @@ -2316,13 +2436,12 @@ public byte[] readBytes(int length) { public void readBytes(byte[] dst, int dstIndex, int length) { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - length) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", readerIdx, length, size, this)); + if (readerIdx > size - length) { + streamReader.readTo(dst, dstIndex, length); + return; } - if (dstIndex > dst.length - length) { - throw new IndexOutOfBoundsException(); + if (dstIndex < 0 || dstIndex > dst.length - length) { + throwIndexOOBExceptionForRead(); } copyToUnsafe(readerIdx, dst, Platform.BYTE_ARRAY_OFFSET + dstIndex, length); readerIndex = readerIdx + length; @@ -2332,29 +2451,92 @@ public void readBytes(byte[] dst) { readBytes(dst, 0, dst.length); } - public void read(ByteBuffer dst) { + public int read(ByteBuffer dst) { int readerIdx = readerIndex; - int len = Math.min(dst.remaining(), size - readerIdx); + int len = dst.remaining(); // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - len) { - throw new IndexOutOfBoundsException( - String.format( - "readerIdx(%d) + length(%d) exceeds size(%d): %s", readerIdx, len, size, this)); + if (readerIdx > size - len) { + return streamReader.readToByteBuffer(dst); + } + if (heapMemory != null) { + dst.put(heapMemory, readerIndex + heapOffset, len); + } else { + dst.put(sliceAsByteBuffer(readerIdx, len)); } readerIndex = readerIdx + len; - dst.put(sliceAsByteBuffer(readerIdx, len)); + return len; } - public byte[] readBytesWithSizeEmbedded() { - final int numBytes = readPositiveVarInt(); + public void read(ByteBuffer dst, int len) { int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", - readerIdx, numBytes, size, this)); + if (readerIdx > size - len) { + streamReader.readToByteBuffer(dst, len); + } else { + if (heapMemory != null) { + dst.put(heapMemory, readerIndex + heapOffset, len); + } else { + dst.put(sliceAsByteBuffer(readerIdx, len)); + } + readerIndex = readerIdx + len; + } + } + + /** + * Read size for following binary, this method will check and fill readable bytes too. This method + * is optimized for small size, it's faster than {@link #readPositiveVarInt}. + */ + public int readBinarySize() { + int binarySize; + int readIdx = readerIndex; + if (size - readIdx >= 5) { + int fourByteValue = unsafeGetInt(readIdx++); + binarySize = fourByteValue & 0x7F; + // Duplicate and manual inline for performance. + // noinspection Duplicates + if ((fourByteValue & 0x80) != 0) { + readIdx++; + binarySize |= (fourByteValue >>> 1) & 0x3f80; + if ((fourByteValue & 0x8000) != 0) { + // merely executed path, make it as a separate method to reduce + // code size of current method for better jvm inline + return continueRead(readIdx, fourByteValue, binarySize); + } + } + readerIndex = readIdx; + } else { + binarySize = readPositiveVarIntSlow(); + readIdx = readerIndex; + } + int diff = size - readIdx; + if (diff < binarySize) { + streamReader.fillBuffer(diff); + } + return binarySize; + } + + private int continueRead(int readIdx, int bulkRead, int binarySize) { + // Duplicate and manual inline for performance. + // noinspection Duplicates + readIdx++; + binarySize |= (bulkRead >>> 2) & 0x1fc000; + if ((bulkRead & 0x800000) != 0) { + readIdx++; + binarySize |= (bulkRead >>> 3) & 0xfe00000; + if ((bulkRead & 0x80000000) != 0) { + binarySize |= (UNSAFE.getByte(heapMemory, address + readIdx++) & 0x7F) << 28; + } + } + int diff = size - readIdx; + if (diff < binarySize) { + streamReader.fillBuffer(diff); } + return binarySize; + } + + public byte[] readBytesAndSize() { + final int numBytes = readBinarySize(); + int readerIdx = readerIndex; final byte[] arr = new byte[numBytes]; byte[] heapMemory = this.heapMemory; if (heapMemory != null) { @@ -2367,94 +2549,87 @@ public byte[] readBytesWithSizeEmbedded() { return arr; } - public byte[] readBytesAlignedSizeEmbedded() { + public byte[] readBytesWithAlignedSize() { final int numBytes = readPositiveAlignedVarInt(); int readerIdx = readerIndex; + final byte[] arr = new byte[numBytes]; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", - readerIdx, numBytes, size, this)); + if (readerIdx > size - numBytes) { + streamReader.readTo(arr, 0, numBytes); + return arr; } - final byte[] arr = new byte[numBytes]; Platform.UNSAFE.copyMemory( this.heapMemory, this.address + readerIdx, arr, Platform.BYTE_ARRAY_OFFSET, numBytes); readerIndex = readerIdx + numBytes; return arr; } - /** - * This method should be used to read data written by {@link - * #writePrimitiveArrayWithSizeEmbedded}. - */ - public char[] readCharsWithSizeEmbedded() { - final int numBytes = readPositiveVarInt(); + /** This method should be used to read data written by {@link #writePrimitiveArrayWithSize}. */ + public char[] readChars(int numBytes) { int readerIdx = readerIndex; + final char[] chars = new char[numBytes >> 1]; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIdx(%d) + length(%d) exceeds size(%d): %s", readerIdx, numBytes, size, this)); + if (readerIdx > size - numBytes) { + streamReader.readToUnsafe(chars, 0, numBytes); + return chars; } - final char[] chars = new char[numBytes / 2]; Platform.copyMemory( heapMemory, address + readerIdx, chars, Platform.CHAR_ARRAY_OFFSET, numBytes); readerIndex = readerIdx + numBytes; return chars; } - public char[] readCharsAlignedSizeEmbedded() { - final int numBytes = readPositiveAlignedVarInt(); + public void readChars(char[] chars, int offset, int numBytes) { final int readerIdx = readerIndex; // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIdx(%d) + length(%d) exceeds size(%d): %s", readerIdx, numBytes, size, this)); + if (readerIdx > size - numBytes) { + streamReader.readToUnsafe(chars, offset, numBytes); + return; } - final char[] chars = new char[numBytes / 2]; - Platform.copyMemory( - heapMemory, address + readerIdx, chars, Platform.CHAR_ARRAY_OFFSET, numBytes); + Platform.copyMemory(heapMemory, address + readerIdx, chars, offset, numBytes); readerIndex = readerIdx + numBytes; - return chars; } - public long[] readLongsWithSizeEmbedded() { - final int numBytes = readPositiveVarInt(); + public char[] readCharsWithAlignedSize() { + final int numBytes = readPositiveAlignedVarInt(); + return readChars(numBytes); + } + + public long[] readLongs(int numBytes) { int readerIdx = readerIndex; + int numElements = numBytes >> 3; + final long[] longs = new long[numElements]; // use subtract to avoid overflow if (readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIdx(%d) + length(%d) exceeds size(%d): %s", readerIdx, numBytes, size, this)); + streamReader.readToUnsafe(longs, 0, numElements); + return longs; } - final long[] longs = new long[numBytes / 8]; Platform.copyMemory( heapMemory, address + readerIdx, longs, Platform.LONG_ARRAY_OFFSET, numBytes); readerIndex = readerIdx + numBytes; return longs; } - public void readChars(char[] chars, int offset, int numBytes) { - final int readerIdx = readerIndex; - // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIdx > size - numBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIdx(%d) + length(%d) exceeds size(%d): %s", readerIdx, numBytes, size, this)); + /** + * Bulk copy method. Copies {@code numBytes} bytes to target unsafe object and pointer. NOTE: This + * is a unsafe method, no check here, please be carefully. + */ + public void readToUnsafe(Object target, long targetPointer, int numBytes) { + int remaining = size - readerIndex; + if (numBytes > remaining) { + streamReader.readToUnsafe(target, targetPointer, numBytes); + } else { + int readerIdx = readerIndex; + Platform.copyMemory(heapMemory, address + readerIdx, target, targetPointer, numBytes); + readerIndex = readerIdx + numBytes; } - Platform.copyMemory(heapMemory, address + readerIdx, chars, offset, numBytes); - readerIndex = readerIdx + numBytes; } public void checkReadableBytes(int minimumReadableBytes) { // use subtract to avoid overflow - if (BoundsChecking.BOUNDS_CHECKING_ENABLED && readerIndex > size - minimumReadableBytes) { - throw new IndexOutOfBoundsException( - String.format( - "readerIndex(%d) + length(%d) exceeds size(%d): %s", - readerIndex, minimumReadableBytes, size, this)); + int remaining = size - readerIndex; + if (minimumReadableBytes > remaining) { + streamReader.fillBuffer(minimumReadableBytes - remaining); } } @@ -2468,12 +2643,6 @@ public void copyToUnsafe(long offset, Object target, long targetPointer, int num Platform.copyMemory(this.heapMemory, thisPointer, target, targetPointer, numBytes); } - public void copyToUnsafeSmall(long offset, Object target, long targetPointer, int numBytes) { - final long thisPointer = this.address + offset; - checkArgument(thisPointer + numBytes <= addressLimit); - Platform.UNSAFE.copyMemory(this.heapMemory, thisPointer, target, targetPointer, numBytes); - } - /** * Bulk copy method. Copies {@code numBytes} bytes from source unsafe object and pointer. NOTE: * This is an unsafe method, no check here, please be carefully. @@ -2484,12 +2653,6 @@ public void copyFromUnsafe(long offset, Object source, long sourcePointer, long Platform.copyMemory(source, sourcePointer, this.heapMemory, thisPointer, numBytes); } - public void copyFromUnsafeSmall(long offset, Object source, long sourcePointer, long numBytes) { - final long thisPointer = this.address + offset; - checkArgument(thisPointer + numBytes <= addressLimit); - Platform.UNSAFE.copyMemory(source, sourcePointer, this.heapMemory, thisPointer, numBytes); - } - /** * Bulk copy method. Copies {@code numBytes} bytes from this memory buffer, starting at position * {@code offset} to the target memory buffer. The bytes will be put into the target buffer @@ -2560,7 +2723,7 @@ public byte[] getBytes(int index, int length) { return Arrays.copyOf(heapMemory, length); } if (index + length > size) { - throw new IllegalArgumentException(); + throwIndexOOBExceptionForRead(length); } byte[] data = new byte[length]; copyToUnsafe(index, data, Platform.BYTE_ARRAY_OFFSET, length); @@ -2569,11 +2732,10 @@ public byte[] getBytes(int index, int length) { public void getBytes(int index, byte[] dst, int dstIndex, int length) { if (dstIndex > dst.length - length) { - throw new IndexOutOfBoundsException(); + throwOOBException(); } if (index > size - length) { - throw new IndexOutOfBoundsException( - String.format("offset(%d) + length(%d) exceeds size(%d): %s", index, length, size, this)); + throwOOBException(); } copyToUnsafe(index, dst, Platform.BYTE_ARRAY_OFFSET + dstIndex, length); } @@ -2621,6 +2783,10 @@ public ByteBuffer sliceAsByteBuffer(int offset, int length) { } } + public FuryStreamReader getStreamReader() { + return streamReader; + } + /** * Compares two memory buffer regions. * @@ -2670,8 +2836,8 @@ public int compare(MemoryBuffer buf2, int offset1, int offset2, int len) { public boolean equalTo(MemoryBuffer buf2, int offset1, int offset2, int len) { final long pos1 = address + offset1; final long pos2 = buf2.address + offset2; - Preconditions.checkArgument(pos1 < addressLimit); - Preconditions.checkArgument(pos2 < buf2.addressLimit); + checkArgument(pos1 < addressLimit); + checkArgument(pos2 < buf2.addressLimit); return Platform.arrayEquals(heapMemory, pos1, buf2.heapMemory, pos2, len); } @@ -2719,7 +2885,12 @@ public void pointTo(byte[] buffer, int offset, int length) { /** Creates a new memory buffer that targets to the given heap memory region. */ public static MemoryBuffer fromByteArray(byte[] buffer, int offset, int length) { - return new MemoryBuffer(buffer, offset, length); + return new MemoryBuffer(buffer, offset, length, null); + } + + public static MemoryBuffer fromByteArray( + byte[] buffer, int offset, int length, FuryStreamReader streamReader) { + return new MemoryBuffer(buffer, offset, length, streamReader); } /** Creates a new memory buffer that targets to the given heap memory region. */ diff --git a/java/fury-core/src/main/java/org/apache/fury/pool/ThreadPoolFury.java b/java/fury-core/src/main/java/org/apache/fury/pool/ThreadPoolFury.java index 86bb1e3a87..3c207a6aaa 100644 --- a/java/fury-core/src/main/java/org/apache/fury/pool/ThreadPoolFury.java +++ b/java/fury-core/src/main/java/org/apache/fury/pool/ThreadPoolFury.java @@ -19,7 +19,6 @@ package org.apache.fury.pool; -import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; @@ -28,6 +27,7 @@ import javax.annotation.concurrent.ThreadSafe; import org.apache.fury.AbstractThreadSafeFury; import org.apache.fury.Fury; +import org.apache.fury.io.FuryInputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.serializer.BufferCallback; @@ -144,12 +144,12 @@ public Object deserialize(MemoryBuffer buffer, Iterable outOfBandB } @Override - public Object deserialize(InputStream inputStream) { + public Object deserialize(FuryInputStream inputStream) { return execute(fury -> fury.deserialize(inputStream)); } @Override - public Object deserialize(InputStream inputStream, Iterable outOfBandBuffers) { + public Object deserialize(FuryInputStream inputStream, Iterable outOfBandBuffers) { return execute(fury -> fury.deserialize(inputStream, outOfBandBuffers)); } @@ -187,7 +187,7 @@ public T deserializeJavaObject(MemoryBuffer buffer, Class cls) { } @Override - public T deserializeJavaObject(InputStream inputStream, Class cls) { + public T deserializeJavaObject(FuryInputStream inputStream, Class cls) { return execute(fury -> fury.deserializeJavaObject(inputStream, cls)); } @@ -225,7 +225,7 @@ public Object deserializeJavaObjectAndClass(MemoryBuffer buffer) { } @Override - public Object deserializeJavaObjectAndClass(InputStream inputStream) { + public Object deserializeJavaObjectAndClass(FuryInputStream inputStream) { return execute(fury -> fury.deserializeJavaObjectAndClass(inputStream)); } diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java index 2994b92864..f3803c1989 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java @@ -1292,7 +1292,7 @@ private Class readClassWithMetaShare(MemoryBuffer buffer) { metaContext, "Meta context must be set before serialization," + " please set meta context by SerializationContext.setMetaContext"); - int id = buffer.readPositiveVarInt(); + int id = buffer.readVarUintSmall(); List readClassInfos = metaContext.readClassInfos; ClassInfo classInfo = readClassInfos.get(id); if (classInfo == null) { @@ -1315,7 +1315,7 @@ private ClassInfo readClassInfoWithMetaShare(MemoryBuffer buffer, MetaContext me metaContext, "Meta context must be set before serialization," + " please set meta context by SerializationContext.setMetaContext"); - int id = buffer.readPositiveVarInt(); + int id = buffer.readVarUintSmall(); List readClassInfos = metaContext.readClassInfos; ClassInfo classInfo = readClassInfos.get(id); if (classInfo == null) { @@ -1399,7 +1399,7 @@ public void readClassDefs(MemoryBuffer buffer) { int classDefOffset = buffer.readInt(); int readerIndex = buffer.readerIndex(); buffer.readerIndex(classDefOffset); - int numClassDefs = buffer.readPositiveVarInt(); + int numClassDefs = buffer.readVarUintSmall(); for (int i = 0; i < numClassDefs; i++) { ClassDef readClassDef = ClassDef.readClassDef(buffer); // Share same class def to reduce memory footprint, since there may be many meta context. @@ -1468,7 +1468,7 @@ public void writeEnumStringBytes(MemoryBuffer buffer, EnumStringBytes byteString // Note: Thread safe fot jit thread to call. public Expression skipRegisteredClassExpr(Expression buffer) { - return new Invoke(buffer, "readPositiveVarInt"); + return new Invoke(buffer, "readVarUintSmall"); } /** @@ -1538,8 +1538,7 @@ public ClassInfo readClassInfo(MemoryBuffer buffer) { currentReadClass = classInfo.cls; return classInfo; } else { - short classId = readClassId(buffer, flag); - ClassInfo classInfo = getOrUpdateClassInfo(classId); + ClassInfo classInfo = getOrUpdateClassInfo(readClassId(buffer, flag)); currentReadClass = classInfo.cls; return classInfo; } @@ -1582,8 +1581,8 @@ private static short readClassId(MemoryBuffer buffer, byte flag) { short classId; // use classId if ((flag & 0x80) != 0) { // class id is written using multiple bytes. - buffer.increaseReaderIndexUnsafe(-1); - classId = (short) buffer.readPositiveVarInt(); + buffer.increaseReaderIndex(-1); + classId = (short) buffer.readVarUintSmall(); } else { classId = (short) (flag & 0x7F); } diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/FieldResolver.java b/java/fury-core/src/main/java/org/apache/fury/resolver/FieldResolver.java index cf9128c69e..ed906328a3 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/FieldResolver.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/FieldResolver.java @@ -427,7 +427,7 @@ public long skipDataBy4(MemoryBuffer buffer, int partFieldInfo) { // bit `1 1 1`: class id embedding + field name > 9 byte. (64 - 3 - 7) bits of MurmurHash3 // for field name. classId = (byte) ((partFieldInfo & 0b1111111111) >>> 3); - buffer.increaseReaderIndexUnsafe(4); + buffer.increaseReaderIndex(4); } ClassInfo classInfo = classResolver.getClassInfo(classId); if (classId >= minPrimitiveClassId && classId <= maxPrimitiveClassId) { @@ -469,7 +469,7 @@ public long skipDataBy8(MemoryBuffer buffer, long partFieldInfo) { if ((partFieldInfo & 0b11) == EMBED_TYPES_4_FLAG) { // class id embedding + field name <= 4 byte. classId = (byte) ((partFieldInfo & 0xff) >> 2); - buffer.increaseReaderIndexUnsafe(-4); + buffer.increaseReaderIndex(-4); } else { // bit `1 1 0`: class id embedding + field name <= 9 byte. // bit `1 1 1`: class id embedding + field name > 9 byte. (64 - 3 - 7) bits of MurmurHash3 diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/MapRefResolver.java b/java/fury-core/src/main/java/org/apache/fury/resolver/MapRefResolver.java index b04d24a162..4c307fbb80 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/MapRefResolver.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/MapRefResolver.java @@ -149,7 +149,7 @@ public byte readRefOrNull(MemoryBuffer buffer) { byte headFlag = buffer.readByte(); if (headFlag == Fury.REF_FLAG) { // read reference id and get object from reference resolver - int referenceId = buffer.readPositiveVarInt(); + int referenceId = buffer.readVarUintSmall(); readObject = getReadObject(referenceId); } else { readObject = null; @@ -170,8 +170,7 @@ public int tryPreserveRefId(MemoryBuffer buffer) { byte headFlag = buffer.readByte(); if (headFlag == Fury.REF_FLAG) { // read reference id and get object from reference resolver - int referenceId = buffer.readPositiveVarInt(); - readObject = getReadObject(referenceId); + readObject = getReadObject(buffer.readVarUintSmall()); } else { readObject = null; if (headFlag == Fury.REF_VALUE_FLAG) { diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java b/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java index 5696fec5dd..a3807d2466 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/ArraySerializers.java @@ -251,7 +251,7 @@ public BooleanArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, boolean[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -271,9 +271,7 @@ public boolean[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; boolean[] values = new boolean[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -288,11 +286,11 @@ public ByteArraySerializer(Fury fury) { @Override public void write(MemoryBuffer buffer, byte[] value) { if (fury.getBufferCallback() == null) { - int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + int size = Math.multiplyExact(value.length, 1); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( - buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); + buffer, new PrimitiveArrayBufferObject(value, offset, 1, value.length)); } } @@ -301,17 +299,13 @@ public byte[] read(MemoryBuffer buffer) { if (fury.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fury.readBufferObject(buffer); int size = buf.remaining(); - int numElements = size / elemSize; - byte[] values = new byte[numElements]; + byte[] values = new byte[size]; buf.copyToUnsafe(0, values, offset, size); return values; } else { int size = buffer.readPositiveVarInt(); - int numElements = size / elemSize; - byte[] values = new byte[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + byte[] values = new byte[size]; + buffer.readToUnsafe(values, offset, size); return values; } } @@ -327,7 +321,7 @@ public CharArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, char[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -347,9 +341,7 @@ public char[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; char[] values = new char[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -380,7 +372,7 @@ public ShortArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, short[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -400,9 +392,7 @@ public short[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; short[] values = new short[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -418,7 +408,7 @@ public IntArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, int[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -438,9 +428,7 @@ public int[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; int[] values = new int[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -456,7 +444,7 @@ public LongArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, long[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -476,9 +464,7 @@ public long[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; long[] values = new long[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -494,7 +480,7 @@ public FloatArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, float[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -514,9 +500,7 @@ public float[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; float[] values = new float[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -532,7 +516,7 @@ public DoubleArraySerializer(Fury fury) { public void write(MemoryBuffer buffer, double[] value) { if (fury.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, elemSize); - buffer.writePrimitiveArrayWithSizeEmbedded(value, offset, size); + buffer.writePrimitiveArrayWithSize(value, offset, size); } else { fury.writeBufferObject( buffer, new PrimitiveArrayBufferObject(value, offset, elemSize, value.length)); @@ -552,9 +536,7 @@ public double[] read(MemoryBuffer buffer) { int size = buffer.readPositiveVarInt(); int numElements = size / elemSize; double[] values = new double[numElements]; - int readerIndex = buffer.readerIndex(); - buffer.copyToUnsafe(readerIndex, values, offset, size); - buffer.readerIndex(readerIndex + size); + buffer.readToUnsafe(values, offset, size); return values; } } @@ -609,7 +591,7 @@ public void write(MemoryBuffer buffer, String[] value) { @Override public String[] read(MemoryBuffer buffer) { - int numElements = buffer.readPositiveVarInt(); + int numElements = buffer.readVarUintSmall(); String[] value = new String[numElements]; if (numElements == 0) { return value; diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/ExternalizableSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/ExternalizableSerializer.java index 95b6bf60f1..736e573b10 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/ExternalizableSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/ExternalizableSerializer.java @@ -23,8 +23,8 @@ import java.io.IOException; import java.lang.invoke.MethodHandle; import org.apache.fury.Fury; -import org.apache.fury.io.FuryObjectInput; -import org.apache.fury.io.FuryObjectOutput; +import org.apache.fury.io.MemoryBufferObjectInput; +import org.apache.fury.io.MemoryBufferObjectOutput; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.util.Platform; import org.apache.fury.util.ReflectionUtils; @@ -32,15 +32,15 @@ /** Serializer for class implements {@link Externalizable}. */ public class ExternalizableSerializer extends Serializer { private final MethodHandle constructor; - private final FuryObjectInput objectInput; - private final FuryObjectOutput objectOutput; + private final MemoryBufferObjectInput objectInput; + private final MemoryBufferObjectOutput objectOutput; public ExternalizableSerializer(Fury fury, Class cls) { super(fury, cls); constructor = ReflectionUtils.getCtrHandle(cls, false); - objectInput = new FuryObjectInput(fury, null); - objectOutput = new FuryObjectOutput(fury, null); + objectInput = new MemoryBufferObjectInput(fury, null); + objectOutput = new MemoryBufferObjectOutput(fury, null); } @Override diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/JavaSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/JavaSerializer.java index 48673d46c2..2e5dd39ae7 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/JavaSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/JavaSerializer.java @@ -30,8 +30,8 @@ import java.nio.ByteBuffer; import org.apache.fury.Fury; import org.apache.fury.io.ClassLoaderObjectInputStream; -import org.apache.fury.io.FuryObjectInput; -import org.apache.fury.io.FuryObjectOutput; +import org.apache.fury.io.MemoryBufferObjectInput; +import org.apache.fury.io.MemoryBufferObjectOutput; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.resolver.ClassResolver; import org.apache.fury.util.LoggerFactory; @@ -49,8 +49,8 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class JavaSerializer extends Serializer { private static final Logger LOG = LoggerFactory.getLogger(JavaSerializer.class); - private final FuryObjectInput objectInput; - private final FuryObjectOutput objectOutput; + private final MemoryBufferObjectInput objectInput; + private final MemoryBufferObjectOutput objectOutput; public JavaSerializer(Fury fury, Class cls) { super(fury, cls); @@ -64,8 +64,8 @@ public JavaSerializer(Fury fury, Class cls) { Serializer.class.getName(), Externalizable.class.getName()); } - objectInput = new FuryObjectInput(fury, null); - objectOutput = new FuryObjectOutput(fury, null); + objectInput = new MemoryBufferObjectInput(fury, null); + objectOutput = new MemoryBufferObjectOutput(fury, null); } @Override diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java b/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java index 9e90fb7b73..72992cbac6 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/Serializers.java @@ -248,13 +248,12 @@ public void write(MemoryBuffer buffer, T value) { byte[] v = (byte[]) GET_VALUE.apply(value); buffer.writeByte(coder); if (coder == 0) { - buffer.writePrimitiveArrayWithSizeEmbedded(v, Platform.BYTE_ARRAY_OFFSET, value.length()); + buffer.writePrimitiveArrayWithSize(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); + buffer.writePrimitiveArrayWithSize(v, Platform.BYTE_ARRAY_OFFSET, value.length() << 1); } } else { char[] v = (char[]) GET_VALUE.apply(value); @@ -327,7 +326,7 @@ public void write(MemoryBuffer buffer, Enum value) { @Override public Enum read(MemoryBuffer buffer) { - return enumConstants[buffer.readPositiveVarInt()]; + return enumConstants[buffer.readVarUintSmall()]; } } diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/StringSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/StringSerializer.java index 0bd8420184..6de440cfa1 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/StringSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/StringSerializer.java @@ -32,6 +32,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import org.apache.fury.Fury; +import org.apache.fury.annotation.CodegenInvoke; import org.apache.fury.codegen.Expression; import org.apache.fury.codegen.Expression.Invoke; import org.apache.fury.codegen.Expression.StaticInvoke; @@ -188,7 +189,7 @@ public void writeCharsStringCompressed(MemoryBuffer buffer, String value) { public void writeCharsStringUncompressed(MemoryBuffer buffer, String value) { int numBytes = MathUtils.doubleExact(value.length()); final char[] chars = (char[]) Platform.getObject(value, STRING_VALUE_FIELD_OFFSET); - buffer.writePrimitiveArrayWithSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); + buffer.writePrimitiveArrayWithSize(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); } public String readString(MemoryBuffer buffer) { @@ -202,10 +203,6 @@ public String readString(MemoryBuffer buffer) { public Expression readStringExpr(Expression strSerializer, Expression buffer) { if (isJava) { if (STRING_VALUE_FIELD_IS_BYTES) { - // Expression coder = inlineInvoke(buffer, "readByte", BYTE_TYPE); - // Expression value = inlineInvoke(buffer, "readBytesWithSizeEmbedded", BINARY_TYPE); - // return new StaticInvoke( - // StringSerializer.class, "newBytesStringZeroCopy", STRING_TYPE, coder, value); return new Invoke(strSerializer, "readBytesString", STRING_TYPE, buffer); } else { if (!STRING_VALUE_FIELD_IS_CHARS) { @@ -225,56 +222,29 @@ public Expression readStringExpr(Expression strSerializer, Expression buffer) { } } - // Invoked by jit. + @CodegenInvoke public String readBytesString(MemoryBuffer buffer) { + byte coder = buffer.readByte(); + // may fill heap array, if array not big enough, a new array will be allocated. + final int numBytes = buffer.readBinarySize(); + byte[] bytes; byte[] heapMemory = buffer.getHeapMemory(); if (heapMemory != null) { - final int targetIndex = buffer.unsafeHeapReaderIndex(); - int arrIndex = targetIndex; - byte coder = heapMemory[arrIndex++]; - // The encoding algorithm are based on kryo UnsafeMemoryOutput.writeVarInt - // varint are written using little endian byte order. - // inline the implementation here since java can't return varIntBytes and varint - // at the same time. - int b = heapMemory[arrIndex++]; - int numBytes = b & 0x7F; - if ((b & 0x80) != 0) { - b = heapMemory[arrIndex++]; - numBytes |= (b & 0x7F) << 7; - if ((b & 0x80) != 0) { - b = heapMemory[arrIndex++]; - numBytes |= (b & 0x7F) << 14; - if ((b & 0x80) != 0) { - b = heapMemory[arrIndex++]; - numBytes |= (b & 0x7F) << 21; - if ((b & 0x80) != 0) { - b = heapMemory[arrIndex]; - numBytes |= (b & 0x7F) << 28; - } - } - } - } - if (coder == UTF8) { - String str = new String(heapMemory, arrIndex, numBytes, StandardCharsets.UTF_8); - buffer.increaseReaderIndexUnsafe(arrIndex - targetIndex + numBytes); - return str; - } - final byte[] bytes = new byte[numBytes]; + final int arrIndex = buffer.unsafeHeapReaderIndex(); + buffer.increaseReaderIndexUnsafe(numBytes); + bytes = new byte[numBytes]; System.arraycopy(heapMemory, arrIndex, bytes, 0, numBytes); - buffer.increaseReaderIndexUnsafe(arrIndex - targetIndex + numBytes); - return newBytesStringZeroCopy(coder, bytes); } else { - byte coder = buffer.readByte(); - final int numBytes = buffer.readPositiveVarInt(); - byte[] bytes = buffer.readBytes(numBytes); - if (coder == UTF8) { - return new String(bytes, 0, numBytes, StandardCharsets.UTF_8); - } + bytes = buffer.readBytes(numBytes); + } + if (coder != UTF8) { return newBytesStringZeroCopy(coder, bytes); + } else { + return new String(bytes, 0, numBytes, StandardCharsets.UTF_8); } } - // Invoked by jit + @CodegenInvoke public String readCompressedCharsString(MemoryBuffer buffer) { byte coder = buffer.readByte(); if (coder == LATIN1) { @@ -317,7 +287,7 @@ public void writeJavaString(MemoryBuffer buffer, String value) { } } else { int numBytes = MathUtils.doubleExact(value.length()); - buffer.writePrimitiveArrayWithSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); + buffer.writePrimitiveArrayWithSize(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); } } } @@ -369,7 +339,7 @@ public String readJavaString(MemoryBuffer buffer) { return readUTF8String(buffer); } } else { - return newCharsStringZeroCopy(buffer.readCharsWithSizeEmbedded()); + return newCharsStringZeroCopy(buffer.readChars(buffer.readVarUintSmall())); } } } @@ -439,7 +409,7 @@ public void writeCharsUTF16(MemoryBuffer buffer, char[] chars, int strLen) { if (Platform.IS_LITTLE_ENDIAN) { buffer.writeByte(UTF16); // FIXME JDK11 utf16 string uses little-endian order. - buffer.writePrimitiveArrayWithSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); + buffer.writePrimitiveArrayWithSize(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); } else { // The `ensure` ensure next operations are safe without bound checks, // and inner heap buffer doesn't change. @@ -477,8 +447,9 @@ public void writeCharsUTF16(MemoryBuffer buffer, char[] chars, int strLen) { } private char[] readLatinChars(MemoryBuffer buffer) { - final int numBytes = buffer.readPositiveVarInt(); + final int numBytes = buffer.readVarUintSmall(); char[] chars = new char[numBytes]; + buffer.checkReadableBytes(numBytes); byte[] targetArray = buffer.getHeapMemory(); if (targetArray != null) { int srcIndex = buffer.unsafeHeapReaderIndex(); @@ -500,16 +471,15 @@ private char[] readUTF16Chars(MemoryBuffer buffer, byte coder) { if (coder != UTF16) { throw new UnsupportedOperationException(String.format("Unsupported coder %s", coder)); } - int numBytes = buffer.readPositiveVarInt(); - int strLen = numBytes >> 1; - char[] chars = new char[strLen]; + int numBytes = buffer.readVarUintSmall(); + char[] chars = new char[numBytes >> 1]; if (Platform.IS_LITTLE_ENDIAN) { // FIXME JDK11 utf16 string uses little-endian order. buffer.readChars(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); } else { + buffer.checkReadableBytes(numBytes); final byte[] targetArray = buffer.getHeapMemory(); if (targetArray != null) { - buffer.checkReadableBytes(numBytes); int charIndex = 0; for (int i = buffer.unsafeHeapReaderIndex(), end = i + numBytes; i < end; i += 2) { char c = @@ -546,9 +516,7 @@ private char[] readUTF16Chars(MemoryBuffer buffer, byte coder) { public static String newCharsStringZeroCopy(char[] data) { if (!STRING_VALUE_FIELD_IS_CHARS) { - throw new IllegalStateException( - String.format( - "String value isn't char[], current java %s isn't supported", Platform.JAVA_VERSION)); + throw new IllegalStateException("String value isn't char[], current java isn't supported"); } // 25% faster than unsafe put field, only 10% slower than `new String(str)` return CHARS_STRING_ZERO_COPY_CTR.apply(data, Boolean.TRUE); @@ -557,11 +525,6 @@ public static String newCharsStringZeroCopy(char[] data) { // coder param first to make inline call args // `(buffer.readByte(), buffer.readBytesWithSizeEmbedded())` work. public static String newBytesStringZeroCopy(byte coder, byte[] data) { - if (!STRING_VALUE_FIELD_IS_BYTES) { - throw new IllegalStateException( - String.format( - "String value isn't byte[], current java %s isn't supported", Platform.JAVA_VERSION)); - } if (coder == LATIN1) { // 700% faster than unsafe put field in java11, only 10% slower than `new String(str)` for // string length 230. @@ -681,7 +644,8 @@ public void writeUTF8String(MemoryBuffer buffer, String value) { } public String readUTF8String(MemoryBuffer buffer) { - int numBytes = buffer.readPositiveVarInt(); + int numBytes = buffer.readVarUintSmall(); + buffer.checkReadableBytes(numBytes); final byte[] targetArray = buffer.getHeapMemory(); if (targetArray != null) { String str = diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/collection/AbstractCollectionSerializer.java b/java/fury-core/src/main/java/org/apache/fury/serializer/collection/AbstractCollectionSerializer.java index e519d83f34..096ad7f2a0 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/collection/AbstractCollectionSerializer.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/collection/AbstractCollectionSerializer.java @@ -493,7 +493,7 @@ private void xwriteElements(Fury fury, MemoryBuffer buffer, Collection value) { * will raise NPE. */ public Collection newCollection(MemoryBuffer buffer) { - numElements = buffer.readPositiveVarInt(); + numElements = buffer.readVarUintSmall(); if (constructor == null) { constructor = ReflectionUtils.getCtrHandle(type, true); } @@ -502,11 +502,16 @@ public Collection newCollection(MemoryBuffer buffer) { fury.getRefResolver().reference(instance); return (Collection) instance; } catch (Throwable e) { - throw new IllegalArgumentException( - "Please provide public no arguments constructor for class " + type, e); + // reduce code size of critical path. + throw buildException(e); } } + private RuntimeException buildException(Throwable e) { + return new IllegalArgumentException( + "Please provide public no arguments constructor for class " + type, e); + } + /** * Get and reset numElements of deserializing collection. Should be called after {@link * #newCollection}. Nested read may overwrite this element, reset is necessary to avoid use wrong diff --git a/java/fury-core/src/main/java/org/apache/fury/serializer/collection/CollectionSerializers.java b/java/fury-core/src/main/java/org/apache/fury/serializer/collection/CollectionSerializers.java index b21456a55f..d464332cc9 100644 --- a/java/fury-core/src/main/java/org/apache/fury/serializer/collection/CollectionSerializers.java +++ b/java/fury-core/src/main/java/org/apache/fury/serializer/collection/CollectionSerializers.java @@ -71,7 +71,7 @@ public short getXtypeId() { @Override public ArrayList newCollection(MemoryBuffer buffer) { - int numElements = buffer.readPositiveVarInt(); + int numElements = buffer.readVarUintSmall(); setNumElements(numElements); ArrayList arrayList = new ArrayList(numElements); fury.getRefResolver().reference(arrayList); @@ -442,7 +442,7 @@ public void write(MemoryBuffer buffer, EnumSet object) { } fury.getClassResolver().writeClassAndUpdateCache(buffer, elemClass); Serializer serializer = fury.getClassResolver().getSerializer(elemClass); - buffer.writePositiveVarIntAligned(object.size()); + buffer.writePositiveVarInt(object.size()); for (Object element : object) { serializer.write(buffer, element); } @@ -453,7 +453,7 @@ public EnumSet read(MemoryBuffer buffer) { Class elemClass = fury.getClassResolver().readClassInfo(buffer).getCls(); EnumSet object = EnumSet.noneOf(elemClass); Serializer elemSerializer = fury.getClassResolver().getSerializer(elemClass); - int length = buffer.readPositiveAlignedVarInt(); + int length = buffer.readPositiveVarInt(); for (int i = 0; i < length; i++) { object.add(elemSerializer.read(buffer)); } @@ -469,13 +469,13 @@ public BitSetSerializer(Fury fury, Class type) { @Override public void write(MemoryBuffer buffer, BitSet set) { long[] values = set.toLongArray(); - buffer.writePrimitiveArrayWithSizeEmbedded( + buffer.writePrimitiveArrayWithSize( values, Platform.LONG_ARRAY_OFFSET, Math.multiplyExact(values.length, 8)); } @Override public BitSet read(MemoryBuffer buffer) { - long[] values = buffer.readLongsWithSizeEmbedded(); + long[] values = buffer.readLongs(buffer.readPositiveVarInt()); return BitSet.valueOf(values); } } diff --git a/java/fury-core/src/main/java/org/apache/fury/type/ClassDef.java b/java/fury-core/src/main/java/org/apache/fury/type/ClassDef.java index fa22d0af25..0e97922db8 100644 --- a/java/fury-core/src/main/java/org/apache/fury/type/ClassDef.java +++ b/java/fury-core/src/main/java/org/apache/fury/type/ClassDef.java @@ -171,7 +171,7 @@ public void writeClassDef(MemoryBuffer buffer) { for (FieldInfo fieldInfo : fieldsInfo) { writeSharedString(buf, map, fieldInfo.definedClass); byte[] bytes = fieldInfo.fieldName.getBytes(StandardCharsets.UTF_8); - buf.writePrimitiveArrayWithSizeEmbedded(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); + buf.writePrimitiveArrayWithSize(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); fieldInfo.fieldType.write(buf); } buf.writePositiveVarInt(extMeta.size()); @@ -179,9 +179,8 @@ public void writeClassDef(MemoryBuffer buffer) { (k, v) -> { byte[] keyBytes = k.getBytes(StandardCharsets.UTF_8); byte[] valueBytes = v.getBytes(StandardCharsets.UTF_8); - buf.writePrimitiveArrayWithSizeEmbedded( - keyBytes, Platform.BYTE_ARRAY_OFFSET, keyBytes.length); - buf.writePrimitiveArrayWithSizeEmbedded( + buf.writePrimitiveArrayWithSize(keyBytes, Platform.BYTE_ARRAY_OFFSET, keyBytes.length); + buf.writePrimitiveArrayWithSize( valueBytes, Platform.BYTE_ARRAY_OFFSET, valueBytes.length); }); serialized = this.serialized = buf.getBytes(0, buf.writerIndex()); @@ -204,7 +203,7 @@ private static void writeSharedString( } else { buffer.writeBoolean(false); byte[] bytes = str.getBytes(StandardCharsets.UTF_8); - buffer.writePrimitiveArrayWithSizeEmbedded(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); + buffer.writePrimitiveArrayWithSize(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); } } @@ -216,15 +215,15 @@ public static ClassDef readClassDef(MemoryBuffer buffer) { int numFields = buffer.readPositiveVarInt(); for (int i = 0; i < numFields; i++) { String definedClass = readSharedString(buffer, strings); - String fieldName = new String(buffer.readBytesWithSizeEmbedded(), StandardCharsets.UTF_8); + String fieldName = new String(buffer.readBytesAndSize(), StandardCharsets.UTF_8); fieldInfos.add(new FieldInfo(definedClass, fieldName, FieldType.read(buffer))); } int extMetaSize = buffer.readPositiveVarInt(); Map extMeta = new HashMap<>(); for (int i = 0; i < extMetaSize; i++) { extMeta.put( - new String(buffer.readBytesWithSizeEmbedded(), StandardCharsets.UTF_8), - new String(buffer.readBytesWithSizeEmbedded(), StandardCharsets.UTF_8)); + new String(buffer.readBytesAndSize(), StandardCharsets.UTF_8), + new String(buffer.readBytesAndSize(), StandardCharsets.UTF_8)); } long id = buffer.readLong(); ClassDef classDef = new ClassDef(className, fieldInfos, extMeta); @@ -235,9 +234,9 @@ public static ClassDef readClassDef(MemoryBuffer buffer) { private static String readSharedString(MemoryBuffer buffer, List strings) { String str; if (buffer.readBoolean()) { - return strings.get(buffer.readPositiveVarInt()); + return strings.get(buffer.readVarUintSmall()); } else { - str = new String(buffer.readBytesWithSizeEmbedded(), StandardCharsets.UTF_8); + str = new String(buffer.readBytesAndSize(), StandardCharsets.UTF_8); strings.add(str); return str; } diff --git a/java/fury-core/src/test/java/org/apache/fury/FuryTest.java b/java/fury-core/src/test/java/org/apache/fury/FuryTest.java index 8baf980d81..faff8daefc 100644 --- a/java/fury-core/src/test/java/org/apache/fury/FuryTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/FuryTest.java @@ -69,7 +69,6 @@ import org.apache.fury.type.Descriptor; import org.apache.fury.util.DateTimeUtils; import org.apache.fury.util.Platform; -import org.apache.fury.util.ReflectionUtils; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -481,95 +480,6 @@ public void testSerializeJavaObject() { beanA); } - @Test - public void testOutputStream() throws IOException { - Fury fury = Fury.builder().requireClassRegistration(false).build(); - ByteArrayOutputStream bas = new ByteArrayOutputStream(); - BeanA beanA = BeanA.createBeanA(2); - fury.serialize(bas, beanA); - fury.serialize(bas, beanA); - bas.flush(); - ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); - Object newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - - fury = Fury.builder().requireClassRegistration(false).build(); - // test reader buffer grow - bis = new ByteArrayInputStream(bas.toByteArray()); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - } - - @Test - public void testBufferedStream() throws IOException { - Fury fury = Fury.builder().requireClassRegistration(false).build(); - ByteArrayOutputStream bas = new ByteArrayOutputStream(); - BeanA beanA = BeanA.createBeanA(2); - fury.serialize(bas, beanA); - fury.serialize(bas, beanA); - bas.flush(); - InputStream bis = - new BufferedInputStream(new ByteArrayInputStream(bas.toByteArray())) { - @Override - public synchronized int read(byte[] b, int off, int len) throws IOException { - return in.read(b, off, Math.min(len, 100)); - } - }; - bis.mark(10); - Object newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - - fury = Fury.builder().requireClassRegistration(false).build(); - // test reader buffer grow - bis = new ByteArrayInputStream(bas.toByteArray()); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - newObj = fury.deserialize(bis); - assertEquals(newObj, beanA); - } - - @Test - public void testJavaOutputStream() throws IOException { - Fury fury = Fury.builder().requireClassRegistration(false).build(); - BeanA beanA = BeanA.createBeanA(2); - { - ByteArrayOutputStream bas = new ByteArrayOutputStream(); - fury.serializeJavaObject(bas, beanA); - fury.serializeJavaObject(bas, beanA); - bas.flush(); - ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); - Object newObj = fury.deserializeJavaObject(bis, BeanA.class); - assertEquals(newObj, beanA); - newObj = fury.deserializeJavaObject(bis, BeanA.class); - assertEquals(newObj, beanA); - } - { - ByteArrayOutputStream bas = new ByteArrayOutputStream(); - fury.serializeJavaObjectAndClass(bas, beanA); - fury.serializeJavaObjectAndClass(bas, beanA); - bas.flush(); - ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); - Object newObj = fury.deserializeJavaObjectAndClass(bis); - assertEquals(newObj, beanA); - newObj = fury.deserializeJavaObjectAndClass(bis); - assertEquals(newObj, beanA); - - fury = Fury.builder().requireClassRegistration(false).build(); - // test reader buffer grow - bis = new ByteArrayInputStream(bas.toByteArray()); - newObj = fury.deserializeJavaObjectAndClass(bis); - assertEquals(newObj, beanA); - newObj = fury.deserializeJavaObjectAndClass(bis); - assertEquals(newObj, beanA); - } - } - @Data static class DomainObject { UUID id; @@ -625,46 +535,6 @@ public void testPkgAccessLevelParentClass() { serDeCheckSerializer(fury, table, "Codec"); } - @Test - public void testBufferReset() { - Fury fury = Fury.builder().withRefTracking(true).requireClassRegistration(false).build(); - byte[] bytes = fury.serialize(new byte[1000 * 1000]); - checkBuffer(fury); - assertEquals(fury.deserialize(bytes), new byte[1000 * 1000]); - bytes = fury.serializeJavaObject(new byte[1000 * 1000]); - checkBuffer(fury); - assertEquals(fury.deserializeJavaObject(bytes, byte[].class), new byte[1000 * 1000]); - - bytes = fury.serializeJavaObjectAndClass(new byte[1000 * 1000]); - checkBuffer(fury); - assertEquals(fury.deserializeJavaObjectAndClass(bytes), new byte[1000 * 1000]); - - ByteArrayOutputStream bas = new ByteArrayOutputStream(); - fury.serialize(bas, new byte[1000 * 1000]); - checkBuffer(fury); - Object o = fury.deserialize(new ByteArrayInputStream(bas.toByteArray())); - assertEquals(o, new byte[1000 * 1000]); - - bas.reset(); - fury.serializeJavaObject(bas, new byte[1000 * 1000]); - checkBuffer(fury); - o = fury.deserializeJavaObject(new ByteArrayInputStream(bas.toByteArray()), byte[].class); - assertEquals(o, new byte[1000 * 1000]); - - bas.reset(); - fury.serializeJavaObjectAndClass(bas, new byte[1000 * 1000]); - checkBuffer(fury); - o = fury.deserializeJavaObjectAndClass(new ByteArrayInputStream(bas.toByteArray())); - assertEquals(o, new byte[1000 * 1000]); - } - - private void checkBuffer(Fury fury) { - Object buf = ReflectionUtils.getObjectFieldValue(fury, "buffer"); - MemoryBuffer buffer = (MemoryBuffer) buf; - assert buffer != null; - assertTrue(buffer.size() < 1000 * 1000); - } - @Data static class PrintReadObject { public PrintReadObject() { diff --git a/java/fury-core/src/test/java/org/apache/fury/StreamTest.java b/java/fury-core/src/test/java/org/apache/fury/StreamTest.java new file mode 100644 index 0000000000..9d69bdc6bd --- /dev/null +++ b/java/fury-core/src/test/java/org/apache/fury/StreamTest.java @@ -0,0 +1,207 @@ +/* + * 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 static org.apache.fury.io.FuryStreamReader.of; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import org.apache.fury.io.FuryInputStream; +import org.apache.fury.memory.MemoryBuffer; +import org.apache.fury.test.bean.BeanA; +import org.apache.fury.util.ReflectionUtils; +import org.testng.annotations.Test; + +public class StreamTest { + @Test + public void testBufferReset() { + Fury fury = Fury.builder().withRefTracking(true).requireClassRegistration(false).build(); + byte[] bytes = fury.serialize(new byte[1000 * 1000]); + checkBuffer(fury); + // assertEquals(fury.deserialize(bytes), new byte[1000 * 1000]); + assertEquals(fury.deserialize(of(new ByteArrayInputStream(bytes))), new byte[1000 * 1000]); + + bytes = fury.serializeJavaObject(new byte[1000 * 1000]); + checkBuffer(fury); + assertEquals(fury.deserializeJavaObject(bytes, byte[].class), new byte[1000 * 1000]); + + bytes = fury.serializeJavaObjectAndClass(new byte[1000 * 1000]); + checkBuffer(fury); + assertEquals(fury.deserializeJavaObjectAndClass(bytes), new byte[1000 * 1000]); + + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + fury.serialize(bas, new byte[1000 * 1000]); + checkBuffer(fury); + Object o = fury.deserialize(of(new ByteArrayInputStream(bas.toByteArray()))); + assertEquals(o, new byte[1000 * 1000]); + assertEquals(fury.deserialize(bas.toByteArray()), new byte[1000 * 1000]); + + bas.reset(); + fury.serializeJavaObject(bas, new byte[1000 * 1000]); + checkBuffer(fury); + o = fury.deserializeJavaObject(of(new ByteArrayInputStream(bas.toByteArray())), byte[].class); + assertEquals(o, new byte[1000 * 1000]); + + bas.reset(); + fury.serializeJavaObjectAndClass(bas, new byte[1000 * 1000]); + checkBuffer(fury); + o = fury.deserializeJavaObjectAndClass(of(new ByteArrayInputStream(bas.toByteArray()))); + assertEquals(o, new byte[1000 * 1000]); + } + + private void checkBuffer(Fury fury) { + Object buf = ReflectionUtils.getObjectFieldValue(fury, "buffer"); + MemoryBuffer buffer = (MemoryBuffer) buf; + assert buffer != null; + assertTrue(buffer.size() < 1000 * 1000); + } + + @Test + public void testOutputStream() throws IOException { + Fury fury = Fury.builder().requireClassRegistration(false).build(); + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + BeanA beanA = BeanA.createBeanA(2); + fury.serialize(bas, beanA); + fury.serialize(bas, beanA); + bas.flush(); + ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); + FuryInputStream stream = of(bis); + MemoryBuffer buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + Object newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + + fury = Fury.builder().requireClassRegistration(false).build(); + // test reader buffer grow + bis = new ByteArrayInputStream(bas.toByteArray()); + stream = of(bis); + buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + } + + @Test + public void testBufferedStream() throws IOException { + Fury fury = Fury.builder().requireClassRegistration(false).build(); + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + BeanA beanA = BeanA.createBeanA(2); + fury.serialize(bas, beanA); + fury.serialize(bas, beanA); + bas.flush(); + InputStream bis = + new BufferedInputStream(new ByteArrayInputStream(bas.toByteArray())) { + @Override + public synchronized int read(byte[] b, int off, int len) throws IOException { + return in.read(b, off, Math.min(len, 100)); + } + }; + bis.mark(10); + FuryInputStream stream = of(bis); + Object newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + + fury = Fury.builder().requireClassRegistration(false).build(); + // test reader buffer grow + bis = new ByteArrayInputStream(bas.toByteArray()); + stream = of(bis); + MemoryBuffer buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + + newObj = fury.deserialize(stream); + assertEquals(newObj, beanA); + newObj = fury.deserialize(buf); + assertEquals(newObj, beanA); + } + + @Test + public void testJavaOutputStream() throws IOException { + Fury fury = Fury.builder().requireClassRegistration(false).build(); + BeanA beanA = BeanA.createBeanA(2); + { + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + fury.serializeJavaObject(bas, beanA); + fury.serializeJavaObject(bas, beanA); + bas.flush(); + ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); + FuryInputStream stream = of(bis); + MemoryBuffer buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + Object newObj = fury.deserializeJavaObject(stream, BeanA.class); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObject(buf, BeanA.class); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObject(stream, BeanA.class); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObject(buf, BeanA.class); + assertEquals(newObj, beanA); + } + { + ByteArrayOutputStream bas = new ByteArrayOutputStream(); + fury.serializeJavaObjectAndClass(bas, beanA); + fury.serializeJavaObjectAndClass(bas, beanA); + bas.flush(); + ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray()); + FuryInputStream stream = of(bis); + MemoryBuffer buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + Object newObj = fury.deserializeJavaObjectAndClass(stream); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(buf); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(stream); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(buf); + assertEquals(newObj, beanA); + + fury = Fury.builder().requireClassRegistration(false).build(); + // test reader buffer grow + bis = new ByteArrayInputStream(bas.toByteArray()); + stream = of(bis); + buf = MemoryBuffer.fromByteArray(bas.toByteArray()); + newObj = fury.deserializeJavaObjectAndClass(stream); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(buf); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(stream); + assertEquals(newObj, beanA); + newObj = fury.deserializeJavaObjectAndClass(buf); + assertEquals(newObj, beanA); + } + } +} diff --git a/java/fury-core/src/test/java/org/apache/fury/io/FuryObjectInputTest.java b/java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectInputTest.java similarity index 93% rename from java/fury-core/src/test/java/org/apache/fury/io/FuryObjectInputTest.java rename to java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectInputTest.java index dbeb4352a1..c0f87159aa 100644 --- a/java/fury-core/src/test/java/org/apache/fury/io/FuryObjectInputTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectInputTest.java @@ -28,7 +28,7 @@ import org.apache.fury.memory.MemoryUtils; import org.testng.annotations.Test; -public class FuryObjectInputTest { +public class MemoryBufferObjectInputTest { @Test public void testFuryObjectInput() throws IOException { @@ -41,7 +41,7 @@ public void testFuryObjectInput() throws IOException { buffer.writeFloat(4.1f); buffer.writeDouble(4.2); fury.writeJavaString(buffer, "abc"); - try (FuryObjectInput input = new FuryObjectInput(fury, buffer)) { + try (MemoryBufferObjectInput input = new MemoryBufferObjectInput(fury, buffer)) { assertEquals(input.readByte(), 1); assertEquals(input.readInt(), 2); assertEquals(input.readLong(), 3); diff --git a/java/fury-core/src/test/java/org/apache/fury/io/FuryObjectOutputTest.java b/java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectOutputTest.java similarity index 89% rename from java/fury-core/src/test/java/org/apache/fury/io/FuryObjectOutputTest.java rename to java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectOutputTest.java index 7da6aa96f4..efc070419c 100644 --- a/java/fury-core/src/test/java/org/apache/fury/io/FuryObjectOutputTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/io/MemoryBufferObjectOutputTest.java @@ -28,13 +28,13 @@ import org.apache.fury.memory.MemoryUtils; import org.testng.annotations.Test; -public class FuryObjectOutputTest { +public class MemoryBufferObjectOutputTest { @Test public void testFuryObjectOutput() throws IOException { Fury fury = Fury.builder().build(); MemoryBuffer buffer = MemoryUtils.buffer(32); - try (FuryObjectOutput output = new FuryObjectOutput(fury, buffer)) { + try (MemoryBufferObjectOutput output = new MemoryBufferObjectOutput(fury, buffer)) { output.writeByte(1); output.writeInt(2); output.writeLong(3); @@ -44,7 +44,7 @@ public void testFuryObjectOutput() throws IOException { output.writeChars("abc"); output.writeUTF("abc"); } - try (FuryObjectInput input = new FuryObjectInput(fury, buffer)) { + try (MemoryBufferObjectInput input = new MemoryBufferObjectInput(fury, buffer)) { assertEquals(input.readByte(), 1); assertEquals(input.readInt(), 2); assertEquals(input.readLong(), 3); diff --git a/java/fury-core/src/test/java/org/apache/fury/io/MockWritableByteChannelTest.java b/java/fury-core/src/test/java/org/apache/fury/io/MockWritableChannelTest.java similarity index 91% rename from java/fury-core/src/test/java/org/apache/fury/io/MockWritableByteChannelTest.java rename to java/fury-core/src/test/java/org/apache/fury/io/MockWritableChannelTest.java index 8c355e59cd..5afda1d57a 100644 --- a/java/fury-core/src/test/java/org/apache/fury/io/MockWritableByteChannelTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/io/MockWritableChannelTest.java @@ -24,11 +24,11 @@ import java.nio.ByteBuffer; import org.testng.annotations.Test; -public class MockWritableByteChannelTest { +public class MockWritableChannelTest { @Test public void testTotalBytes() { - try (MockWritableByteChannel channel = new MockWritableByteChannel()) { + try (MockWritableChannel channel = new MockWritableChannel()) { channel.write(ByteBuffer.allocate(100)); channel.write(ByteBuffer.allocateDirect(100)); ByteBuffer buffer = ByteBuffer.allocate(100); diff --git a/java/fury-core/src/test/java/org/apache/fury/memory/MemoryBufferTest.java b/java/fury-core/src/test/java/org/apache/fury/memory/MemoryBufferTest.java index 89d8b8215b..f6f15e1676 100644 --- a/java/fury-core/src/test/java/org/apache/fury/memory/MemoryBufferTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/memory/MemoryBufferTest.java @@ -211,14 +211,14 @@ public void testWritePrimitiveArrayWithSizeEmbedded() { for (int i = 0; i < chars.length; i++) { chars[i] = (char) random.nextInt(); } - buf.writePrimitiveArrayWithSizeEmbedded(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); - buf.writePrimitiveArrayWithSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, chars.length * 2); - assertEquals(bytes, buf.readBytesWithSizeEmbedded()); - assertEquals(chars, buf.readCharsWithSizeEmbedded()); - buf.writePrimitiveArrayAlignedSizeEmbedded(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); - buf.writePrimitiveArrayAlignedSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, chars.length * 2); - assertEquals(bytes, buf.readBytesAlignedSizeEmbedded()); - assertEquals(chars, buf.readCharsAlignedSizeEmbedded()); + buf.writePrimitiveArrayWithSize(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); + buf.writePrimitiveArrayWithSize(chars, Platform.CHAR_ARRAY_OFFSET, chars.length * 2); + assertEquals(bytes, buf.readBytesAndSize()); + assertEquals(chars, buf.readChars(buf.readPositiveVarInt())); + buf.writePrimitiveArrayAlignedSize(bytes, Platform.BYTE_ARRAY_OFFSET, bytes.length); + buf.writePrimitiveArrayAlignedSize(chars, Platform.CHAR_ARRAY_OFFSET, chars.length * 2); + assertEquals(bytes, buf.readBytesWithAlignedSize()); + assertEquals(chars, buf.readCharsWithAlignedSize()); } @Test diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/PrimitiveSerializersTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/PrimitiveSerializersTest.java index 42a4b3b3d1..33539b1b6d 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/PrimitiveSerializersTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/PrimitiveSerializersTest.java @@ -73,6 +73,7 @@ public static class PrimitiveStruct { int int2; long long1; long long2; + long long3; float float1; float float2; double double1; @@ -93,6 +94,7 @@ public void testPrimitiveStruct(boolean compressNumber, boolean codegen) { Integer.MIN_VALUE, Long.MIN_VALUE, Long.MIN_VALUE, + -3763915443215605988L, // test Long.reverseBytes in readVarLongOnBE Float.MIN_VALUE, Float.MIN_VALUE, Double.MIN_VALUE, diff --git a/java/fury-core/src/test/java/org/apache/fury/serializer/StringSerializerTest.java b/java/fury-core/src/test/java/org/apache/fury/serializer/StringSerializerTest.java index c2c26cae70..a6c56e88d1 100644 --- a/java/fury-core/src/test/java/org/apache/fury/serializer/StringSerializerTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/serializer/StringSerializerTest.java @@ -85,7 +85,8 @@ private static String readJavaStringZeroCopy(MemoryBuffer buffer) { if (STRING_VALUE_FIELD_IS_BYTES) { return readJDK11String(buffer); } else if (STRING_VALUE_FIELD_IS_CHARS) { - return StringSerializer.newCharsStringZeroCopy(buffer.readCharsWithSizeEmbedded()); + return StringSerializer.newCharsStringZeroCopy( + buffer.readChars(buffer.readPositiveVarInt())); } return null; } catch (Exception e) { @@ -95,7 +96,7 @@ private static String readJavaStringZeroCopy(MemoryBuffer buffer) { static String readJDK11String(MemoryBuffer buffer) { byte coder = buffer.readByte(); - byte[] value = buffer.readBytesWithSizeEmbedded(); + byte[] value = buffer.readBytesAndSize(); return newBytesStringZeroCopy(coder, value); } @@ -126,7 +127,7 @@ static void writeJDK8String(MemoryBuffer buffer, String value) { final char[] chars = (char[]) Platform.getObject(value, ReflectionUtils.getFieldOffset(String.class, "value")); int numBytes = MathUtils.doubleExact(value.length()); - buffer.writePrimitiveArrayWithSizeEmbedded(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); + buffer.writePrimitiveArrayWithSize(chars, Platform.CHAR_ARRAY_OFFSET, numBytes); } @Test diff --git a/java/fury-format/src/main/java/org/apache/fury/format/type/DataTypes.java b/java/fury-format/src/main/java/org/apache/fury/format/type/DataTypes.java index f484ebc8cd..fb832e44a7 100644 --- a/java/fury-format/src/main/java/org/apache/fury/format/type/DataTypes.java +++ b/java/fury-format/src/main/java/org/apache/fury/format/type/DataTypes.java @@ -41,7 +41,7 @@ import org.apache.arrow.vector.types.pojo.FieldType; import org.apache.arrow.vector.types.pojo.Schema; import org.apache.fury.exception.FuryException; -import org.apache.fury.io.FuryOutputStream; +import org.apache.fury.io.MemoryBufferOutputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.type.Type; import org.apache.fury.util.DecimalUtils; @@ -464,7 +464,7 @@ public static Field fieldOfSchema(Schema schema, int index) { } public static void serializeSchema(Schema schema, MemoryBuffer buffer) { - try (FuryOutputStream outputStream = new FuryOutputStream(buffer); + try (MemoryBufferOutputStream outputStream = new MemoryBufferOutputStream(buffer); WriteChannel writeChannel = new WriteChannel(Channels.newChannel(outputStream))) { MessageSerializer.serialize(writeChannel, schema); } catch (IOException e) { diff --git a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowSerializers.java b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowSerializers.java index 9aa6943ef4..3394b019c3 100644 --- a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowSerializers.java +++ b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowSerializers.java @@ -31,9 +31,9 @@ import org.apache.arrow.vector.ipc.message.IpcOption; import org.apache.arrow.vector.ipc.message.MessageSerializer; import org.apache.fury.Fury; -import org.apache.fury.io.FuryReadableByteChannel; -import org.apache.fury.io.FuryWritableByteChannel; -import org.apache.fury.io.MockWritableByteChannel; +import org.apache.fury.io.MemoryBufferReadableChannel; +import org.apache.fury.io.MemoryBufferWritableChannel; +import org.apache.fury.io.MockWritableChannel; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.serializer.BufferObject; @@ -70,7 +70,7 @@ public void write(MemoryBuffer buffer, VectorSchemaRoot root) { public VectorSchemaRoot read(MemoryBuffer buffer) { MemoryBuffer buf = fury.readBufferObject(buffer); try { - ReadableByteChannel channel = new FuryReadableByteChannel(buf); + ReadableByteChannel channel = new MemoryBufferReadableChannel(buf); ArrowStreamReader reader = new ArrowStreamReader(channel, allocator); // FIXME close reader will close `root`. // since there is no possibility for resource leak, we can skip `reader.close` @@ -93,9 +93,9 @@ private static class VectorSchemaRootBufferObject implements BufferObject { VectorSchemaRootBufferObject(VectorSchemaRoot root) { this.root = root; - MockWritableByteChannel mockWritableByteChannel = new MockWritableByteChannel(); - write(root, mockWritableByteChannel); - totalBytes = mockWritableByteChannel.totalBytes(); + MockWritableChannel mockWritableChannel = new MockWritableChannel(); + write(root, mockWritableChannel); + totalBytes = mockWritableChannel.totalBytes(); } @Override @@ -105,7 +105,7 @@ public int totalBytes() { @Override public void writeTo(MemoryBuffer buffer) { - write(root, new FuryWritableByteChannel(buffer)); + write(root, new MemoryBufferWritableChannel(buffer)); } private static void write(VectorSchemaRoot root, WritableByteChannel byteChannel) { @@ -119,7 +119,7 @@ private static void write(VectorSchemaRoot root, WritableByteChannel byteChannel @Override public MemoryBuffer toBuffer() { MemoryBuffer buffer = MemoryUtils.buffer(totalBytes); - write(root, new FuryWritableByteChannel(buffer)); + write(root, new MemoryBufferWritableChannel(buffer)); return buffer.slice(0, buffer.writerIndex()); } } @@ -130,9 +130,9 @@ public static class ArrowTableBufferObject implements BufferObject { public ArrowTableBufferObject(ArrowTable table) { this.table = table; - MockWritableByteChannel mockWritableByteChannel = new MockWritableByteChannel(); - write(table, mockWritableByteChannel); - totalBytes = mockWritableByteChannel.totalBytes(); + MockWritableChannel mockWritableChannel = new MockWritableChannel(); + write(table, mockWritableChannel); + totalBytes = mockWritableChannel.totalBytes(); } @Override @@ -142,7 +142,7 @@ public int totalBytes() { @Override public void writeTo(MemoryBuffer buffer) { - write(table, new FuryWritableByteChannel(buffer)); + write(table, new MemoryBufferWritableChannel(buffer)); } private static void write(ArrowTable table, WritableByteChannel byteChannel) { @@ -160,7 +160,7 @@ private static void write(ArrowTable table, WritableByteChannel byteChannel) { @Override public MemoryBuffer toBuffer() { MemoryBuffer buffer = MemoryUtils.buffer(totalBytes); - write(table, new FuryWritableByteChannel(buffer)); + write(table, new MemoryBufferWritableChannel(buffer)); return buffer.slice(0, buffer.writerIndex()); } } diff --git a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowTableSerializer.java b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowTableSerializer.java index d459e4281f..1ccf361899 100644 --- a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowTableSerializer.java +++ b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowTableSerializer.java @@ -28,7 +28,7 @@ import org.apache.arrow.vector.ipc.ArrowStreamReader; import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; import org.apache.fury.Fury; -import org.apache.fury.io.FuryReadableByteChannel; +import org.apache.fury.io.MemoryBufferReadableChannel; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.serializer.Serializers; import org.apache.fury.type.Type; @@ -60,7 +60,7 @@ public ArrowTable read(MemoryBuffer buffer) { MemoryBuffer buf = fury.readBufferObject(buffer); List recordBatches = new ArrayList<>(); try { - ReadableByteChannel channel = new FuryReadableByteChannel(buf); + ReadableByteChannel channel = new MemoryBufferReadableChannel(buf); ArrowStreamReader reader = new ArrowStreamReader(channel, allocator); VectorSchemaRoot root = reader.getVectorSchemaRoot(); while (reader.loadNextBatch()) { diff --git a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowUtils.java b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowUtils.java index 1f32a498d1..30b2c7cbb3 100644 --- a/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowUtils.java +++ b/java/fury-format/src/main/java/org/apache/fury/format/vectorized/ArrowUtils.java @@ -29,8 +29,8 @@ import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; import org.apache.arrow.vector.ipc.message.MessageSerializer; import org.apache.arrow.vector.types.pojo.Schema; -import org.apache.fury.io.FuryInputStream; -import org.apache.fury.io.FuryOutputStream; +import org.apache.fury.io.MemoryBufferInputStream; +import org.apache.fury.io.MemoryBufferOutputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.util.DecimalUtils; @@ -63,7 +63,7 @@ public static ArrowWriter createArrowWriter(Schema schema) { public static void serializeRecordBatch(ArrowRecordBatch recordBatch, MemoryBuffer buffer) { // TODO(chaokunyang) add custom WritableByteChannel to avoid copy in `WritableByteChannelImpl` try (WriteChannel channel = - new WriteChannel(Channels.newChannel(new FuryOutputStream(buffer)))) { + new WriteChannel(Channels.newChannel(new MemoryBufferOutputStream(buffer)))) { MessageSerializer.serialize(channel, recordBatch); } catch (IOException e) { throw new RuntimeException(String.format("Serialize record batch %s failed", recordBatch), e); @@ -73,7 +73,8 @@ public static void serializeRecordBatch(ArrowRecordBatch recordBatch, MemoryBuff public static ArrowRecordBatch deserializeRecordBatch(MemoryBuffer recordBatchMessageBuffer) { // TODO(chaokunyang) add custom ReadableByteChannel to avoid copy in `ReadableByteChannelImpl` try (ReadChannel channel = - new ReadChannel(Channels.newChannel(new FuryInputStream(recordBatchMessageBuffer)))) { + new ReadChannel( + Channels.newChannel(new MemoryBufferInputStream(recordBatchMessageBuffer)))) { return MessageSerializer.deserializeRecordBatch(channel, allocator); } catch (IOException e) { throw new RuntimeException("Deserialize record batch failed", e); diff --git a/java/fury-format/src/test/java/org/apache/fury/format/CrossLanguageTest.java b/java/fury-format/src/test/java/org/apache/fury/format/CrossLanguageTest.java index b5f6e3419c..edab44de1e 100644 --- a/java/fury-format/src/test/java/org/apache/fury/format/CrossLanguageTest.java +++ b/java/fury-format/src/test/java/org/apache/fury/format/CrossLanguageTest.java @@ -62,7 +62,7 @@ import org.apache.fury.format.vectorized.ArrowTable; import org.apache.fury.format.vectorized.ArrowUtils; import org.apache.fury.format.vectorized.ArrowWriter; -import org.apache.fury.io.FuryOutputStream; +import org.apache.fury.io.MemoryBufferOutputStream; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.serializer.BufferObject; @@ -178,7 +178,7 @@ public void testRecordBatchBasic() throws IOException { Path dataFile = Files.createTempFile("foo", "data"); MemoryBuffer buffer = MemoryUtils.buffer(128); try (ArrowStreamWriter writer = - new ArrowStreamWriter(root, null, new FuryOutputStream(buffer))) { + new ArrowStreamWriter(root, null, new MemoryBufferOutputStream(buffer))) { writer.start(); for (int i = 0; i < 1; i++) { vector.allocateNew(16); @@ -220,7 +220,7 @@ public void testRecordBatchWriter() throws IOException { VectorSchemaRoot root = ArrowUtils.createVectorSchemaRoot(encoder.schema()); ArrowWriter arrowWriter = new ArrowWriter(root); try (ArrowStreamWriter writer = - new ArrowStreamWriter(root, null, new FuryOutputStream(buffer))) { + new ArrowStreamWriter(root, null, new MemoryBufferOutputStream(buffer))) { writer.start(); for (int i = 0; i < numRows; i++) { BinaryRow row = encoder.toRow(foo); @@ -245,7 +245,8 @@ public void testRecordBatchWriter() throws IOException { ArrowUtils.serializeRecordBatch(recordBatch, buffer); arrowWriter.reset(); ArrowStreamWriter.writeEndOfStream( - new WriteChannel(Channels.newChannel(new FuryOutputStream(buffer))), new IpcOption()); + new WriteChannel(Channels.newChannel(new MemoryBufferOutputStream(buffer))), + new IpcOption()); Files.write(dataFile, buffer.getBytes(0, buffer.writerIndex())); Assert.assertTrue(executeCommand(command, 30)); } diff --git a/java/fury-format/src/test/java/org/apache/fury/format/vectorized/ArrowSerializersTest.java b/java/fury-format/src/test/java/org/apache/fury/format/vectorized/ArrowSerializersTest.java index 72cd98baa1..364b986be9 100644 --- a/java/fury-format/src/test/java/org/apache/fury/format/vectorized/ArrowSerializersTest.java +++ b/java/fury-format/src/test/java/org/apache/fury/format/vectorized/ArrowSerializersTest.java @@ -19,21 +19,25 @@ package org.apache.fury.format.vectorized; +import static org.apache.fury.format.vectorized.ArrowUtilsTest.createVectorSchemaRoot; import static org.testng.Assert.assertEquals; import java.io.IOException; +import java.nio.channels.ReadableByteChannel; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; import org.apache.arrow.vector.VectorSchemaRoot; import org.apache.arrow.vector.VectorUnloader; +import org.apache.arrow.vector.ipc.ArrowStreamReader; import org.apache.arrow.vector.ipc.ArrowStreamWriter; import org.apache.arrow.vector.ipc.message.ArrowRecordBatch; import org.apache.arrow.vector.types.pojo.Schema; import org.apache.fury.Fury; import org.apache.fury.config.Language; -import org.apache.fury.io.FuryWritableByteChannel; +import org.apache.fury.io.MemoryBufferReadableChannel; +import org.apache.fury.io.MemoryBufferWritableChannel; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.memory.MemoryUtils; import org.apache.fury.resolver.ClassResolver; @@ -53,6 +57,29 @@ public void testRegisterArrowSerializer() throws Exception { ArrowSerializers.VectorSchemaRootSerializer.class); } + @Test + public void testArrowTableBufferObject() throws IOException { + int size = 200; + VectorSchemaRoot root = createVectorSchemaRoot(size); + Schema schema = root.getSchema(); + List recordBatches = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + VectorUnloader unloader = new VectorUnloader(root); + recordBatches.add(unloader.getRecordBatch()); + } + ArrowTable table = new ArrowTable(schema, recordBatches); + ArrowSerializers.ArrowTableBufferObject o = new ArrowSerializers.ArrowTableBufferObject(table); + MemoryBuffer buf = o.toBuffer(); + ReadableByteChannel channel = new MemoryBufferReadableChannel(buf); + ArrowStreamReader reader = new ArrowStreamReader(channel, ArrowUtils.allocator); + VectorSchemaRoot newRoot = reader.getVectorSchemaRoot(); + while (reader.loadNextBatch()) { + recordBatches.add(new VectorUnloader(root).getRecordBatch()); + } + ArrowTable newTable = new ArrowTable(root.getSchema(), recordBatches, ArrowUtils.allocator); + assertTableEqual(newTable, table); + } + @Test public void testWriteVectorSchemaRoot() throws IOException { Collection bufferObjects = new ArrayList<>(); @@ -71,7 +98,7 @@ public void testWriteVectorSchemaRoot() throws IOException { MemoryBuffer buffer2 = MemoryUtils.buffer(32); try (ArrowStreamWriter writer = - new ArrowStreamWriter(root, null, new FuryWritableByteChannel(buffer2))) { + new ArrowStreamWriter(root, null, new MemoryBufferWritableChannel(buffer2))) { // this will make root empty. writer.writeBatch(); }