Skip to content

Commit 8961513

Browse files
Andrei Shikovpull[bot]
authored andcommitted
Use struct for building mapbuffer buckets
Summary: Instead of copying integer values into key/value data array, this implementation keeps a structured vector of `Bucket` structs (which is sized to be serialized later). This approach provides multiple benefits to modify and access buckets (e.g. sort them based on the key, allowing for out of order inserts (D33611758)) Changelog: [Internal] Reviewed By: javache Differential Revision: D33601231 fbshipit-source-id: 62ace1374936cb504836d6eae672e909ea404e3f
1 parent 44e9df5 commit 8961513

File tree

3 files changed

+28
-95
lines changed

3 files changed

+28
-95
lines changed

ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp

Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,18 @@ using namespace facebook::react;
1212
namespace facebook {
1313
namespace react {
1414

15-
// TODO T83483191: Add asserts to check overflowing on additions
16-
MapBufferBuilder::MapBufferBuilder()
17-
: MapBufferBuilder::MapBufferBuilder(INITIAL_KEY_VALUE_SIZE) {}
18-
1915
MapBuffer MapBufferBuilder::EMPTY() {
2016
return MapBufferBuilder().build();
2117
}
2218

23-
MapBufferBuilder::MapBufferBuilder(uint16_t initialSize) {
24-
keyValuesSize_ = initialSize;
25-
keyValues_ = new Byte[keyValuesSize_];
26-
// First Key should be written right after the header.
27-
keyValuesOffset_ = HEADER_SIZE;
19+
MapBufferBuilder::MapBufferBuilder(uint32_t initialSize) {
20+
buckets_.reserve(initialSize);
2821

2922
dynamicDataSize_ = 0;
3023
dynamicDataValues_ = nullptr;
3124
dynamicDataOffset_ = 0;
3225
}
3326

34-
void MapBufferBuilder::ensureKeyValueSpace() {
35-
int32_t oldKeyValuesSize = keyValuesSize_;
36-
react_native_assert(
37-
(keyValuesSize_ < std::numeric_limits<uint16_t>::max() / 2) &&
38-
"Error trying to assign a value beyond the capacity of uint16_t: ");
39-
keyValuesSize_ *= 2;
40-
uint8_t *newKeyValues = new Byte[keyValuesSize_];
41-
uint8_t *oldKeyValues = keyValues_;
42-
memcpy(newKeyValues, keyValues_, oldKeyValuesSize);
43-
keyValues_ = newKeyValues;
44-
delete[] oldKeyValues;
45-
}
46-
4727
void MapBufferBuilder::storeKeyValue(
4828
Key key,
4929
uint8_t *value,
@@ -58,24 +38,15 @@ void MapBufferBuilder::storeKeyValue(
5838
abort();
5939
}
6040

61-
// TODO T83483191: header.count points to the next index
62-
// TODO T83483191: add test to verify storage of sparse keys
63-
int32_t keyOffset = getKeyOffset(_header.count);
64-
int32_t valueOffset = keyOffset + KEY_SIZE;
65-
66-
int32_t nextKeyValueOffset = keyOffset + BUCKET_SIZE;
67-
if (nextKeyValueOffset >= keyValuesSize_) {
68-
ensureKeyValueSpace();
69-
}
41+
uint64_t data = 0;
42+
auto *dataPtr = reinterpret_cast<uint8_t *>(&data);
43+
memcpy(dataPtr, value, valueSize);
7044

71-
memcpy(keyValues_ + keyOffset, &key, KEY_SIZE);
72-
memcpy(keyValues_ + valueOffset, value, valueSize);
45+
buckets_.emplace_back(key, data);
7346

7447
_header.count++;
7548

7649
minKeyToStore_ = key + 1;
77-
// Move keyValuesOffset_ to the next available [key, value] position
78-
keyValuesOffset_ = std::max(nextKeyValueOffset, keyValuesOffset_);
7950
}
8051

8152
void MapBufferBuilder::putBool(Key key, bool value) {
@@ -168,56 +139,27 @@ void MapBufferBuilder::putMapBuffer(Key key, MapBuffer const &map) {
168139
}
169140

170141
MapBuffer MapBufferBuilder::build() {
171-
react_native_assert(
172-
(keyValues_ != nullptr) &&
173-
"Error when building mapbuffer with invalid datastructures.");
174-
175142
// Create buffer: [header] + [key, values] + [dynamic data]
176-
int32_t bufferSize = keyValuesOffset_ + dynamicDataOffset_;
143+
auto bucketSize = buckets_.size() * BUCKET_SIZE;
144+
int32_t bufferSize = HEADER_SIZE + bucketSize + dynamicDataOffset_;
177145

178146
_header.bufferSize = bufferSize;
179147

180-
// Copy header at the beginning of "keyValues_"
181-
memcpy(keyValues_, &_header, HEADER_SIZE);
182-
183148
std::vector<uint8_t> buffer(bufferSize);
184-
185-
memcpy(buffer.data(), keyValues_, keyValuesOffset_);
149+
memcpy(buffer.data(), &_header, HEADER_SIZE);
150+
memcpy(buffer.data() + HEADER_SIZE, buckets_.data(), bucketSize);
186151

187152
if (dynamicDataValues_ != nullptr) {
188153
memcpy(
189-
buffer.data() + keyValuesOffset_,
154+
buffer.data() + HEADER_SIZE + bucketSize,
190155
dynamicDataValues_,
191156
dynamicDataOffset_);
192157
}
193158

194-
auto map = MapBuffer(std::move(buffer));
195-
196-
// TODO T83483191: we should invalidate the class once the build() method is
197-
// called.
198-
199-
if (keyValues_ != nullptr) {
200-
delete[] keyValues_;
201-
}
202-
keyValues_ = nullptr;
203-
keyValuesSize_ = 0;
204-
keyValuesOffset_ = 0;
205-
206-
if (dynamicDataValues_ != nullptr) {
207-
delete[] dynamicDataValues_;
208-
dynamicDataValues_ = nullptr;
209-
}
210-
dynamicDataSize_ = 0;
211-
dynamicDataOffset_ = 0;
212-
_header = {ALIGNMENT, 0, 0};
213-
214-
return map;
159+
return MapBuffer(std::move(buffer));
215160
}
216161

217162
MapBufferBuilder::~MapBufferBuilder() {
218-
if (keyValues_ != nullptr) {
219-
delete[] keyValues_;
220-
}
221163
if (dynamicDataValues_ != nullptr) {
222164
delete[] dynamicDataValues_;
223165
}

ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,13 @@
1010
#include <react/debug/react_native_assert.h>
1111
#include <react/renderer/mapbuffer/MapBuffer.h>
1212
#include <react/renderer/mapbuffer/primitives.h>
13-
#include <stdlib.h>
13+
#include <cstdlib>
1414

1515
namespace facebook {
1616
namespace react {
1717

18-
// Default initial size for _keyValues array
19-
// 108 = 10 entries = 10*10 + 8 sizeof(header)
20-
constexpr uint16_t INITIAL_KEY_VALUE_SIZE = 108;
21-
22-
// Default initial size for _dynamicDataValues array
23-
constexpr int32_t INITIAL_DYNAMIC_DATA_SIZE = 200;
18+
// Default reserved size for buckets_ vector
19+
constexpr uint16_t INITIAL_BUCKETS_SIZE = 10;
2420

2521
/**
2622
* MapBufferBuilder is a builder class for MapBuffer
@@ -29,25 +25,11 @@ class MapBufferBuilder {
2925
private:
3026
Header _header = {ALIGNMENT, 0, 0};
3127

32-
void ensureKeyValueSpace();
33-
3428
void ensureDynamicDataSpace(int32_t size);
3529

3630
void storeKeyValue(Key key, uint8_t *value, int32_t valueSize);
3731

38-
// Array of [key,value] map entries:
39-
// - Key is represented in 2 bytes
40-
// - Value is stored into 8 bytes. The 8 bytes of the value will contain the
41-
// actual value for the key or a pointer to the actual value (based on the
42-
// type)
43-
uint8_t *keyValues_ = nullptr;
44-
45-
// Amount of bytes allocated on _keyValues
46-
uint16_t keyValuesSize_ = 0;
47-
48-
// Relative offset on the _keyValues array.
49-
// This represents the first byte that can be written in _keyValues array
50-
int32_t keyValuesOffset_ = 0;
32+
std::vector<Bucket> buckets_{};
5133

5234
// This array contains data for dynamic values in the MapBuffer.
5335
// A dynamic value is a String or another MapBuffer.
@@ -66,9 +48,7 @@ class MapBufferBuilder {
6648
uint16_t minKeyToStore_ = 0;
6749

6850
public:
69-
MapBufferBuilder();
70-
71-
MapBufferBuilder(uint16_t initialSize);
51+
MapBufferBuilder(uint32_t initialSize = INITIAL_BUCKETS_SIZE);
7252

7353
~MapBufferBuilder();
7454

ReactCommon/react/renderer/mapbuffer/primitives.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ struct Header {
3030
int32_t bufferSize; // Amount of bytes used to store the map in memory
3131
};
3232

33+
static_assert(sizeof(Header) == 8, "MapBuffer header size is incorrect.");
34+
35+
struct __attribute__((__packed__)) Bucket {
36+
Key key;
37+
uint64_t data;
38+
39+
Bucket(Key key, uint64_t data) : key(key), data(data){};
40+
};
41+
42+
static_assert(sizeof(Bucket) == 10, "MapBuffer bucket size is incorrect.");
43+
3344
constexpr static int32_t KEY_SIZE = sizeof(Key);
3445
constexpr static int32_t HEADER_SIZE = sizeof(Header);
3546
constexpr static int32_t INT_SIZE = sizeof(int32_t);

0 commit comments

Comments
 (0)