From 81e42493158edd5e7b88f98c19c87e9d61ba4aba Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Wed, 30 Mar 2022 20:27:23 -0700 Subject: [PATCH] Adapt ReadableMapBuffer to MapBuffer interface Summary: Updates `ReadableMapBuffer` to conform to `MapBuffer` interface, to allow interchangeable use of `Readable/WritableMapBuffer` in the code. Notable changes: - MapBuffer.Entry getters are now represented as Kotlin properties and appended `Value` suffix (e.g. `entry.getInt()` becomes `entry.getIntValue()` in Java, or `entry.intValue` in Kotlin) - `ByteBuffer` is imported in constructor instead of on demand. This method doesn't copy the data as the bytes are read directly from native heap, and benchmarking a 500-byte `MapBuffer` shows no difference in import speed. - Internal logic of `ReadableMapBuffer` uses `UShort` kotlin type for key retrieval, for more correct representation of values. - Explicit exception throws are replaced with `require` and `check` methods for `IllegalArgumentException` and `IllegalStateException` (default FB conversion). The change also updates `ReadableMapBuffer` usages to `MapBuffer` interface where possible (virtually everywhere except JNI methods). Changelog: [Android][Changed] - Adopt `MapBuffer` interface for `ReadableMapBuffer` Reviewed By: mdvacca Differential Revision: D35218633 fbshipit-source-id: 515dd974c27b2978ade325b2e1750ab8f068a20a --- .../common/mapbuffer/MapBufferSoLoader.kt | 33 ++ .../common/mapbuffer/ReadableMapBuffer.java | 418 ------------------ .../common/mapbuffer/ReadableMapBuffer.kt | 298 +++++++++++++ .../mapbuffer/ReadableMapBufferSoLoader.java | 33 -- .../common/mapbuffer/WritableMapBuffer.kt | 2 +- .../main/java/com/facebook/react/fabric/BUCK | 2 + .../react/fabric/FabricJSIModuleProvider.java | 4 +- .../fabric/mounting/MountingManager.java | 8 +- .../facebook/react/uimanager/ViewManager.java | 14 +- .../java/com/facebook/react/views/text/BUCK | 2 + .../views/text/ReactTextViewManager.java | 17 +- .../react/views/text/TextAttributeProps.java | 51 +-- .../text/TextLayoutManagerMapBuffer.java | 45 +- .../views/view/ReactMapBufferPropSetter.kt | 122 +++-- 14 files changed, 460 insertions(+), 589 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBufferSoLoader.kt delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt delete mode 100644 ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBufferSoLoader.kt b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBufferSoLoader.kt new file mode 100644 index 00000000000000..f88cb5572ef67d --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBufferSoLoader.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.common.mapbuffer + +import com.facebook.react.bridge.ReactMarker +import com.facebook.react.bridge.ReactMarkerConstants +import com.facebook.soloader.SoLoader +import com.facebook.systrace.Systrace + +object MapBufferSoLoader { + @Volatile private var didInit = false + + @JvmStatic + fun staticInit() { + if (didInit) { + return + } + didInit = true + + Systrace.beginSection( + Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, + "ReadableMapBufferSoLoader.staticInit::load:mapbufferjni") + ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START) + SoLoader.loadLibrary("mapbufferjni") + ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END) + Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE) + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java deleted file mode 100644 index 4115ccc8923f33..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.common.mapbuffer; - -import androidx.annotation.Nullable; -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Iterator; - -/** - * TODO T83483191: add documentation. - * - *

NOTE: {@link ReadableMapBuffer} is NOT thread safe. - */ -public class ReadableMapBuffer implements Iterable { - - static { - ReadableMapBufferSoLoader.staticInit(); - } - - /** - * Data types supported by MapBuffer. Keep in sync with definition in `MapBuffer.h`, as enum - * serialization relies on correct order. - */ - public enum DataType { - BOOL, - INT, - DOUBLE, - STRING, - MAP; - } - - // Value used to verify if the data is serialized with LittleEndian order. - private static final int ALIGNMENT = 0xFE; - - // 8 bytes = 2 (alignment) + 2 (count) + 4 (size) - private static final int HEADER_SIZE = 8; - - // 10 bytes = 2 (key) + 2 (type) + 8 (value) - private static final int BUCKET_SIZE = 12; - - // 2 bytes = 2 (key) - private static final int TYPE_OFFSET = 2; - - // 4 bytes = 2 (key) + 2 (type) - private static final int VALUE_OFFSET = 4; - - private static final int INT_SIZE = 4; - - @Nullable ByteBuffer mBuffer = null; - - // Amount of items serialized on the ByteBuffer - private int mCount = 0; - - @DoNotStrip - private ReadableMapBuffer(HybridData hybridData) { - mHybridData = hybridData; - } - - private ReadableMapBuffer(ByteBuffer buffer) { - mBuffer = buffer; - readHeader(); - } - - private native ByteBuffer importByteBuffer(); - - @SuppressWarnings("unused") - @DoNotStrip - @Nullable - private HybridData mHybridData; - - private static int getKeyOffsetForBucketIndex(int bucketIndex) { - return HEADER_SIZE + BUCKET_SIZE * bucketIndex; - } - - // returns the relative offset of the first byte of dynamic data - private int getOffsetForDynamicData() { - // TODO T83483191: check if there's dynamic data? - return getKeyOffsetForBucketIndex(mCount); - } - - /** - * @param key Key to search for - * @return the "bucket index" for a key or -1 if not found. It uses a binary search algorithm - * (log(n)) - */ - private int getBucketIndexForKey(int key) { - importByteBufferAndReadHeader(); - int lo = 0; - int hi = getCount() - 1; - while (lo <= hi) { - final int mid = (lo + hi) >>> 1; - final int midVal = readUnsignedShort(getKeyOffsetForBucketIndex(mid)); - if (midVal < key) { - lo = mid + 1; - } else if (midVal > key) { - hi = mid - 1; - } else { - return mid; - } - } - return -1; - } - - private DataType readDataType(int bucketIndex) { - int value = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex) + TYPE_OFFSET); - return DataType.values()[value]; - } - - private int getTypedValueOffsetForKey(int key, DataType expected) { - int bucketIndex = getBucketIndexForKey(key); - if (bucketIndex == -1) { - throw new IllegalArgumentException("Key not found: " + key); - } - - DataType dataType = readDataType(bucketIndex); - if (dataType != expected) { - throw new IllegalStateException( - "Expected " - + expected - + " for key: " - + key - + " found " - + dataType.toString() - + " instead."); - } - - return getKeyOffsetForBucketIndex(bucketIndex) + VALUE_OFFSET; - } - - private int readUnsignedShort(int bufferPosition) { - return mBuffer.getShort(bufferPosition) & 0xFFFF; - } - - private double readDoubleValue(int bufferPosition) { - return mBuffer.getDouble(bufferPosition); - } - - private int readIntValue(int bufferPosition) { - return mBuffer.getInt(bufferPosition); - } - - private boolean readBooleanValue(int bufferPosition) { - return readIntValue(bufferPosition) == 1; - } - - private String readStringValue(int bufferPosition) { - int offset = getOffsetForDynamicData() + mBuffer.getInt(bufferPosition); - - int sizeOfString = mBuffer.getInt(offset); - byte[] result = new byte[sizeOfString]; - - int stringOffset = offset + INT_SIZE; - - mBuffer.position(stringOffset); - mBuffer.get(result, 0, sizeOfString); - - return new String(result); - } - - private ReadableMapBuffer readMapBufferValue(int position) { - int offset = getOffsetForDynamicData() + mBuffer.getInt(position); - - int sizeMapBuffer = mBuffer.getInt(offset); - byte[] buffer = new byte[sizeMapBuffer]; - - int bufferOffset = offset + INT_SIZE; - - mBuffer.position(bufferOffset); - mBuffer.get(buffer, 0, sizeMapBuffer); - - return new ReadableMapBuffer(ByteBuffer.wrap(buffer)); - } - - private void readHeader() { - // byte order - short storedAlignment = mBuffer.getShort(); - if (storedAlignment != ALIGNMENT) { - mBuffer.order(ByteOrder.LITTLE_ENDIAN); - } - // count - mCount = readUnsignedShort(mBuffer.position()); - } - - /** - * Binary search of the key inside the mapBuffer (log(n)). - * - * @param key Key to search for - * @return true if and only if the Key received as a parameter is stored in the MapBuffer. - */ - public boolean hasKey(int key) { - // TODO T83483191: Add tests - return getBucketIndexForKey(key) != -1; - } - - @Nullable - public DataType getType(int key) { - int bucketIndex = getBucketIndexForKey(key); - - if (bucketIndex == -1) { - throw new IllegalArgumentException("Key not found: " + key); - } - - return readDataType(bucketIndex); - } - - /** @return amount of elements stored into the MapBuffer */ - public int getCount() { - importByteBufferAndReadHeader(); - return mCount; - } - - /** - * @param key {@link int} representing the key - * @return return the int associated to the Key received as a parameter. - */ - public int getInt(int key) { - return readIntValue(getTypedValueOffsetForKey(key, DataType.INT)); - } - - /** - * @param key {@link int} representing the key - * @return return the double associated to the Key received as a parameter. - */ - public double getDouble(int key) { - return readDoubleValue(getTypedValueOffsetForKey(key, DataType.DOUBLE)); - } - - /** - * @param key {@link int} representing the key - * @return return the int associated to the Key received as a parameter. - */ - public String getString(int key) { - return readStringValue(getTypedValueOffsetForKey(key, DataType.STRING)); - } - - public boolean getBoolean(int key) { - return readBooleanValue(getTypedValueOffsetForKey(key, DataType.BOOL)); - } - - /** - * @param key {@link int} representing the key - * @return return the int associated to the Key received as a parameter. - */ - public ReadableMapBuffer getMapBuffer(int key) { - return readMapBufferValue(getTypedValueOffsetForKey(key, DataType.MAP)); - } - - /** - * Import ByteBuffer from C++, read the header and move the current cursor at the start of the - * payload. - */ - private ByteBuffer importByteBufferAndReadHeader() { - if (mBuffer != null) { - return mBuffer; - } - - // mBuffer = importByteBufferAllocateDirect(); - mBuffer = importByteBuffer(); - - readHeader(); - return mBuffer; - } - - private void assertKeyExists(int key, int bucketIndex) { - int storedKey = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex)); - if (storedKey != key) { - throw new IllegalStateException( - "Stored key doesn't match parameter - expected: " + key + " - found: " + storedKey); - } - } - - @Override - public int hashCode() { - ByteBuffer byteBuffer = importByteBufferAndReadHeader(); - byteBuffer.rewind(); - return byteBuffer.hashCode(); - } - - @Override - public boolean equals(@Nullable Object obj) { - if (!(obj instanceof ReadableMapBuffer)) { - return false; - } - - ReadableMapBuffer other = (ReadableMapBuffer) obj; - ByteBuffer thisByteBuffer = importByteBufferAndReadHeader(); - ByteBuffer otherByteBuffer = other.importByteBufferAndReadHeader(); - if (thisByteBuffer == otherByteBuffer) { - return true; - } - thisByteBuffer.rewind(); - otherByteBuffer.rewind(); - return thisByteBuffer.equals(otherByteBuffer); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("{"); - for (MapBufferEntry entry : this) { - int key = entry.getKey(); - builder.append(key); - builder.append('='); - switch (entry.getType()) { - case BOOL: - builder.append(entry.getBoolean()); - break; - case INT: - builder.append(entry.getInt()); - break; - case DOUBLE: - builder.append(entry.getDouble()); - break; - case STRING: - builder.append(entry.getString()); - break; - case MAP: - builder.append(entry.getReadableMapBuffer().toString()); - break; - } - builder.append(','); - } - builder.append('}'); - return builder.toString(); - } - - /** @return an {@link Iterator} for the entries of this MapBuffer. */ - @Override - public Iterator iterator() { - return new Iterator() { - int current = 0; - final int last = getCount() - 1; - - @Override - public boolean hasNext() { - return current <= last; - } - - @Override - public MapBufferEntry next() { - return new MapBufferEntry(getKeyOffsetForBucketIndex(current++)); - } - }; - } - - /** This class represents an Entry of the {@link ReadableMapBuffer} class. */ - public class MapBufferEntry { - private final int mBucketOffset; - - private MapBufferEntry(int position) { - mBucketOffset = position; - } - - private void assertType(DataType expected) { - DataType dataType = getType(); - if (expected != dataType) { - throw new IllegalStateException( - "Expected " - + expected - + " for key: " - + getKey() - + " found " - + dataType.toString() - + " instead."); - } - } - - /** @return a {@link short} that represents the key of this {@link MapBufferEntry}. */ - public int getKey() { - return readUnsignedShort(mBucketOffset); - } - - public DataType getType() { - return DataType.values()[readUnsignedShort(mBucketOffset + TYPE_OFFSET)]; - } - - /** @return the double value that is stored in this {@link MapBufferEntry}. */ - public double getDouble() { - // TODO T83483191 Extend serialization of MapBuffer to return null if there's no value - // stored in this MapBufferEntry. - assertType(DataType.DOUBLE); - return readDoubleValue(mBucketOffset + VALUE_OFFSET); - } - - /** @return the int value that is stored in this {@link MapBufferEntry}. */ - public int getInt() { - assertType(DataType.INT); - return readIntValue(mBucketOffset + VALUE_OFFSET); - } - - /** @return the boolean value that is stored in this {@link MapBufferEntry}. */ - public boolean getBoolean() { - assertType(DataType.BOOL); - return readBooleanValue(mBucketOffset + VALUE_OFFSET); - } - - /** @return the String value that is stored in this {@link MapBufferEntry}. */ - public String getString() { - assertType(DataType.STRING); - return readStringValue(mBucketOffset + VALUE_OFFSET); - } - - /** - * @return the {@link ReadableMapBuffer} value that is stored in this {@link MapBufferEntry}. - */ - public ReadableMapBuffer getReadableMapBuffer() { - assertType(DataType.MAP); - return readMapBufferValue(mBucketOffset + VALUE_OFFSET); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt new file mode 100644 index 00000000000000..c3143431d7aa0a --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt @@ -0,0 +1,298 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.common.mapbuffer + +import com.facebook.jni.HybridData +import com.facebook.proguard.annotations.DoNotStrip +import com.facebook.react.common.mapbuffer.MapBuffer.Companion.KEY_RANGE +import java.lang.StringBuilder +import java.nio.ByteBuffer +import java.nio.ByteOrder +import javax.annotation.concurrent.NotThreadSafe + +/** + * Read-only implementation of the [MapBuffer], imported from C++ environment. Use + * ` to create it. + * + * See [MapBuffer] documentation for more details + */ +@NotThreadSafe +@DoNotStrip +class ReadableMapBuffer : MapBuffer { + + // Hybrid data must be kept in the `mHybridData` field for fbjni to work + @field:DoNotStrip private val mHybridData: HybridData? + + // Byte data of the mapBuffer + private val buffer: ByteBuffer + // Amount of items serialized on the ByteBuffer + override var count = 0 + private set + + @DoNotStrip + private constructor(hybridData: HybridData) { + this.mHybridData = hybridData + this.buffer = importByteBuffer() + readHeader() + } + + private constructor(buffer: ByteBuffer) { + this.mHybridData = null + this.buffer = buffer + readHeader() + } + + private external fun importByteBuffer(): ByteBuffer + + private fun readHeader() { + // byte order + val storedAlignment = buffer.short + if (storedAlignment.toInt() != ALIGNMENT) { + buffer.order(ByteOrder.LITTLE_ENDIAN) + } + // count + count = readUnsignedShort(buffer.position()).toInt() + } + + // returns the relative offset of the first byte of dynamic data + private val offsetForDynamicData: Int + get() = getKeyOffsetForBucketIndex(count) + + /** + * @param key Key to search for + * @return the "bucket index" for a key or -1 if not found. It uses a binary search algorithm + * (log(n)) + */ + private fun getBucketIndexForKey(intKey: Int): Int { + if (intKey !in KEY_RANGE) { + return -1 + } + val key = intKey.toUShort() + + var lo = 0 + var hi = count - 1 + while (lo <= hi) { + val mid = lo + hi ushr 1 + val midVal = readUnsignedShort(getKeyOffsetForBucketIndex(mid)) + when { + midVal < key -> lo = mid + 1 + midVal > key -> hi = mid - 1 + else -> return mid + } + } + return -1 + } + + private fun readDataType(bucketIndex: Int): MapBuffer.DataType { + val value = readUnsignedShort(getKeyOffsetForBucketIndex(bucketIndex) + TYPE_OFFSET).toInt() + return MapBuffer.DataType.values()[value] + } + + private fun getTypedValueOffsetForKey(key: Int, expected: MapBuffer.DataType): Int { + val bucketIndex = getBucketIndexForKey(key) + require(bucketIndex != -1) { "Key not found: $key" } + val dataType = readDataType(bucketIndex) + check(!(dataType !== expected)) { "Expected $expected for key: $key, found $dataType instead." } + return getKeyOffsetForBucketIndex(bucketIndex) + VALUE_OFFSET + } + + private fun readUnsignedShort(bufferPosition: Int): UShort { + return buffer.getShort(bufferPosition).toUShort() + } + + private fun readDoubleValue(bufferPosition: Int): Double { + return buffer.getDouble(bufferPosition) + } + + private fun readIntValue(bufferPosition: Int): Int { + return buffer.getInt(bufferPosition) + } + + private fun readBooleanValue(bufferPosition: Int): Boolean { + return readIntValue(bufferPosition) == 1 + } + + private fun readStringValue(bufferPosition: Int): String { + val offset = offsetForDynamicData + buffer.getInt(bufferPosition) + val sizeOfString = buffer.getInt(offset) + val result = ByteArray(sizeOfString) + val stringOffset = offset + Int.SIZE_BYTES + buffer.position(stringOffset) + buffer[result, 0, sizeOfString] + return String(result) + } + + private fun readMapBufferValue(position: Int): ReadableMapBuffer { + val offset = offsetForDynamicData + buffer.getInt(position) + val sizeMapBuffer = buffer.getInt(offset) + val newBuffer = ByteArray(sizeMapBuffer) + val bufferOffset = offset + Int.SIZE_BYTES + buffer.position(bufferOffset) + buffer[newBuffer, 0, sizeMapBuffer] + return ReadableMapBuffer(ByteBuffer.wrap(newBuffer)) + } + + private fun getKeyOffsetForBucketIndex(bucketIndex: Int): Int { + return HEADER_SIZE + BUCKET_SIZE * bucketIndex + } + + override fun contains(key: Int): Boolean { + // TODO T83483191: Add tests + return getBucketIndexForKey(key) != -1 + } + + override fun getKeyOffset(key: Int): Int = getBucketIndexForKey(key) + + override fun entryAt(offset: Int): MapBuffer.Entry = + MapBufferEntry(getKeyOffsetForBucketIndex(offset)) + + override fun getType(key: Int): MapBuffer.DataType { + val bucketIndex = getBucketIndexForKey(key) + require(bucketIndex != -1) { "Key not found: $key" } + return readDataType(bucketIndex) + } + + override fun getInt(key: Int): Int = + readIntValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.INT)) + + override fun getDouble(key: Int): Double = + readDoubleValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.DOUBLE)) + + override fun getString(key: Int): String = + readStringValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.STRING)) + + override fun getBoolean(key: Int): Boolean = + readBooleanValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.BOOL)) + + override fun getMapBuffer(key: Int): ReadableMapBuffer = + readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) + + override fun hashCode(): Int { + buffer.rewind() + return buffer.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (other !is ReadableMapBuffer) { + return false + } + val thisByteBuffer = buffer + val otherByteBuffer = other.buffer + if (thisByteBuffer === otherByteBuffer) { + return true + } + thisByteBuffer.rewind() + otherByteBuffer.rewind() + return thisByteBuffer == otherByteBuffer + } + + override fun toString(): String { + val builder = StringBuilder("{") + for (entry in this) { + val key = entry.key + builder.append(key) + builder.append('=') + when (entry.type) { + MapBuffer.DataType.BOOL -> builder.append(entry.booleanValue) + MapBuffer.DataType.INT -> builder.append(entry.intValue) + MapBuffer.DataType.DOUBLE -> builder.append(entry.doubleValue) + MapBuffer.DataType.STRING -> builder.append(entry.stringValue) + MapBuffer.DataType.MAP -> builder.append(entry.mapBufferValue.toString()) + } + builder.append(',') + } + builder.append('}') + return builder.toString() + } + + override fun iterator(): Iterator { + return object : Iterator { + var current = 0 + val last = count - 1 + + override fun hasNext(): Boolean { + return current <= last + } + + override fun next(): MapBuffer.Entry { + return MapBufferEntry(getKeyOffsetForBucketIndex(current++)) + } + } + } + + private inner class MapBufferEntry(private val bucketOffset: Int) : MapBuffer.Entry { + private fun assertType(expected: MapBuffer.DataType) { + val dataType = type + check(!(expected !== dataType)) { + ("Expected " + + expected + + " for key: " + + key + + " found " + + dataType.toString() + + " instead.") + } + } + + override val key: Int + get() = readUnsignedShort(bucketOffset).toInt() + override val type: MapBuffer.DataType + get() = MapBuffer.DataType.values()[readUnsignedShort(bucketOffset + TYPE_OFFSET).toInt()] + + override val doubleValue: Double + get() { + assertType(MapBuffer.DataType.DOUBLE) + return readDoubleValue(bucketOffset + VALUE_OFFSET) + } + + override val intValue: Int + get() { + assertType(MapBuffer.DataType.INT) + return readIntValue(bucketOffset + VALUE_OFFSET) + } + + override val booleanValue: Boolean + get() { + assertType(MapBuffer.DataType.BOOL) + return readBooleanValue(bucketOffset + VALUE_OFFSET) + } + + override val stringValue: String + get() { + assertType(MapBuffer.DataType.STRING) + return readStringValue(bucketOffset + VALUE_OFFSET) + } + + override val mapBufferValue: MapBuffer + get() { + assertType(MapBuffer.DataType.MAP) + return readMapBufferValue(bucketOffset + VALUE_OFFSET) + } + } + + 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 + + // 10 bytes = 2 (key) + 2 (type) + 8 (value) + private const val BUCKET_SIZE = 12 + + // 2 bytes = 2 (key) + private const val TYPE_OFFSET = 2 + + // 4 bytes = 2 (key) + 2 (type) + private const val VALUE_OFFSET = 4 + + init { + MapBufferSoLoader.staticInit() + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java deleted file mode 100644 index b03ef0f8b06a71..00000000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBufferSoLoader.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.common.mapbuffer; - -import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE; - -import com.facebook.react.bridge.ReactMarker; -import com.facebook.react.bridge.ReactMarkerConstants; -import com.facebook.soloader.SoLoader; -import com.facebook.systrace.Systrace; - -public class ReadableMapBufferSoLoader { - private static volatile boolean sDidInit = false; - - public static void staticInit() { - if (sDidInit) { - return; - } - Systrace.beginSection( - Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, - "ReadableMapBufferSoLoader.staticInit::load:mapbufferjni"); - ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_START); - SoLoader.loadLibrary("mapbufferjni"); - ReactMarker.logMarker(ReactMarkerConstants.LOAD_REACT_NATIVE_MAPBUFFER_SO_FILE_END); - Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); - sDidInit = true; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt index 81656037c3a489..7cc67f9e070cb2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/WritableMapBuffer.kt @@ -164,7 +164,7 @@ class WritableMapBuffer : MapBuffer { companion object { init { - ReadableMapBufferSoLoader.staticInit() + MapBufferSoLoader.staticInit() } } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK index ecdd934d07c96b..28be0c856ba205 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/BUCK @@ -14,6 +14,7 @@ rn_android_library( "pfh:ReactNative_CommonInfrastructurePlaceholde", "supermodule:xplat/default/public.react_native.infra", ], + language = "KOTLIN", provided_deps = [ react_native_dep("third-party/android/androidx:annotation"), react_native_dep("third-party/android/androidx:core"), @@ -21,6 +22,7 @@ rn_android_library( react_native_dep("third-party/android/androidx:legacy-support-core-ui"), react_native_dep("third-party/android/androidx:legacy-support-core-utils"), ], + pure_kotlin = False, required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java index 1dfe46d75f40e1..b381aec166722a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricJSIModuleProvider.java @@ -11,7 +11,7 @@ import com.facebook.react.bridge.JSIModuleProvider; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UIManager; -import com.facebook.react.common.mapbuffer.ReadableMapBufferSoLoader; +import com.facebook.react.common.mapbuffer.MapBufferSoLoader; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.fabric.events.EventBeatManager; import com.facebook.react.uimanager.ViewManagerRegistry; @@ -46,7 +46,7 @@ public UIManager get() { final Binding binding = new Binding(); if (ReactFeatureFlags.isMapBufferSerializationEnabled()) { - ReadableMapBufferSoLoader.staticInit(); + MapBufferSoLoader.staticInit(); } binding.register( diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java index 74305bfacbb8fc..30ebf9dc040c84 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.java @@ -23,7 +23,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.RetryableMountingLayerException; import com.facebook.react.bridge.UiThreadUtil; -import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.fabric.FabricUIManager; import com.facebook.react.fabric.events.EventEmitterWrapper; import com.facebook.react.fabric.mounting.mountitems.MountItem; @@ -396,9 +396,9 @@ public long measure( public long measureMapBuffer( @NonNull ReactContext context, @NonNull String componentName, - @NonNull ReadableMapBuffer localData, - @NonNull ReadableMapBuffer props, - @Nullable ReadableMapBuffer state, + @NonNull MapBuffer localData, + @NonNull MapBuffer props, + @Nullable MapBuffer state, float width, @NonNull YogaMeasureMode widthMode, float height, diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java index 3a0879c98a5bdf..34019c736368df 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java @@ -15,7 +15,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.touch.JSResponderHandler; import com.facebook.react.touch.ReactInterceptingViewGroup; import com.facebook.react.uimanager.annotations.ReactProp; @@ -333,9 +333,9 @@ public long measure( * ViewManager * * @param context {@link com.facebook.react.bridge.ReactContext} used for the view. - * @param localData {@link ReadableMapBuffer} containing "local data" defined in C++ - * @param props {@link ReadableMapBuffer} containing JS props - * @param state {@link ReadableMapBuffer} containing state defined in C++ + * @param localData {@link MapBuffer} containing "local data" defined in C++ + * @param props {@link MapBuffer} containing JS props + * @param state {@link MapBuffer} containing state defined in C++ * @param width width of the view (usually zero) * @param widthMode widthMode used during calculation of layout * @param height height of the view (usually zero) @@ -353,10 +353,10 @@ public long measure( */ public long measure( Context context, - ReadableMapBuffer localData, - ReadableMapBuffer props, + MapBuffer localData, + MapBuffer props, // TODO(T114731225): review whether state parameter is needed - @Nullable ReadableMapBuffer state, + @Nullable MapBuffer state, float width, YogaMeasureMode widthMode, float height, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK index d9bf82ff8e7c42..b011038126d7d4 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/BUCK @@ -9,6 +9,8 @@ rn_android_library( "pfh:ReactNative_CommonInfrastructurePlaceholde", "supermodule:xplat/default/public.react_native.infra", ], + language = "KOTLIN", + pure_kotlin = False, required_for_source_only_abi = True, visibility = [ "PUBLIC", diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index 6aad6e761e3098..b26cefbceab5b1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -15,7 +15,7 @@ import com.facebook.react.bridge.ReadableNativeMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.common.annotations.VisibleForTesting; -import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.config.ReactFeatureFlags; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.IViewManagerWithChildren; @@ -112,7 +112,7 @@ public Object updateState( } if (ReactFeatureFlags.isMapBufferSerializationEnabled()) { - ReadableMapBuffer stateMapBuffer = stateWrapper.getStateDataMapBuffer(); + MapBuffer stateMapBuffer = stateWrapper.getStateDataMapBuffer(); if (stateMapBuffer != null) { return getReactTextUpdate(view, props, stateMapBuffer); } @@ -142,11 +142,10 @@ public Object updateState( TextAttributeProps.getJustificationMode(props)); } - private Object getReactTextUpdate( - ReactTextView view, ReactStylesDiffMap props, ReadableMapBuffer state) { + private Object getReactTextUpdate(ReactTextView view, ReactStylesDiffMap props, MapBuffer state) { - ReadableMapBuffer attributedString = state.getMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING); - ReadableMapBuffer paragraphAttributes = state.getMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES); + MapBuffer attributedString = state.getMapBuffer(TX_STATE_KEY_ATTRIBUTED_STRING); + MapBuffer paragraphAttributes = state.getMapBuffer(TX_STATE_KEY_PARAGRAPH_ATTRIBUTES); Spannable spanned = TextLayoutManagerMapBuffer.getOrCreateSpannableForText( view.getContext(), attributedString, mReactTextViewManagerCallback); @@ -205,9 +204,9 @@ public long measure( @Override public long measure( Context context, - ReadableMapBuffer localData, - ReadableMapBuffer props, - @Nullable ReadableMapBuffer state, + MapBuffer localData, + MapBuffer props, + @Nullable MapBuffer state, float width, YogaMeasureMode widthMode, float height, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java index e3f15b9ebe6fcb..8706e70b102b6c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextAttributeProps.java @@ -16,7 +16,7 @@ import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.ReactAccessibilityDelegate; import com.facebook.react.uimanager.ReactStylesDiffMap; @@ -137,51 +137,48 @@ public class TextAttributeProps { private TextAttributeProps() {} - /** - * Build a TextAttributeProps using data from the {@link ReadableMapBuffer} received as a - * parameter. - */ - public static TextAttributeProps fromReadableMapBuffer(ReadableMapBuffer props) { + /** Build a TextAttributeProps using data from the {@link MapBuffer} received as a parameter. */ + public static TextAttributeProps fromMapBuffer(MapBuffer props) { TextAttributeProps result = new TextAttributeProps(); // TODO T83483191: Review constants that are not being set! - Iterator iterator = props.iterator(); + Iterator iterator = props.iterator(); while (iterator.hasNext()) { - ReadableMapBuffer.MapBufferEntry entry = iterator.next(); + MapBuffer.Entry entry = iterator.next(); switch (entry.getKey()) { case TA_KEY_FOREGROUND_COLOR: - result.setColor(entry.getInt()); + result.setColor(entry.getIntValue()); break; case TA_KEY_BACKGROUND_COLOR: - result.setBackgroundColor(entry.getInt()); + result.setBackgroundColor(entry.getIntValue()); break; case TA_KEY_OPACITY: break; case TA_KEY_FONT_FAMILY: - result.setFontFamily(entry.getString()); + result.setFontFamily(entry.getStringValue()); break; case TA_KEY_FONT_SIZE: - result.setFontSize((float) entry.getDouble()); + result.setFontSize((float) entry.getDoubleValue()); break; case TA_KEY_FONT_SIZE_MULTIPLIER: break; case TA_KEY_FONT_WEIGHT: - result.setFontWeight(entry.getString()); + result.setFontWeight(entry.getStringValue()); break; case TA_KEY_FONT_STYLE: - result.setFontStyle(entry.getString()); + result.setFontStyle(entry.getStringValue()); break; case TA_KEY_FONT_VARIANT: - result.setFontVariant(entry.getReadableMapBuffer()); + result.setFontVariant(entry.getMapBufferValue()); break; case TA_KEY_ALLOW_FONT_SCALING: - result.setAllowFontScaling(entry.getBoolean()); + result.setAllowFontScaling(entry.getBooleanValue()); break; case TA_KEY_LETTER_SPACING: - result.setLetterSpacing((float) entry.getDouble()); + result.setLetterSpacing((float) entry.getDoubleValue()); break; case TA_KEY_LINE_HEIGHT: - result.setLineHeight((float) entry.getDouble()); + result.setLineHeight((float) entry.getDoubleValue()); break; case TA_KEY_ALIGNMENT: break; @@ -190,23 +187,23 @@ public static TextAttributeProps fromReadableMapBuffer(ReadableMapBuffer props) case TA_KEY_TEXT_DECORATION_COLOR: break; case TA_KEY_TEXT_DECORATION_LINE: - result.setTextDecorationLine(entry.getString()); + result.setTextDecorationLine(entry.getStringValue()); break; case TA_KEY_TEXT_DECORATION_STYLE: break; case TA_KEY_TEXT_SHADOW_RADIUS: - result.setTextShadowRadius((float) entry.getDouble()); + result.setTextShadowRadius((float) entry.getDoubleValue()); break; case TA_KEY_TEXT_SHADOW_COLOR: - result.setTextShadowColor(entry.getInt()); + result.setTextShadowColor(entry.getIntValue()); break; case TA_KEY_IS_HIGHLIGHTED: break; case TA_KEY_LAYOUT_DIRECTION: - result.setLayoutDirection(entry.getString()); + result.setLayoutDirection(entry.getStringValue()); break; case TA_KEY_ACCESSIBILITY_ROLE: - result.setAccessibilityRole(entry.getString()); + result.setAccessibilityRole(entry.getStringValue()); break; } } @@ -419,17 +416,17 @@ private void setFontVariant(@Nullable ReadableArray fontVariant) { mFontFeatureSettings = ReactTypefaceUtils.parseFontVariant(fontVariant); } - private void setFontVariant(@Nullable ReadableMapBuffer fontVariant) { + private void setFontVariant(@Nullable MapBuffer fontVariant) { if (fontVariant == null || fontVariant.getCount() == 0) { mFontFeatureSettings = null; return; } List features = new ArrayList<>(); - Iterator iterator = fontVariant.iterator(); + Iterator iterator = fontVariant.iterator(); while (iterator.hasNext()) { - ReadableMapBuffer.MapBufferEntry entry = iterator.next(); - String value = entry.getString(); + MapBuffer.Entry entry = iterator.next(); + String value = entry.getStringValue(); if (value != null) { switch (value) { case "small-caps": diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java index 81b8af845dca46..bf59c915de0403 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/TextLayoutManagerMapBuffer.java @@ -28,7 +28,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.bridge.WritableArray; import com.facebook.react.common.build.ReactBuildConfig; -import com.facebook.react.common.mapbuffer.ReadableMapBuffer; +import com.facebook.react.common.mapbuffer.MapBuffer; import com.facebook.react.uimanager.PixelUtil; import com.facebook.yoga.YogaConstants; import com.facebook.yoga.YogaMeasureMode; @@ -78,7 +78,7 @@ public class TextLayoutManagerMapBuffer { private static final Object sSpannableCacheLock = new Object(); private static final boolean DEFAULT_INCLUDE_FONT_PADDING = true; - private static final LruCache sSpannableCache = + private static final LruCache sSpannableCache = new LruCache<>(spannableCacheSize); private static final ConcurrentHashMap sTagToSpannableCache = new ConcurrentHashMap<>(); @@ -97,39 +97,36 @@ public static void deleteCachedSpannableForTag(int reactTag) { sTagToSpannableCache.remove(reactTag); } - public static boolean isRTL(ReadableMapBuffer attributedString) { - ReadableMapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS); + public static boolean isRTL(MapBuffer attributedString) { + MapBuffer fragments = attributedString.getMapBuffer(AS_KEY_FRAGMENTS); if (fragments.getCount() == 0) { return false; } - ReadableMapBuffer fragment = fragments.getMapBuffer((short) 0); - ReadableMapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES); + MapBuffer fragment = fragments.getMapBuffer((short) 0); + MapBuffer textAttributes = fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES); return TextAttributeProps.getLayoutDirection( textAttributes.getString(TextAttributeProps.TA_KEY_LAYOUT_DIRECTION)) == LayoutDirection.RTL; } private static void buildSpannableFromFragment( - Context context, - ReadableMapBuffer fragments, - SpannableStringBuilder sb, - List ops) { + Context context, MapBuffer fragments, SpannableStringBuilder sb, List ops) { for (int i = 0, length = fragments.getCount(); i < length; i++) { - ReadableMapBuffer fragment = fragments.getMapBuffer(i); + MapBuffer fragment = fragments.getMapBuffer(i); int start = sb.length(); TextAttributeProps textAttributes = - TextAttributeProps.fromReadableMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES)); + TextAttributeProps.fromMapBuffer(fragment.getMapBuffer(FR_KEY_TEXT_ATTRIBUTES)); sb.append( TextTransform.apply(fragment.getString(FR_KEY_STRING), textAttributes.mTextTransform)); int end = sb.length(); int reactTag = - fragment.hasKey(FR_KEY_REACT_TAG) ? fragment.getInt(FR_KEY_REACT_TAG) : View.NO_ID; - if (fragment.hasKey(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) { + fragment.contains(FR_KEY_REACT_TAG) ? fragment.getInt(FR_KEY_REACT_TAG) : View.NO_ID; + if (fragment.contains(FR_KEY_IS_ATTACHMENT) && fragment.getBoolean(FR_KEY_IS_ATTACHMENT)) { float width = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_WIDTH)); float height = PixelUtil.toPixelFromSP(fragment.getDouble(FR_KEY_HEIGHT)); ops.add( @@ -203,7 +200,7 @@ private static void buildSpannableFromFragment( // public because both ReactTextViewManager and ReactTextInputManager need to use this public static Spannable getOrCreateSpannableForText( Context context, - ReadableMapBuffer attributedString, + MapBuffer attributedString, @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { Spannable preparedSpannableText; @@ -228,7 +225,7 @@ public static Spannable getOrCreateSpannableForText( private static Spannable createSpannableFromAttributedString( Context context, - ReadableMapBuffer attributedString, + MapBuffer attributedString, @Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) { SpannableStringBuilder sb = new SpannableStringBuilder(); @@ -351,8 +348,8 @@ private static Layout createLayout( public static long measureText( Context context, - ReadableMapBuffer attributedString, - ReadableMapBuffer paragraphAttributes, + MapBuffer attributedString, + MapBuffer paragraphAttributes, float width, YogaMeasureMode widthYogaMeasureMode, float height, @@ -363,7 +360,7 @@ public static long measureText( // TODO(5578671): Handle text direction (see View#getTextDirectionHeuristic) TextPaint textPaint = sTextPaintInstance; Spannable text; - if (attributedString.hasKey(AS_KEY_CACHE_ID)) { + if (attributedString.contains(AS_KEY_CACHE_ID)) { int cacheId = attributedString.getInt(AS_KEY_CACHE_ID); if (ENABLE_MEASURE_LOGGING) { FLog.e(TAG, "Get cached spannable for cacheId[" + cacheId + "]"); @@ -387,7 +384,7 @@ public static long measureText( TextAttributeProps.getTextBreakStrategy( paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); boolean includeFontPadding = - paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING) + paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) : DEFAULT_INCLUDE_FONT_PADDING; int hyphenationFrequency = @@ -415,7 +412,7 @@ public static long measureText( hyphenationFrequency); int maximumNumberOfLines = - paragraphAttributes.hasKey(PA_KEY_MAX_NUMBER_OF_LINES) + paragraphAttributes.contains(PA_KEY_MAX_NUMBER_OF_LINES) ? paragraphAttributes.getInt(PA_KEY_MAX_NUMBER_OF_LINES) : UNSET; @@ -554,8 +551,8 @@ public static long measureText( public static WritableArray measureLines( @NonNull Context context, - ReadableMapBuffer attributedString, - ReadableMapBuffer paragraphAttributes, + MapBuffer attributedString, + MapBuffer paragraphAttributes, float width) { TextPaint textPaint = sTextPaintInstance; @@ -566,7 +563,7 @@ public static WritableArray measureLines( TextAttributeProps.getTextBreakStrategy( paragraphAttributes.getString(PA_KEY_TEXT_BREAK_STRATEGY)); boolean includeFontPadding = - paragraphAttributes.hasKey(PA_KEY_INCLUDE_FONT_PADDING) + paragraphAttributes.contains(PA_KEY_INCLUDE_FONT_PADDING) ? paragraphAttributes.getBoolean(PA_KEY_INCLUDE_FONT_PADDING) : DEFAULT_INCLUDE_FONT_PADDING; int hyphenationFrequency = diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt index 11a281778c80b4..5ee668497e6189 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactMapBufferPropSetter.kt @@ -14,7 +14,7 @@ import com.facebook.react.bridge.DynamicFromObject import com.facebook.react.bridge.JavaOnlyArray import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.ReadableMap -import com.facebook.react.common.mapbuffer.ReadableMapBuffer +import com.facebook.react.common.mapbuffer.MapBuffer import com.facebook.react.uimanager.PixelUtil import com.facebook.react.uimanager.PointerEvents @@ -97,134 +97,131 @@ object ReactMapBufferPropSetter { private const val UNDEF_COLOR = Int.MAX_VALUE - fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: ReadableMapBuffer) { + fun setProps(view: ReactViewGroup, viewManager: ReactViewManager, props: MapBuffer) { for (entry in props) { when (entry.key) { VP_ACCESSIBILITY_ACTIONS -> { - viewManager.accessibilityActions(view, entry.readableMapBuffer) + viewManager.accessibilityActions(view, entry.mapBufferValue) } VP_ACCESSIBILITY_HINT -> { - viewManager.setAccessibilityHint(view, entry.string.takeIf { it.isNotEmpty() }) + viewManager.setAccessibilityHint(view, entry.stringValue.takeIf { it.isNotEmpty() }) } VP_ACCESSIBILITY_LABEL -> { - viewManager.setAccessibilityLabel(view, entry.string.takeIf { it.isNotEmpty() }) + viewManager.setAccessibilityLabel(view, entry.stringValue.takeIf { it.isNotEmpty() }) } VP_ACCESSIBILITY_LABELLED_BY -> { - viewManager.accessibilityLabelledBy(view, entry.readableMapBuffer) + viewManager.accessibilityLabelledBy(view, entry.mapBufferValue) } VP_ACCESSIBILITY_LIVE_REGION -> { - view.accessibilityLiveRegion(entry.int) + view.accessibilityLiveRegion(entry.intValue) } VP_ACCESSIBILITY_ROLE -> { - viewManager.setAccessibilityRole(view, entry.string.takeIf { it.isNotEmpty() }) + viewManager.setAccessibilityRole(view, entry.stringValue.takeIf { it.isNotEmpty() }) } VP_ACCESSIBILITY_STATE -> { - viewManager.accessibilityState(view, entry.readableMapBuffer) + viewManager.accessibilityState(view, entry.mapBufferValue) } VP_ACCESSIBILITY_VALUE -> { - viewManager.accessibilityValue(view, entry.string) + viewManager.accessibilityValue(view, entry.stringValue) } VP_ACCESSIBLE -> { - viewManager.setAccessible(view, entry.boolean) + viewManager.setAccessible(view, entry.booleanValue) } VP_BACKFACE_VISIBILITY -> { - viewManager.backfaceVisibility(view, entry.int) + viewManager.backfaceVisibility(view, entry.intValue) } VP_BG_COLOR -> { // TODO: color for some reason can be object in Java but not in C++ - viewManager.backgroundColor(view, entry.int) + viewManager.backgroundColor(view, entry.intValue) } VP_BORDER_COLOR -> { - viewManager.borderColor(view, entry.readableMapBuffer) + viewManager.borderColor(view, entry.mapBufferValue) } VP_BORDER_RADII -> { - viewManager.borderRadius(view, entry.readableMapBuffer) + viewManager.borderRadius(view, entry.mapBufferValue) } VP_BORDER_STYLE -> { - viewManager.borderStyle(view, entry.int) + viewManager.borderStyle(view, entry.intValue) } VP_ELEVATION -> { - viewManager.setElevation(view, entry.double.toFloat()) + viewManager.setElevation(view, entry.doubleValue.toFloat()) } VP_FOCUSABLE -> { - viewManager.setFocusable(view, entry.boolean) + viewManager.setFocusable(view, entry.booleanValue) } VP_HAS_TV_FOCUS -> { - viewManager.setTVPreferredFocus(view, entry.boolean) + viewManager.setTVPreferredFocus(view, entry.booleanValue) } VP_HIT_SLOP -> { - view.hitSlop(entry.readableMapBuffer) + view.hitSlop(entry.mapBufferValue) } VP_IMPORTANT_FOR_ACCESSIBILITY -> { - view.importantForAccessibility(entry.int) + view.importantForAccessibility(entry.intValue) } VP_NATIVE_BACKGROUND -> { - viewManager.nativeBackground(view, entry.readableMapBuffer) + viewManager.nativeBackground(view, entry.mapBufferValue) } VP_NATIVE_FOREGROUND -> { - viewManager.nativeForeground(view, entry.readableMapBuffer) + viewManager.nativeForeground(view, entry.mapBufferValue) } VP_NATIVE_ID -> { - viewManager.setNativeId(view, entry.string.takeIf { it.isNotEmpty() }) + viewManager.setNativeId(view, entry.stringValue.takeIf { it.isNotEmpty() }) } VP_OFFSCREEN_ALPHA_COMPOSITING -> { - viewManager.setNeedsOffscreenAlphaCompositing(view, entry.boolean) + viewManager.setNeedsOffscreenAlphaCompositing(view, entry.booleanValue) } VP_OPACITY -> { - viewManager.setOpacity(view, entry.double.toFloat()) + viewManager.setOpacity(view, entry.doubleValue.toFloat()) } VP_POINTER_EVENTS -> { - view.pointerEvents(entry.int) + view.pointerEvents(entry.intValue) } VP_POINTER_ENTER -> { - viewManager.setPointerEnter(view, entry.boolean) + viewManager.setPointerEnter(view, entry.booleanValue) } VP_POINTER_LEAVE -> { - viewManager.setPointerLeave(view, entry.boolean) + viewManager.setPointerLeave(view, entry.booleanValue) } VP_POINTER_MOVE -> { - viewManager.setPointerMove(view, entry.boolean) + viewManager.setPointerMove(view, entry.booleanValue) } VP_REMOVE_CLIPPED_SUBVIEW -> { - viewManager.setRemoveClippedSubviews(view, entry.boolean) + viewManager.setRemoveClippedSubviews(view, entry.booleanValue) } VP_RENDER_TO_HARDWARE_TEXTURE -> { - viewManager.setRenderToHardwareTexture(view, entry.boolean) + viewManager.setRenderToHardwareTexture(view, entry.booleanValue) } VP_SHADOW_COLOR -> { // TODO: color for some reason can be object in Java but not in C++ - viewManager.shadowColor(view, entry.int) + viewManager.shadowColor(view, entry.intValue) } VP_TEST_ID -> { - viewManager.setTestId(view, entry.string.takeIf { it.isNotEmpty() }) + viewManager.setTestId(view, entry.stringValue.takeIf { it.isNotEmpty() }) } VP_TRANSFORM -> { - viewManager.transform(view, entry.readableMapBuffer) + viewManager.transform(view, entry.mapBufferValue) } VP_ZINDEX -> { - viewManager.setZIndex(view, entry.int.toFloat()) + viewManager.setZIndex(view, entry.intValue.toFloat()) } YG_BORDER_WIDTH -> { - viewManager.borderWidth(view, entry.readableMapBuffer) + viewManager.borderWidth(view, entry.mapBufferValue) } YG_OVERFLOW -> { - viewManager.overflow(view, entry.int) + viewManager.overflow(view, entry.intValue) } } } } - private fun ReactViewManager.accessibilityActions( - view: ReactViewGroup, - mapBuffer: ReadableMapBuffer - ) { + private fun ReactViewManager.accessibilityActions(view: ReactViewGroup, mapBuffer: MapBuffer) { val actions = mutableListOf() for (entry in mapBuffer) { val map = JavaOnlyMap() - val action = entry.readableMapBuffer + val action = entry.mapBufferValue if (action != null) { map.putString("name", action.getString(ACCESSIBILITY_ACTION_NAME)) - if (action.hasKey(ACCESSIBILITY_ACTION_LABEL)) { + if (action.contains(ACCESSIBILITY_ACTION_LABEL)) { map.putString("label", action.getString(ACCESSIBILITY_ACTION_LABEL)) } } @@ -245,7 +242,7 @@ object ReactMapBufferPropSetter { ViewCompat.setAccessibilityLiveRegion(this, mode) } - private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.accessibilityState(view: ReactViewGroup, value: MapBuffer) { val accessibilityState = JavaOnlyMap() accessibilityState.putBoolean("selected", value.getBoolean(ACCESSIBILITY_STATE_SELECTED)) accessibilityState.putBoolean("busy", value.getBoolean(ACCESSIBILITY_STATE_BUSY)) @@ -273,17 +270,14 @@ object ReactMapBufferPropSetter { setAccessibilityValue(view, map) } - private fun ReactViewManager.accessibilityLabelledBy( - view: ReactViewGroup, - value: ReadableMapBuffer - ) { + private fun ReactViewManager.accessibilityLabelledBy(view: ReactViewGroup, value: MapBuffer) { val converted = if (value.count == 0) { DynamicFromObject(null) } else { val array = JavaOnlyArray() for (label in value) { - array.pushString(label.string) + array.pushString(label.stringValue) } DynamicFromObject(array) } @@ -306,7 +300,7 @@ object ReactMapBufferPropSetter { setBackgroundColor(view, color) } - private fun ReactViewManager.borderColor(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.borderColor(view: ReactViewGroup, value: MapBuffer) { for (entry in value) { val index = when (val key = entry.key) { @@ -319,12 +313,12 @@ object ReactMapBufferPropSetter { EDGE_END -> 6 else -> throw IllegalArgumentException("Unknown key for border color: $key") } - val colorValue = entry.int + val colorValue = entry.intValue setBorderColor(view, index, colorValue.takeIf { it != -1 }) } } - private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.borderRadius(view: ReactViewGroup, value: MapBuffer) { for (entry in value) { val index = when (val key = entry.key) { @@ -339,7 +333,7 @@ object ReactMapBufferPropSetter { CORNER_BOTTOM_END -> 8 else -> throw IllegalArgumentException("Unknown key for border style: $key") } - val borderRadius = entry.double + val borderRadius = entry.doubleValue if (!borderRadius.isNaN()) { setBorderRadius(view, index, borderRadius.toFloat()) } @@ -357,7 +351,7 @@ object ReactMapBufferPropSetter { setBorderStyle(view, stringValue) } - private fun ReactViewGroup.hitSlop(value: ReadableMapBuffer) { + private fun ReactViewGroup.hitSlop(value: MapBuffer) { val rect = Rect( PixelUtil.toPixelFromDIP(value.getDouble(EDGE_LEFT)).toInt(), @@ -392,15 +386,15 @@ object ReactMapBufferPropSetter { setPointerEvents(pointerEvents) } - private fun ReactViewManager.transform(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.transform(view: ReactViewGroup, value: MapBuffer) { val list = JavaOnlyArray() for (entry in value) { - list.pushDouble(entry.double) + list.pushDouble(entry.doubleValue) } setTransform(view, list) } - private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.borderWidth(view: ReactViewGroup, value: MapBuffer) { for (entry in value) { val index = when (val key = entry.key) { @@ -413,7 +407,7 @@ object ReactMapBufferPropSetter { EDGE_END -> 6 else -> throw IllegalArgumentException("Unknown key for border width: $key") } - val borderWidth = entry.double + val borderWidth = entry.doubleValue if (!borderWidth.isNaN()) { setBorderWidth(view, index, borderWidth.toFloat()) } @@ -437,15 +431,15 @@ object ReactMapBufferPropSetter { setShadowColor(view, color) } - private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.nativeBackground(view: ReactViewGroup, value: MapBuffer) { setNativeBackground(view, value.toJsDrawableDescription()) } - private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: ReadableMapBuffer) { + private fun ReactViewManager.nativeForeground(view: ReactViewGroup, value: MapBuffer) { setNativeForeground(view, value.toJsDrawableDescription()) } - private fun ReadableMapBuffer.toJsDrawableDescription(): ReadableMap? { + private fun MapBuffer.toJsDrawableDescription(): ReadableMap? { if (count == 0) { return null } @@ -459,11 +453,11 @@ object ReactMapBufferPropSetter { } 1 -> { result.putString("type", "RippleAndroid") - if (hasKey(NATIVE_DRAWABLE_COLOR)) { + if (contains(NATIVE_DRAWABLE_COLOR)) { result.putInt("color", getInt(NATIVE_DRAWABLE_COLOR)) } result.putBoolean("borderless", getBoolean(NATIVE_DRAWABLE_BORDERLESS)) - if (hasKey(NATIVE_DRAWABLE_RIPPLE_RADIUS)) { + if (contains(NATIVE_DRAWABLE_RIPPLE_RADIUS)) { result.putDouble("rippleRadius", getDouble(NATIVE_DRAWABLE_RIPPLE_RADIUS)) } }