Skip to content

Commit 4b55c8c

Browse files
committed
Fix ByteBuffer handling in VariantUtil and VariantBuilder
1 parent 4c8f4d4 commit 4b55c8c

4 files changed

Lines changed: 48 additions & 2 deletions

File tree

parquet-variant/src/main/java/org/apache/parquet/variant/VariantBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ public void appendBinary(ByteBuffer binary) {
374374
writePos += 1;
375375
VariantUtil.writeLong(writeBuffer, writePos, binarySize, VariantUtil.U32_SIZE);
376376
writePos += VariantUtil.U32_SIZE;
377-
ByteBuffer.wrap(writeBuffer, writePos, binarySize).put(binary);
377+
ByteBuffer.wrap(writeBuffer, writePos, binarySize).put(binary.duplicate());
378378
writePos += binarySize;
379379
}
380380

parquet-variant/src/main/java/org/apache/parquet/variant/VariantUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -843,7 +843,7 @@ static HashMap<String, Integer> getMetadataMap(ByteBuffer metadata) {
843843
} else {
844844
// ByteBuffer does not have an array, so we need to use the `get` method to read the bytes.
845845
byte[] metadataArray = new byte[nextOffset - offset];
846-
slice(metadata, stringStart + offset).get(metadataArray);
846+
slice(metadata, pos + stringStart + offset).get(metadataArray);
847847
result.put(new String(metadataArray), id);
848848
}
849849
offset = nextOffset;

parquet-variant/src/test/java/org/apache/parquet/variant/TestVariantObject.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,4 +341,39 @@ public void testInvalidObject() {
341341
Assert.assertEquals("Cannot read ARRAY value as OBJECT", e.getMessage());
342342
}
343343
}
344+
345+
@Test
346+
public void testMetadataWithNonZeroPositionReadOnly() {
347+
// Build a variant with object fields to populate the metadata dictionary
348+
VariantBuilder vb = new VariantBuilder();
349+
VariantObjectBuilder obj = vb.startObject();
350+
obj.appendKey("name");
351+
obj.appendString("Alice");
352+
obj.appendKey("age");
353+
obj.appendInt(30);
354+
vb.endObject();
355+
Variant variant = vb.build();
356+
357+
// Get the raw metadata bytes
358+
ByteBuffer metaBuf = variant.getMetadataBuffer();
359+
byte[] metaBytes = new byte[metaBuf.remaining()];
360+
metaBuf.duplicate().get(metaBytes);
361+
362+
// Embed in a larger buffer with a non-zero position, then make read-only
363+
// to force the else-branch in getMetadataMap.
364+
byte[] padded = new byte[10 + metaBytes.length];
365+
System.arraycopy(metaBytes, 0, padded, 10, metaBytes.length);
366+
ByteBuffer offsetMetadata = ByteBuffer.wrap(padded);
367+
offsetMetadata.position(10);
368+
offsetMetadata.limit(10 + metaBytes.length);
369+
offsetMetadata = offsetMetadata.asReadOnlyBuffer();
370+
371+
// ImmutableMetadata calls getMetadataMap, which had the bug.
372+
// getMetadataMap builds a key->id dictionary from the metadata buffer.
373+
// With a non-zero position and read-only buffer, the else-branch is taken,
374+
// which previously used the wrong offset.
375+
ImmutableMetadata immutableMetadata = new ImmutableMetadata(offsetMetadata);
376+
Assert.assertEquals(0, immutableMetadata.getOrInsert("name"));
377+
Assert.assertEquals(1, immutableMetadata.getOrInsert("age"));
378+
}
344379
}

parquet-variant/src/test/java/org/apache/parquet/variant/TestVariantScalarBuilder.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,17 @@ public void testBinaryBuilder() {
387387
}
388388
}
389389

390+
@Test
391+
public void testBinaryBuilderDoesNotMutateCallerBuffer() {
392+
ByteBuffer buf = ByteBuffer.wrap(new byte[] {0, 1, 2, 3});
393+
int positionBefore = buf.position();
394+
int remainingBefore = buf.remaining();
395+
VariantBuilder vb = new VariantBuilder();
396+
vb.appendBinary(buf);
397+
Assert.assertEquals(positionBefore, buf.position());
398+
Assert.assertEquals(remainingBefore, buf.remaining());
399+
}
400+
390401
@Test
391402
public void testStringBuilder() {
392403
IntStream.range(VariantUtil.MAX_SHORT_STR_SIZE - 3, VariantUtil.MAX_SHORT_STR_SIZE + 3)

0 commit comments

Comments
 (0)