This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new d34c5aadd perf(format): cache compact row layout per nested slot
(#3717)
d34c5aadd is described below
commit d34c5aadd165014999cb0161eb384a9166bc459a
Author: Steven Schlansker <[email protected]>
AuthorDate: Sun May 31 19:36:50 2026 -0700
perf(format): cache compact row layout per nested slot (#3717)
## Why?
Avoid redundant re-computation of compact codec offsets layout, saving
cpu and memory allocations
and
fix(format): honor input offset when copying inline-struct bytes
## What does this PR do?
perf(format): cache compact row layout per nested slot
Hoist offsets, widths, and nullability into CompactRowLayout, built
once per encoder. getStruct does no per-call lookup; inline-struct
reads go directly to the parent buffer; the writer reads schema-
derived state from the cache; compact rows skip the unused extData[]
allocation. For List<Struct> fields the parent layout also carries
the element layout so getArray(i) does not rebuild it.
Fix a latent inline-width bug in CompactBinaryRowWriter that was
masked by the prior slicing getStruct: writeUnaligned and
writeAlignedBytes dropped the input offset, so re-emitting a child
row via BinaryWriter.write(int, BinaryRow) copied the wrong bytes.
## Related issues
## AI Contribution Checklist
- [x] Substantial AI assistance was used in this PR: `yes` / `no`
AI Usage Disclosure
- substantial_ai_assistance: yes
- scope: all
- affected_files_or_subsystems: Java row format
- ai_review: :heavy_check_mark:
- ai_review_artifacts:
> One commit, perf(format): cache compact row layout per nested slot.
Caches fixedOffsets, per-field fixedWidths, allFieldsNotNullable, and
bitmapWidthInBytes in a new CompactRowLayout value, computed once per
array (element type is fixed) or memoized per parent extData slot.
> The getStruct hot path previously rebuilt fixedOffsets and re-walked
allNotNullable on every call.
> Recommendation: Approve. Numbers in the commit message match a real
bottleneck (the int[] + allNotNullable walk per nested decode), the
implementation preserves the existing wire behavior and ownership model,
no public API regression, all format tests pass, and the new caching
follows the existing extData-slot pattern rather than inventing a new
mechanism.
> — Reviewed by Claude (Opus 4.7), 2026-05-29
> Skill used: I ran this through the fory-code-review skill, which
delegated the diff inspection to a read-only review subagent loaded with
the Fory review checklist, the lesson-derived red flags, and the Java
runtime rules.
> Verdict
> No blockers. Recommend submission. Both commits are coherent and
tested:
> - d5cbdc2b is a real, narrow bugfix: the three put/copyFrom(..., 0,
numBytes) sites in CompactBinaryRowWriter were copying from the start of
the source buffer instead of from baseOffset. After schema sorting,
nested inline structs land at non-zero offsets, so this corrupted writes
for any non-trivial schema.
CompactCodecTest.testCopyInlineStructViaWriter exercises that exact
failure mode. Wire format unchanged — only the source byte range was
wrong.
> - 2b1825d7 precomputes a CompactRowLayout once per writer instead of
recomputing field offsets and nested layouts on every write. Cache is
per-CompactBinaryRowWriter instance, no map, no growth, no thread-safety
surface (writers are already not thread-safe). CompactRowLayoutTest pins
identity via assertSame at multiple nesting levels and covers the
allFieldsNotNullable bitmap-skip path. CompactBinaryRow.getStruct also
drops a per-call buffer.slice() allocation by pointing to the parent
buffer at the right offset instead.
- human_verification: :heavy_check_mark:
- performance_verification: :heavy_check_mark:
- provenance_license_confirmation: :heavy_check_mark:
## Does this PR introduce any user-facing change?
Compact codec user enjoys more CPU and memory on business logic without
even realizing it.
No public API or wire changes.
## Benchmark
```
RowFormatAllocationProbe bytes/op vs apache/main, compact mode,
median of five:
root 100208 -> 74305 (-25.9%)
array 9032 -> 6629 (-26.6%)
matrix 72005 -> 52760 (-26.7%)
map 7744 -> 6401 (-17.3%)
encode 55423 -> 41762 (-24.6%)
Standard mode unchanged within noise.
```
---------
Co-authored-by: Claude (on behalf of Steven Schlansker)
<[email protected]>
---
.../fory/format/encoder/BinaryRowEncoder.java | 5 +-
.../fory/format/encoder/CompactCodecFormat.java | 7 -
.../fory/format/encoder/DefaultCodecFormat.java | 6 -
.../org/apache/fory/format/encoder/Encoding.java | 3 -
.../fory/format/encoder/RowCodecBuilder.java | 2 +-
.../apache/fory/format/row/binary/BinaryArray.java | 3 +-
.../apache/fory/format/row/binary/BinaryRow.java | 8 +
.../fory/format/row/binary/CompactBinaryArray.java | 43 ++--
.../fory/format/row/binary/CompactBinaryRow.java | 69 +++---
.../fory/format/row/binary/CompactRowLayout.java | 73 +++++++
.../row/binary/writer/BaseBinaryRowWriter.java | 2 +-
.../format/row/binary/writer/BinaryRowWriter.java | 2 +-
.../row/binary/writer/CompactBinaryRowWriter.java | 79 +++----
.../encoder/BinaryRowEncoderPointToTest.java | 3 +-
.../fory/format/encoder/CompactCodecTest.java | 26 +++
.../format/row/binary/CompactRowLayoutTest.java | 232 +++++++++++++++++++++
16 files changed, 433 insertions(+), 130 deletions(-)
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/BinaryRowEncoder.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/BinaryRowEncoder.java
index 296ee546f..7cafa0ab2 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/BinaryRowEncoder.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/BinaryRowEncoder.java
@@ -29,7 +29,6 @@ import org.apache.fory.memory.MemoryUtils;
class BinaryRowEncoder<T> implements RowEncoder<T> {
private final Schema schema;
- private final Encoding codecFactory;
private final GeneratedRowEncoder codec;
private final BaseBinaryRowWriter writer;
private final boolean sizeEmbedded;
@@ -38,12 +37,10 @@ class BinaryRowEncoder<T> implements RowEncoder<T> {
BinaryRowEncoder(
final Schema schema,
- final Encoding codecFactory,
final GeneratedRowEncoder codec,
final BaseBinaryRowWriter writer,
final boolean sizeEmbedded) {
this.schema = schema;
- this.codecFactory = codecFactory;
this.codec = codec;
this.writer = writer;
this.sizeEmbedded = sizeEmbedded;
@@ -82,7 +79,7 @@ class BinaryRowEncoder<T> implements RowEncoder<T> {
schema, schemaHash, peerSchemaHash));
}
final int rowSize = size - 8;
- final BinaryRow row = codecFactory.newRow(schema);
+ final BinaryRow row = writer.newRow();
row.pointTo(buffer, buffer.readerIndex(), rowSize);
buffer.increaseReaderIndex(rowSize);
return fromRow(row);
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/CompactCodecFormat.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/CompactCodecFormat.java
index 4c18e9679..c92be822b 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/CompactCodecFormat.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/CompactCodecFormat.java
@@ -23,10 +23,8 @@ import java.util.Collection;
import java.util.Map;
import org.apache.fory.format.row.binary.BinaryArray;
import org.apache.fory.format.row.binary.BinaryMap;
-import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.row.binary.CompactBinaryArray;
import org.apache.fory.format.row.binary.CompactBinaryMap;
-import org.apache.fory.format.row.binary.CompactBinaryRow;
import org.apache.fory.format.row.binary.writer.BaseBinaryRowWriter;
import org.apache.fory.format.row.binary.writer.BinaryArrayWriter;
import org.apache.fory.format.row.binary.writer.CompactBinaryArrayWriter;
@@ -76,11 +74,6 @@ enum CompactCodecFormat implements Encoding {
return new CompactMapEncoderBuilder(mapType, beanToken);
}
- @Override
- public BinaryRow newRow(final Schema schema) {
- return new CompactBinaryRow(schema);
- }
-
@Override
public BinaryArray newArray(final Field field) {
return new CompactBinaryArray(field);
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/DefaultCodecFormat.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/DefaultCodecFormat.java
index 78f42e69b..8ee0f3a8f 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/DefaultCodecFormat.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/DefaultCodecFormat.java
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.Map;
import org.apache.fory.format.row.binary.BinaryArray;
import org.apache.fory.format.row.binary.BinaryMap;
-import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.row.binary.writer.BaseBinaryRowWriter;
import org.apache.fory.format.row.binary.writer.BinaryArrayWriter;
import org.apache.fory.format.row.binary.writer.BinaryRowWriter;
@@ -72,11 +71,6 @@ enum DefaultCodecFormat implements Encoding {
return new MapEncoderBuilder(mapType, beanToken);
}
- @Override
- public BinaryRow newRow(final Schema schema) {
- return new BinaryRow(schema);
- }
-
@Override
public BinaryArray newArray(final Field field) {
return new BinaryArray(field);
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/Encoding.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/Encoding.java
index 56217ef5b..c28b4d3b1 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/Encoding.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/Encoding.java
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.Map;
import org.apache.fory.format.row.binary.BinaryArray;
import org.apache.fory.format.row.binary.BinaryMap;
-import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.row.binary.writer.BaseBinaryRowWriter;
import org.apache.fory.format.row.binary.writer.BinaryArrayWriter;
import org.apache.fory.format.type.Field;
@@ -47,8 +46,6 @@ interface Encoding {
MapEncoderBuilder newMapEncoder(TypeRef<? extends Map<?, ?>> mapType,
TypeRef<?> beanToken);
- BinaryRow newRow(Schema schema);
-
BinaryArray newArray(Field field);
BinaryMap newMap(Field field);
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowCodecBuilder.java
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowCodecBuilder.java
index b2f6b6538..eeb624d70 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowCodecBuilder.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/encoder/RowCodecBuilder.java
@@ -63,7 +63,7 @@ public class RowCodecBuilder<T> extends
BaseCodecBuilder<RowCodecBuilder<T>> {
@Override
public RowEncoder<T> apply(final BaseBinaryRowWriter writer) {
return new BinaryRowEncoder<T>(
- schema, codecFormat, rowEncoderFactory.apply(writer), writer,
sizeEmbedded);
+ schema, rowEncoderFactory.apply(writer), writer, sizeEmbedded);
}
};
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryArray.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryArray.java
index fdb85c8f2..6d65c5339 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryArray.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryArray.java
@@ -58,12 +58,13 @@ public class BinaryArray extends UnsafeTrait implements
ArrayData {
public BinaryArray(Field field) {
this(field, elementSize(field));
+ initializeExtData(1); // Only require at most one slot to cache the schema
for array type.
}
+ /** Skips {@code extData}; subclass must override {@code getStruct(int,
Field, int)}. */
protected BinaryArray(Field field, int elementSize) {
this.field = field;
this.elementSize = elementSize;
- initializeExtData(1); // Only require at most one slot to cache the schema
for array type.
}
private static int elementSize(Field field) {
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryRow.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryRow.java
index dc099913b..fdac982a2 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryRow.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/BinaryRow.java
@@ -76,6 +76,14 @@ public class BinaryRow extends UnsafeTrait implements Row {
initializeExtData(numFields);
}
+ /** Skips {@code extData}; subclass must override {@code getStruct(int,
Field, int)}. */
+ protected BinaryRow(Schema schema, int bitmapWidthInBytes) {
+ this.schema = schema;
+ this.numFields = schema.numFields();
+ Preconditions.checkArgument(numFields > 0);
+ this.bitmapWidthInBytes = bitmapWidthInBytes;
+ }
+
protected int computeBitmapWidthInBytes() {
return BitUtils.calculateBitmapWidthInBytes(numFields);
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryArray.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryArray.java
index 40d10de9c..277c5d788 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryArray.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryArray.java
@@ -24,7 +24,6 @@ import
org.apache.fory.format.row.binary.writer.CompactBinaryArrayWriter;
import org.apache.fory.format.row.binary.writer.CompactBinaryRowWriter;
import org.apache.fory.format.type.DataTypes;
import org.apache.fory.format.type.Field;
-import org.apache.fory.format.type.Schema;
import org.apache.fory.memory.MemoryBuffer;
public class CompactBinaryArray extends BinaryArray {
@@ -33,12 +32,30 @@ public class CompactBinaryArray extends BinaryArray {
private final boolean elementNullable;
private int headerInBytes;
+ /** {@code null} when the element is not a struct. */
+ private final CompactRowLayout elementLayout;
+
+ /** Cold path. Hot reads use {@link #CompactBinaryArray(Field,
CompactRowLayout)}. */
public CompactBinaryArray(final Field field) {
+ this(field, null);
+ }
+
+ /** {@code prebuiltElementLayout} may be {@code null}; built on demand for
struct elements. */
+ CompactBinaryArray(final Field field, final CompactRowLayout
prebuiltElementLayout) {
super(field, CompactBinaryArrayWriter.elementWidth(field));
DataTypes.ListType listType = (DataTypes.ListType) field.type();
elementField = listType.valueField();
fixedWidth = CompactBinaryRowWriter.fixedWidthFor(elementField);
elementNullable = elementField.nullable();
+ if (elementField.type() instanceof DataTypes.StructType) {
+ elementLayout =
+ prebuiltElementLayout != null
+ ? prebuiltElementLayout
+ : new CompactRowLayout(
+
CompactBinaryRowWriter.sortSchema(DataTypes.createSchema(elementField)));
+ } else {
+ elementLayout = null;
+ }
}
@Override
@@ -105,28 +122,18 @@ public class CompactBinaryArray extends BinaryArray {
return null;
}
assert field == elementField;
+ final CompactBinaryRow row = elementLayout.newRow();
if (fixedWidth == -1) {
- return super.getStruct(ordinal, field, extDataSlot);
- }
- if (extData[extDataSlot] == null) {
- extData[extDataSlot] = newSchema(field);
+ final long offsetAndSize = getInt64(ordinal);
+ final int relativeOffset = (int) (offsetAndSize >> 32);
+ final int size = (int) offsetAndSize;
+ row.pointTo(getBuffer(), getBaseOffset() + relativeOffset, size);
+ } else {
+ row.pointTo(getBuffer(), getOffset(ordinal), fixedWidth);
}
- final BinaryRow row = newRow((Schema) extData[extDataSlot]);
- row.pointTo(getBuffer(), getOffset(ordinal), fixedWidth);
return row;
}
- @Override
- protected Schema newSchema(final Field field) {
- return CompactBinaryRowWriter.sortSchema(super.newSchema(field));
- }
-
- @Override
- protected BinaryRow newRow(final Schema schema) {
- // TODO: don't re-compute fixed offsets
- return new CompactBinaryRow(schema,
CompactBinaryRowWriter.fixedOffsets(schema));
- }
-
@Override
protected BinaryArray newArray(final Field field) {
return new CompactBinaryArray(field);
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryRow.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryRow.java
index 946418b2b..7cdff675f 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryRow.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactBinaryRow.java
@@ -19,8 +19,6 @@
package org.apache.fory.format.row.binary;
-import org.apache.fory.format.row.binary.writer.CompactBinaryRowWriter;
-import org.apache.fory.format.type.DataTypes;
import org.apache.fory.format.type.Field;
import org.apache.fory.format.type.Schema;
import org.apache.fory.memory.MemoryBuffer;
@@ -41,47 +39,39 @@ import org.apache.fory.memory.MemoryBuffer;
* <b>The compact format is still under development and may not be stable
yet.</b>
*/
public class CompactBinaryRow extends BinaryRow {
- private final boolean allFieldsNotNullable;
- private final int[] fixedOffsets;
+ private final CompactRowLayout layout;
private final int bitmapOffset;
public CompactBinaryRow(final Schema schema) {
- this(schema, CompactBinaryRowWriter.fixedOffsets(schema));
+ this(new CompactRowLayout(schema));
}
- public CompactBinaryRow(final Schema schema, final int[] fixedOffsets) {
- super(schema);
- this.fixedOffsets = fixedOffsets;
- bitmapOffset = fixedOffsets[fixedOffsets.length - 1];
- allFieldsNotNullable =
CompactBinaryRowWriter.allNotNullable(schema.fields());
+ CompactBinaryRow(CompactRowLayout layout) {
+ super(layout.schema, layout.bitmapWidthInBytes);
+ this.layout = layout;
+ this.bitmapOffset = layout.fixedOffsets[layout.fixedOffsets.length - 1];
}
- @Override
- protected int computeBitmapWidthInBytes() {
- // cannot use field due to initialization order
- if (CompactBinaryRowWriter.allNotNullable(schema.fields())) {
- return 0;
- }
- return CompactBinaryRowWriter.headerBytes(schema);
+ CompactRowLayout getLayout() {
+ return layout;
}
@Override
public boolean isNullAt(final int ordinal) {
- if (allFieldsNotNullable) {
+ if (layout.allFieldsNotNullable) {
return false;
}
return super.isNullAt(ordinal);
}
- // TODO: this should use StableValue once it's available
@Override
public int getOffset(final int ordinal) {
- return baseOffset + fixedOffsets[ordinal];
+ return baseOffset + layout.fixedOffsets[ordinal];
}
@Override
public MemoryBuffer getBuffer(final int ordinal) {
- final int fixedWidthBinary = CompactBinaryRowWriter.fixedWidthFor(schema,
ordinal);
+ final int fixedWidthBinary = layout.fixedWidths[ordinal];
if (fixedWidthBinary >= 0) {
if (isNullAt(ordinal)) {
return null;
@@ -94,7 +84,7 @@ public class CompactBinaryRow extends BinaryRow {
@Override
public byte[] getBinary(final int ordinal) {
- final int fixedWidthBinary = CompactBinaryRowWriter.fixedWidthFor(schema,
ordinal);
+ final int fixedWidthBinary = layout.fixedWidths[ordinal];
if (fixedWidthBinary >= 0) {
if (isNullAt(ordinal)) {
return null;
@@ -112,27 +102,30 @@ public class CompactBinaryRow extends BinaryRow {
if (isNullAt(ordinal)) {
return null;
}
- final int fixedWidthBinary = CompactBinaryRowWriter.fixedWidthFor(schema,
ordinal);
+ final CompactBinaryRow row = layout.childLayouts[ordinal].newRow();
+ final int fixedWidthBinary = layout.fixedWidths[ordinal];
if (fixedWidthBinary == -1) {
- return super.getStruct(ordinal, field, extDataSlot);
- }
- if (extData[extDataSlot] == null) {
- extData[extDataSlot] = DataTypes.createSchema(field);
+ final long offsetAndSize = getInt64(ordinal);
+ final int relativeOffset = (int) (offsetAndSize >> 32);
+ final int size = (int) offsetAndSize;
+ row.pointTo(getBuffer(), getBaseOffset() + relativeOffset, size);
+ } else {
+ row.pointTo(getBuffer(), getOffset(ordinal), fixedWidthBinary);
}
- final BinaryRow row = newRow((Schema) extData[extDataSlot]);
- row.pointTo(getBuffer().slice(getOffset(ordinal), fixedWidthBinary), 0,
fixedWidthBinary);
return row;
}
@Override
- protected Schema newSchema(final Field field) {
- return CompactBinaryRowWriter.sortSchema(super.newSchema(field));
- }
-
- @Override
- protected BinaryRow newRow(final Schema schema) {
- // TODO: avoid re-computing these offsets
- return new CompactBinaryRow(schema,
CompactBinaryRowWriter.fixedOffsets(schema));
+ BinaryArray getArray(final int ordinal, final Field field) {
+ if (isNullAt(ordinal)) {
+ return null;
+ }
+ final long offsetAndSize = getInt64(ordinal);
+ final int relativeOffset = (int) (offsetAndSize >> 32);
+ final int size = (int) offsetAndSize;
+ final CompactBinaryArray array = new CompactBinaryArray(field,
layout.childLayouts[ordinal]);
+ array.pointTo(getBuffer(), getBaseOffset() + relativeOffset, size);
+ return array;
}
@Override
@@ -152,6 +145,6 @@ public class CompactBinaryRow extends BinaryRow {
@Override
protected BinaryRow rowForCopy() {
- return new CompactBinaryRow(schema, fixedOffsets);
+ return new CompactBinaryRow(layout);
}
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactRowLayout.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactRowLayout.java
new file mode 100644
index 000000000..1fa02b22a
--- /dev/null
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/CompactRowLayout.java
@@ -0,0 +1,73 @@
+/*
+ * 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.fory.format.row.binary;
+
+import java.util.List;
+import org.apache.fory.annotation.Internal;
+import org.apache.fory.format.row.binary.writer.CompactBinaryRowWriter;
+import org.apache.fory.format.type.DataTypes;
+import org.apache.fory.format.type.Field;
+import org.apache.fory.format.type.Schema;
+
+/**
+ * Schema-derived layout for {@link CompactBinaryRow}, shared across rows.
Read-only after
+ * construction.
+ */
+@Internal
+public final class CompactRowLayout {
+ public final Schema schema;
+ public final int[] fixedOffsets;
+ public final int[] fixedWidths;
+ public final boolean allFieldsNotNullable;
+ public final int bitmapWidthInBytes;
+
+ /**
+ * Nested layout per field: struct row for struct fields, element row for
{@code List<Struct>}.
+ */
+ public final CompactRowLayout[] childLayouts;
+
+ public CompactRowLayout(Schema schema) {
+ this.schema = schema;
+ this.fixedOffsets = CompactBinaryRowWriter.fixedOffsets(schema);
+ final List<Field> fields = schema.fields();
+ this.fixedWidths = new int[fields.size()];
+ this.childLayouts = new CompactRowLayout[fields.size()];
+ for (int i = 0; i < fields.size(); i++) {
+ final Field field = fields.get(i);
+ fixedWidths[i] = CompactBinaryRowWriter.fixedWidthFor(field);
+ if (field.type() instanceof DataTypes.StructType) {
+ childLayouts[i] = new CompactRowLayout(DataTypes.createSchema(field));
+ } else if (field.type() instanceof DataTypes.ListType) {
+ final Field elementField = ((DataTypes.ListType)
field.type()).valueField();
+ if (elementField.type() instanceof DataTypes.StructType) {
+ childLayouts[i] =
+ new CompactRowLayout(
+
CompactBinaryRowWriter.sortSchema(DataTypes.createSchema(elementField)));
+ }
+ }
+ }
+ this.allFieldsNotNullable = CompactBinaryRowWriter.allNotNullable(fields);
+ this.bitmapWidthInBytes = allFieldsNotNullable ? 0 :
CompactBinaryRowWriter.headerBytes(schema);
+ }
+
+ public CompactBinaryRow newRow() {
+ return new CompactBinaryRow(this);
+ }
+}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BaseBinaryRowWriter.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BaseBinaryRowWriter.java
index 81616bc36..7bcc25b4b 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BaseBinaryRowWriter.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BaseBinaryRowWriter.java
@@ -133,5 +133,5 @@ public abstract class BaseBinaryRowWriter extends
BinaryWriter {
return row;
}
- protected abstract BinaryRow newRow();
+ public abstract BinaryRow newRow();
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BinaryRowWriter.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BinaryRowWriter.java
index 9bc3cdb9b..2c466da24 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BinaryRowWriter.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/BinaryRowWriter.java
@@ -122,7 +122,7 @@ public class BinaryRowWriter extends BaseBinaryRowWriter {
}
@Override
- protected BinaryRow newRow() {
+ public BinaryRow newRow() {
return new BinaryRow(getSchema());
}
}
diff --git
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/CompactBinaryRowWriter.java
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/CompactBinaryRowWriter.java
index 481029ef9..ed65775a7 100644
---
a/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/CompactBinaryRowWriter.java
+++
b/java/fory-format/src/main/java/org/apache/fory/format/row/binary/writer/CompactBinaryRowWriter.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.row.binary.CompactBinaryRow;
+import org.apache.fory.format.row.binary.CompactRowLayout;
import org.apache.fory.format.type.DataType;
import org.apache.fory.format.type.DataTypes;
import org.apache.fory.format.type.Field;
@@ -33,54 +34,36 @@ import org.apache.fory.memory.MemoryBuffer;
/** Writer class to produce {@link CompactBinaryRow}-formatted rows. */
public class CompactBinaryRowWriter extends BaseBinaryRowWriter {
- private final int headerSize;
- private final boolean allFieldsFixedSize;
- private final boolean allFieldsNotNullable;
private final int fixedSize;
- private final int[] fixedOffsets;
+ private final CompactRowLayout layout;
public CompactBinaryRowWriter(final Schema schema) {
super(sortSchema(schema), computeFixedRegionSize(schema));
- headerSize = headerBytes(schema);
- allFieldsFixedSize = allFieldsFixedSize(schema);
- fixedSize = computeAlignedFixedRegionSize();
- fixedOffsets = fixedOffsets(schema);
- allFieldsNotNullable = allNotNullable(schema.fields());
+ this.layout = new CompactRowLayout(getSchema());
+ this.fixedSize = computeAlignedFixedRegionSize(layout, bytesBeforeBitMap);
}
public CompactBinaryRowWriter(final Schema schema, final BinaryWriter
writer) {
super(sortSchema(schema), writer, computeFixedRegionSize(schema));
- headerSize = headerBytes(schema);
- allFieldsFixedSize = allFieldsFixedSize(schema);
- fixedSize = computeAlignedFixedRegionSize();
- fixedOffsets = fixedOffsets(schema);
- allFieldsNotNullable = allNotNullable(schema.fields());
+ this.layout = new CompactRowLayout(getSchema());
+ this.fixedSize = computeAlignedFixedRegionSize(layout, bytesBeforeBitMap);
}
public CompactBinaryRowWriter(final Schema schema, final MemoryBuffer
buffer) {
super(sortSchema(schema), buffer, computeFixedRegionSize(schema));
- headerSize = headerBytes(schema);
- allFieldsFixedSize = allFieldsFixedSize(schema);
- fixedSize = computeAlignedFixedRegionSize();
- fixedOffsets = fixedOffsets(schema);
- allFieldsNotNullable = allNotNullable(schema.fields());
+ this.layout = new CompactRowLayout(getSchema());
+ this.fixedSize = computeAlignedFixedRegionSize(layout, bytesBeforeBitMap);
}
- private static boolean allFieldsFixedSize(final Schema schema) {
- for (final Field f : schema.fields()) {
- if (fixedWidthFor(f) == -1) {
- return false;
+ private static int computeAlignedFixedRegionSize(
+ final CompactRowLayout layout, final int bytesBeforeBitMap) {
+ final int totalUnalignedSize = layout.bitmapWidthInBytes +
bytesBeforeBitMap;
+ for (final int width : layout.fixedWidths) {
+ if (width == -1) {
+ return roundNumberOfBytesToNearestWord(totalUnalignedSize);
}
}
- return true;
- }
-
- private int computeAlignedFixedRegionSize() {
- final int totalUnalignedSize = headerSize + bytesBeforeBitMap;
- if (allFieldsFixedSize) {
- return totalUnalignedSize;
- }
- return roundNumberOfBytesToNearestWord(totalUnalignedSize);
+ return totalUnalignedSize;
}
// TODO: this should use StableValue once it's available
@@ -115,12 +98,12 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
protected int headerInBytes() {
- return headerSize;
+ return layout.bitmapWidthInBytes;
}
@Override
public int getOffset(final int ordinal) {
- return startIndex + fixedOffsets[ordinal];
+ return startIndex + layout.fixedOffsets[ordinal];
}
/** Total size of fixed region: fixed size inline values plus variable sized
values' pointers. */
@@ -207,7 +190,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
}
public void resetFor(final CompactBinaryRowWriter nestedWriter, final int
ordinal) {
- if (fixedWidthFor(getSchema(), ordinal) == -1) {
+ if (layout.fixedWidths[ordinal] == -1) {
nestedWriter.reset();
} else {
nestedWriter.startIndex = getOffset(ordinal);
@@ -218,7 +201,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
protected void resetHeader() {
final int bitmapStart = startIndex + bytesBeforeBitMap;
- final int end = startIndex + computeAlignedFixedRegionSize();
+ final int end = startIndex + fixedSize;
for (int i = bitmapStart; i < end; i += 1) {
buffer.putByte(i, 0);
}
@@ -226,7 +209,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public void setNullAt(final int ordinal) {
- if (allFieldsNotNullable) {
+ if (layout.allFieldsNotNullable) {
throw new IllegalStateException(
"Field " + getSchema().field(ordinal) + " has null value for
not-null field");
}
@@ -235,7 +218,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
protected void clearValue(final int ordinal) {
- final int fixedWidth = fixedWidthFor(getSchema(), ordinal);
+ final int fixedWidth = layout.fixedWidths[ordinal];
if (fixedWidth > 0) {
final int off = getOffset(ordinal);
for (int i = off; i < off + fixedWidth; i++) {
@@ -248,7 +231,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public void setNotNullAt(final int ordinal) {
- if (allFieldsNotNullable) {
+ if (layout.allFieldsNotNullable) {
return;
}
super.setNotNullAt(ordinal);
@@ -256,7 +239,7 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public boolean isNullAt(final int ordinal) {
- if (allFieldsNotNullable) {
+ if (layout.allFieldsNotNullable) {
return false;
}
return super.isNullAt(ordinal);
@@ -295,9 +278,9 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public void writeUnaligned(
final int ordinal, final byte[] input, final int offset, final int
numBytes) {
- final int inlineWidth = fixedWidthFor(getSchema(), ordinal);
+ final int inlineWidth = layout.fixedWidths[ordinal];
if (inlineWidth > 0) {
- buffer.put(getOffset(ordinal), input, 0, numBytes);
+ buffer.put(getOffset(ordinal), input, offset, numBytes);
} else {
super.writeUnaligned(ordinal, input, offset, numBytes);
}
@@ -306,10 +289,10 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public void writeUnaligned(
final int ordinal, final MemoryBuffer input, final int offset, final int
numBytes) {
- final int inlineWidth = fixedWidthFor(getSchema(), ordinal);
+ final int inlineWidth = layout.fixedWidths[ordinal];
if (inlineWidth > 0) {
assert inlineWidth == numBytes;
- buffer.copyFrom(getOffset(ordinal), input, 0, numBytes);
+ buffer.copyFrom(getOffset(ordinal), input, offset, numBytes);
} else {
super.writeUnaligned(ordinal, input, offset, numBytes);
}
@@ -318,16 +301,16 @@ public class CompactBinaryRowWriter extends
BaseBinaryRowWriter {
@Override
public void writeAlignedBytes(
final int ordinal, final MemoryBuffer input, final int baseOffset, final
int numBytes) {
- final int inlineWidth = fixedWidthFor(getSchema(), ordinal);
+ final int inlineWidth = layout.fixedWidths[ordinal];
if (inlineWidth > 0) {
- buffer.copyFrom(getOffset(ordinal), input, 0, numBytes);
+ buffer.copyFrom(getOffset(ordinal), input, baseOffset, numBytes);
} else {
super.writeAlignedBytes(ordinal, input, baseOffset, numBytes);
}
}
@Override
- protected BinaryRow newRow() {
- return new CompactBinaryRow(getSchema(), fixedOffsets);
+ public BinaryRow newRow() {
+ return layout.newRow();
}
}
diff --git
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/BinaryRowEncoderPointToTest.java
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/BinaryRowEncoderPointToTest.java
index ff31773c2..645e7d28c 100644
---
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/BinaryRowEncoderPointToTest.java
+++
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/BinaryRowEncoderPointToTest.java
@@ -62,8 +62,7 @@ public class BinaryRowEncoderPointToTest {
};
BinaryRowEncoder<Tiny> encoder =
- new BinaryRowEncoder<>(
- schema, DefaultCodecFormat.INSTANCE, captor, new
BinaryRowWriter(schema), false);
+ new BinaryRowEncoder<>(schema, captor, new BinaryRowWriter(schema),
false);
encoder.decode(payload);
Assert.assertNotNull(captured[0], "decode must hand the row to fromRow");
diff --git
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/CompactCodecTest.java
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/CompactCodecTest.java
index a1f582777..60c3f0719 100644
---
a/java/fory-format/src/test/java/org/apache/fory/format/encoder/CompactCodecTest.java
+++
b/java/fory-format/src/test/java/org/apache/fory/format/encoder/CompactCodecTest.java
@@ -41,6 +41,7 @@ import org.apache.fory.format.row.binary.BinaryArray;
import org.apache.fory.format.row.binary.BinaryMap;
import org.apache.fory.format.row.binary.BinaryRow;
import org.apache.fory.format.row.binary.CompactBinaryRow;
+import org.apache.fory.format.row.binary.writer.CompactBinaryRowWriter;
import org.apache.fory.format.type.DataTypes;
import org.apache.fory.format.type.Field;
import org.apache.fory.memory.MemoryBuffer;
@@ -266,6 +267,31 @@ public class CompactCodecTest {
assertEquals(buffer.size(), 7);
}
+ /** After sort, f1 sits at non-zero baseOffset; re-emitting it must use that
offset. */
+ @Test
+ public void testCopyInlineStructViaWriter() {
+ final InlineNestedType bean = new InlineNestedType();
+ bean.f1 = new Nested1();
+ bean.f1.f1 = (short) 0x4242;
+ bean.f2 = new Nested2();
+ bean.f2.f1 = 0x12345678;
+ final RowEncoder<InlineNestedType> encoder =
+
Encoders.buildBeanCodec(InlineNestedType.class).compactEncoding().build().get();
+ final CompactBinaryRow source = (CompactBinaryRow) encoder.toRow(bean);
+ final MemoryBuffer sourceBuffer = MemoryUtils.wrap(source.toBytes());
+ source.pointTo(sourceBuffer, 0, sourceBuffer.size());
+
+ final CompactBinaryRowWriter writer = new
CompactBinaryRowWriter(encoder.schema());
+ writer.reset();
+ writer.write(0, source.getStruct(0));
+ writer.write(1, source.getStruct(1));
+ final BinaryRow copy = writer.getRow();
+ final MemoryBuffer copyBuffer = MemoryUtils.wrap(copy.toBytes());
+ copy.pointTo(copyBuffer, 0, copyBuffer.size());
+
+ assertEquals(encoder.fromRow(copy), bean);
+ }
+
@Data
public static class InlineNestedArrayType {
public InlineNestedType f1;
diff --git
a/java/fory-format/src/test/java/org/apache/fory/format/row/binary/CompactRowLayoutTest.java
b/java/fory-format/src/test/java/org/apache/fory/format/row/binary/CompactRowLayoutTest.java
new file mode 100644
index 000000000..78183107b
--- /dev/null
+++
b/java/fory-format/src/test/java/org/apache/fory/format/row/binary/CompactRowLayoutTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.fory.format.row.binary;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
+import static org.testng.Assert.assertTrue;
+
+import java.util.List;
+import lombok.Data;
+import org.apache.fory.format.encoder.Encoders;
+import org.apache.fory.format.encoder.RowEncoder;
+import org.apache.fory.memory.MemoryBuffer;
+import org.apache.fory.memory.MemoryUtils;
+import org.testng.annotations.Test;
+
+public class CompactRowLayoutTest {
+
+ @Data
+ public static class Nested1 {
+ public short f1;
+ }
+
+ @Data
+ public static class Nested2 {
+ public int f1;
+ }
+
+ @Data
+ public static class InlineNestedType {
+ public Nested1 f1;
+ public Nested2 f2;
+ }
+
+ @Test
+ public void nestedStructCopyRoundTrip() {
+ final InlineNestedType bean = new InlineNestedType();
+ bean.f1 = new Nested1();
+ bean.f1.f1 = 42;
+ bean.f2 = new Nested2();
+ bean.f2.f1 = 75;
+ final RowEncoder<InlineNestedType> encoder =
+
Encoders.buildBeanCodec(InlineNestedType.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ final CompactBinaryRow copy = (CompactBinaryRow) row.copy();
+ assertNotSame(copy, row);
+ assertSame(copy.getLayout(), row.getLayout());
+ assertEquals(encoder.fromRow(copy), bean);
+ }
+
+ @Test
+ public void nestedStructLayoutPrebuilt() {
+ final InlineNestedType bean = new InlineNestedType();
+ bean.f1 = new Nested1();
+ bean.f1.f1 = 42;
+ bean.f2 = new Nested2();
+ bean.f2.f1 = 75;
+ final RowEncoder<InlineNestedType> encoder =
+
Encoders.buildBeanCodec(InlineNestedType.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ final CompactRowLayout layout = row.getLayout();
+ final CompactRowLayout child0 = layout.childLayouts[0];
+ final CompactRowLayout child1 = layout.childLayouts[1];
+ assertNotNull(child0);
+ assertNotNull(child1);
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ assertSame(((CompactBinaryRow) row.getStruct(0)).getLayout(), child0);
+ assertSame(((CompactBinaryRow) row.getStruct(1)).getLayout(), child1);
+ final CompactBinaryRow row2 = (CompactBinaryRow) encoder.toRow(bean);
+ assertSame(row2.getLayout().childLayouts[0], child0);
+ assertSame(row2.getLayout().childLayouts[1], child1);
+ }
+
+ @Data
+ public static class VarWidthInner {
+ public int i;
+ public String s;
+ }
+
+ @Data
+ public static class VarWidthOuter {
+ public VarWidthInner f1;
+ }
+
+ @Test
+ public void varWidthNestedStructRead() {
+ final VarWidthOuter bean = new VarWidthOuter();
+ bean.f1 = new VarWidthInner();
+ bean.f1.i = 42;
+ bean.f1.s = "luna";
+ final RowEncoder<VarWidthOuter> encoder =
+
Encoders.buildBeanCodec(VarWidthOuter.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ assertEquals(row.getLayout().fixedWidths[0], -1);
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ final CompactBinaryRow inner = (CompactBinaryRow) row.getStruct(0);
+ assertSame(inner.getLayout(), row.getLayout().childLayouts[0]);
+ assertEquals(encoder.fromRow(row), bean);
+ }
+
+ /**
+ * Pins {@code getBaseOffset() +} in the var-width getStruct branch by
giving the parent a
+ * non-zero offset.
+ */
+ @Test
+ public void varWidthNestedStructReadWithNonZeroParentOffset() {
+ final VarWidthOuter inner = new VarWidthOuter();
+ inner.f1 = new VarWidthInner();
+ inner.f1.i = 0x12345678;
+ inner.f1.s = "luna";
+ final OuterHolder bean = new OuterHolder();
+ bean.f1 = inner;
+ final RowEncoder<OuterHolder> encoder =
+
Encoders.buildBeanCodec(OuterHolder.class).compactEncoding().build().get();
+ final CompactBinaryRow root = (CompactBinaryRow) encoder.toRow(bean);
+ final MemoryBuffer buffer = MemoryUtils.wrap(root.toBytes());
+ root.pointTo(buffer, 0, buffer.size());
+
+ final CompactBinaryRow middle = (CompactBinaryRow) root.getStruct(0);
+ assertTrue(middle.getBaseOffset() > 0);
+ assertEquals(middle.getLayout().fixedWidths[0], -1);
+ assertNotNull(middle.getStruct(0));
+ assertEquals(encoder.fromRow(root), bean);
+ }
+
+ @Data
+ public static class OuterHolder {
+ public VarWidthOuter f1;
+ }
+
+ @Data
+ public static class ListOfStructHolder {
+ public List<VarWidthInner> elements;
+ }
+
+ @Test
+ public void listOfStructElementLayoutPrebuilt() {
+ final ListOfStructHolder bean = new ListOfStructHolder();
+ final VarWidthInner inner = new VarWidthInner();
+ inner.i = 7;
+ inner.s = "luna";
+ bean.elements = List.of(inner);
+ final RowEncoder<ListOfStructHolder> encoder =
+
Encoders.buildBeanCodec(ListOfStructHolder.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ final CompactRowLayout elementLayout = row.getLayout().childLayouts[0];
+ assertNotNull(elementLayout);
+
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ final CompactBinaryRow element = (CompactBinaryRow)
row.getArray(0).getStruct(0);
+ assertSame(element.getLayout(), elementLayout);
+ assertEquals(encoder.fromRow(row), bean);
+ }
+
+ @Test
+ public void listOfStructMultipleElements() {
+ final ListOfStructHolder bean = new ListOfStructHolder();
+ final VarWidthInner a = new VarWidthInner();
+ a.i = 1;
+ a.s = "a";
+ final VarWidthInner b = new VarWidthInner();
+ b.i = 2;
+ b.s = "bbb";
+ final VarWidthInner c = new VarWidthInner();
+ c.i = 3;
+ c.s = "cccccc";
+ bean.elements = List.of(a, b, c);
+ final RowEncoder<ListOfStructHolder> encoder =
+
Encoders.buildBeanCodec(ListOfStructHolder.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ final CompactRowLayout elementLayout = row.getLayout().childLayouts[0];
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ for (int i = 0; i < 3; i++) {
+ final CompactBinaryRow element = (CompactBinaryRow)
row.getArray(0).getStruct(i);
+ assertSame(element.getLayout(), elementLayout);
+ }
+ assertEquals(encoder.fromRow(row), bean);
+ }
+
+ @Data
+ public static class AllPrimitive {
+ public int i;
+ public long l;
+ public boolean b;
+ }
+
+ @Test
+ public void allFieldsNotNullableSkipsBitmap() {
+ final AllPrimitive bean = new AllPrimitive();
+ bean.i = 42;
+ bean.l = 0x1122334455667788L;
+ bean.b = true;
+ final RowEncoder<AllPrimitive> encoder =
+
Encoders.buildBeanCodec(AllPrimitive.class).compactEncoding().build().get();
+ final CompactBinaryRow row = (CompactBinaryRow) encoder.toRow(bean);
+ final CompactRowLayout layout = row.getLayout();
+ assertTrue(layout.allFieldsNotNullable);
+ assertEquals(layout.bitmapWidthInBytes, 0);
+ final MemoryBuffer buffer = MemoryUtils.wrap(row.toBytes());
+ row.pointTo(buffer, 0, buffer.size());
+ assertFalse(row.isNullAt(0));
+ assertFalse(row.isNullAt(1));
+ assertFalse(row.isNullAt(2));
+ assertEquals(encoder.fromRow(row), bean);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]