Skip to content

Optimize MapBuffer representation (#57361)#57361

Open
javache wants to merge 4 commits into
react:mainfrom
javache:export-D109848478
Open

Optimize MapBuffer representation (#57361)#57361
javache wants to merge 4 commits into
react:mainfrom
javache:export-D109848478

Conversation

@javache

@javache javache commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary:

Broadens the former header-shrink change into a single representation-optimization commit for MapBuffer. It bundles three layout optimizations that were previously split: (1) the header is reduced to a single 2-byte count field; (2) multi-byte values are read via memcpy so unaligned access is well-defined on all platforms; (3) every dynamic-data entry (String, Map, MapBufferList, IntBuffer, DoubleBuffer) packs its [offset][byteLength] into the bucket's 8-byte value instead of writing an in-band length prefix into the dynamic data section.

Net effect: 4 fewer bytes per dynamic entry, one fewer indirection on read (the length is already in the bucket), and every dynamic entry becomes self-delimiting from its bucket alone. No public API change — only the internal serialized representation.

Changelog: [Internal]

Differential Revision: D109848478

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 29, 2026
@meta-codesync

meta-codesync Bot commented Jun 29, 2026

Copy link
Copy Markdown

@javache has exported this pull request. If you are a Meta employee, you can view the originating Diff in D109848478.

javache added a commit to javache/react-native that referenced this pull request Jun 29, 2026
Summary:
Pull Request resolved: react#57361

Broadens the former header-shrink change into a single representation-optimization commit for MapBuffer. It bundles three layout optimizations that were previously split: (1) the header is reduced to a single 2-byte `count` field; (2) multi-byte values are read via `memcpy` so unaligned access is well-defined on all platforms; (3) every dynamic-data entry (`String`, `Map`, `MapBufferList`, `IntBuffer`, `DoubleBuffer`) packs its `[offset][byteLength]` into the bucket's 8-byte value instead of writing an in-band length prefix into the dynamic data section.

Net effect: 4 fewer bytes per dynamic entry, one fewer indirection on read (the length is already in the bucket), and every dynamic entry becomes self-delimiting from its bucket alone. No public API change — only the internal serialized representation.

Changelog: [Internal]

Differential Revision: D109848478
@javache javache force-pushed the export-D109848478 branch from a811f07 to c50599d Compare June 29, 2026 11:21
@meta-codesync meta-codesync Bot changed the title Optimize MapBuffer representation Optimize MapBuffer representation (#57361) Jun 29, 2026
javache and others added 4 commits July 1, 2026 04:50
Summary:
`buckets_.data()` and `dynamicData_.data()` return `nullptr` when the vector is empty, and glibc marks `memcpy`'s src argument as `nonnull`. Passing `nullptr` is UB even with a size of 0, which trips UBSan halt-on-error on any MapBuffer that has no buckets (`EMPTY()`) or no dynamic-data entries (scalar-only maps). Guard both memcpys with an empty check.

Changelog:
[General][Fixed] - Avoid `memcpy(_, nullptr, 0)` UB in `MapBufferBuilder::build` for empty / scalar-only MapBuffers

Differential Revision: D110316404
Summary:
Adds two new MapBuffer entry types, `IntBuffer` and `DoubleBuffer`, for storing homogeneous arrays of ints and doubles compactly in the dynamic data section. Unlike `Map` / map lists, these carry no per-element key/type overhead: a batch of N values costs ~N*elementSize bytes plus a single 4-byte count prefix instead of N 12-byte buckets. The bucket value holds the offset of the array within the dynamic data section.

Covers the full surface: the C++ reader (`MapBuffer::getIntBuffer` / `getDoubleBuffer`), the C++ builder (`MapBufferBuilder::putIntBuffer` / `putDoubleBuffer`), and the Kotlin reader API (`MapBuffer.getIntBuffer` / `getDoubleBuffer`, `Entry.intBufferValue` / `doubleBufferValue`). The `DataType` enum gains `IntBuffer = 6` and `DoubleBuffer = 7`, kept in sync across C++ and Kotlin.

Changelog:
[General][Added] - Add `IntBuffer` and `DoubleBuffer` entry types to MapBuffer for compact homogeneous int/double arrays

Differential Revision: D109848476
Summary:
Introduces a dedicated `MapBufferList` `DataType` for an ordered array of nested MapBuffers, instead of overloading the `Map` type for lists. This makes a list of MapBuffers self-describing and distinguishable from a single nested `Map` (they were byte-distinct in payload but previously shared the `Map` type tag). Updates the C++ builder (`putMapBufferList`), the Kotlin `MapBuffer` interface, `ReadableMapBuffer`, and `WritableMapBuffer`, and adds cross-language JNI round-trip coverage in the serialization instrumentation test.

Changelog:
[Android][Added] - Add a dedicated `MapBufferList` type to `MapBuffer` for ordered lists of nested `MapBuffer`s

Differential Revision: D109848477
Summary:
Pull Request resolved: react#57361

Broadens the former header-shrink change into a single representation-optimization commit for MapBuffer. It bundles three layout optimizations that were previously split: (1) the header is reduced to a single 2-byte `count` field; (2) multi-byte values are read via `memcpy` so unaligned access is well-defined on all platforms; (3) every dynamic-data entry (`String`, `Map`, `MapBufferList`, `IntBuffer`, `DoubleBuffer`) packs its `[offset][byteLength]` into the bucket's 8-byte value instead of writing an in-band length prefix into the dynamic data section.

Net effect: 4 fewer bytes per dynamic entry, one fewer indirection on read (the length is already in the bucket), and every dynamic entry becomes self-delimiting from its bucket alone. No public API change — only the internal serialized representation.

Changelog: [Internal]

Differential Revision: D109848478
@javache javache force-pushed the export-D109848478 branch from c50599d to 8218129 Compare July 1, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant