Skip to content

Commit 4e582b0

Browse files
authored
[flexbuffers] Add "AlignedBlob", a version of "Blob" with explicit alignment. (#8993)
A blob is an array of bytes and has no intrinsic alignment (i.e. the alignment is 1). The alignment of the existing flexbuffers blob is solely affected by the width of the integer needed to store the blob's size: that integer's width becomes the alignment of the blob. The proposed AlignedBlob function here piggybacks on this effect and simply uses a user-defined alignment for the width of the integer that stores the blob's size; this automatically imparts that same alignment on the blob itself. (The width is bounded below by the actual width needed to store the blob's size.) The ability to control the alignment of a blob is important for use cases in which the blob itself stores structured data that we want to access without further copies (e.g. other flatbuffer messages).
1 parent 8396e00 commit 4e582b0

2 files changed

Lines changed: 42 additions & 11 deletions

File tree

include/flatbuffers/flexbuffers.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,11 +1207,20 @@ class Builder FLATBUFFERS_FINAL_CLASS {
12071207
String(str);
12081208
}
12091209

1210+
size_t AlignedBlob(const void* data, size_t len, BitWidth alignment) {
1211+
// The requested alignment must not be smaller than the one required to
1212+
// store the length.
1213+
return CreateAlignedBlob(data, len, 0, FBT_BLOB,
1214+
std::max(alignment, WidthU(len)));
1215+
}
1216+
size_t AlignedBlob(const std::vector<uint8_t>& v, BitWidth alignment) {
1217+
return AlignedBlob(v.data(), v.size(), alignment);
1218+
}
12101219
size_t Blob(const void* data, size_t len) {
12111220
return CreateBlob(data, len, 0, FBT_BLOB);
12121221
}
12131222
size_t Blob(const std::vector<uint8_t>& v) {
1214-
return CreateBlob(v.data(), v.size(), 0, FBT_BLOB);
1223+
return Blob(v.data(), v.size());
12151224
}
12161225

12171226
void Blob(const char* key, const void* data, size_t len) {
@@ -1693,11 +1702,16 @@ class Builder FLATBUFFERS_FINAL_CLASS {
16931702

16941703
size_t CreateBlob(const void* data, size_t len, size_t trailing, Type type) {
16951704
auto bit_width = WidthU(len);
1696-
auto byte_width = Align(bit_width);
1705+
return CreateAlignedBlob(data, len, trailing, type, bit_width);
1706+
}
1707+
1708+
size_t CreateAlignedBlob(const void* data, size_t len, size_t trailing,
1709+
Type type, BitWidth alignment) {
1710+
auto byte_width = Align(alignment);
16971711
Write<uint64_t>(len, byte_width);
16981712
auto sloc = buf_.size();
16991713
WriteBytes(data, len + trailing);
1700-
stack_.push_back(Value(static_cast<uint64_t>(sloc), type, bit_width));
1714+
stack_.push_back(Value(static_cast<uint64_t>(sloc), type, alignment));
17011715
return sloc;
17021716
}
17031717

tests/flexbuffers_test.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "flexbuffers_test.h"
22

3+
#include <memory>
34
#include <limits>
45

56
#include "flatbuffers/flexbuffers.h"
@@ -13,6 +14,13 @@ namespace tests {
1314
// Shortcuts for the infinity.
1415
static const auto infinity_d = std::numeric_limits<double>::infinity();
1516

17+
static bool IsAligned(const void* ptr, std::size_t alignment) {
18+
void* p = const_cast<void*>(ptr);
19+
std::size_t space = 2 * alignment;
20+
void* q = std::align(alignment, alignment, p, space);
21+
return q != nullptr && p == ptr && space == 2 * alignment;
22+
}
23+
1624
void FlexBuffersTest() {
1725
flexbuffers::Builder slb(512,
1826
flexbuffers::BUILDER_FLAG_SHARE_KEYS_AND_STRINGS);
@@ -29,7 +37,10 @@ void FlexBuffersTest() {
2937
slb.IndirectFloat(4.0f);
3038
auto i_f = slb.LastValue();
3139
uint8_t blob[] = {77};
32-
slb.Blob(blob, 1);
40+
uint32_t aligned_blob[] = {88, 99};
41+
slb.Blob(blob, sizeof blob);
42+
slb.AlignedBlob(aligned_blob, sizeof aligned_blob,
43+
flexbuffers::BIT_WIDTH_32);
3344
slb += false;
3445
slb.ReuseValue(i_f);
3546
});
@@ -62,7 +73,7 @@ void FlexBuffersTest() {
6273
auto map = flexbuffers::GetRoot(slb.GetBuffer()).AsMap();
6374
TEST_EQ(map.size(), 7);
6475
auto vec = map["vec"].AsVector();
65-
TEST_EQ(vec.size(), 6);
76+
TEST_EQ(vec.size(), 7);
6677
TEST_EQ(vec[0].AsInt64(), -100);
6778
TEST_EQ_STR(vec[1].AsString().c_str(), "Fred");
6879
TEST_EQ(vec[1].AsInt64(), 0); // Number parsing failed.
@@ -80,9 +91,15 @@ void FlexBuffersTest() {
8091
auto blob = vec[3].AsBlob();
8192
TEST_EQ(blob.size(), 1);
8293
TEST_EQ(blob.data()[0], 77);
83-
TEST_EQ(vec[4].IsBool(), true); // Check if type is a bool
84-
TEST_EQ(vec[4].AsBool(), false); // Check if value is false
85-
TEST_EQ(vec[5].AsDouble(), 4.0); // This is shared with vec[2] !
94+
TEST_EQ(vec[4].IsBlob(), true);
95+
auto aligned_blob = vec[4].AsBlob();
96+
TEST_EQ(aligned_blob.size(), 8);
97+
TEST_EQ(reinterpret_cast<const uint32_t*>(aligned_blob.data())[0], 88);
98+
TEST_EQ(reinterpret_cast<const uint32_t*>(aligned_blob.data())[1], 99);
99+
TEST_EQ(IsAligned(aligned_blob.data(), 4), true);
100+
TEST_EQ(vec[5].IsBool(), true); // Check if type is a bool
101+
TEST_EQ(vec[5].AsBool(), false); // Check if value is false
102+
TEST_EQ(vec[6].AsDouble(), 4.0); // This is shared with vec[2] !
86103
auto tvec = map["bar"].AsTypedVector();
87104
TEST_EQ(tvec.size(), 3);
88105
TEST_EQ(tvec[2].AsInt8(), 3);
@@ -107,9 +124,9 @@ void FlexBuffersTest() {
107124
TEST_EQ(vec[2].MutateFloat(2.0f), true);
108125
TEST_EQ(vec[2].AsFloat(), 2.0f);
109126
TEST_EQ(vec[2].MutateFloat(3.14159), false); // Double does not fit in float.
110-
TEST_EQ(vec[4].AsBool(), false); // Is false before change
111-
TEST_EQ(vec[4].MutateBool(true), true); // Can change a bool
112-
TEST_EQ(vec[4].AsBool(), true); // Changed bool is now true
127+
TEST_EQ(vec[5].AsBool(), false); // Is false before change
128+
TEST_EQ(vec[5].MutateBool(true), true); // Can change a bool
129+
TEST_EQ(vec[5].AsBool(), true); // Changed bool is now true
113130

114131
// Parse from JSON:
115132
flatbuffers::Parser parser;

0 commit comments

Comments
 (0)