mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
ea712f825a
Summary: This method can produce unnecessary copy of the string, replacing it with reference to optimize. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D33607765 fbshipit-source-id: e04eddffec063c0c8e173bfdf690e7b8e1050525
228 lines
6.7 KiB
C++
228 lines
6.7 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "MapBufferBuilder.h"
|
|
|
|
using namespace facebook::react;
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
// TODO T83483191: Add asserts to check overflowing on additions
|
|
MapBufferBuilder::MapBufferBuilder()
|
|
: MapBufferBuilder::MapBufferBuilder(INITIAL_KEY_VALUE_SIZE) {}
|
|
|
|
MapBuffer MapBufferBuilder::EMPTY() {
|
|
return MapBufferBuilder().build();
|
|
}
|
|
|
|
MapBufferBuilder::MapBufferBuilder(uint16_t initialSize) {
|
|
keyValuesSize_ = initialSize;
|
|
keyValues_ = new Byte[keyValuesSize_];
|
|
// First Key should be written right after the header.
|
|
keyValuesOffset_ = HEADER_SIZE;
|
|
|
|
dynamicDataSize_ = 0;
|
|
dynamicDataValues_ = nullptr;
|
|
dynamicDataOffset_ = 0;
|
|
}
|
|
|
|
void MapBufferBuilder::ensureKeyValueSpace() {
|
|
int32_t oldKeyValuesSize = keyValuesSize_;
|
|
react_native_assert(
|
|
(keyValuesSize_ < std::numeric_limits<uint16_t>::max() / 2) &&
|
|
"Error trying to assign a value beyond the capacity of uint16_t: ");
|
|
keyValuesSize_ *= 2;
|
|
uint8_t *newKeyValues = new Byte[keyValuesSize_];
|
|
uint8_t *oldKeyValues = keyValues_;
|
|
memcpy(newKeyValues, keyValues_, oldKeyValuesSize);
|
|
keyValues_ = newKeyValues;
|
|
delete[] oldKeyValues;
|
|
}
|
|
|
|
void MapBufferBuilder::storeKeyValue(
|
|
Key key,
|
|
uint8_t *value,
|
|
int32_t valueSize) {
|
|
if (key < minKeyToStore_) {
|
|
LOG(ERROR) << "Error: key out of order - key: " << key;
|
|
abort();
|
|
}
|
|
if (valueSize > MAX_VALUE_SIZE) {
|
|
LOG(ERROR) << "Error: size of value must be <= MAX_VALUE_SIZE. ValueSize: "
|
|
<< valueSize;
|
|
abort();
|
|
}
|
|
|
|
// TODO T83483191: header.count points to the next index
|
|
// TODO T83483191: add test to verify storage of sparse keys
|
|
int32_t keyOffset = getKeyOffset(_header.count);
|
|
int32_t valueOffset = keyOffset + KEY_SIZE;
|
|
|
|
int32_t nextKeyValueOffset = keyOffset + BUCKET_SIZE;
|
|
if (nextKeyValueOffset >= keyValuesSize_) {
|
|
ensureKeyValueSpace();
|
|
}
|
|
|
|
memcpy(keyValues_ + keyOffset, &key, KEY_SIZE);
|
|
memcpy(keyValues_ + valueOffset, value, valueSize);
|
|
|
|
_header.count++;
|
|
|
|
minKeyToStore_ = key + 1;
|
|
// Move keyValuesOffset_ to the next available [key, value] position
|
|
keyValuesOffset_ = std::max(nextKeyValueOffset, keyValuesOffset_);
|
|
}
|
|
|
|
void MapBufferBuilder::putBool(Key key, bool value) {
|
|
putInt(key, (int)value);
|
|
}
|
|
|
|
void MapBufferBuilder::putDouble(Key key, double value) {
|
|
uint8_t *bytePointer = reinterpret_cast<uint8_t *>(&value);
|
|
storeKeyValue(key, bytePointer, DOUBLE_SIZE);
|
|
}
|
|
|
|
void MapBufferBuilder::putNull(Key key) {
|
|
putInt(key, NULL_VALUE);
|
|
}
|
|
|
|
void MapBufferBuilder::putInt(Key key, int32_t value) {
|
|
uint8_t *bytePointer = reinterpret_cast<uint8_t *>(&(value));
|
|
storeKeyValue(key, bytePointer, INT_SIZE);
|
|
}
|
|
|
|
void MapBufferBuilder::ensureDynamicDataSpace(int32_t size) {
|
|
if (dynamicDataValues_ == nullptr) {
|
|
dynamicDataSize_ = size;
|
|
dynamicDataValues_ = new Byte[dynamicDataSize_];
|
|
dynamicDataOffset_ = 0;
|
|
return;
|
|
}
|
|
|
|
if (dynamicDataOffset_ + size >= dynamicDataSize_) {
|
|
int32_t oldDynamicDataSize = dynamicDataSize_;
|
|
react_native_assert(
|
|
(dynamicDataSize_ < std::numeric_limits<int32_t>::max() / 2) &&
|
|
"Error: trying to assign a value beyond the capacity of int");
|
|
dynamicDataSize_ *= dynamicDataSize_;
|
|
|
|
react_native_assert(
|
|
(dynamicDataSize_ < std::numeric_limits<int32_t>::max() - size) &&
|
|
"Error: trying to assign a value beyond the capacity of int");
|
|
|
|
// sum size to ensure that the size always fit into newDynamicDataValues
|
|
dynamicDataSize_ += size;
|
|
uint8_t *newDynamicDataValues = new Byte[dynamicDataSize_];
|
|
uint8_t *oldDynamicDataValues = dynamicDataValues_;
|
|
memcpy(newDynamicDataValues, dynamicDataValues_, oldDynamicDataSize);
|
|
dynamicDataValues_ = newDynamicDataValues;
|
|
delete[] oldDynamicDataValues;
|
|
}
|
|
}
|
|
|
|
void MapBufferBuilder::putString(Key key, std::string const &value) {
|
|
int32_t strLength = static_cast<int32_t>(value.length());
|
|
const char *cstring = getCstring(&value);
|
|
|
|
// format [lenght of string (int)] + [Array of Characters in the string]
|
|
int32_t sizeOfLength = INT_SIZE;
|
|
// TODO T83483191: review if map.getBufferSize() should be an int32_t or long
|
|
// instead of an int16 (because strings can be longer than int16);
|
|
|
|
int32_t sizeOfDynamicData = sizeOfLength + strLength;
|
|
ensureDynamicDataSpace(sizeOfDynamicData);
|
|
memcpy(dynamicDataValues_ + dynamicDataOffset_, &strLength, sizeOfLength);
|
|
memcpy(
|
|
dynamicDataValues_ + dynamicDataOffset_ + sizeOfLength,
|
|
cstring,
|
|
strLength);
|
|
|
|
// Store Key and pointer to the string
|
|
putInt(key, dynamicDataOffset_);
|
|
|
|
dynamicDataOffset_ += sizeOfDynamicData;
|
|
}
|
|
|
|
void MapBufferBuilder::putMapBuffer(Key key, MapBuffer &map) {
|
|
int32_t mapBufferSize = map.getBufferSize();
|
|
|
|
// format [lenght of buffer (int)] + [bytes of MapBuffer]
|
|
int32_t sizeOfDynamicData = mapBufferSize + INT_SIZE;
|
|
|
|
// format [Array of bytes of the mapBuffer]
|
|
ensureDynamicDataSpace(sizeOfDynamicData);
|
|
|
|
memcpy(dynamicDataValues_ + dynamicDataOffset_, &mapBufferSize, INT_SIZE);
|
|
// Copy the content of the map into dynamicDataValues_
|
|
map.copy(dynamicDataValues_ + dynamicDataOffset_ + INT_SIZE);
|
|
|
|
// Store Key and pointer to the string
|
|
putInt(key, dynamicDataOffset_);
|
|
|
|
dynamicDataOffset_ += sizeOfDynamicData;
|
|
}
|
|
|
|
MapBuffer MapBufferBuilder::build() {
|
|
react_native_assert(
|
|
(keyValues_ != nullptr) &&
|
|
"Error when building mapbuffer with invalid datastructures.");
|
|
|
|
// Create buffer: [header] + [key, values] + [dynamic data]
|
|
int32_t bufferSize = keyValuesOffset_ + dynamicDataOffset_;
|
|
|
|
_header.bufferSize = bufferSize;
|
|
|
|
// Copy header at the beginning of "keyValues_"
|
|
memcpy(keyValues_, &_header, HEADER_SIZE);
|
|
|
|
std::vector<uint8_t> buffer(bufferSize);
|
|
|
|
memcpy(buffer.data(), keyValues_, keyValuesOffset_);
|
|
|
|
if (dynamicDataValues_ != nullptr) {
|
|
memcpy(
|
|
buffer.data() + keyValuesOffset_,
|
|
dynamicDataValues_,
|
|
dynamicDataOffset_);
|
|
}
|
|
|
|
auto map = MapBuffer(std::move(buffer));
|
|
|
|
// TODO T83483191: we should invalidate the class once the build() method is
|
|
// called.
|
|
|
|
if (keyValues_ != nullptr) {
|
|
delete[] keyValues_;
|
|
}
|
|
keyValues_ = nullptr;
|
|
keyValuesSize_ = 0;
|
|
keyValuesOffset_ = 0;
|
|
|
|
if (dynamicDataValues_ != nullptr) {
|
|
delete[] dynamicDataValues_;
|
|
dynamicDataValues_ = nullptr;
|
|
}
|
|
dynamicDataSize_ = 0;
|
|
dynamicDataOffset_ = 0;
|
|
_header = {ALIGNMENT, 0, 0};
|
|
|
|
return map;
|
|
}
|
|
|
|
MapBufferBuilder::~MapBufferBuilder() {
|
|
if (keyValues_ != nullptr) {
|
|
delete[] keyValues_;
|
|
}
|
|
if (dynamicDataValues_ != nullptr) {
|
|
delete[] dynamicDataValues_;
|
|
}
|
|
}
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|