diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 699c267339a..c4e2927d156 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1665,7 +1665,9 @@ public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer : public abstract fun getBoolean (I)Z public abstract fun getCount ()I public abstract fun getDouble (I)D + public abstract fun getDoubleBuffer (I)[D public abstract fun getInt (I)I + public abstract fun getIntBuffer (I)[I public abstract fun getKeyOffset (I)I public abstract fun getLong (I)J public abstract fun getMapBuffer (I)Lcom/facebook/react/common/mapbuffer/MapBuffer; @@ -1680,9 +1682,12 @@ public final class com/facebook/react/common/mapbuffer/MapBuffer$Companion { public final class com/facebook/react/common/mapbuffer/MapBuffer$DataType : java/lang/Enum { public static final field BOOL Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field DOUBLE Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field DOUBLE_BUFFER Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field INT Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field INT_BUFFER Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field LONG Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field MAP Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; + public static final field MAP_BUFFER_LIST Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static final field STRING Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; public static fun getEntries ()Lkotlin/enums/EnumEntries; public static fun valueOf (Ljava/lang/String;)Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; @@ -1691,10 +1696,13 @@ public final class com/facebook/react/common/mapbuffer/MapBuffer$DataType : java public abstract interface class com/facebook/react/common/mapbuffer/MapBuffer$Entry { public abstract fun getBooleanValue ()Z + public abstract fun getDoubleBufferValue ()[D public abstract fun getDoubleValue ()D + public abstract fun getIntBufferValue ()[I public abstract fun getIntValue ()I public abstract fun getKey ()I public abstract fun getLongValue ()J + public abstract fun getMapBufferListValue ()Ljava/util/List; public abstract fun getMapBufferValue ()Lcom/facebook/react/common/mapbuffer/MapBuffer; public abstract fun getStringValue ()Ljava/lang/String; public abstract fun getType ()Lcom/facebook/react/common/mapbuffer/MapBuffer$DataType; @@ -1708,7 +1716,9 @@ public final class com/facebook/react/common/mapbuffer/ReadableMapBuffer : com/f public fun getBoolean (I)Z public fun getCount ()I public fun getDouble (I)D + public fun getDoubleBuffer (I)[D public fun getInt (I)I + public fun getIntBuffer (I)[I public fun getKeyOffset (I)I public fun getLong (I)J public synthetic fun getMapBuffer (I)Lcom/facebook/react/common/mapbuffer/MapBuffer; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt index 44af587bd9f..feba0dc97d6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt @@ -46,6 +46,9 @@ public interface MapBuffer : Iterable { STRING, MAP, LONG, + INT_BUFFER, + DOUBLE_BUFFER, + MAP_BUFFER_LIST, } /** @@ -151,8 +154,8 @@ public interface MapBuffer : Iterable { public fun getMapBuffer(key: Int): MapBuffer /** - * Provides parsed [List] value if the entry for given key exists with [DataType.MAP] - * type + * Provides parsed [List] value if the entry for given key exists with + * [DataType.MAP_BUFFER_LIST] type * * @param key key to lookup [List] value for * @return value associated with the requested key @@ -161,6 +164,30 @@ public interface MapBuffer : Iterable { */ public fun getMapBufferList(key: Int): List + /** + * Provides parsed [IntArray] value if the entry for given key exists with [DataType.INT_BUFFER] + * type. This is a compact representation of a homogeneous list of ints with no per-element + * key/type overhead. + * + * @param key key to lookup the [IntArray] value for + * @return value associated with the requested key + * @throws IllegalArgumentException if the key doesn't exist + * @throws IllegalStateException if the data type doesn't match + */ + public fun getIntBuffer(key: Int): IntArray + + /** + * Provides parsed [DoubleArray] value if the entry for given key exists with + * [DataType.DOUBLE_BUFFER] type. This is a compact representation of a homogeneous list of + * doubles with no per-element key/type overhead. + * + * @param key key to lookup the [DoubleArray] value for + * @return value associated with the requested key + * @throws IllegalArgumentException if the key doesn't exist + * @throws IllegalStateException if the data type doesn't match + */ + public fun getDoubleBuffer(key: Int): DoubleArray + /** Iterable entry representing parsed MapBuffer values */ public interface Entry { /** @@ -213,5 +240,26 @@ public interface MapBuffer : Iterable { * @throws IllegalStateException if the data type doesn't match [DataType.MAP] */ public val mapBufferValue: MapBuffer + + /** + * Entry value represented as [IntArray] + * + * @throws IllegalStateException if the data type doesn't match [DataType.INT_BUFFER] + */ + public val intBufferValue: IntArray + + /** + * Entry value represented as [DoubleArray] + * + * @throws IllegalStateException if the data type doesn't match [DataType.DOUBLE_BUFFER] + */ + public val doubleBufferValue: DoubleArray + + /** + * Entry value represented as [List] + * + * @throws IllegalStateException if the data type doesn't match [DataType.MAP_BUFFER_LIST] + */ + public val mapBufferListValue: List } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt index c73ccb6da1d..1e9a196bdbb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt @@ -51,13 +51,11 @@ private constructor( ReadableMapBuffer(buffer.duplicate().apply { position(offset) }, offset) private fun readHeader() { - // byte order - val storedAlignment = buffer.short - if (storedAlignment.toInt() != ALIGNMENT) { - buffer.order(ByteOrder.LITTLE_ENDIAN) - } - // count - count = readUnsignedShort(buffer.position()).toInt() + // The C++ writer always serializes in little-endian byte order. ByteBuffer + // defaults to big-endian and duplicate() resets the order, so set it + // explicitly on every instance, including nested clones. + buffer.order(ByteOrder.LITTLE_ENDIAN) + count = readUnsignedShort(offsetToMapBuffer).toInt() } /** @@ -122,26 +120,27 @@ private constructor( return readIntValue(bufferPosition) == 1 } + // Dynamic-data entries store [offset][byteLength] in the bucket's 8-byte + // value: getInt(bufferPosition) is the offset, getInt(bufferPosition + 4) is + // the byte length. The dynamic data section itself carries no length prefix. private fun readStringValue(bufferPosition: Int): String { val offset = offsetForDynamicData + buffer.getInt(bufferPosition) - val sizeOfString = buffer.getInt(offset) + val sizeOfString = buffer.getInt(bufferPosition + Int.SIZE_BYTES) val result = ByteArray(sizeOfString) - val stringOffset = offset + Int.SIZE_BYTES - buffer.position(stringOffset) + buffer.position(offset) buffer[result, 0, sizeOfString] return String(result) } private fun readMapBufferValue(position: Int): ReadableMapBuffer { val offset = offsetForDynamicData + buffer.getInt(position) - return cloneWithOffset(offset + Int.SIZE_BYTES) + return cloneWithOffset(offset) } private fun readMapBufferListValue(position: Int): List { val readMapBufferList = arrayListOf() - var offset = offsetForDynamicData + buffer.getInt(position) - val sizeMapBufferList = buffer.getInt(offset) - offset += Int.SIZE_BYTES + val offset = offsetForDynamicData + buffer.getInt(position) + val sizeMapBufferList = buffer.getInt(position + Int.SIZE_BYTES) var curLen = 0 while (curLen < sizeMapBufferList) { val sizeMapBuffer = buffer.getInt(offset + curLen) @@ -152,6 +151,20 @@ private constructor( return readMapBufferList } + private fun readIntBufferValue(bufferPosition: Int): IntArray { + val offset = offsetForDynamicData + buffer.getInt(bufferPosition) + val byteLength = buffer.getInt(bufferPosition + Int.SIZE_BYTES) + val count = byteLength / Int.SIZE_BYTES + return IntArray(count) { i -> buffer.getInt(offset + i * Int.SIZE_BYTES) } + } + + private fun readDoubleBufferValue(bufferPosition: Int): DoubleArray { + val offset = offsetForDynamicData + buffer.getInt(bufferPosition) + val byteLength = buffer.getInt(bufferPosition + Int.SIZE_BYTES) + val count = byteLength / Double.SIZE_BYTES + return DoubleArray(count) { i -> buffer.getDouble(offset + i * Double.SIZE_BYTES) } + } + private fun getKeyOffsetForBucketIndex(bucketIndex: Int): Int { return offsetToMapBuffer + HEADER_SIZE + BUCKET_SIZE * bucketIndex } @@ -191,7 +204,13 @@ private constructor( readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) override fun getMapBufferList(key: Int): List = - readMapBufferListValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) + readMapBufferListValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP_BUFFER_LIST)) + + override fun getIntBuffer(key: Int): IntArray = + readIntBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.INT_BUFFER)) + + override fun getDoubleBuffer(key: Int): DoubleArray = + readDoubleBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.DOUBLE_BUFFER)) override fun hashCode(): Int { buffer.rewind() @@ -229,6 +248,9 @@ private constructor( append('"') } MapBuffer.DataType.MAP -> append(entry.mapBufferValue.toString()) + MapBuffer.DataType.INT_BUFFER -> append(entry.intBufferValue.contentToString()) + MapBuffer.DataType.DOUBLE_BUFFER -> append(entry.doubleBufferValue.contentToString()) + MapBuffer.DataType.MAP_BUFFER_LIST -> append(entry.mapBufferListValue.toString()) } } } @@ -311,16 +333,31 @@ private constructor( assertType(MapBuffer.DataType.MAP) return readMapBufferValue(bucketOffset + VALUE_OFFSET) } + + override val intBufferValue: IntArray + get() { + assertType(MapBuffer.DataType.INT_BUFFER) + return readIntBufferValue(bucketOffset + VALUE_OFFSET) + } + + override val doubleBufferValue: DoubleArray + get() { + assertType(MapBuffer.DataType.DOUBLE_BUFFER) + return readDoubleBufferValue(bucketOffset + VALUE_OFFSET) + } + + override val mapBufferListValue: List + get() { + assertType(MapBuffer.DataType.MAP_BUFFER_LIST) + return readMapBufferListValue(bucketOffset + VALUE_OFFSET) + } } public companion object { - // Value used to verify if the data is serialized with LittleEndian order. - private const val ALIGNMENT = 0xFE - - // 8 bytes = 2 (alignment) + 2 (count) + 4 (size) - private const val HEADER_SIZE = 8 + // 2 bytes = 2 (count) + private const val HEADER_SIZE = 2 - // 10 bytes = 2 (key) + 2 (type) + 8 (value) + // 12 bytes = 2 (key) + 2 (type) + 8 (value) private const val BUCKET_SIZE = 12 // 2 bytes = 2 (key) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt index d75e8a39a8a..6e3d1359df0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt @@ -126,6 +126,10 @@ internal class WritableMapBuffer : MapBuffer { override fun getMapBufferList(key: Int): List = verifyValue(key, values.get(key)) + override fun getIntBuffer(key: Int): IntArray = verifyValue(key, values.get(key)) + + override fun getDoubleBuffer(key: Int): DoubleArray = verifyValue(key, values.get(key)) + /** Generalizes verification of the value types based on the requested type. */ private inline fun verifyValue(key: Int, value: Any?): T { require(value != null) { "Key not found: $key" } @@ -176,6 +180,15 @@ internal class WritableMapBuffer : MapBuffer { override val mapBufferValue: MapBuffer get() = verifyValue(key, values.valueAt(index)) + + override val intBufferValue: IntArray + get() = verifyValue(key, values.valueAt(index)) + + override val doubleBufferValue: DoubleArray + get() = verifyValue(key, values.valueAt(index)) + + override val mapBufferListValue: List + get() = verifyValue(key, values.valueAt(index)) } /* diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp index 401a6670d03..18868bf75c9 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp @@ -8,8 +8,25 @@ #include "MapBuffer.h" #include +#include + namespace facebook::react { +namespace { +// Reads a value of type T from a (possibly unaligned) offset in the buffer. +// MapBuffer's packed layout places multi-byte values at offsets that are not +// naturally aligned for their type (e.g. an 8-byte value at a 2-byte boundary), +// so dereferencing a reinterpret_cast pointer there is undefined behavior and +// can fault on 32-bit ARM. memcpy compiles to a single unaligned load on +// arm64/x86 and to alignment-safe loads on armv7. +template +inline T readUnaligned(const uint8_t* data, int32_t offset) { + T value; + std::memcpy(&value, data + offset, sizeof(T)); + return value; +} +} // namespace + static inline int32_t bucketOffset(int32_t index) { return sizeof(MapBuffer::Header) + sizeof(MapBuffer::Bucket) * index; } @@ -18,17 +35,19 @@ static inline int32_t valueOffset(int32_t bucketIndex) { return bucketOffset(bucketIndex) + offsetof(MapBuffer::Bucket, data); } +// Dynamic-data entries pack [offset (low 32 bits)][byteLength (high 32 bits)] +// into the bucket's 8-byte value, so the payload in the dynamic data section +// carries no in-band length prefix. This returns the position of the high +// 32 bits (the length). +static inline int32_t lengthOffset(int32_t bucketIndex) { + return valueOffset(bucketIndex) + static_cast(sizeof(int32_t)); +} + // TODO T83483191: Extend MapBuffer C++ implementation to support basic random // access MapBuffer::MapBuffer(std::vector data) : bytes_(std::move(data)) { auto header = reinterpret_cast(bytes_.data()); count_ = header->count; - - if (header->bufferSize != bytes_.size()) { - LOG(ERROR) << "Error: Data size does not match, expected " - << header->bufferSize << " found: " << bytes_.size(); - abort(); - } } int32_t MapBuffer::getKeyBucket(Key key) const { @@ -37,8 +56,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const { while (lo <= hi) { int32_t mid = (lo + hi) >> 1; - Key midVal = - *reinterpret_cast(bytes_.data() + bucketOffset(mid)); + Key midVal = readUnaligned(bytes_.data(), bucketOffset(mid)); if (midVal < key) { lo = mid + 1; @@ -53,8 +71,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const { } inline int32_t MapBuffer::getIntAtBucket(int32_t bucketIndex) const { - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } int32_t MapBuffer::getInt(Key key) const { @@ -74,8 +91,7 @@ int64_t MapBuffer::getLong(Key key) const { return 0; } - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } bool MapBuffer::getBool(Key key) const { @@ -89,8 +105,7 @@ double MapBuffer::getDouble(Key key) const { return 0; } - return *reinterpret_cast( - bytes_.data() + valueOffset(bucketIndex)); + return readUnaligned(bytes_.data(), valueOffset(bucketIndex)); } int32_t MapBuffer::getDynamicDataOffset() const { @@ -108,8 +123,8 @@ std::string MapBuffer::getString(Key key) const { int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); int32_t stringLength = - *reinterpret_cast(bytes_.data() + offset); - const uint8_t* stringPtr = bytes_.data() + offset + sizeof(int); + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + const uint8_t* stringPtr = bytes_.data() + offset; return {stringPtr, stringPtr + stringLength}; } @@ -123,16 +138,15 @@ MapBuffer MapBuffer::getMapBuffer(Key key) const { int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); int32_t mapBufferLength = - *reinterpret_cast(bytes_.data() + offset); - size_t maxLength = bytes_.size() - offset - sizeof(int32_t); - if (mapBufferLength > maxLength) { - mapBufferLength = maxLength; + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + size_t maxLength = bytes_.size() - offset; + if (static_cast(mapBufferLength) > maxLength) { + mapBufferLength = static_cast(maxLength); } std::vector value(mapBufferLength); - memcpy( - value.data(), bytes_.data() + offset + sizeof(int32_t), mapBufferLength); + memcpy(value.data(), bytes_.data() + offset, mapBufferLength); return MapBuffer(std::move(value)); } @@ -147,13 +161,12 @@ std::vector MapBuffer::getMapBufferList(MapBuffer::Key key) const { std::vector mapBufferList; int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); int32_t mapBufferListLength = - *reinterpret_cast(bytes_.data() + offset); - offset = offset + sizeof(uint32_t); + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); int32_t curLen = 0; while (curLen < mapBufferListLength) { int32_t mapBufferLength = - *reinterpret_cast(bytes_.data() + offset + curLen); + readUnaligned(bytes_.data(), offset + curLen); curLen = curLen + sizeof(uint32_t); std::vector value(mapBufferLength); memcpy(value.data(), bytes_.data() + offset + curLen, mapBufferLength); @@ -163,6 +176,46 @@ std::vector MapBuffer::getMapBufferList(MapBuffer::Key key) const { return mapBufferList; } +std::vector MapBuffer::getIntBuffer(MapBuffer::Key key) const { + auto bucketIndex = getKeyBucket(key); + react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer"); + if (bucketIndex == -1) { + return {}; + } + + int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); + int32_t byteLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + int32_t count = byteLength / static_cast(sizeof(int32_t)); + + std::vector result(count); + if (byteLength > 0) { + memcpy( + result.data(), bytes_.data() + offset, static_cast(byteLength)); + } + return result; +} + +std::vector MapBuffer::getDoubleBuffer(MapBuffer::Key key) const { + auto bucketIndex = getKeyBucket(key); + react_native_assert(bucketIndex != -1 && "Key not found in MapBuffer"); + if (bucketIndex == -1) { + return {}; + } + + int32_t offset = getDynamicDataOffset() + getIntAtBucket(bucketIndex); + int32_t byteLength = + readUnaligned(bytes_.data(), lengthOffset(bucketIndex)); + int32_t count = byteLength / static_cast(sizeof(double)); + + std::vector result(count); + if (byteLength > 0) { + memcpy( + result.data(), bytes_.data() + offset, static_cast(byteLength)); + } + return result; +} + size_t MapBuffer::size() const { return bytes_.size(); } diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h index 1ae3595368c..8c57d80a450 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBuffer.h @@ -40,11 +40,11 @@ class JReadableMapBuffer; * * MapBuffer data is stored in a continuous chunk of memory (bytes_ field below) with the following layout: * - * ┌─────────────────────Header──────────────────────┐ - * │ 10 bytes │ - * ├─Alignment─┬─Item count─┬──────Buffer size───────┤ - * │ 2 bytes │ 2 bytes │ 4 bytes │ - * └───────────┴────────────┴────────────────────────┘ + * ┌──────Header──────┐ + * │ 2 bytes │ + * ├────Item count────┤ + * │ 2 bytes │ + * └──────────────────┘ * ┌────────────────────────────────────────────────────────────────────────────────────────┐ * │ Buckets (one per item in the map) │ * │ │ @@ -69,14 +69,8 @@ class MapBuffer { public: using Key = uint16_t; - // The first value in the buffer, used to check correct encoding/endianness on - // JVM side. - constexpr static uint16_t HEADER_ALIGNMENT = 0xFE; - struct Header { - uint16_t alignment = HEADER_ALIGNMENT; // alignment of serialization uint16_t count; // amount of items in the map - uint32_t bufferSize; // Amount of bytes used to store the map in memory }; #pragma pack(push, 1) @@ -89,13 +83,14 @@ class MapBuffer { }; #pragma pack(pop) - static_assert(sizeof(Header) == 8, "MapBuffer header size is incorrect."); + static_assert(sizeof(Header) == 2, "MapBuffer header size is incorrect."); static_assert(sizeof(Bucket) == 12, "MapBuffer bucket size is incorrect."); /** * Data types available for serialization in MapBuffer - * Keep in sync with `DataType` enum in `JReadableMapBuffer.java`, which - * expects the same values after reading them through JNI. + * Keep in sync with the `DataType` enum in `MapBuffer.kt` + * (packages/react-native/ReactAndroid/.../common/mapbuffer/MapBuffer.kt), + * which is ordinal-indexed on the JVM side, so the order must match exactly. */ enum DataType : uint16_t { Boolean = 0, @@ -104,6 +99,19 @@ class MapBuffer { String = 3, Map = 4, Long = 5, + // Homogeneous, length-prefixed arrays stored contiguously in the dynamic + // data section. Unlike Map, they carry no per-element key/type overhead, so + // 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 is the offset of the + // array within the dynamic data section. + IntBuffer = 6, + DoubleBuffer = 7, + // A homogeneous, ordered array of nested MapBuffers. Stored in the dynamic + // data section as [int32 totalSize] followed by a sequence of + // [int32 childSize][child bytes]. Distinct from `Map` so that a list of + // MapBuffers is self-describing (a single Map and a list are byte-distinct + // in payload but previously shared the `Map` type tag). + MapBufferList = 8, }; explicit MapBuffer(std::vector data); @@ -131,6 +139,10 @@ class MapBuffer { std::vector getMapBufferList(MapBuffer::Key key) const; + std::vector getIntBuffer(MapBuffer::Key key) const; + + std::vector getDoubleBuffer(MapBuffer::Key key) const; + size_t size() const; const uint8_t *data() const; diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp index eb6e859fec2..1944a01f261 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp @@ -15,6 +15,14 @@ constexpr uint32_t LONG_SIZE = sizeof(uint64_t); constexpr uint32_t DOUBLE_SIZE = sizeof(double); constexpr uint32_t MAX_BUCKET_VALUE_SIZE = sizeof(uint64_t); +// Dynamic-data entries store their location in the bucket's 8-byte value as +// [offset (low 32 bits)][byteLength (high 32 bits)], so the payload in the +// dynamic data section needs no in-band length prefix. +static inline uint64_t packOffsetAndLength(int32_t offset, int32_t length) { + return static_cast(static_cast(offset)) | + (static_cast(static_cast(length)) << 32); +} + MapBuffer MapBufferBuilder::EMPTY() { return MapBufferBuilder(0).build(); } @@ -22,7 +30,6 @@ MapBuffer MapBufferBuilder::EMPTY() { MapBufferBuilder::MapBufferBuilder(uint32_t initialSize) { buckets_.reserve(initialSize); header_.count = 0; - header_.bufferSize = 0; } void MapBufferBuilder::storeKeyValue( @@ -84,81 +91,111 @@ void MapBufferBuilder::putLong(MapBuffer::Key key, int64_t value) { } void MapBufferBuilder::putString(MapBuffer::Key key, const std::string& value) { - // The wire format encodes lengths and offsets as int32_t (see - // MapBuffer::getString). Without an explicit narrowing cast, `auto` deduces - // size_t (8 bytes on 64-bit) and `memcpy(&x, ..., INT_SIZE)` then copies only - // the first 4 bytes of an 8-byte value: silent truncation on little-endian, - // wrong (high) bytes on big-endian. auto strSize = static_cast(value.size()); - const char* strData = value.data(); - - // format [length of string (int)] + [Array of Characters in the string] auto offset = static_cast(dynamicData_.size()); - dynamicData_.resize(offset + INT_SIZE + strSize, 0); - memcpy(dynamicData_.data() + offset, &strSize, INT_SIZE); - memcpy(dynamicData_.data() + offset + INT_SIZE, strData, strSize); - // Store Key and pointer to the string + // The bucket stores [offset][byteLength]; the dynamic section holds only the + // raw string bytes. + dynamicData_.resize(offset + strSize, 0); + if (strSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), strSize); + } + + uint64_t data = packOffsetAndLength(offset, strSize); storeKeyValue( key, MapBuffer::DataType::String, - reinterpret_cast(&offset), - INT_SIZE); + reinterpret_cast(&data), + sizeof(data)); } void MapBufferBuilder::putMapBuffer(MapBuffer::Key key, const MapBuffer& map) { - // Wire format encodes lengths and offsets as int32_t (see - // MapBuffer::getMapBuffer). Cast explicitly so memcpy(&x, ..., INT_SIZE) - // copies the full value, not the first 4 bytes of an 8-byte size_t. auto mapBufferSize = static_cast(map.size()); - auto offset = static_cast(dynamicData_.size()); - // format [length of buffer (int)] + [bytes of MapBuffer] - dynamicData_.resize(offset + INT_SIZE + mapBufferSize, 0); - memcpy(dynamicData_.data() + offset, &mapBufferSize, INT_SIZE); - // Copy the content of the map into dynamicData_ - memcpy(dynamicData_.data() + offset + INT_SIZE, map.data(), mapBufferSize); + // The bucket stores [offset][byteLength]; the dynamic section holds only the + // serialized child MapBuffer bytes. + dynamicData_.resize(offset + mapBufferSize, 0); + memcpy(dynamicData_.data() + offset, map.data(), mapBufferSize); - // Store Key and pointer to the string + uint64_t data = packOffsetAndLength(offset, mapBufferSize); storeKeyValue( key, MapBuffer::DataType::Map, - reinterpret_cast(&offset), - INT_SIZE); + reinterpret_cast(&data), + sizeof(data)); } void MapBufferBuilder::putMapBufferList( MapBuffer::Key key, const std::vector& mapBufferList) { auto offset = static_cast(dynamicData_.size()); - int32_t dataSize = 0; - for (const MapBuffer& mapBuffer : mapBufferList) { - dataSize = dataSize + INT_SIZE + static_cast(mapBuffer.size()); - } - - dynamicData_.resize(offset + INT_SIZE, 0); - memcpy(dynamicData_.data() + offset, &dataSize, INT_SIZE); + // The bucket stores [offset][byteLength] for the whole list region; within it + // each child stays framed as [int32 childSize][child bytes] so the children + // remain individually delimited. for (const MapBuffer& mapBuffer : mapBufferList) { auto mapBufferSize = static_cast(mapBuffer.size()); - auto dynamicDataSize = static_cast(dynamicData_.size()); - dynamicData_.resize(dynamicDataSize + INT_SIZE + mapBufferSize, 0); - // format [length of buffer (int)] + [bytes of MapBuffer] - memcpy(dynamicData_.data() + dynamicDataSize, &mapBufferSize, INT_SIZE); - // Copy the content of the map into dynamicData_ + auto pos = static_cast(dynamicData_.size()); + dynamicData_.resize(pos + INT_SIZE + mapBufferSize, 0); + memcpy(dynamicData_.data() + pos, &mapBufferSize, INT_SIZE); memcpy( - dynamicData_.data() + dynamicDataSize + INT_SIZE, - mapBuffer.data(), - mapBufferSize); + dynamicData_.data() + pos + INT_SIZE, mapBuffer.data(), mapBufferSize); } - // Store Key and pointer to the string + auto totalSize = static_cast(dynamicData_.size()) - offset; + uint64_t data = packOffsetAndLength(offset, totalSize); + // Uses the dedicated MapBufferList type so the entry is self-describing and + // distinguishable from a single Map. storeKeyValue( key, - MapBuffer::DataType::Map, - reinterpret_cast(&offset), - INT_SIZE); + MapBuffer::DataType::MapBufferList, + reinterpret_cast(&data), + sizeof(data)); +} + +void MapBufferBuilder::putIntBuffer( + MapBuffer::Key key, + const std::vector& value) { + // The bucket stores [offset][byteLength]; the dynamic section holds the raw + // int32 elements. Element count is recovered as byteLength / sizeof(int32_t). + auto payloadSize = static_cast(value.size() * sizeof(int32_t)); + auto offset = static_cast(dynamicData_.size()); + dynamicData_.resize(offset + payloadSize, 0); + if (payloadSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), payloadSize); + } + + uint64_t data = packOffsetAndLength(offset, payloadSize); + storeKeyValue( + key, + MapBuffer::DataType::IntBuffer, + reinterpret_cast(&data), + sizeof(data)); +} + +void MapBufferBuilder::putDoubleBuffer( + MapBuffer::Key key, + const std::vector& value) { + // The bucket stores [offset][byteLength]; the dynamic section holds the raw + // double elements. Element count is recovered as byteLength / sizeof(double). + // Doubles are copied byte-for-byte; the reader uses memcpy, so the payload + // needs no special alignment for correctness. A consumer that wants a + // zero-copy typed view on the JVM (ByteBuffer::asDoubleBuffer) must ensure + // 8-byte alignment itself. + auto payloadSize = static_cast(value.size() * sizeof(double)); + auto offset = static_cast(dynamicData_.size()); + dynamicData_.resize(offset + payloadSize, 0); + if (payloadSize > 0) { + memcpy(dynamicData_.data() + offset, value.data(), payloadSize); + } + + uint64_t data = packOffsetAndLength(offset, payloadSize); + storeKeyValue( + key, + MapBuffer::DataType::DoubleBuffer, + reinterpret_cast(&data), + sizeof(data)); } static inline bool compareBuckets( @@ -173,8 +210,6 @@ MapBuffer MapBufferBuilder::build() { auto headerSize = sizeof(MapBuffer::Header); auto bufferSize = headerSize + bucketSize + dynamicData_.size(); - header_.bufferSize = static_cast(bufferSize); - if (needsSort_) { std::sort(buckets_.begin(), buckets_.end(), compareBuckets); } @@ -183,11 +218,19 @@ MapBuffer MapBufferBuilder::build() { std::vector buffer(bufferSize); memcpy(buffer.data(), &header_, headerSize); - memcpy(buffer.data() + headerSize, buckets_.data(), bucketSize); - memcpy( - buffer.data() + headerSize + bucketSize, - dynamicData_.data(), - dynamicData_.size()); + // buckets_.data() / dynamicData_.data() return nullptr when the vector is + // empty; passing nullptr to memcpy is UB even with size 0 (glibc marks the + // src argument nonnull) and trips UBSan halt-on-error on empty / scalar-only + // MapBuffers. + if (!buckets_.empty()) { + memcpy(buffer.data() + headerSize, buckets_.data(), bucketSize); + } + if (!dynamicData_.empty()) { + memcpy( + buffer.data() + headerSize + bucketSize, + dynamicData_.data(), + dynamicData_.size()); + } return MapBuffer(std::move(buffer)); } diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h index af054da5578..2963c77a9b3 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h @@ -39,6 +39,10 @@ class MapBufferBuilder { void putMapBufferList(MapBuffer::Key key, const std::vector &mapBufferList); + void putIntBuffer(MapBuffer::Key key, const std::vector &value); + + void putDoubleBuffer(MapBuffer::Key key, const std::vector &value); + MapBuffer build(); private: diff --git a/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp b/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp index 3cc169563fc..05c70d203f5 100644 --- a/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp @@ -49,7 +49,7 @@ TEST(MapBufferTest, testSimpleLongMap) { } TEST(MapBufferTest, testMapBufferExtension) { - // 26 = 2 buckets: 2*10 + 6 sizeof(header) + // initialSize is a reserve hint for the number of buckets int initialSize = 26; auto buffer = MapBufferBuilder(initialSize); @@ -205,6 +205,101 @@ TEST(MapBufferTest, testMapListEntries) { EXPECT_EQ(mapBufferList2[1].getDouble(3), 908.1); } +TEST(MapBufferTest, testEmptyMapBufferList) { + auto builder = MapBufferBuilder(); + + builder.putMapBufferList(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getMapBufferList(0).size(), 0); +} + +// Place the list behind another dynamic-data entry so its offset is non-zero, +// exercising `getDynamicDataOffset() + getIntAtBucket(...)` against a non-zero +// base rather than the zero-offset path testMapListEntries covers. +TEST(MapBufferTest, testMapListEntriesAtNonZeroOffset) { + std::vector mapBufferList; + auto inner = MapBufferBuilder(); + inner.putString(0, "inner"); + inner.putInt(1, 42); + mapBufferList.push_back(inner.build()); + + auto builder = MapBufferBuilder(); + builder.putString(0, "prefix"); + builder.putMapBufferList(1, mapBufferList); + auto map = builder.build(); + + EXPECT_EQ(map.getString(0), "prefix"); + std::vector readList = map.getMapBufferList(1); + EXPECT_EQ(readList.size(), 1); + EXPECT_EQ(readList[0].getString(0), "inner"); + EXPECT_EQ(readList[0].getInt(1), 42); +} + +TEST(MapBufferTest, testIntBufferEntries) { + auto builder = MapBufferBuilder(); + + std::vector values{ + 1, + -2, + 3, + std::numeric_limits::min(), + std::numeric_limits::max()}; + builder.putIntBuffer(0, values); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 1); + EXPECT_EQ(map.getIntBuffer(0), values); +} + +TEST(MapBufferTest, testEmptyIntBuffer) { + auto builder = MapBufferBuilder(); + + builder.putIntBuffer(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getIntBuffer(0).size(), 0); +} + +TEST(MapBufferTest, testDoubleBufferEntries) { + auto builder = MapBufferBuilder(); + + std::vector values{0.0, -1.5, 3.14159, 1e300, -1e-300}; + builder.putDoubleBuffer(0, values); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 1); + EXPECT_EQ(map.getDoubleBuffer(0), values); +} + +TEST(MapBufferTest, testEmptyDoubleBuffer) { + auto builder = MapBufferBuilder(); + + builder.putDoubleBuffer(0, {}); + auto map = builder.build(); + + EXPECT_EQ(map.getDoubleBuffer(0).size(), 0); +} + +// Mirrors the batched-animated-props use case: a pair of typed streams plus +// some scalar metadata, with keys inserted out of order to exercise both the +// dynamic-data section and the bucket sort path. +TEST(MapBufferTest, testIntAndDoubleBuffersAlongsideScalars) { + std::vector intStream{1, 100, 1, 2, 4, 15, 4}; + std::vector doubleStream{0.5, 12.0, 0.25}; + + auto builder = MapBufferBuilder(); + builder.putDoubleBuffer(2, doubleStream); + builder.putInt(0, 7); + builder.putIntBuffer(1, intStream); + auto map = builder.build(); + + EXPECT_EQ(map.count(), 3); + EXPECT_EQ(map.getInt(0), 7); + EXPECT_EQ(map.getIntBuffer(1), intStream); + EXPECT_EQ(map.getDoubleBuffer(2), doubleStream); +} + TEST(MapBufferTest, testMapRandomAccess) { auto builder = MapBufferBuilder(); builder.putInt(1234, 4321); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 9a5ce2066c8..29cac6b7e29 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -3252,9 +3252,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3262,9 +3263,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3276,9 +3280,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3287,7 +3289,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index ff685621148..b9181848267 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -3161,9 +3161,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3171,9 +3172,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3185,9 +3189,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3196,7 +3198,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 25e566d2466..d7afed562a8 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -3249,9 +3249,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -3259,9 +3260,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -3273,9 +3277,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -3284,7 +3286,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api index 14984a9b86d..5a9c2896db8 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api @@ -5482,9 +5482,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5492,9 +5493,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5506,9 +5510,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5517,7 +5519,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api index ec05c677501..77bdd100764 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleNewarchCxx.api @@ -5406,9 +5406,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5416,9 +5417,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5430,9 +5434,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5441,7 +5443,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api index ca2d1156795..122500670b6 100644 --- a/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api @@ -5479,9 +5479,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -5489,9 +5490,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -5503,9 +5507,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -5514,7 +5516,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api index 4f98056312e..f04650553f4 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api @@ -2167,9 +2167,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2177,9 +2178,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2191,9 +2195,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2202,7 +2204,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api index 16d1eb0c64a..c1c9641c9be 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonNewarchCxx.api @@ -2103,9 +2103,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2113,9 +2114,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2127,9 +2131,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2138,7 +2140,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList); diff --git a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api index 23e2b116a3f..043d7175793 100644 --- a/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api @@ -2164,9 +2164,10 @@ class facebook::react::MapBuffer { public int32_t getInt(facebook::react::MapBuffer::Key key) const; public int64_t getLong(facebook::react::MapBuffer::Key key) const; public size_t size() const; - public static constexpr uint16_t HEADER_ALIGNMENT; public std::string getString(facebook::react::MapBuffer::Key key) const; + public std::vector getDoubleBuffer(facebook::react::MapBuffer::Key key) const; public std::vector getMapBufferList(facebook::react::MapBuffer::Key key) const; + public std::vector getIntBuffer(facebook::react::MapBuffer::Key key) const; public uint16_t count() const; public using Key = uint16_t; } @@ -2174,9 +2175,12 @@ class facebook::react::MapBuffer { enum facebook::react::MapBuffer::DataType : uint16_t { Boolean, Double, + DoubleBuffer, Int, + IntBuffer, Long, Map, + MapBufferList, String, } @@ -2188,9 +2192,7 @@ struct facebook::react::MapBuffer::Bucket { } struct facebook::react::MapBuffer::Header { - public uint16_t alignment; public uint16_t count; - public uint32_t bufferSize; } class facebook::react::MapBufferBuilder { @@ -2199,7 +2201,9 @@ class facebook::react::MapBufferBuilder { public static facebook::react::MapBuffer EMPTY(); public void putBool(facebook::react::MapBuffer::Key key, bool value); public void putDouble(facebook::react::MapBuffer::Key key, double value); + public void putDoubleBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putInt(facebook::react::MapBuffer::Key key, int32_t value); + public void putIntBuffer(facebook::react::MapBuffer::Key key, const std::vector& value); public void putLong(facebook::react::MapBuffer::Key key, int64_t value); public void putMapBuffer(facebook::react::MapBuffer::Key key, const facebook::react::MapBuffer& map); public void putMapBufferList(facebook::react::MapBuffer::Key key, const std::vector& mapBufferList);