diff --git a/ReactCommon/react/renderer/mapbuffer/Android.mk b/ReactCommon/react/renderer/mapbuffer/Android.mk index f496322b1dd..f6c9584ba33 100644 --- a/ReactCommon/react/renderer/mapbuffer/Android.mk +++ b/ReactCommon/react/renderer/mapbuffer/Android.mk @@ -15,7 +15,7 @@ LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../../ -LOCAL_SHARED_LIBRARIES := libreact_utils +LOCAL_SHARED_LIBRARIES := libreact_utils glog libglog_init LOCAL_CFLAGS := \ -DLOG_TAG=\"Fabric\" @@ -25,3 +25,5 @@ LOCAL_CFLAGS += -fexceptions -frtti -std=c++14 -Wall include $(BUILD_SHARED_LIBRARY) $(call import-module,react/utils) +$(call import-module,glog) +$(call import-module,fbgloginit) diff --git a/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp b/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp index 2113ecf41e0..e95bdd7509d 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp +++ b/ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp @@ -7,15 +7,126 @@ #include "MapBuffer.h" +using namespace facebook::react; + namespace facebook { namespace react { -MapBuffer::MapBuffer() {} +MapBuffer::MapBuffer(int initialSize) { + _dataSize = initialSize; + _data = new Byte[_dataSize]; + // TODO: Should we clean up memory here? +} -MapBuffer::~MapBuffer() {} +void MapBuffer::makeSpace() { + int oldDataSize = _dataSize; + _dataSize *= 2; + uint8_t *_newdata = new Byte[_dataSize]; + uint8_t *_oldData = _data; + memcpy(_newdata, _data, oldDataSize); + _data = _newdata; + delete[] _oldData; +} -int MapBuffer::getSize() { - return 0; +void MapBuffer::putBytes(Key key, uint8_t *value, int valueSize) { + if (key != _header.count) { + LOG(ERROR) + << "Error: key out of order (for now keys should we stored contiguous) " + << key; + throw "Error: key out of order (for now keys should we stored contiguous) - key: " + + std::to_string(key); + } + + int valueOffset = getValueOffset(key); + if (valueOffset + valueSize > _dataSize) { + makeSpace(); + } + + memcpy(_data + getKeyOffset(key), &key, KEY_SIZE); + memcpy(_data + valueOffset, value, valueSize); + _header.count++; +} + +void MapBuffer::putBool(Key key, bool value) { + putInt(key, (int)value); +} + +void MapBuffer::putDouble(Key key, double value) { + uint8_t *bytePointer = reinterpret_cast(&value); + putBytes(key, bytePointer, DOUBLE_SIZE); +} + +void MapBuffer::putNull(Key key) { + putInt(key, NULL_VALUE); +} + +void MapBuffer::putInt(Key key, int value) { + uint8_t *bytePointer = reinterpret_cast(&(value)); + putBytes(key, bytePointer, INT_SIZE); +} + +void MapBuffer::finish() { + // Copy header at the beginning of "_data" + memcpy(_data, &_header, HEADER_SIZE); + // TODO: create a MapBufferBuilder instead of calling the finish method. +} + +// TODO: All the "getXXX" methods are currently operating on a "finished" map. +// Next step: create a MapBufferBuilder, move "putXXX" methods into the +// MapBufferBuilder, make MapBuffer class immutable. +int MapBuffer::getInt(Key key) { + checkKeyConsistency(_header, _data, key); + + int value = 0; + memcpy( + reinterpret_cast(&value), + reinterpret_cast(_data + getValueOffset(key)), + INT_SIZE); + return value; +} + +bool MapBuffer::getBool(Key key) { + return getInt(key) != 0; +} + +double MapBuffer::getDouble(Key key) { + checkKeyConsistency(_header, _data, key); + + // TODO: extract this code into a "template method" and reuse it for other + // types + double value = 0; + memcpy( + reinterpret_cast(&value), + reinterpret_cast(_data + getValueOffset(key)), + DOUBLE_SIZE); + return value; +} + +bool MapBuffer::isNull(Key key) { + return getInt(key) == NULL_VALUE; +} + +uint16_t MapBuffer::getBufferSize() { + return _dataSize; +} + +void MapBuffer::copy(uint8_t *output) { + memcpy(output, _data, _dataSize); +} + +uint16_t MapBuffer::getSize() { + uint16_t size = 0; + memcpy( + reinterpret_cast(&size), + reinterpret_cast( + _data + UINT16_SIZE), // TODO refactor this: + UINT16_SIZE describes + // the position in the header + UINT16_SIZE); + return size; +} + +MapBuffer::~MapBuffer() { + delete[] _data; } } // namespace react diff --git a/ReactCommon/react/renderer/mapbuffer/MapBuffer.h b/ReactCommon/react/renderer/mapbuffer/MapBuffer.h index 1a3e5b12e3b..a56074737d6 100644 --- a/ReactCommon/react/renderer/mapbuffer/MapBuffer.h +++ b/ReactCommon/react/renderer/mapbuffer/MapBuffer.h @@ -7,11 +7,14 @@ #pragma once -#include +#include namespace facebook { namespace react { +// 506 = 5 entries = 50*10 + 6 sizeof(header) +const int INITIAL_SIZE = 506; + /** * MapBuffer is an optimized map format for transferring data like props between * C++ and other platforms The implementation of this map is optimized to: @@ -28,11 +31,51 @@ namespace react { * - have minimal APK size and build time impact. */ class MapBuffer { - public: - MapBuffer(); - virtual ~MapBuffer(); + private: + Header _header = {ALIGNMENT, 0, 0}; - int getSize(); + void makeSpace(); + + void putBytes(Key key, uint8_t *value, int valueSize); + + // Buffer and its size + uint8_t *_data; + + uint16_t _dataSize; + + public: + MapBuffer() : MapBuffer(INITIAL_SIZE) {} + + MapBuffer(int initialSize); + + ~MapBuffer(); + + void putInt(Key key, int value); + + void putBool(Key key, bool value); + + void putDouble(Key key, double value); + + void putNull(Key key); + + // TODO: create a MapBufferBuilder instead or add checks to verify + // if it's ok to read and write the Map + void finish(); + + int getInt(Key key); + + bool getBool(Key key); + + double getDouble(Key key); + + uint16_t getBufferSize(); + + // TODO: review parameters of copy method + void copy(uint8_t *output); + + bool isNull(Key key); + + uint16_t getSize(); }; } // namespace react diff --git a/ReactCommon/react/renderer/mapbuffer/Primitives.h b/ReactCommon/react/renderer/mapbuffer/Primitives.h new file mode 100644 index 00000000000..7800ac70acc --- /dev/null +++ b/ReactCommon/react/renderer/mapbuffer/Primitives.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include + +// TODO: Enable CHECK_CONSISTENCY only in debug mode or test environments (or +// just in demand) +#define CHECK_CONSISTENCY 1 + +constexpr static int NULL_VALUE = 0; + +// Value used to verify if the data is serialized with LittleEndian order +constexpr static int ALIGNMENT = 0xFE; + +using Key = uint16_t; + +using Byte = uint8_t; + +namespace facebook { +namespace react { + +struct Header { + uint16_t alignment; // alignment of serialization + uint16_t count; // amount of items + uint16_t bufferSize; // Size of buffer that contains Strings and Objects +}; + +constexpr static int KEY_SIZE = sizeof(Key); +constexpr static int HEADER_SIZE = sizeof(Header); +constexpr static int INT_SIZE = sizeof(int); +constexpr static int DOUBLE_SIZE = sizeof(double); +constexpr static int UINT8_SIZE = sizeof(uint8_t); +constexpr static int UINT16_SIZE = sizeof(uint16_t); +constexpr static int UINT64_SIZE = sizeof(uint64_t); + +// 10 bytes : 2 key + 8 value +constexpr static int BUCKET_SIZE = KEY_SIZE + UINT64_SIZE; + +/** + * Returns the offset of the key received by parameter. + */ +inline int getKeyOffset(Key key) { + return HEADER_SIZE + BUCKET_SIZE * key; +} + +/** + * Returns the offset of the value associated to the key received by parameter. + */ +inline int getValueOffset(Key key) { + return getKeyOffset(key) + KEY_SIZE; +} + +inline void +checkKeyConsistency(const Header &header, const uint8_t *data, Key key) { +#ifdef CHECK_CONSISTENCY + if (key >= header.count) { + LOG(ERROR) << "Error: Key is higher than size of Map - key '" << key + << "' - size: '" << header.count << "'"; + exit(1); + } + + Key storedKey = 0; + memcpy( + reinterpret_cast(&storedKey), + reinterpret_cast(data + getKeyOffset(key)), + KEY_SIZE); + + if (storedKey != key) { + LOG(ERROR) << "Error while reading key, expecting '" << key << "' found: '" + << storedKey << "'"; + exit(1); + } +#endif +} + +} // namespace react +} // namespace facebook