mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Initial implementation of MapBuffer
Summary: This diff defines an initial implementation of MapBuffer class. This is an unfinished implementation and the API and internals is going to change considerably in the next days. The purpose of this stack is to experiment with ByteBuffers moving data from C++ into Java and learn about what're the performance implications of this model. The format of serialization is going to change in the next few days. I'm going to follow a format similar to https://fb.quip.com/3ENaA782rkkC I'm expecting to iterate on this API as we expand the development of the new JNI system, PLEASE read all the TODOs as you are reviewing the code. changelog: [internal] internal Reviewed By: sammy-SC Differential Revision: D26364354 fbshipit-source-id: 94e434f699a4250dd240342386eddeaa6acd3ba2
This commit is contained in:
committed by
Facebook GitHub Bot
parent
aaede1029d
commit
022936bcf5
@@ -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)
|
||||
|
||||
@@ -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<uint8_t *>(&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<uint8_t *>(&(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<uint8_t *>(&value),
|
||||
reinterpret_cast<const uint8_t *>(_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<uint8_t *>(&value),
|
||||
reinterpret_cast<const uint8_t *>(_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<uint16_t *>(&size),
|
||||
reinterpret_cast<const uint16_t *>(
|
||||
_data + UINT16_SIZE), // TODO refactor this: + UINT16_SIZE describes
|
||||
// the position in the header
|
||||
UINT16_SIZE);
|
||||
return size;
|
||||
}
|
||||
|
||||
MapBuffer::~MapBuffer() {
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
||||
@@ -7,11 +7,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <react/renderer/mapbuffer/Primitives.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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 <glog/logging.h>
|
||||
|
||||
// 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<Key *>(&storedKey),
|
||||
reinterpret_cast<const Key *>(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
|
||||
Reference in New Issue
Block a user