Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/react-native/ReactAndroid/api/ReactAndroid.api
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public interface MapBuffer : Iterable<MapBuffer.Entry> {
STRING,
MAP,
LONG,
INT_BUFFER,
DOUBLE_BUFFER,
MAP_BUFFER_LIST,
}

/**
Expand Down Expand Up @@ -151,8 +154,8 @@ public interface MapBuffer : Iterable<MapBuffer.Entry> {
public fun getMapBuffer(key: Int): MapBuffer

/**
* Provides parsed [List<MapBuffer>] value if the entry for given key exists with [DataType.MAP]
* type
* Provides parsed [List<MapBuffer>] value if the entry for given key exists with
* [DataType.MAP_BUFFER_LIST] type
*
* @param key key to lookup [List<MapBuffer>] value for
* @return value associated with the requested key
Expand All @@ -161,6 +164,30 @@ public interface MapBuffer : Iterable<MapBuffer.Entry> {
*/
public fun getMapBufferList(key: Int): List<MapBuffer>

/**
* 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 {
/**
Expand Down Expand Up @@ -213,5 +240,26 @@ public interface MapBuffer : Iterable<MapBuffer.Entry> {
* @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<MapBuffer>]
*
* @throws IllegalStateException if the data type doesn't match [DataType.MAP_BUFFER_LIST]
*/
public val mapBufferListValue: List<MapBuffer>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@ private constructor(
return readMapBufferList
}

private fun readIntBufferValue(bufferPosition: Int): IntArray {
var offset = offsetForDynamicData + buffer.getInt(bufferPosition)
val count = buffer.getInt(offset)
offset += Int.SIZE_BYTES
return IntArray(count) { i -> buffer.getInt(offset + i * Int.SIZE_BYTES) }
}

private fun readDoubleBufferValue(bufferPosition: Int): DoubleArray {
var offset = offsetForDynamicData + buffer.getInt(bufferPosition)
val count = buffer.getInt(offset)
offset += Int.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
}
Expand Down Expand Up @@ -191,7 +205,13 @@ private constructor(
readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP))

override fun getMapBufferList(key: Int): List<ReadableMapBuffer> =
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()
Expand Down Expand Up @@ -229,6 +249,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())
}
}
}
Expand Down Expand Up @@ -311,6 +334,24 @@ 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<MapBuffer>
get() {
assertType(MapBuffer.DataType.MAP_BUFFER_LIST)
return readMapBufferListValue(bucketOffset + VALUE_OFFSET)
}
}

public companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ internal class WritableMapBuffer : MapBuffer {

override fun getMapBufferList(key: Int): List<MapBuffer> = 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 <reified T> verifyValue(key: Int, value: Any?): T {
require(value != null) { "Key not found: $key" }
Expand Down Expand Up @@ -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<MapBuffer>
get() = verifyValue(key, values.valueAt(index))
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,46 @@ std::vector<MapBuffer> MapBuffer::getMapBufferList(MapBuffer::Key key) const {
return mapBufferList;
}

std::vector<int32_t> 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 count = *reinterpret_cast<const int32_t*>(bytes_.data() + offset);

std::vector<int32_t> result(count);
if (count > 0) {
memcpy(
result.data(),
bytes_.data() + offset + sizeof(int32_t),
static_cast<size_t>(count) * sizeof(int32_t));
}
return result;
}

std::vector<double> 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 count = *reinterpret_cast<const int32_t*>(bytes_.data() + offset);

std::vector<double> result(count);
if (count > 0) {
memcpy(
result.data(),
bytes_.data() + offset + sizeof(int32_t),
static_cast<size_t>(count) * sizeof(double));
}
return result;
}

size_t MapBuffer::size() const {
return bytes_.size();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ class MapBuffer {

/**
* 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,
Expand All @@ -104,6 +105,17 @@ 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. 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<uint8_t> data);
Expand Down Expand Up @@ -131,6 +143,10 @@ class MapBuffer {

std::vector<MapBuffer> getMapBufferList(MapBuffer::Key key) const;

std::vector<int32_t> getIntBuffer(MapBuffer::Key key) const;

std::vector<double> getDoubleBuffer(MapBuffer::Key key) const;

size_t size() const;

const uint8_t *data() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,57 @@ void MapBufferBuilder::putMapBufferList(
mapBufferSize);
}

// Store Key and pointer to the string
// Store Key and pointer to the list. Uses the dedicated MapBufferList type so
// the entry is self-describing and distinguishable from a single Map.
storeKeyValue(
key,
MapBuffer::DataType::Map,
MapBuffer::DataType::MapBufferList,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}

void MapBufferBuilder::putIntBuffer(
MapBuffer::Key key,
const std::vector<int32_t>& value) {
// Wire format: [element count (int32)] + [count * int32]. The count is the
// number of elements, not bytes; see MapBuffer::getIntBuffer.
auto count = static_cast<int32_t>(value.size());
auto payloadSize = static_cast<int32_t>(value.size() * sizeof(int32_t));

auto offset = static_cast<int32_t>(dynamicData_.size());
dynamicData_.resize(offset + INT_SIZE + payloadSize, 0);
memcpy(dynamicData_.data() + offset, &count, INT_SIZE);
if (payloadSize > 0) {
memcpy(dynamicData_.data() + offset + INT_SIZE, value.data(), payloadSize);
}

storeKeyValue(
key,
MapBuffer::DataType::IntBuffer,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}

void MapBufferBuilder::putDoubleBuffer(
MapBuffer::Key key,
const std::vector<double>& value) {
// Wire format: [element count (int32)] + [count * 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 count = static_cast<int32_t>(value.size());
auto payloadSize = static_cast<int32_t>(value.size() * sizeof(double));

auto offset = static_cast<int32_t>(dynamicData_.size());
dynamicData_.resize(offset + INT_SIZE + payloadSize, 0);
memcpy(dynamicData_.data() + offset, &count, INT_SIZE);
if (payloadSize > 0) {
memcpy(dynamicData_.data() + offset + INT_SIZE, value.data(), payloadSize);
}

storeKeyValue(
key,
MapBuffer::DataType::DoubleBuffer,
reinterpret_cast<const uint8_t*>(&offset),
INT_SIZE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class MapBufferBuilder {

void putMapBufferList(MapBuffer::Key key, const std::vector<MapBuffer> &mapBufferList);

void putIntBuffer(MapBuffer::Key key, const std::vector<int32_t> &value);

void putDoubleBuffer(MapBuffer::Key key, const std::vector<double> &value);

MapBuffer build();

private:
Expand Down
Loading
Loading