diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt index db6f49de94d76b..481dd409798fb8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/MapBuffer.kt @@ -126,6 +126,16 @@ interface MapBuffer : Iterable { */ fun getMapBuffer(key: Int): MapBuffer + /** + * Provides parsed [List] value if the entry for given key exists with [DataType.MAP] + * type + * @param key key to lookup [List] 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 + */ + fun getMapBufferList(key: Int): List + /** Iterable entry representing parsed MapBuffer values */ interface Entry { /** 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 index c3143431d7aa0a..e1bef54ad15f34 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt +++ b/ReactAndroid/src/main/java/com/facebook/react/common/mapbuffer/ReadableMapBuffer.kt @@ -137,6 +137,24 @@ class ReadableMapBuffer : MapBuffer { return ReadableMapBuffer(ByteBuffer.wrap(newBuffer)) } + private fun readMapBufferListValue(position: Int): List { + val readMapBufferList = arrayListOf() + var offset = offsetForDynamicData + buffer.getInt(position) + val sizeMapBufferList = buffer.getInt(offset) + offset += Int.SIZE_BYTES + var curLen = 0 + while (curLen < sizeMapBufferList) { + val sizeMapBuffer = buffer.getInt(offset + curLen) + val newMapBuffer = ByteArray(sizeMapBuffer) + curLen = curLen + Int.SIZE_BYTES + buffer.position(offset + curLen) + buffer[newMapBuffer, 0, sizeMapBuffer] + readMapBufferList.add(ReadableMapBuffer(ByteBuffer.wrap(newMapBuffer))) + curLen = curLen + sizeMapBuffer + } + return readMapBufferList + } + private fun getKeyOffsetForBucketIndex(bucketIndex: Int): Int { return HEADER_SIZE + BUCKET_SIZE * bucketIndex } @@ -172,6 +190,9 @@ class ReadableMapBuffer : MapBuffer { override fun getMapBuffer(key: Int): ReadableMapBuffer = readMapBufferValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) + override fun getMapBufferList(key: Int): List = + readMapBufferListValue(getTypedValueOffsetForKey(key, MapBuffer.DataType.MAP)) + override fun hashCode(): Int { buffer.rewind() return buffer.hashCode() 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 7cc67f9e070cb2..bb27c26c1ef09f 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 @@ -106,6 +106,8 @@ class WritableMapBuffer : MapBuffer { override fun getMapBuffer(key: Int): MapBuffer = verifyValue(key, values.get(key)) + override fun getMapBufferList(key: Int): List = 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" } diff --git a/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp b/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp index 94d8c87bd323a4..eaffddf401ba2f 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp +++ b/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp @@ -112,6 +112,31 @@ MapBuffer MapBuffer::getMapBuffer(Key key) const { return MapBuffer(std::move(value)); } +std::vector MapBuffer::getMapBufferList(MapBuffer::Key key) const { + std::vector mapBufferList; + + int32_t dynamicDataOffset = getDynamicDataOffset(); + int32_t offset = getInt(key); + int32_t mapBufferListLength = *reinterpret_cast( + bytes_.data() + dynamicDataOffset + offset); + offset = offset + sizeof(uint32_t); + + int32_t curLen = 0; + while (curLen < mapBufferListLength) { + int32_t mapBufferLength = *reinterpret_cast( + bytes_.data() + dynamicDataOffset + offset + curLen); + curLen = curLen + sizeof(uint32_t); + std::vector value(mapBufferLength); + memcpy( + value.data(), + bytes_.data() + dynamicDataOffset + offset + curLen, + mapBufferLength); + mapBufferList.emplace_back(std::move(value)); + curLen = curLen + mapBufferLength; + } + return mapBufferList; +} + size_t MapBuffer::size() const { return bytes_.size(); } diff --git a/ReactCommon/react/renderer/mapbuffer/MapBuffer.h b/ReactCommon/react/renderer/mapbuffer/MapBuffer.h index dcfa25ec703a26..b4088f1631ee70 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBuffer.h +++ b/ReactCommon/react/renderer/mapbuffer/MapBuffer.h @@ -126,6 +126,8 @@ class MapBuffer { // TODO T83483191: review this declaration MapBuffer getMapBuffer(MapBuffer::Key key) const; + std::vector getMapBufferList(MapBuffer::Key key) const; + size_t size() const; uint8_t const *data() const; diff --git a/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp b/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp index 93883437012bc3..24f5d1cc2fc088 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp +++ b/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp @@ -114,6 +114,39 @@ void MapBufferBuilder::putMapBuffer(MapBuffer::Key key, MapBuffer const &map) { INT_SIZE); } +void MapBufferBuilder::putMapBufferList( + MapBuffer::Key key, + const std::vector &mapBufferList) { + int32_t offset = dynamicData_.size(); + int32_t dataSize = 0; + for (const MapBuffer &mapBuffer : mapBufferList) { + dataSize = dataSize + INT_SIZE + mapBuffer.size(); + } + + dynamicData_.resize(offset + INT_SIZE, 0); + memcpy(dynamicData_.data() + offset, &dataSize, INT_SIZE); + + for (const MapBuffer &mapBuffer : mapBufferList) { + int32_t mapBufferSize = mapBuffer.size(); + int32_t dynamicDataSize = 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_ + memcpy( + dynamicData_.data() + dynamicDataSize + INT_SIZE, + mapBuffer.data(), + mapBufferSize); + } + + // Store Key and pointer to the string + storeKeyValue( + key, + MapBuffer::DataType::Map, + reinterpret_cast(&offset), + INT_SIZE); +} + static inline bool compareBuckets( MapBuffer::Bucket const &a, MapBuffer::Bucket const &b) { diff --git a/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h b/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h index aa8ae879407487..8f54bfd125432b 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h +++ b/ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h @@ -9,6 +9,7 @@ #include #include +#include namespace facebook { namespace react { @@ -35,6 +36,10 @@ class MapBufferBuilder { void putMapBuffer(MapBuffer::Key key, MapBuffer const &map); + void putMapBufferList( + MapBuffer::Key key, + const std::vector &mapBufferList); + MapBuffer build(); private: diff --git a/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp b/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp index f0d5c737541a76..f6aec1ccf4af62 100644 --- a/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp +++ b/ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -150,6 +151,30 @@ TEST(MapBufferTest, testMapEntries) { EXPECT_EQ(readMap2.getInt(1), 1234); } +TEST(MapBufferTest, testMapListEntries) { + std::vector mapBufferList; + auto builder = MapBufferBuilder(); + builder.putString(0, "This is a test"); + builder.putInt(1, 1234); + mapBufferList.push_back(builder.build()); + + auto builder2 = MapBufferBuilder(); + builder2.putInt(2, 4321); + builder2.putDouble(3, 908.1); + mapBufferList.push_back(builder2.build()); + + auto builder3 = MapBufferBuilder(); + builder3.putMapBufferList(5, std::move(mapBufferList)); + auto map = builder3.build(); + + std::vector mapBufferList2 = map.getMapBufferList(5); + + EXPECT_EQ(mapBufferList2.size(), 2); + EXPECT_EQ(mapBufferList2[0].getString(0), "This is a test"); + EXPECT_EQ(mapBufferList2[0].getInt(1), 1234); + EXPECT_EQ(mapBufferList2[1].getDouble(3), 908.1); +} + TEST(MapBufferTest, testMapRandomAccess) { auto builder = MapBufferBuilder(); builder.putInt(1234, 4321);