This is an automated email from the ASF dual-hosted git repository.

xiangfu0 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new c5eeb91e388 Let OBJECT codecs serialize payloads into caller buffers 
(#19836)
c5eeb91e388 is described below

commit c5eeb91e388ab5cc697a8262881ccbd3b668e076
Author: Xiang Fu <[email protected]>
AuthorDate: Fri Oct 16 11:40:22 2026 -0700

    Let OBJECT codecs serialize payloads into caller buffers (#19836)
    
    * Let OBJECT codecs serialize payloads into caller buffers
    
    Roaring, UltraLogLog, and primitive-list codecs now own direct writes 
alongside their existing byte-array API. Call getSerializedSize before buffer 
serialization; unsupported codecs keep the legacy fallback, and all 198 
combined tests pass.
    
    * Centralize OBJECT byte-array allocation
    
    Keep typed forwarding calls so legacy serializers retain their allocation 
behavior while sharing the default implementation.
    
    * Clarify caller-buffer serialization contracts
    
    Document sufficient remaining capacity, codec override requirements and the 
intended caller. Cover excess buffer space in the existing serialization tests.
---
 .../apache/pinot/core/common/ObjectSerDeUtils.java | 213 +++++++++++++++++----
 .../pinot/core/common/ObjectSerDeUtilsTest.java    | 163 ++++++++++++++++
 2 files changed, 338 insertions(+), 38 deletions(-)

diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/common/ObjectSerDeUtils.java 
b/pinot-core/src/main/java/org/apache/pinot/core/common/ObjectSerDeUtils.java
index c5427f76065..b3c17dc809c 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/common/ObjectSerDeUtils.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/common/ObjectSerDeUtils.java
@@ -56,9 +56,12 @@ import it.unimi.dsi.fastutil.objects.ObjectSet;
 import java.io.IOException;
 import java.lang.foreign.MemorySegment;
 import java.math.BigDecimal;
+import java.nio.BufferOverflowException;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.nio.IntBuffer;
+import java.nio.ReadOnlyBufferException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -325,11 +328,36 @@ public class ObjectSerDeUtils {
 
   /// Serializer/De-serializer for a specific type of object.
   ///
+  /// Implementations must override either the byte-array serializer or both 
[#getSerializedSize(Object)] and
+  /// the buffer serializer [#serialize(Object, ByteBuffer)]. This contract is 
enforced by convention; the serializer
+  /// defaults delegate to each other.
+  ///
   /// @param <T> Type of the object
   public interface ObjectSerDe<T> {
 
     /// Serializes a value into a byte array.
-    byte[] serialize(T value);
+    /// Typed forwarding overrides preserve receiver specialization when 
invoking this default.
+    default byte[] serialize(T value) {
+      byte[] bytes = new byte[getSerializedSize(value)];
+      serialize(value, ByteBuffer.wrap(bytes));
+      return bytes;
+    }
+
+    /// Returns the exact size of a representable payload, or `-1` when 
allocation-free sizing is unsupported.
+    /// This method must not serialize the value or allocate a payload array. 
The value must remain unchanged between
+    /// sizing and serialization. The destination must have at least this many 
bytes remaining.
+    default int getSerializedSize(T value) {
+      return -1;
+    }
+
+    /// Writes the canonical payload at the destination's current position and 
advances it by the payload size.
+    /// Supports heap and direct buffers, preserves the destination's limit 
and byte order, and does not retain the
+    /// buffer. Direct-writing overrides must not mutate the value. A 
read-only destination or insufficient remaining
+    /// space fails before any write. The default implementation preserves the 
legacy serializer behavior and
+    /// serializes once into a temporary array; codecs can override it for 
direct writes.
+    default void serialize(T value, ByteBuffer destination) {
+      destination.put(serialize(value));
+    }
 
     /// De-serializes a value from a byte array.
     T deserialize(byte[] bytes);
@@ -413,17 +441,31 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<IntArrayList> INT_ARRAY_LIST_SER_DE = new 
ObjectSerDe<IntArrayList>() {
 
+    @Override
+    public int getSerializedSize(IntArrayList intArrayList) {
+      return Math.addExact(Integer.BYTES, 
Math.multiplyExact(intArrayList.size(), Integer.BYTES));
+    }
+
     @Override
     public byte[] serialize(IntArrayList intArrayList) {
-      int size = intArrayList.size();
-      byte[] bytes = new byte[Integer.BYTES + size * Integer.BYTES];
-      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-      byteBuffer.putInt(size);
-      int[] values = intArrayList.elements();
-      for (int i = 0; i < size; i++) {
-        byteBuffer.putInt(values[i]);
+      return ObjectSerDe.super.serialize(intArrayList);
+    }
+
+    @Override
+    public void serialize(IntArrayList intArrayList, ByteBuffer destination) {
+      checkSerializationBuffer(destination, getSerializedSize(intArrayList));
+      ByteOrder originalOrder = destination.order();
+      destination.order(ByteOrder.BIG_ENDIAN);
+      try {
+        int size = intArrayList.size();
+        destination.putInt(size);
+        int[] values = intArrayList.elements();
+        for (int i = 0; i < size; i++) {
+          destination.putInt(values[i]);
+        }
+      } finally {
+        destination.order(originalOrder);
       }
-      return bytes;
     }
 
     @Override
@@ -450,17 +492,31 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<LongArrayList> LONG_ARRAY_LIST_SER_DE = new 
ObjectSerDe<LongArrayList>() {
 
+    @Override
+    public int getSerializedSize(LongArrayList longArrayList) {
+      return Math.addExact(Integer.BYTES, 
Math.multiplyExact(longArrayList.size(), Long.BYTES));
+    }
+
     @Override
     public byte[] serialize(LongArrayList longArrayList) {
-      int size = longArrayList.size();
-      byte[] bytes = new byte[Integer.BYTES + size * Long.BYTES];
-      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-      byteBuffer.putInt(size);
-      long[] values = longArrayList.elements();
-      for (int i = 0; i < size; i++) {
-        byteBuffer.putLong(values[i]);
+      return ObjectSerDe.super.serialize(longArrayList);
+    }
+
+    @Override
+    public void serialize(LongArrayList longArrayList, ByteBuffer destination) 
{
+      checkSerializationBuffer(destination, getSerializedSize(longArrayList));
+      ByteOrder originalOrder = destination.order();
+      destination.order(ByteOrder.BIG_ENDIAN);
+      try {
+        int size = longArrayList.size();
+        destination.putInt(size);
+        long[] values = longArrayList.elements();
+        for (int i = 0; i < size; i++) {
+          destination.putLong(values[i]);
+        }
+      } finally {
+        destination.order(originalOrder);
       }
-      return bytes;
     }
 
     @Override
@@ -487,17 +543,31 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<FloatArrayList> FLOAT_ARRAY_LIST_SER_DE = 
new ObjectSerDe<FloatArrayList>() {
 
+    @Override
+    public int getSerializedSize(FloatArrayList floatArrayList) {
+      return Math.addExact(Integer.BYTES, 
Math.multiplyExact(floatArrayList.size(), Float.BYTES));
+    }
+
     @Override
     public byte[] serialize(FloatArrayList floatArrayList) {
-      int size = floatArrayList.size();
-      byte[] bytes = new byte[Integer.BYTES + size * Float.BYTES];
-      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-      byteBuffer.putInt(size);
-      float[] values = floatArrayList.elements();
-      for (int i = 0; i < size; i++) {
-        byteBuffer.putFloat(values[i]);
+      return ObjectSerDe.super.serialize(floatArrayList);
+    }
+
+    @Override
+    public void serialize(FloatArrayList floatArrayList, ByteBuffer 
destination) {
+      checkSerializationBuffer(destination, getSerializedSize(floatArrayList));
+      ByteOrder originalOrder = destination.order();
+      destination.order(ByteOrder.BIG_ENDIAN);
+      try {
+        int size = floatArrayList.size();
+        destination.putInt(size);
+        float[] values = floatArrayList.elements();
+        for (int i = 0; i < size; i++) {
+          destination.putInt(Float.floatToRawIntBits(values[i]));
+        }
+      } finally {
+        destination.order(originalOrder);
       }
-      return bytes;
     }
 
     @Override
@@ -524,17 +594,31 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<DoubleArrayList> DOUBLE_ARRAY_LIST_SER_DE = 
new ObjectSerDe<DoubleArrayList>() {
 
+    @Override
+    public int getSerializedSize(DoubleArrayList doubleArrayList) {
+      return Math.addExact(Integer.BYTES, 
Math.multiplyExact(doubleArrayList.size(), Double.BYTES));
+    }
+
     @Override
     public byte[] serialize(DoubleArrayList doubleArrayList) {
-      int size = doubleArrayList.size();
-      byte[] bytes = new byte[Integer.BYTES + size * Double.BYTES];
-      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-      byteBuffer.putInt(size);
-      double[] values = doubleArrayList.elements();
-      for (int i = 0; i < size; i++) {
-        byteBuffer.putDouble(values[i]);
+      return ObjectSerDe.super.serialize(doubleArrayList);
+    }
+
+    @Override
+    public void serialize(DoubleArrayList doubleArrayList, ByteBuffer 
destination) {
+      checkSerializationBuffer(destination, 
getSerializedSize(doubleArrayList));
+      ByteOrder originalOrder = destination.order();
+      destination.order(ByteOrder.BIG_ENDIAN);
+      try {
+        int size = doubleArrayList.size();
+        destination.putInt(size);
+        double[] values = doubleArrayList.elements();
+        for (int i = 0; i < size; i++) {
+          destination.putLong(Double.doubleToRawLongBits(values[i]));
+        }
+      } finally {
+        destination.order(originalOrder);
       }
-      return bytes;
     }
 
     @Override
@@ -1280,9 +1364,30 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<RoaringBitmap> ROARING_BITMAP_SER_DE = new 
ObjectSerDe<RoaringBitmap>() {
 
+    @Override
+    public int getSerializedSize(RoaringBitmap bitmap) {
+      int size = bitmap.serializedSizeInBytes();
+      Preconditions.checkArgument(size >= 0, "Serialized bitmap exceeds the 
maximum buffer size");
+      return size;
+    }
+
     @Override
     public byte[] serialize(RoaringBitmap bitmap) {
-      return RoaringBitmapUtils.serialize(bitmap);
+      return ObjectSerDe.super.serialize(bitmap);
+    }
+
+    @Override
+    public void serialize(RoaringBitmap bitmap, ByteBuffer destination) {
+      // Recompute the size to validate standalone writes; mutable bitmaps 
cannot safely cache it across calls.
+      checkSerializationBuffer(destination, getSerializedSize(bitmap));
+      ByteOrder originalOrder = destination.order();
+      // Roaring uses little-endian wire bytes; restore the caller's byte 
order after writing.
+      destination.order(ByteOrder.LITTLE_ENDIAN);
+      try {
+        RoaringBitmapUtils.serialize(bitmap, destination);
+      } finally {
+        destination.order(originalOrder);
+      }
     }
 
     @Override
@@ -1729,12 +1834,21 @@ public class ObjectSerDeUtils {
 
   public static final ObjectSerDe<UltraLogLog> ULTRA_LOG_LOG_OBJECT_SER_DE = 
new ObjectSerDe<UltraLogLog>() {
 
+    @Override
+    public int getSerializedSize(UltraLogLog value) {
+      return Math.addExact(1, value.getState().length);
+    }
+
     @Override
     public byte[] serialize(UltraLogLog value) {
-      ByteBuffer buff = ByteBuffer.wrap(new byte[(1 << value.getP()) + 1]);
-      buff.put((byte) value.getP());
-      buff.put(value.getState());
-      return buff.array();
+      return ObjectSerDe.super.serialize(value);
+    }
+
+    @Override
+    public void serialize(UltraLogLog value, ByteBuffer destination) {
+      checkSerializationBuffer(destination, getSerializedSize(value));
+      destination.put((byte) value.getP());
+      destination.put(value.getState());
     }
 
     @Override
@@ -1984,6 +2098,29 @@ public class ObjectSerDeUtils {
     return SER_DES[objectTypeValue].serialize(value);
   }
 
+  /// Returns the codec's exact size for a representable payload, or `-1` if 
allocation-free sizing is unavailable.
+  /// Intended for callers writing OBJECT payloads into an existing transport 
buffer, such as an Arrow vector.
+  /// A result of `-1` selects the legacy byte-array fallback. The value must 
remain unchanged until serialization ends.
+  public static int getSerializedSize(Object value, int objectTypeValue) {
+    return SER_DES[objectTypeValue].getSerializedSize(value);
+  }
+
+  /// Writes the payload through its type's codec, advancing the destination 
position and preserving its byte order.
+  /// The destination must have sufficient remaining space; its limit does not 
need to match the payload size.
+  /// The codec uses a temporary payload array when it does not support direct 
serialization.
+  public static void serialize(Object value, int objectTypeValue, ByteBuffer 
destination) {
+    SER_DES[objectTypeValue].serialize(value, destination);
+  }
+
+  private static void checkSerializationBuffer(ByteBuffer destination, int 
size) {
+    if (destination.isReadOnly()) {
+      throw new ReadOnlyBufferException();
+    }
+    if (destination.remaining() < size) {
+      throw new BufferOverflowException();
+    }
+  }
+
   public static <T> T deserialize(CustomObject customObject) {
     return (T) 
SER_DES[customObject.getType()].deserialize(customObject.getBuffer());
   }
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/common/ObjectSerDeUtilsTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/common/ObjectSerDeUtilsTest.java
index c7932fcbf02..41c969962a5 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/common/ObjectSerDeUtilsTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/common/ObjectSerDeUtilsTest.java
@@ -32,10 +32,14 @@ import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
 import it.unimi.dsi.fastutil.longs.LongArrayList;
 import it.unimi.dsi.fastutil.objects.ObjectArrayList;
 import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
+import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ReadOnlyBufferException;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.HashMap;
+import java.util.HexFormat;
 import java.util.List;
 import java.util.Map;
 import java.util.PriorityQueue;
@@ -49,6 +53,9 @@ import org.apache.datasketches.tuple.TupleSketch;
 import org.apache.datasketches.tuple.aninteger.IntegerSummary;
 import org.apache.datasketches.tuple.aninteger.IntegerSummarySetOperations;
 import org.apache.datasketches.tuple.aninteger.IntegerTupleSketch;
+import org.apache.pinot.common.utils.RoaringBitmapUtils;
+import org.apache.pinot.core.common.ObjectSerDeUtils.ObjectSerDe;
+import org.apache.pinot.core.common.ObjectSerDeUtils.ObjectType;
 import 
org.apache.pinot.core.query.aggregation.function.PercentileEstAggregationFunction;
 import 
org.apache.pinot.core.query.aggregation.function.PercentileTDigestAggregationFunction;
 import org.apache.pinot.core.query.aggregation.function.funnel.FunnelStepEvent;
@@ -69,10 +76,12 @@ import 
org.apache.pinot.segment.local.customobject.tdigest.TDigest;
 import org.apache.pinot.segment.local.customobject.tdigest.TDigest.Centroid;
 import org.apache.pinot.segment.local.customobject.tdigest.TDigestAccumulator;
 import org.apache.pinot.segment.local.utils.UltraLogLogUtils;
+import org.roaringbitmap.RoaringBitmap;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertThrows;
 import static org.testng.Assert.assertTrue;
 
@@ -101,6 +110,160 @@ public class ObjectSerDeUtilsTest {
           + 
"QUChLz+AAABBXvGHP4AAAEGCsOA/gAAAQZutuD+AAABBvS/yP4AAAEHr7EA/gAAAQhhGxT+AAABCTlsKP4AAAEKWasI/gAAAQvfH"
           + "yz+AAABDgpJvP4AAAESXLNE=";
 
+  @DataProvider(name = "directSerializationValues")
+  public Object[][] directSerializationValues() {
+    RoaringBitmap runBitmap = new RoaringBitmap();
+    runBitmap.add(100L, 10000L);
+    runBitmap.runOptimize();
+    return new Object[][]{
+        {new IntArrayList(), ObjectType.IntArrayList, 
HexFormat.of().parseHex("00000000")},
+        {IntArrayList.wrap(new int[]{Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 
99}, 3), ObjectType.IntArrayList,
+            HexFormat.of().parseHex("00000003800000007fffffff00000000")},
+        {new LongArrayList(), ObjectType.LongArrayList, 
HexFormat.of().parseHex("00000000")},
+        {LongArrayList.wrap(new long[]{Long.MIN_VALUE, Long.MAX_VALUE, 0, 99}, 
3), ObjectType.LongArrayList,
+            
HexFormat.of().parseHex("0000000380000000000000007fffffffffffffff0000000000000000")},
+        {new FloatArrayList(), ObjectType.FloatArrayList, 
HexFormat.of().parseHex("00000000")},
+        {FloatArrayList.wrap(new float[]{Float.intBitsToFloat(0x7fa12345), 
-0.0f, Float.POSITIVE_INFINITY,
+            Float.NEGATIVE_INFINITY, 99}, 4), ObjectType.FloatArrayList,
+            
HexFormat.of().parseHex("000000047fa12345800000007f800000ff800000")},
+        {new DoubleArrayList(), ObjectType.DoubleArrayList, 
HexFormat.of().parseHex("00000000")},
+        {DoubleArrayList.wrap(new 
double[]{Double.longBitsToDouble(0x7ff0123456789abcL), -0.0d,
+            Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, 99}, 4), 
ObjectType.DoubleArrayList,
+            
HexFormat.of().parseHex("000000047ff0123456789abc80000000000000007ff0000000000000fff0000000000000")},
+        {UltraLogLog.create(3), ObjectType.UltraLogLog, 
HexFormat.of().parseHex("030000000000000000")},
+        {UltraLogLog.wrap(new byte[]{0, 1, 2, 3, 4, 5, 6, 7}), 
ObjectType.UltraLogLog,
+            HexFormat.of().parseHex("030001020304050607")},
+        {new RoaringBitmap(), ObjectType.RoaringBitmap, 
HexFormat.of().parseHex("3a30000000000000")},
+        {RoaringBitmap.bitmapOf(1, 3), ObjectType.RoaringBitmap,
+            
HexFormat.of().parseHex("3a30000001000000000001001000000001000300")},
+        {runBitmap, ObjectType.RoaringBitmap, 
RoaringBitmapUtils.serialize(runBitmap)}
+    };
+  }
+
+  @Test(dataProvider = "directSerializationValues")
+  public void testSerializeIntoBuffer(Object value, ObjectType type, byte[] 
expected) {
+    int typeId = type.getValue();
+    Object inputState = copyInputState(value);
+    assertEquals(ObjectSerDeUtils.getSerializedSize(value, typeId), 
expected.length);
+    assertEquals(ObjectSerDeUtils.serialize(value, typeId), expected);
+    for (boolean direct : new boolean[]{false, true}) {
+      for (ByteOrder order : new ByteOrder[]{ByteOrder.BIG_ENDIAN, 
ByteOrder.LITTLE_ENDIAN}) {
+        for (int extraRemaining : new int[]{0, 5}) {
+          ByteBuffer destination = direct ? 
ByteBuffer.allocateDirect(expected.length + 10)
+              : ByteBuffer.allocate(expected.length + 10);
+          while (destination.hasRemaining()) {
+            destination.put((byte) 0x5a);
+          }
+          destination.position(5).limit(5 + expected.length + extraRemaining);
+          destination.order(order);
+          ObjectSerDeUtils.serialize(value, typeId, destination);
+          assertEquals(destination.position(), 5 + expected.length);
+          assertEquals(destination.limit(), 5 + expected.length + 
extraRemaining);
+          assertSame(destination.order(), order);
+          destination.clear();
+          for (int i = 0; i < 5; i++) {
+            assertEquals(destination.get(), (byte) 0x5a);
+          }
+          byte[] actual = new byte[expected.length];
+          destination.get(actual);
+          assertEquals(actual, expected);
+          while (destination.hasRemaining()) {
+            assertEquals(destination.get(), (byte) 0x5a);
+          }
+        }
+      }
+    }
+    Object decoded = ObjectSerDeUtils.deserialize(expected, type);
+    assertEquals(ObjectSerDeUtils.serialize(decoded, typeId), expected);
+    assertEquals(copyInputState(value), inputState);
+    assertEquals(ObjectSerDeUtils.getSerializedSize(value, typeId), 
expected.length);
+    assertEquals(ObjectSerDeUtils.serialize(value, typeId), expected);
+  }
+
+  @Test(dataProvider = "directSerializationValues")
+  public void testSerializeIntoInvalidBuffer(Object value, ObjectType type, 
byte[] expected) {
+    ByteBuffer tooSmall = ByteBuffer.allocate(expected.length + 
10).order(ByteOrder.LITTLE_ENDIAN);
+    Arrays.fill(tooSmall.array(), (byte) 0x5a);
+    byte[] before = tooSmall.array().clone();
+    tooSmall.position(5).limit(5 + expected.length - 1);
+    assertThrows(BufferOverflowException.class, () -> 
ObjectSerDeUtils.serialize(value, type.getValue(), tooSmall));
+    assertEquals(tooSmall.position(), 5);
+    assertSame(tooSmall.order(), ByteOrder.LITTLE_ENDIAN);
+    assertEquals(tooSmall.array(), before);
+
+    ByteBuffer readOnly = ByteBuffer.allocateDirect(expected.length + 
5).asReadOnlyBuffer();
+    readOnly.position(5).order(ByteOrder.LITTLE_ENDIAN);
+    assertThrows(ReadOnlyBufferException.class, () -> 
ObjectSerDeUtils.serialize(value, type.getValue(), readOnly));
+    assertEquals(readOnly.position(), 5);
+    assertSame(readOnly.order(), ByteOrder.LITTLE_ENDIAN);
+  }
+
+  @Test
+  public void testDefaultBufferSerialization() {
+    int[] serializationCount = {0};
+    ObjectSerDe<Integer> codec = new ObjectSerDe<>() {
+      @Override
+      public byte[] serialize(Integer value) {
+        serializationCount[0]++;
+        return new byte[]{value.byteValue()};
+      }
+
+      @Override
+      public Integer deserialize(byte[] bytes) {
+        return (int) bytes[0];
+      }
+
+      @Override
+      public Integer deserialize(ByteBuffer byteBuffer) {
+        return (int) byteBuffer.get();
+      }
+    };
+    assertEquals(codec.getSerializedSize(42), -1);
+    assertEquals(serializationCount[0], 0);
+    ByteBuffer destination = 
ByteBuffer.allocateDirect(3).order(ByteOrder.LITTLE_ENDIAN);
+    destination.position(1).limit(2);
+    codec.serialize(42, destination);
+    assertEquals(serializationCount[0], 1);
+    assertEquals(destination.position(), 2);
+    assertEquals(destination.limit(), 2);
+    assertSame(destination.order(), ByteOrder.LITTLE_ENDIAN);
+    assertEquals(destination.get(1), (byte) 42);
+
+    assertEquals(ObjectSerDeUtils.getSerializedSize("abc", 
ObjectType.String.getValue()), -1);
+    ByteBuffer stringDestination = ByteBuffer.allocate(3);
+    ObjectSerDeUtils.serialize("abc", ObjectType.String.getValue(), 
stringDestination);
+    assertEquals(stringDestination.array(), new byte[]{'a', 'b', 'c'});
+  }
+
+  private static Object copyInputState(Object value) {
+    if (value instanceof IntArrayList list) {
+      return list.elements().clone();
+    }
+    if (value instanceof LongArrayList list) {
+      return list.elements().clone();
+    }
+    if (value instanceof FloatArrayList list) {
+      float[] elements = list.elements();
+      int[] bits = new int[elements.length];
+      for (int i = 0; i < elements.length; i++) {
+        bits[i] = Float.floatToRawIntBits(elements[i]);
+      }
+      return bits;
+    }
+    if (value instanceof DoubleArrayList list) {
+      double[] elements = list.elements();
+      long[] bits = new long[elements.length];
+      for (int i = 0; i < elements.length; i++) {
+        bits[i] = Double.doubleToRawLongBits(elements[i]);
+      }
+      return bits;
+    }
+    if (value instanceof UltraLogLog sketch) {
+      return sketch.getState().clone();
+    }
+    return ((RoaringBitmap) value).clone();
+  }
+
   @Test
   public void testString() {
     for (int i = 0; i < NUM_ITERATIONS; i++) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to