Skip to content

Commit 5b489d5

Browse files
Andrei Shikovfacebook-github-bot
authored andcommitted
Use vector for dynamic data in MapBufferBuilder
Summary: Replaces dynamic manually managed array with a vector of bytes, removing the need for managing memory manually. This approach is a little bit slower than before, as vector is allocating memory more conservatively. This can be improved in the future by providing an estimate for the data size. Changelog: [Internal] Reviewed By: javache Differential Revision: D33611759 fbshipit-source-id: a0e5e57c4e413206a9f891cd5630ecc9088a9bf7
1 parent b1ef440 commit 5b489d5

File tree

6 files changed

+54
-133
lines changed

6 files changed

+54
-133
lines changed

ReactCommon/react/renderer/mapbuffer/MapBuffer.cpp

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ namespace react {
1515
// TODO T83483191: Extend MapBuffer C++ implementation to support basic random
1616
// access
1717
MapBuffer::MapBuffer(std::vector<uint8_t> data) : bytes_(std::move(data)) {
18-
count_ = 0;
1918
memcpy(
2019
reinterpret_cast<uint8_t *>(&count_),
2120
bytes_.data() + HEADER_COUNT_OFFSET,
2221
UINT16_SIZE);
2322

2423
// TODO T83483191: extract memcpy calls into an inline function to simplify
2524
// the code
26-
int32_t dataSize;
25+
uint32_t dataSize;
2726
memcpy(
2827
reinterpret_cast<uint8_t *>(&dataSize),
2928
bytes_.data() + HEADER_BUFFER_SIZE_OFFSET,
@@ -113,23 +112,16 @@ bool MapBuffer::isNull(Key key) const {
113112
return getInt(key) == NULL_VALUE;
114113
}
115114

116-
int32_t MapBuffer::getBufferSize() const {
115+
uint32_t MapBuffer::size() const {
117116
return bytes_.size();
118117
}
119118

120-
void MapBuffer::copy(uint8_t *output) const {
121-
memcpy(output, bytes_.data(), bytes_.size());
119+
uint8_t const *MapBuffer::data() const {
120+
return bytes_.data();
122121
}
123122

124-
uint16_t MapBuffer::getCount() const {
125-
uint16_t size = 0;
126-
127-
memcpy(
128-
reinterpret_cast<uint8_t *>(&size),
129-
bytes_.data() + HEADER_COUNT_OFFSET,
130-
UINT16_SIZE);
131-
132-
return size;
123+
uint16_t MapBuffer::count() const {
124+
return count_;
133125
}
134126

135127
} // namespace react

ReactCommon/react/renderer/mapbuffer/MapBuffer.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,13 @@ class MapBuffer {
6565
// TODO T83483191: review this declaration
6666
MapBuffer getMapBuffer(Key key) const;
6767

68-
int32_t getBufferSize() const;
68+
bool isNull(Key key) const;
6969

70-
// TODO T83483191: review parameters of copy method
71-
void copy(uint8_t *output) const;
70+
uint32_t size() const;
7271

73-
bool isNull(Key key) const;
72+
uint8_t const *data() const;
7473

75-
uint16_t getCount() const;
74+
uint16_t count() const;
7675
};
7776

7877
} // namespace react

ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.cpp

Lines changed: 27 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,17 @@ namespace facebook {
1313
namespace react {
1414

1515
MapBuffer MapBufferBuilder::EMPTY() {
16-
return MapBufferBuilder().build();
16+
return MapBufferBuilder(0).build();
1717
}
1818

1919
MapBufferBuilder::MapBufferBuilder(uint32_t initialSize) {
2020
buckets_.reserve(initialSize);
21-
22-
dynamicDataSize_ = 0;
23-
dynamicDataValues_ = nullptr;
24-
dynamicDataOffset_ = 0;
2521
}
2622

2723
void MapBufferBuilder::storeKeyValue(
2824
Key key,
29-
uint8_t *value,
30-
int32_t valueSize) {
25+
uint8_t const *value,
26+
uint32_t valueSize) {
3127
if (key < minKeyToStore_) {
3228
LOG(ERROR) << "Error: key out of order - key: " << key;
3329
abort();
@@ -54,7 +50,7 @@ void MapBufferBuilder::putBool(Key key, bool value) {
5450
}
5551

5652
void MapBufferBuilder::putDouble(Key key, double value) {
57-
uint8_t *bytePointer = reinterpret_cast<uint8_t *>(&value);
53+
auto const *bytePointer = reinterpret_cast<uint8_t *>(&value);
5854
storeKeyValue(key, bytePointer, DOUBLE_SIZE);
5955
}
6056

@@ -63,107 +59,58 @@ void MapBufferBuilder::putNull(Key key) {
6359
}
6460

6561
void MapBufferBuilder::putInt(Key key, int32_t value) {
66-
uint8_t *bytePointer = reinterpret_cast<uint8_t *>(&(value));
62+
auto const *bytePointer = reinterpret_cast<uint8_t *>(&(value));
6763
storeKeyValue(key, bytePointer, INT_SIZE);
6864
}
6965

70-
void MapBufferBuilder::ensureDynamicDataSpace(int32_t size) {
71-
if (dynamicDataValues_ == nullptr) {
72-
dynamicDataSize_ = size;
73-
dynamicDataValues_ = new Byte[dynamicDataSize_];
74-
dynamicDataOffset_ = 0;
75-
return;
76-
}
77-
78-
if (dynamicDataOffset_ + size >= dynamicDataSize_) {
79-
int32_t oldDynamicDataSize = dynamicDataSize_;
80-
react_native_assert(
81-
(dynamicDataSize_ < std::numeric_limits<int32_t>::max() / 2) &&
82-
"Error: trying to assign a value beyond the capacity of int");
83-
dynamicDataSize_ *= dynamicDataSize_;
84-
85-
react_native_assert(
86-
(dynamicDataSize_ < std::numeric_limits<int32_t>::max() - size) &&
87-
"Error: trying to assign a value beyond the capacity of int");
88-
89-
// sum size to ensure that the size always fit into newDynamicDataValues
90-
dynamicDataSize_ += size;
91-
uint8_t *newDynamicDataValues = new Byte[dynamicDataSize_];
92-
uint8_t *oldDynamicDataValues = dynamicDataValues_;
93-
memcpy(newDynamicDataValues, dynamicDataValues_, oldDynamicDataSize);
94-
dynamicDataValues_ = newDynamicDataValues;
95-
delete[] oldDynamicDataValues;
96-
}
97-
}
98-
9966
void MapBufferBuilder::putString(Key key, std::string const &value) {
100-
int32_t strLength = static_cast<int32_t>(value.length());
101-
const char *cstring = getCstring(&value);
67+
int32_t strSize = value.size();
68+
const char *strData = value.data();
10269

103-
// format [lenght of string (int)] + [Array of Characters in the string]
104-
int32_t sizeOfLength = INT_SIZE;
105-
// TODO T83483191: review if map.getBufferSize() should be an int32_t or long
70+
auto offset = dynamicData_.size();
71+
// format [length of string (int)] + [Array of Characters in the string]
72+
// TODO T83483191: review if map.size() should be an int32_t or long
10673
// instead of an int16 (because strings can be longer than int16);
107-
108-
int32_t sizeOfDynamicData = sizeOfLength + strLength;
109-
ensureDynamicDataSpace(sizeOfDynamicData);
110-
memcpy(dynamicDataValues_ + dynamicDataOffset_, &strLength, sizeOfLength);
111-
memcpy(
112-
dynamicDataValues_ + dynamicDataOffset_ + sizeOfLength,
113-
cstring,
114-
strLength);
74+
dynamicData_.resize(offset + INT_SIZE + strSize, 0);
75+
memcpy(dynamicData_.data() + offset, &strSize, INT_SIZE);
76+
memcpy(dynamicData_.data() + offset + INT_SIZE, strData, strSize);
11577

11678
// Store Key and pointer to the string
117-
putInt(key, dynamicDataOffset_);
118-
119-
dynamicDataOffset_ += sizeOfDynamicData;
79+
putInt(key, offset);
12080
}
12181

12282
void MapBufferBuilder::putMapBuffer(Key key, MapBuffer const &map) {
123-
int32_t mapBufferSize = map.getBufferSize();
83+
int32_t mapBufferSize = map.size();
12484

125-
// format [lenght of buffer (int)] + [bytes of MapBuffer]
126-
int32_t sizeOfDynamicData = mapBufferSize + INT_SIZE;
85+
auto offset = dynamicData_.size();
12786

128-
// format [Array of bytes of the mapBuffer]
129-
ensureDynamicDataSpace(sizeOfDynamicData);
130-
131-
memcpy(dynamicDataValues_ + dynamicDataOffset_, &mapBufferSize, INT_SIZE);
132-
// Copy the content of the map into dynamicDataValues_
133-
map.copy(dynamicDataValues_ + dynamicDataOffset_ + INT_SIZE);
87+
// format [length of buffer (int)] + [bytes of MapBuffer]
88+
dynamicData_.resize(offset + INT_SIZE + mapBufferSize, 0);
89+
memcpy(dynamicData_.data() + offset, &mapBufferSize, INT_SIZE);
90+
// Copy the content of the map into dynamicData_
91+
memcpy(dynamicData_.data() + offset + INT_SIZE, map.data(), mapBufferSize);
13492

13593
// Store Key and pointer to the string
136-
putInt(key, dynamicDataOffset_);
137-
138-
dynamicDataOffset_ += sizeOfDynamicData;
94+
putInt(key, offset);
13995
}
14096

14197
MapBuffer MapBufferBuilder::build() {
14298
// Create buffer: [header] + [key, values] + [dynamic data]
14399
auto bucketSize = buckets_.size() * BUCKET_SIZE;
144-
int32_t bufferSize = HEADER_SIZE + bucketSize + dynamicDataOffset_;
100+
uint32_t bufferSize = HEADER_SIZE + bucketSize + dynamicData_.size();
145101

146102
_header.bufferSize = bufferSize;
147103

148104
std::vector<uint8_t> buffer(bufferSize);
149105
memcpy(buffer.data(), &_header, HEADER_SIZE);
150106
memcpy(buffer.data() + HEADER_SIZE, buckets_.data(), bucketSize);
151-
152-
if (dynamicDataValues_ != nullptr) {
153-
memcpy(
154-
buffer.data() + HEADER_SIZE + bucketSize,
155-
dynamicDataValues_,
156-
dynamicDataOffset_);
157-
}
107+
memcpy(
108+
buffer.data() + HEADER_SIZE + bucketSize,
109+
dynamicData_.data(),
110+
dynamicData_.size());
158111

159112
return MapBuffer(std::move(buffer));
160113
}
161114

162-
MapBufferBuilder::~MapBufferBuilder() {
163-
if (dynamicDataValues_ != nullptr) {
164-
delete[] dynamicDataValues_;
165-
}
166-
}
167-
168115
} // namespace react
169116
} // namespace facebook

ReactCommon/react/renderer/mapbuffer/MapBufferBuilder.h

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,11 @@ class MapBufferBuilder {
2525
private:
2626
Header _header = {ALIGNMENT, 0, 0};
2727

28-
void ensureDynamicDataSpace(int32_t size);
29-
30-
void storeKeyValue(Key key, uint8_t *value, int32_t valueSize);
28+
void storeKeyValue(Key key, uint8_t const *value, uint32_t valueSize);
3129

3230
std::vector<Bucket> buckets_{};
3331

34-
// This array contains data for dynamic values in the MapBuffer.
35-
// A dynamic value is a String or another MapBuffer.
36-
uint8_t *dynamicDataValues_ = nullptr;
37-
38-
// Amount of bytes allocated on _dynamicDataValues
39-
int32_t dynamicDataSize_ = 0;
40-
41-
// Relative offset on the _dynamicDataValues array.
42-
// This represents the first byte that can be written in _dynamicDataValues
43-
// array
44-
int32_t dynamicDataOffset_ = 0;
32+
std::vector<Byte> dynamicData_{};
4533

4634
// Minimmum key to store in the MapBuffer (this is used to guarantee
4735
// consistency)
@@ -50,8 +38,6 @@ class MapBufferBuilder {
5038
public:
5139
MapBufferBuilder(uint32_t initialSize = INITIAL_BUCKETS_SIZE);
5240

53-
~MapBufferBuilder();
54-
5541
static MapBuffer EMPTY();
5642

5743
void putInt(Key key, int32_t value);

ReactCommon/react/renderer/mapbuffer/primitives.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace react {
2727
struct Header {
2828
uint16_t alignment; // alignment of serialization
2929
uint16_t count; // amount of items in the map
30-
int32_t bufferSize; // Amount of bytes used to store the map in memory
30+
uint32_t bufferSize; // Amount of bytes used to store the map in memory
3131
};
3232

3333
static_assert(sizeof(Header) == 8, "MapBuffer header size is incorrect.");
@@ -48,9 +48,10 @@ constexpr static int32_t DOUBLE_SIZE = sizeof(double);
4848
constexpr static int32_t UINT8_SIZE = sizeof(uint8_t);
4949
constexpr static int32_t UINT16_SIZE = sizeof(uint16_t);
5050
constexpr static int32_t UINT64_SIZE = sizeof(uint64_t);
51-
constexpr static int32_t HEADER_ALIGNMENT_OFFSET = 0;
52-
constexpr static int32_t HEADER_COUNT_OFFSET = UINT16_SIZE;
53-
constexpr static int32_t HEADER_BUFFER_SIZE_OFFSET = UINT16_SIZE * 2;
51+
constexpr static int32_t HEADER_ALIGNMENT_OFFSET = offsetof(Header, alignment);
52+
constexpr static int32_t HEADER_COUNT_OFFSET = offsetof(Header, count);
53+
constexpr static int32_t HEADER_BUFFER_SIZE_OFFSET =
54+
offsetof(Header, bufferSize);
5455

5556
constexpr static int32_t MAX_VALUE_SIZE = UINT64_SIZE;
5657

@@ -71,10 +72,6 @@ inline int32_t getValueOffset(Key key) {
7172
return getKeyOffset(key) + KEY_SIZE;
7273
}
7374

74-
static inline const char *getCstring(const std::string *str) {
75-
return str ? str->c_str() : "";
76-
}
77-
7875
inline void
7976
checkKeyConsistency(const Header &header, const uint8_t *data, Key key) {
8077
#ifdef CHECK_CONSISTENCY

ReactCommon/react/renderer/mapbuffer/tests/MapBufferTest.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ TEST(MapBufferTest, testSimpleIntMap) {
2121

2222
auto map = builder.build();
2323

24-
EXPECT_EQ(map.getCount(), 2);
24+
EXPECT_EQ(map.count(), 2);
2525
EXPECT_EQ(map.getInt(0), 1234);
2626
EXPECT_EQ(map.getInt(1), 4321);
2727
}
@@ -38,7 +38,7 @@ TEST(MapBufferTest, testMapBufferExtension) {
3838

3939
auto map = buffer.build();
4040

41-
EXPECT_EQ(map.getCount(), 4);
41+
EXPECT_EQ(map.count(), 4);
4242

4343
EXPECT_EQ(map.getInt(0), 1234);
4444
EXPECT_EQ(map.getInt(1), 4321);
@@ -54,7 +54,7 @@ TEST(MapBufferTest, testBoolEntries) {
5454

5555
auto map = buffer.build();
5656

57-
EXPECT_EQ(map.getCount(), 2);
57+
EXPECT_EQ(map.count(), 2);
5858
EXPECT_EQ(map.getBool(0), true);
5959
EXPECT_EQ(map.getBool(1), false);
6060
}
@@ -67,7 +67,7 @@ TEST(MapBufferTest, testNullEntries) {
6767

6868
auto map = buffer.build();
6969

70-
EXPECT_EQ(map.getCount(), 2);
70+
EXPECT_EQ(map.count(), 2);
7171
EXPECT_EQ(map.isNull(0), true);
7272
EXPECT_EQ(map.isNull(1), false);
7373
// TODO T83483191: serialize null values to be distinguishable from '0'
@@ -84,7 +84,7 @@ TEST(MapBufferTest, testDoubleEntries) {
8484

8585
auto map = buffer.build();
8686

87-
EXPECT_EQ(map.getCount(), 2);
87+
EXPECT_EQ(map.count(), 2);
8888

8989
EXPECT_EQ(map.getDouble(0), 123.4);
9090
EXPECT_EQ(map.getDouble(1), 432.1);
@@ -134,12 +134,12 @@ TEST(MapBufferTest, testUTFStringEntries) {
134134
TEST(MapBufferTest, testEmptyMap) {
135135
auto builder = MapBufferBuilder();
136136
auto map = builder.build();
137-
EXPECT_EQ(map.getCount(), 0);
137+
EXPECT_EQ(map.count(), 0);
138138
}
139139

140140
TEST(MapBufferTest, testEmptyMapConstant) {
141141
auto map = MapBufferBuilder::EMPTY();
142-
EXPECT_EQ(map.getCount(), 0);
142+
EXPECT_EQ(map.count(), 0);
143143
}
144144

145145
TEST(MapBufferTest, testMapEntries) {
@@ -148,7 +148,7 @@ TEST(MapBufferTest, testMapEntries) {
148148
builder.putInt(1, 1234);
149149
auto map = builder.build();
150150

151-
EXPECT_EQ(map.getCount(), 2);
151+
EXPECT_EQ(map.count(), 2);
152152
EXPECT_EQ(map.getString(0), "This is a test");
153153
EXPECT_EQ(map.getInt(1), 1234);
154154

@@ -157,12 +157,12 @@ TEST(MapBufferTest, testMapEntries) {
157157
builder2.putMapBuffer(1, map);
158158
auto map2 = builder2.build();
159159

160-
EXPECT_EQ(map2.getCount(), 2);
160+
EXPECT_EQ(map2.count(), 2);
161161
EXPECT_EQ(map2.getInt(0), 4321);
162162

163163
MapBuffer readMap2 = map2.getMapBuffer(1);
164164

165-
EXPECT_EQ(readMap2.getCount(), 2);
165+
EXPECT_EQ(readMap2.count(), 2);
166166
EXPECT_EQ(readMap2.getString(0), "This is a test");
167167
EXPECT_EQ(readMap2.getInt(1), 1234);
168168
}

0 commit comments

Comments
 (0)