// Copyright 2009-present MongoDB, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include // IWYU pragma: export // #include #include #include #include // IWYU pragma: export #include // IWYU pragma: export #include #include #include #include #include #include #include #include namespace bsoncxx { namespace v1 { namespace document { /// /// A BSON document. /// class value { public: /// /// The type of the deleter used to free the underlying BSON bytes. /// /// A deleter `D` that is wrapped by @ref deleter_type must: /// /// - be `std::is_nothrow_destructible`, /// - be `std::is_nothrow_move_constructible`, /// - not throw an exception when invoked with a `std::uint8_t*`. /// /// These requirements are the same as those for a `std::unique_ptr` deleter `D`, but do not include nothrow /// move assignability due to being wrapped by @ref deleter_type. /// /// @important Despite being a `std::function`, @ref deleter_type is assumed to behave like a C++23 /// `std::move_only_function`. That is, it is assumed to be nothrow destructible, nothrow move constructible, and /// nothrow move assignable. /// using deleter_type = std::function; /// /// The deleter used to free copied BSON bytes when a user-provided deleter is not specified. /// using default_deleter_type = std::default_delete; /// /// The type of a pointer to @ref noop_deleter. /// using noop_deleter_type = void(BSONCXX_ABI_CDECL*)(std::uint8_t*); /// /// The deleter used to avoid freeing preallocated storage representing an empty BSON document. /// static BSONCXX_ABI_EXPORT_CDECL(void) noop_deleter(std::uint8_t*); /// /// The type of the unique pointer used to manage the underlying BSON bytes. /// using unique_ptr_type = std::unique_ptr; private: unique_ptr_type _data; template using is_value = detail::is_alike; template using to_bson_expr = decltype(to_bson(std::declval(), std::declval())); template struct has_to_bson : detail::conjunction>, detail::is_detected> {}; template using from_bson_expr = decltype(from_bson(std::declval(), std::declval())); template struct has_from_bson : detail::conjunction>, detail::is_detected> { }; template struct is_valid_deleter : detail::disjunction< // std::function may not be nothrow move constructible across all pre-C++20 implementations. // Deliberately allow an exemption that assumes it is de facto nothrow move constructible. std::is_same, // Same requirements for D in std::unique_ptr but without (nothrow) move assignability which is not // required for T in std::function. detail::conjunction< std::is_convertible, std::is_nothrow_destructible, std::is_nothrow_move_constructible>> {}; public: /// @copydoc v1::document::view::const_iterator using const_iterator = v1::document::view::const_iterator; /// @copydoc v1::document::view::iterator using iterator = const_iterator; /// /// Destroy this object. /// ~value() = default; /// /// Move construction. /// /// @par Postconditions: /// - `this->data() == nullptr` /// - `this->get_deleter()` is in a valid but unspecified state. /// value(value&& other) noexcept = default; /// /// Move assignment. /// /// @par Postconditions: /// - `this->data() == nullptr` /// - `this->get_deleter()` is in a valid but unspecified state. /// value& operator=(value&& other) noexcept = default; /// /// Copy construction. /// /// The copied value is allocated using `operator new[]` and the deleter is set to @ref /// default_deleter_type. /// value(value const& other) : value{other.view()} {} /// /// Copy assignment. /// /// The copied value is allocated using `operator new[]` and the deleter is set to @ref /// default_deleter_type. /// value& operator=(value const& other) { *this = value{other.view()}; return *this; } /// /// Initialize as an empty document (or array). /// /// The value is equivalent to a default-initialized @ref bsoncxx::v1::document::view and the deleter is set to @ref /// bsoncxx::v1::document::value::noop_deleter. /// /// @warning Modifying the pointed-to data after default construction is undefined behavior. /// value() : _data{const_cast(v1::document::view{}.data()), &noop_deleter} {} /// /// Initialize as owning `data` which will be freed with `deleter`. /// /// @par Constraints: /// - `Deleter` must satisfy the requirements described by @ref deleter_type. /// /// @par Preconditions: /// - If `data` is not null, the size of the storage region pointed to by `data` must be greater than or equal to 5. /// - The embedded length must be less than or equal to the size of the storage region pointed to by `data`. /// - `deleter` must be capable of freeing the storage region pointed to by `data`. /// template ::value>* = nullptr> value(std::uint8_t* data, Deleter deleter) : _data{data, std::move(deleter)} {} /// /// Equivalent to @ref value(std::uint8_t* data, Deleter deleter), but validates the embedded length against /// `length`. /// /// @par Preconditions: /// - If `data` is not null, the size of the storage region pointed to by `data` must be greater than or equal to /// `length`. /// /// @exception bsoncxx::v1::exception with @ref bsoncxx::v1::document::view::errc::invalid_length if `length` is /// less than `5` or less than `this->size()`. /// template ::value>* = nullptr> value(std::uint8_t* data, std::size_t length, Deleter deleter) : value{data, std::move(deleter)} { (void)v1::document::view{data, length}; // May throw. } /// /// Initialize as owning `data` which will be freed with @ref default_deleter_type. /// /// @par Preconditions: /// - If `data` is not null, the size of the storage region pointed to by `data` must be greater than or equal to 5. /// - The embedded length must be less than or equal to the size of the storage region pointed to by `data`. /// explicit value(std::uint8_t* data) : value{data, default_deleter_type{}} {} /// /// Equivalent to @ref value(std::uint8_t* data), but validates the embedded length against `length`. /// /// @par Preconditions: /// - If `data` is not null, the size of the storage region pointed to by `data` must be greater than or equal to /// `length`. /// /// @exception bsoncxx::v1::exception with @ref bsoncxx::v1::document::view::errc::invalid_length if `length` is /// less than `5` or less than `this->size()`. /// explicit value(std::uint8_t* data, std::size_t length) : value{data} { (void)v1::document::view{data, length}; // May throw. } /// /// Initialize as owning `ptr`. /// /// @par Preconditions: /// - If `data` is not null, the size of the storage region pointed to by `data` must be greater than or equal to 5. /// - The embedded length must be less than or equal to the size of the storage region pointed to by `data`. /// explicit value(unique_ptr_type ptr) : _data{std::move(ptr)} {} /// /// Equivalent to @ref value(unique_ptr_type ptr), but validates the embedded length against `length`. /// /// @par Preconditions: /// - If `ptr` is not null, the size of the storage region pointed to by `data` must be greater than or equal to /// `length`. /// /// @exception bsoncxx::v1::exception with @ref bsoncxx::v1::document::view::errc::invalid_length if `length` is /// less than `5` or less than `this->size()`. /// explicit value(unique_ptr_type ptr, std::size_t length) : value{std::move(ptr)} { (void)v1::document::view{_data.get(), length}; // May throw. } /// /// Initialize with a copy of the BSON bytes referenced by `view`. /// /// If `view` is equivalent to a default-initialized @ref bsoncxx::v1::document::view, this value is /// equivalent to `value()`. /// /// If `view` is invalid, this value is equivalent to `value{nullptr}`. /// /// Otherwise, the copied value is allocated using `operator new[]` and the deleter is set to @ref /// default_deleter_type. /// explicit value(v1::document::view view) { if (!view) { return; } v1::document::view const empty; if (view.data() == empty.data()) { _data = unique_ptr_type{const_cast(empty.data()), &noop_deleter}; } else { _data = unique_ptr_type{new std::uint8_t[view.size()], default_deleter_type{}}; std::memcpy(_data.get(), view.data(), view.size()); } } /// /// Equivalent to `to_bson(v, *this);` after default-initialization. /// /// @par Constraints: /// - `T` is not @ref bsoncxx::v1::document::value. /// - `to_bson(v, *this)` is a valid and unambiguous overload found by ADL. /// template ::value>* = nullptr> explicit value(T const& v) : value{} { to_bson(v, *this); // ADL. } /// /// Equivalent to `this->reset(view)`. /// value& operator=(v1::document::view view) { this->reset(view); return *this; } /// /// Equivalent to `*this = value{v}`. /// /// @par Constraints: /// - `T` is not @ref bsoncxx::v1::document::value. /// - `to_bson(v, *this)` is a valid and unambiguous overload found by ADL. /// template ::value>* = nullptr> value& operator=(T const& v) { *this = value{v}; return *this; } /// /// Equivalent to `from_bson(v, this->view())`. /// /// @par Constraints: /// - `T` is not @ref bsoncxx::v1::document::value. /// - `from_bson(v, this->view())` is a valid and unambiguous overload found by ADL. /// template ::value>* = nullptr> void get(T& v) const { from_bson(v, this->view()); // ADL. } /// /// Equivalent to `T value; this->get(value); return value;`. /// /// @par Constraints: /// - `T` is not @ref bsoncxx::v1::document::value. /// - `T` is default-constructible. /// - `from_bson(v, this->view())` is a valid and unambiguous overload found by ADL. /// template < typename T, detail::enable_if_t, has_from_bson>::value>* = nullptr> T get() const { T res; this->get(res); return res; } /// /// Return the current deleter. /// deleter_type const& get_deleter() const { return _data.get_deleter(); } /// /// Release ownership of the underlying BSON bytes. /// unique_ptr_type release() { return std::move(_data); } /// /// Replace the underlying BSON bytes with `v`. /// void reset(value v) { *this = std::move(v); } /// /// Replace the underlying BSON bytes with a copy of `v`. /// /// If `v` is invalid, reset to a default-initialized value. /// /// The copied value is allocated using `operator new[]` and the deleter is set to @ref /// default_deleter_type. /// void reset(v1::document::view v) { *this = value{v}; } /// /// Return a view of the BSON bytes as a document. /// v1::document::view view() const { return v1::document::view{_data.get()}; } /// /// Implicitly convert to `this->view()`. /// /* explicit(false) */ operator v1::document::view() const { return this->view(); } /// @copydoc v1::document::view::cbegin() const const_iterator cbegin() const { return this->view().cbegin(); } /// @copydoc v1::document::view::cend() const const_iterator cend() const { return this->view().cend(); } /// @copydoc v1::document::view::begin() const const_iterator begin() const { return this->view().begin(); } /// @copydoc v1::document::view::end() const const_iterator end() const { return this->view().end(); } /// @copydoc v1::document::view::find(v1::stdx::string_view key) const const_iterator find(v1::stdx::string_view key) const { return this->view().find(key); } /// @copydoc v1::document::view::operator[](v1::stdx::string_view key) const v1::element::view operator[](v1::stdx::string_view key) const { return this->view()[key]; } /// @copydoc v1::document::view::data() const std::uint8_t const* data() const { return _data.get(); } /// @copydoc v1::document::view::size() const std::size_t size() const { return this->view().size(); } /// @copydoc v1::document::view::length() const std::size_t length() const { return this->view().length(); } /// @copydoc v1::document::view::empty() const bool empty() const { return this->view().empty(); } /// @copydoc v1::document::view::operator bool() const explicit operator bool() const { return this->view().operator bool(); } /// @copydoc v1::document::view::operator==(v1::document::view lhs, v1::document::view rhs) friend bool operator==(value const& lhs, value const& rhs) { return lhs.view() == rhs.view(); } /// @copydoc v1::document::view::operator!=(v1::document::view lhs, v1::document::view rhs) friend bool operator!=(value const& lhs, value const& rhs) { return !(lhs == rhs); } }; } // namespace document } // namespace v1 } // namespace bsoncxx #include /// /// @file /// Provides @ref bsoncxx::v1::document::value. /// /// @par Includes /// - @ref bsoncxx/v1/document/view.hpp /// - @ref bsoncxx/v1/element/view.hpp ///