Arawoof06 commented on code in PR #1364:
URL: https://github.com/apache/arrow-java/pull/1364#discussion_r4304112151


##########
vector/src/main/java/org/apache/arrow/vector/util/VectorAppender.java:
##########
@@ -153,10 +153,9 @@ public ValueVector visit(BaseVariableWidthVector 
deltaVector, Void value) {
         targetVector.getValidityBuffer());
 
     // append data buffer
-    MemoryUtil.copyMemory(
-        deltaVector.getDataBuffer().memoryAddress() + deltaDataStart,
-        targetVector.getDataBuffer().memoryAddress() + targetDataSize,
-        deltaDataSize);
+    targetVector

Review Comment:
   Agreed. Added a `validateDelta` pass at the top of both visits that runs 
before any realloc or `concatBits`, so a rejected delta now leaves the target 
as it was. The test checks that too (see below).



##########
vector/src/main/java/org/apache/arrow/vector/util/VectorAppender.java:
##########
@@ -153,10 +153,9 @@ public ValueVector visit(BaseVariableWidthVector 
deltaVector, Void value) {
         targetVector.getValidityBuffer());
 
     // append data buffer
-    MemoryUtil.copyMemory(
-        deltaVector.getDataBuffer().memoryAddress() + deltaDataStart,
-        targetVector.getDataBuffer().memoryAddress() + targetDataSize,
-        deltaDataSize);
+    targetVector

Review Comment:
   Yes, worth it. `validateDelta` now checks the validity buffer against 
`getValidityBufferSizeFromCount(valueCount)` and the offset buffer against 
`(valueCount + 1) * OFFSET_WIDTH` before anything reads them, so `concatBits`, 
the offset `copyMemory` and the rebasing loop only run on buffers that cover 
the value count.



##########
vector/src/main/java/org/apache/arrow/vector/util/VectorAppender.java:
##########
@@ -153,10 +153,9 @@ public ValueVector visit(BaseVariableWidthVector 
deltaVector, Void value) {
         targetVector.getValidityBuffer());
 
     // append data buffer
-    MemoryUtil.copyMemory(
-        deltaVector.getDataBuffer().memoryAddress() + deltaDataStart,
-        targetVector.getDataBuffer().memoryAddress() + targetDataSize,
-        deltaDataSize);
+    targetVector
+        .getDataBuffer()
+        .setBytes(targetDataSize, deltaVector.getDataBuffer(), deltaDataStart, 
deltaDataSize);

Review Comment:
   Done. It walks the offsets once and requires `0 <= first`, non-decreasing 
offsets throughout, and last `<=` data buffer capacity, so intermediate offsets 
are covered too. `targetDataSize + deltaDataSize` uses `Math.addExact` now.



##########
vector/src/main/java/org/apache/arrow/vector/util/VectorAppender.java:
##########
@@ -229,10 +228,9 @@ public ValueVector visit(BaseLargeVariableWidthVector 
deltaVector, Void value) {
         targetVector.getValidityBuffer());
 
     // append data buffer
-    MemoryUtil.copyMemory(
-        deltaVector.getDataBuffer().memoryAddress() + deltaDataStart,
-        targetVector.getDataBuffer().memoryAddress() + targetDataSize,
-        deltaDataSize);
+    targetVector

Review Comment:
   Same changes on the large path, with long offsets and `Math.addExact(long, 
long)`.



##########
vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowReaderWriter.java:
##########
@@ -649,6 +649,66 @@ public void testDeltaDictionary() throws Exception {
     AutoCloseables.close(closeableList);
   }
 
+  @Test
+  public void testDeltaDictionaryOffsetsExceedDataBuffer() throws Exception {
+    try (VarCharVector dictVector = newVarCharVector("dict", allocator)) {
+      setVector(
+          dictVector,
+          "foo".getBytes(StandardCharsets.UTF_8),
+          "bar".getBytes(StandardCharsets.UTF_8));
+      Dictionary dictionary = new Dictionary(dictVector, new 
DictionaryEncoding(1L, false, null));
+      DictionaryProvider.MapDictionaryProvider provider =
+          new DictionaryProvider.MapDictionaryProvider();
+      provider.put(dictionary);
+      Field encodedField =
+          new Field(
+              "f",
+              new FieldType(true, new ArrowType.Int(32, true), 
dictionary.getEncoding()),
+              null);
+      Schema schema =
+          new Schema(
+              Collections.singletonList(
+                  DictionaryUtility.toMessageFormat(encodedField, provider, 
new HashSet<>())));
+
+      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+      WriteChannel out = new WriteChannel(newChannel(outStream));
+      MessageSerializer.serialize(out, schema);
+
+      List<AutoCloseable> closeableList = new ArrayList<>();
+      serializeDictionaryBatch(out, dictionary, false, closeableList);
+
+      // delta dictionary whose offsets point far past its 8-byte data buffer
+      ArrowBuf validity = allocator.buffer(0);
+      ArrowBuf offsets = allocator.buffer(12);
+      offsets.setInt(0, 0);
+      offsets.setInt(4, 4);
+      offsets.setInt(8, 4096);
+      offsets.writerIndex(12);
+      ArrowBuf data = allocator.buffer(8);
+      data.setBytes(0, "bazzquxx".getBytes(StandardCharsets.UTF_8));
+      data.writerIndex(8);
+      ArrowRecordBatch deltaBatch =
+          new ArrowRecordBatch(
+              2,
+              Collections.singletonList(new ArrowFieldNode(2, 0)),
+              Arrays.asList(validity, offsets, data));
+      ArrowDictionaryBatch deltaDictionaryBatch = new ArrowDictionaryBatch(1L, 
deltaBatch, true);
+      MessageSerializer.serialize(out, deltaDictionaryBatch);
+      closeableList.add(deltaDictionaryBatch);
+      AutoCloseables.close(validity, offsets, data);
+
+      // write eos
+      out.writeIntLittleEndian(0);
+
+      try (ArrowStreamReader reader =
+          new ArrowStreamReader(
+              new 
ByteArrayReadableSeekableByteChannel(outStream.toByteArray()), allocator)) {

Review Comment:
   The new checks are explicit capacity/offset comparisons rather than relying 
on `ArrowBuf` bounds checking, so they apply with 
`arrow.enable_unsafe_memory_access=true` as well, and I left the test 
unconditional instead of adding an `assumeTrue`. After the failure it now 
asserts the dictionary still has 2 values, `foo` and `bar`, and that validity 
bits 2 and 3 are unset. With only the earlier `setBytes` change, that validity 
assertion fails.



##########
vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowReaderWriter.java:
##########
@@ -649,6 +649,66 @@ public void testDeltaDictionary() throws Exception {
     AutoCloseables.close(closeableList);
   }
 
+  @Test
+  public void testDeltaDictionaryOffsetsExceedDataBuffer() throws Exception {
+    try (VarCharVector dictVector = newVarCharVector("dict", allocator)) {
+      setVector(
+          dictVector,
+          "foo".getBytes(StandardCharsets.UTF_8),
+          "bar".getBytes(StandardCharsets.UTF_8));
+      Dictionary dictionary = new Dictionary(dictVector, new 
DictionaryEncoding(1L, false, null));
+      DictionaryProvider.MapDictionaryProvider provider =
+          new DictionaryProvider.MapDictionaryProvider();
+      provider.put(dictionary);
+      Field encodedField =
+          new Field(
+              "f",
+              new FieldType(true, new ArrowType.Int(32, true), 
dictionary.getEncoding()),
+              null);
+      Schema schema =
+          new Schema(
+              Collections.singletonList(
+                  DictionaryUtility.toMessageFormat(encodedField, provider, 
new HashSet<>())));
+
+      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+      WriteChannel out = new WriteChannel(newChannel(outStream));
+      MessageSerializer.serialize(out, schema);
+
+      List<AutoCloseable> closeableList = new ArrayList<>();
+      serializeDictionaryBatch(out, dictionary, false, closeableList);
+
+      // delta dictionary whose offsets point far past its 8-byte data buffer
+      ArrowBuf validity = allocator.buffer(0);
+      ArrowBuf offsets = allocator.buffer(12);
+      offsets.setInt(0, 0);
+      offsets.setInt(4, 4);
+      offsets.setInt(8, 4096);
+      offsets.writerIndex(12);
+      ArrowBuf data = allocator.buffer(8);
+      data.setBytes(0, "bazzquxx".getBytes(StandardCharsets.UTF_8));
+      data.writerIndex(8);
+      ArrowRecordBatch deltaBatch =
+          new ArrowRecordBatch(
+              2,
+              Collections.singletonList(new ArrowFieldNode(2, 0)),
+              Arrays.asList(validity, offsets, data));
+      ArrowDictionaryBatch deltaDictionaryBatch = new ArrowDictionaryBatch(1L, 
deltaBatch, true);
+      MessageSerializer.serialize(out, deltaDictionaryBatch);
+      closeableList.add(deltaDictionaryBatch);
+      AutoCloseables.close(validity, offsets, data);
+
+      // write eos
+      out.writeIntLittleEndian(0);
+
+      try (ArrowStreamReader reader =
+          new ArrowStreamReader(
+              new 
ByteArrayReadableSeekableByteChannel(outStream.toByteArray()), allocator)) {

Review Comment:
   Added. The malformed case is parameterized over VarChar and LargeVarChar and 
over four offset shapes: past the data buffer, decreasing intermediate offset, 
last before first, and negative start. There is also a positive test for both 
types with offsets `{3, 6, 9}` over `xxxbazqux`, which expects `foo, bar, baz, 
qux`.



##########
vector/src/test/java/org/apache/arrow/vector/ipc/TestArrowReaderWriter.java:
##########
@@ -649,6 +649,66 @@ public void testDeltaDictionary() throws Exception {
     AutoCloseables.close(closeableList);
   }
 
+  @Test
+  public void testDeltaDictionaryOffsetsExceedDataBuffer() throws Exception {
+    try (VarCharVector dictVector = newVarCharVector("dict", allocator)) {
+      setVector(
+          dictVector,
+          "foo".getBytes(StandardCharsets.UTF_8),
+          "bar".getBytes(StandardCharsets.UTF_8));
+      Dictionary dictionary = new Dictionary(dictVector, new 
DictionaryEncoding(1L, false, null));
+      DictionaryProvider.MapDictionaryProvider provider =
+          new DictionaryProvider.MapDictionaryProvider();
+      provider.put(dictionary);
+      Field encodedField =
+          new Field(
+              "f",
+              new FieldType(true, new ArrowType.Int(32, true), 
dictionary.getEncoding()),
+              null);
+      Schema schema =
+          new Schema(
+              Collections.singletonList(
+                  DictionaryUtility.toMessageFormat(encodedField, provider, 
new HashSet<>())));
+
+      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+      WriteChannel out = new WriteChannel(newChannel(outStream));
+      MessageSerializer.serialize(out, schema);
+
+      List<AutoCloseable> closeableList = new ArrayList<>();
+      serializeDictionaryBatch(out, dictionary, false, closeableList);
+
+      // delta dictionary whose offsets point far past its 8-byte data buffer
+      ArrowBuf validity = allocator.buffer(0);
+      ArrowBuf offsets = allocator.buffer(12);
+      offsets.setInt(0, 0);
+      offsets.setInt(4, 4);
+      offsets.setInt(8, 4096);
+      offsets.writerIndex(12);
+      ArrowBuf data = allocator.buffer(8);
+      data.setBytes(0, "bazzquxx".getBytes(StandardCharsets.UTF_8));
+      data.writerIndex(8);
+      ArrowRecordBatch deltaBatch =
+          new ArrowRecordBatch(
+              2,
+              Collections.singletonList(new ArrowFieldNode(2, 0)),
+              Arrays.asList(validity, offsets, data));
+      ArrowDictionaryBatch deltaDictionaryBatch = new ArrowDictionaryBatch(1L, 
deltaBatch, true);
+      MessageSerializer.serialize(out, deltaDictionaryBatch);
+      closeableList.add(deltaDictionaryBatch);
+      AutoCloseables.close(validity, offsets, data);
+
+      // write eos
+      out.writeIntLittleEndian(0);
+
+      try (ArrowStreamReader reader =
+          new ArrowStreamReader(
+              new 
ByteArrayReadableSeekableByteChannel(outStream.toByteArray()), allocator)) {
+        assertThrows(IndexOutOfBoundsException.class, reader::loadNextBatch);
+      }
+      AutoCloseables.close(closeableList);

Review Comment:
   Fixed. The stream setup moved into a helper that closes everything in a 
`finally`, and each test only holds the reader in try-with-resources.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to