Skip to content

Commit 52c56be

Browse files
committed
CXX-1476 Handle searches for empty key names correctly
Also, adds a regression test for CXX-1476
1 parent c80ce3e commit 52c56be

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

src/bsoncxx/document/view.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,23 +109,41 @@ view::const_iterator view::end() const {
109109

110110
view::const_iterator view::find(stdx::string_view key) const {
111111
bson_t b;
112-
bson_iter_t iter;
113-
114112
if (!bson_init_static(&b, _data, _length)) {
115113
return cend();
116114
}
117115

116+
bson_iter_t iter;
118117
if (!bson_iter_init(&iter, &b)) {
119118
return cend();
120119
}
121120

122-
if (key.empty()) {
123-
return cend();
124-
}
121+
// See CDRIVER-2414: If we had a bson_iter_find_init that accepted
122+
// the key length, then we could call it without needing to make a
123+
// temporary std::string from the 'key', obviating the need for
124+
// most of the rest of this method.
125+
126+
// Logically, a default constructed string_view represents the
127+
// empty string just as does string_view(""), but they have,
128+
// potentially, different represntations, the former having .data
129+
// returning nullptr though the latter probably does not. But the
130+
// C functions like strncmp below can't be called with nullptr. If
131+
// we were called with a string_data such that its .data() member
132+
// returns nullptr, then, barring undefined behavior, its length
133+
// is known to be zero, and it is equivalent to the empty string,
134+
// an instance of which we reset it to.
135+
if (key.data() == nullptr)
136+
key = "";
125137

126138
while (bson_iter_next(&iter)) {
127-
const char* ikey = bson_iter_key(&iter);
128-
if (0 == strncmp(key.data(), ikey, key.size()) && strlen(ikey) == key.size()) {
139+
// See CDRIVER-2414: If bson_iter_key returned the length, we
140+
// could avoid the strlen, or, better still, not use strncmp
141+
// by using an O(1) promotion to stdx::string view, and then
142+
// using the relops for that class on both sides.
143+
const auto ikey = bson_iter_key(&iter);
144+
if ((0 == strncmp(key.data(), ikey, key.size())) && (strlen(ikey) == key.size())) {
145+
// NOTE: Technically, we are here accessing internals of bson_iter_t, which
146+
// we should not do.
129147
return const_iterator(element(iter.raw, iter.len, iter.off));
130148
}
131149
}

src/bsoncxx/test/bson_get_values.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,20 @@ TEST_CASE("array view begin/end/find give expected types", "[bsoncxx]") {
317317
REQUIRE(iter == ary.begin());
318318
}
319319
}
320+
321+
TEST_CASE("CXX-1476: CXX-992 regression fixes", "[bsoncxx]") {
322+
SECTION("request for field 'o' does not return field 'op'") {
323+
constexpr auto k_json = R"({ "op" : 1, "o" : 2 })";
324+
const auto bson = from_json(k_json);
325+
REQUIRE(bson.view()["o"].key() == stdx::string_view("o"));
326+
}
327+
328+
SECTION("empty key is not ignored") {
329+
constexpr auto k_json = R"({ "" : 1 })";
330+
const auto bson = from_json(k_json);
331+
REQUIRE(bson.view().find("") != bson.view().cend());
332+
REQUIRE(bson.view().find(stdx::string_view()) != bson.view().cend());
333+
}
334+
}
335+
320336
} // namespace

0 commit comments

Comments
 (0)