rayokota opened a new issue, #420:
URL: https://github.com/apache/arrow-dotnet/issues/420

   ### Describe the bug, including details regarding any error messages, 
version, and platform.
   
   ### Describe the bug
   
   `VariantEncodingHelper` writes and reads the Variant object value header with
   `field_id_size_minus_one` and `field_offset_size_minus_one` in each other's 
bit positions.
   
   The Variant spec (`apache/parquet-format`, `VariantEncoding.md`) defines the 
object
   `value_header` — the 6 bits above the 2 basic-type bits — as:
   
   ```
                     5   4  3     2 1     0
                   +---+---+-------+-------+
   value_header    | R |   |       |       |
                   +---+---+-------+-------+
                         ^     ^       ^
                         |     |       +-- field_offset_size_minus_one
                         |     +-- field_id_size_minus_one
                         +-- is_large
   ```
   
   That is: bits 0-1 hold `field_offset_size_minus_one`, bits 2-3 hold 
`field_id_size_minus_one`.
   
   `src/Apache.Arrow.Scalars/Variant/VariantEncodingHelper.cs` has them 
inverted, in both
   directions:
   ```csharp
   // MakeObjectHeader, lines 121-122
   ((fieldIdSize - 1) & 0x03) |          // bits 0-1  — spec: field_offset_size
   (((offsetSize - 1) & 0x03) << 2) |    // bits 2-3  — spec: field_id_size
   
   // ParseObjectHeader, lines 133-134
   fieldIdSize = (valueHeader & 0x03) + 1;
   offsetSize  = ((valueHeader >> 2) & 0x03) + 1;
   ```
   
   The explanatory comment at lines 107-108 documents the same swapped layout, 
so the whole
   block is internally consistent — it is not a typo in a single expression.
   
   `is_large` (bit 4) is correct. `MakeArrayHeader`/`ParseArrayHeader` and the 
metadata header
   helpers were checked and match the spec. This affects the object header only.
   
   ### Impact
   
   Reader and writer share the inverted convention, so arrow-dotnet round-trips 
its own output
   correctly. The bug is only observable across implementations, and only when
   `fieldIdSize != offsetSize` — when the two are equal, swapping them is a 
no-op.
   
   Those sizes are computed independently in `VariantValueWriter.cs:693-695`: 
`fieldIdSize` from
   the maximum field ID, `offsetSize` from the encoded data length. They 
diverge routinely — for
   example an object drawn from a >255-entry metadata dictionary (2-byte field 
IDs) whose own
   field data is under 256 bytes (1-byte offsets).
   
   Consequences:
   
   - Objects written by arrow-dotnet with differing sizes are misparsed by any 
spec-compliant
     reader (parquet-java, parquet-mr, Spark, arrow-rs).
   - Objects written by those implementations are misparsed by arrow-dotnet.
   - The failure is silent: field IDs and offsets are read at the wrong widths, 
so it surfaces as
     garbage field values or out-of-range offsets rather than a clean error.
   
   ### Why the tests do not catch it
   
   `test/Apache.Arrow.Scalars.Tests/VariantEncodingHelperTests.cs:89-98` tests
   `MakeObjectHeader` → `ParseObjectHeader` as a round-trip. Because both sides 
share the
   inverted convention, this test cannot detect a swap; it passes even on its 
asymmetric
   `InlineData(2, 3, false)` case.
   
   Every hand-written object vector in `TestVectors.cs` (lines 281, 295, 313) 
uses header byte
   `0x02`, i.e. `field_id_size=1, offset_size=1` — precisely the degenerate 
case where the swap
   is invisible.
   
   ### Expected behavior
   
   `MakeObjectHeader(fieldIdSize: 2, offsetSize: 1, isLarge: false)` should 
return `0x12`:
   
       field_id_size_minus_one = 1  -> value_header bits 2-3
       offset_size_minus_one   = 0  -> value_header bits 0-1
       value_header = 0b000100 = 0x04
       header byte  = (0x04 << 2) | Object(2) = 0x12
   
   It currently returns `0x06`. `ParseObjectHeader(0x12)` should yield
   `fieldIdSize=2, offsetSize=1`; it currently yields `fieldIdSize=1, 
offsetSize=2`.


-- 
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