Skip to content

CXX-3233 add bsoncxx v1 implementations and tests #1430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/bsoncxx/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,8 @@ set_dist_list(src_bsoncxx_lib_DIST
bsoncxx/v_noabi/bsoncxx/types/bson_value/value.hh
bsoncxx/v1/config/config.hpp.in
bsoncxx/v1/config/version.hpp.in
bsoncxx/v1/document/view.hh
bsoncxx/v1/element/view.hh
bsoncxx/v1/types/value.hh
bsoncxx/v1/types/view.hh
)
35 changes: 35 additions & 0 deletions src/bsoncxx/lib/bsoncxx/v1/array/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@

//

#include <bsoncxx/v1/exception.hpp>

#include <bsoncxx/v1/document/view.hh>
#include <bsoncxx/v1/element/view.hh>

#include <cstdint>

#include <bsoncxx/private/bson.hh>
#include <bsoncxx/private/immortal.hh>
#include <bsoncxx/private/itoa.hh>
#include <bsoncxx/private/type_traits.hh>

namespace bsoncxx {
namespace v1 {
namespace array {

using code = v1::document::view::errc;

static_assert(is_regular<view>::value, "bsoncxx::v1::array::view must be regular");
static_assert(is_semitrivial<view>::value, "bsoncxx::v1::array::view must be semitrivial");

Expand All @@ -30,6 +42,29 @@ static_assert(
is_nothrow_moveable<view::const_iterator>::value,
"bsoncxx::v1::array::view::const_iterator must be nothrow moveable");

view::const_iterator view::find(std::uint32_t i) const {
if (!_view) {
return this->cend();
}
Comment on lines +46 to +48
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is not present in v_noabi). This is a consequence of the new, well-defined "invalid" state of an array view.


bsoncxx::itoa key{i};

bson_t bson;

if (!bson_init_static(&bson, _view.data(), _view.length())) {
throw v1::exception{code::invalid_data};
}

bson_iter_t iter;

if (!bson_iter_init_find_w_len(&iter, &bson, key.c_str(), static_cast<int>(key.length()))) {
return this->end();
}
Comment on lines +54 to +62
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is slightly different to v_noabi's bson_init_static -> bson_iter_init -> bson_iter_init_find implementation, but should still be equivalent in observable behavior.


return const_iterator::internal::make_const_iterator(
_view.data(), _view.length(), bson_iter_offset(&iter), bson_iter_key_len(&iter));
}
Comment on lines +64 to +66
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invokes this private constructor, which is public in v_noabi.


} // namespace array
} // namespace v1
} // namespace bsoncxx
104 changes: 104 additions & 0 deletions src/bsoncxx/lib/bsoncxx/v1/decimal128.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,117 @@

//

#include <bsoncxx/v1/exception.hpp>

#include <climits>
#include <cstddef>
#include <string>
#include <system_error>

#include <bsoncxx/private/bson.hh>
#include <bsoncxx/private/immortal.hh>
#include <bsoncxx/private/type_traits.hh>

namespace bsoncxx {
namespace v1 {

using code = v1::decimal128::errc;

static_assert(is_regular<decimal128>::value, "bsoncxx::v1::decimal128 must be regular");
static_assert(is_semitrivial<decimal128>::value, "bsoncxx::v1::decimal128 must be semitrivial");

decimal128::decimal128(v1::stdx::string_view sv) {
if (sv.empty()) {
throw v1::exception{code::empty_string};
}

if (sv.size() > std::size_t{INT_MAX}) {
throw v1::exception{code::invalid_string_length};
}
Comment on lines +39 to +45
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are not present in v_noabi.


bson_decimal128_t d128;

if (!bson_decimal128_from_string_w_len(sv.data(), static_cast<int>(sv.size()), &d128)) {
throw v1::exception{code::invalid_string_data};
}

_high = d128.high;
_low = d128.low;
}

std::string decimal128::to_string() const {
bson_decimal128_t d128;
d128.high = _high;
d128.low = _low;
char str[BSON_DECIMAL128_STRING];
bson_decimal128_to_string(&d128, str);
return {str};
}

std::error_category const& decimal128::error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
return "bsoncxx::v1::decimal128";
}

std::string message(int v) const noexcept override {
switch (static_cast<code>(v)) {
case code::zero:
return "zero";
case code::empty_string:
return "string must not be empty";
case code::invalid_string_length:
return "length of string is too long (exceeds INT_MAX)";
case code::invalid_string_data:
return "string is not a valid Decimal128 representation";
default:
return "unknown: " + std::to_string(v);
}
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
if (ec.category() == v1::source_error_category()) {
using condition = v1::source_errc;

auto const source = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::empty_string:
case code::invalid_string_length:
case code::invalid_string_data:
return source == condition::bsoncxx;

case code::zero:
default:
return false;
}
}

if (ec.category() == v1::type_error_category()) {
using condition = v1::type_errc;

auto const type = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::empty_string:
case code::invalid_string_length:
case code::invalid_string_data:
return type == condition::invalid_argument;

case code::zero:
default:
return false;
}
}

return false;
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

} // namespace v1
} // namespace bsoncxx
2 changes: 2 additions & 0 deletions src/bsoncxx/lib/bsoncxx/v1/document/value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace document {
static_assert(is_regular<value>::value, "bsoncxx::v1::document::value must be regular");
static_assert(is_nothrow_moveable<value>::value, "bsoncxx::v1::document::value must be nothrow moveable");

void value::noop_deleter(std::uint8_t*) { /* noop */ }

} // namespace document
} // namespace v1
} // namespace bsoncxx
176 changes: 175 additions & 1 deletion src/bsoncxx/lib/bsoncxx/v1/document/view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include <bsoncxx/v1/document/view.hpp>
#include <bsoncxx/v1/document/view.hh>

//

#include <bsoncxx/v1/exception.hpp>

#include <bsoncxx/v1/element/view.hh>

#include <cstdint>
#include <string>
#include <system_error>

#include <bsoncxx/private/bson.hh>
#include <bsoncxx/private/immortal.hh>
#include <bsoncxx/private/itoa.hh>
#include <bsoncxx/private/type_traits.hh>

namespace bsoncxx {
namespace v1 {
namespace document {

using code = v1::document::view::errc;

static_assert(is_regular<view>::value, "bsoncxx::v1::document::view must be regular");
static_assert(is_semitrivial<view>::value, "bsoncxx::v1::document::view must be semitrivial");

Expand All @@ -30,6 +43,167 @@ static_assert(
is_nothrow_moveable<view::const_iterator>::value,
"bsoncxx::v1::document::view::const_iterator must be nothrow moveable");

namespace {

constexpr std::uint8_t k_default_view[5] = {5u, 0u, 0u, 0u, 0u};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the special empty document representation that is used to avoid v1::document::value allocation (with a no-op deleter).


} // namespace

view::view() : view{k_default_view} {}

view::view(std::uint8_t const* data, std::size_t length) : view{data} {
if (length < _empty_length || length < this->size()) {
throw v1::exception{v1::document::view::errc::invalid_length};
}
Comment on lines +55 to +57
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks are not present in v_noabi. This is a consequence of the new "embedded" length representation (as computed by this->size()).

This is the only time the length < _empty_length comparison is necessary. Beyond this point, it is a class invariant that data is either null or points to a storage region that is large enough to compute this->size() without undefined behavior.

}

view::const_iterator view::cbegin() const {
if (!this->operator bool()) {
return this->cend();
}
Comment on lines +61 to +63
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check (and the one in find() below) is not present in v_noabi. This is a consequence of the new, well-defined "invalid" state of a document view.


bson_iter_t iter;

if (!bson_iter_init_from_data(&iter, _data, this->size())) {
throw v1::exception{code::invalid_data};
}

if (!bson_iter_next(&iter)) {
return this->cend();
}

return const_iterator::internal::make_const_iterator(
_data, this->size(), bson_iter_offset(&iter), bson_iter_key_len(&iter));
Comment on lines +75 to +76
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invokes this private constructor, which is public in v_noabi.

}

view::const_iterator view::find(v1::stdx::string_view key) const {
if (!this->operator bool()) {
return this->cend();
}

if (key.size() >= std::size_t{INT_MAX}) {
return this->cend();
}
Comment on lines +84 to +86
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is not present in v_noabi. This guards the cast to int in the later call to bson_iter_init_find_w_len (passing -1 does not help).


// Support null as equivalent to empty.
if (!key.data()) {
key = "";
}
Comment on lines +88 to +91
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preserves v_noabi behavior.


bson_t bson;

if (!bson_init_static(&bson, _data, this->size())) {
throw v1::exception{code::invalid_data};
}

bson_iter_t iter;

if (!bson_iter_init_find_w_len(&iter, &bson, key.data(), static_cast<int>(key.size()))) {
return this->end();
}

return const_iterator::internal::make_const_iterator(
_data, this->size(), bson_iter_offset(&iter), bson_iter_key_len(&iter));
}

std::error_category const& view::error_category() {
class type final : public std::error_category {
char const* name() const noexcept override {
return "bsoncxx::v1::document::view";
}

std::string message(int v) const noexcept override {
switch (static_cast<code>(v)) {
case code::zero:
return "zero";
case code::invalid_length:
return "length is invalid";
case code::invalid_data:
return "data is invalid";
default:
return "unknown: " + std::to_string(v);
}
}

bool equivalent(int v, std::error_condition const& ec) const noexcept override {
if (ec.category() == v1::source_error_category()) {
using condition = v1::source_errc;

auto const source = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::invalid_length:
case code::invalid_data:
return source == condition::bsoncxx;

case code::zero:
default:
return false;
}
}

if (ec.category() == v1::type_error_category()) {
using condition = v1::type_errc;

auto const type = static_cast<condition>(ec.value());

switch (static_cast<code>(v)) {
case code::invalid_length:
return type == condition::invalid_argument;

case code::invalid_data:
return type == condition::runtime_error;
Comment on lines +154 to +155
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a reminder, type_errc::invalid_argument is meant to only apply to immediate arguments given to the invoked function from which the exception may be thrown. Here, because invalid_data is only thrown when bson_* functions report failure (not by checking the value of an immediate function parameter), it is categorized as a type_errc::runtime_error. For this purpose, data members of this are not considered to be "arguments" to the given member function. This rationale applies to all other v1 error codes.


case code::zero:
default:
return false;
}
}

return false;
}
};

static bsoncxx::immortal<type> const instance;

return instance.value();
}

view::const_iterator& view::const_iterator::operator++() {
if (!_element) {
return *this;
}

auto iter_opt = to_bson_iter(_element);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is using ADL to invoke bsoncxx::v1::element::view::to_bson_iter(view const&) in lib/bsoncxx/v1/element/view.hh.

if (!iter_opt) {
throw v1::exception{code::invalid_data};
}
auto& iter = *iter_opt;

if (bson_iter_next(&iter)) {
_element = v1::element::view::internal::make(
_element.raw(), _element.length(), bson_iter_offset(&iter), bson_iter_key_len(&iter));
} else {
_element = {};
}

return *this;
}

view::const_iterator::const_iterator(v1::element::view element) : _element(element) {}

view::const_iterator view::const_iterator::internal::make_const_iterator(
std::uint8_t const* raw,
std::size_t length,
std::uint32_t offset,
std::uint32_t keylen) {
return const_iterator{v1::element::view::internal::make(
raw,
static_cast<std::uint32_t>(length), // Guarded by `bson_init_static`.
offset,
keylen)};
}

} // namespace document
} // namespace v1
} // namespace bsoncxx
Loading