6 #ifndef JBSON_DOCUMENT_HPP
7 #define JBSON_DOCUMENT_HPP
14 #include "detail/config.hpp"
16 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
17 #include <boost/iterator/iterator_facade.hpp>
18 #include <boost/iterator/iterator_traits.hpp>
19 #include <boost/optional.hpp>
20 JBSON_CLANG_POP_WARNINGS
22 #include "document_fwd.hpp"
23 #include "element_fwd.hpp"
24 #include "detail/traits.hpp"
25 #include "element.hpp"
33 const char* what()
const noexcept
override {
return "invalid_document_size"; }
44 template <
typename Value,
typename BaseIterator>
46 : boost::iterator_facade<document_iter<Value, BaseIterator>, Value, boost::forward_traversal_tag, Value&> {
60 document_iter(BaseIterator it1, BaseIterator it2) : m_start(it1), m_end(it2) {
71 template <
class OtherValue,
typename OtherIt>
73 std::enable_if_t<std::is_convertible<OtherValue, element_type>::value&&
74 std::is_convertible<OtherIt, BaseIterator>::value>* =
nullptr)
75 : m_start(other.m_start), m_end(other.m_end) {
81 #ifndef DOXYGEN_SHOULD_SKIP_THIS
83 friend class boost::iterator_core_access;
88 template <
typename OtherValue,
typename OtherIterator>
90 return other.m_start == m_start;
94 if(m_start == m_end) {
99 std::advance(m_start, m_cur->size());
118 BaseIterator m_start;
120 boost::optional<std::remove_const_t<element_type>> m_cur;
123 #ifdef DOXYGEN_SHOULD_SKIP_THIS
127 template <
typename Container>
void init_empty(Container& c);
129 template <
typename Container>
130 void init_empty(Container& c, std::enable_if_t<container_has_push_back<Container>::value>* =
nullptr) {
131 static constexpr std::array<char, 5> arr{{5, 0, 0, 0,
'\0'}};
132 boost::range::push_back(c, arr);
135 template <
typename Container>
137 std::enable_if_t<std::is_constructible<Container, std::array<char, 5>>::value>* =
nullptr) {
138 static constexpr std::array<char, 5> arr{{5, 0, 0, 0,
'\0'}};
142 template <
typename Container>
143 void init_empty(Container&, std::enable_if_t<!container_has_push_back<Container>::value>* =
nullptr,
144 std::enable_if_t<!std::is_constructible<Container, std::array<char, 5>>::value>* =
nullptr) {}
146 #endif // DOXYGEN_SHOULD_SKIP_THIS
168 return (std::underlying_type_t<validity_level>)a < (std::underlying_type_t<validity_level>)b;
176 return static_cast<validity_level>((std::underlying_type_t<validity_level>)a &
177 (std::underlying_type_t<validity_level>)b);
182 return static_cast<validity_level>((std::underlying_type_t<validity_level>)a |
183 (std::underlying_type_t<validity_level>)b);
225 template <
typename SomeType>
227 std::enable_if_t<std::is_same<
container_type, std::decay_t<SomeType>>::value>* =
nullptr)
228 : m_data(std::forward<SomeType>(c)) {
231 << detail::expected_size(
sizeof(int32_t)));
234 << detail::expected_size(
235 detail::little_endian_to_native<int32_t>(m_data.begin(), m_data.end()))
236 << detail::actual_size(boost::distance(m_data)));
244 template <
typename OtherContainer>
246 std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>* =
247 nullptr) noexcept(std::is_nothrow_constructible<
container_type, OtherContainer>::value)
248 : m_data(other.m_data) {}
255 template <
typename OtherContainer>
257 std::enable_if_t<!std::is_constructible<container_type, OtherContainer>::value &&
259 std::is_constructible<
container_type,
typename OtherContainer::const_iterator,
260 typename OtherContainer::const_iterator>::value>* =
nullptr)
261 : m_data(other.m_data.
begin(), other.m_data.
end()) {}
264 template <
size_t N>
explicit basic_document(std::array<char, N>, std::enable_if_t<(N < 5)>* =
nullptr) =
delete;
271 template <
typename ForwardRange>
278 typename container_type::iterator, boost::mpl::_1>>,
279 std::true_type>::value>* =
nullptr,
280 std::enable_if_t<!std::is_same<
container_type, std::decay_t<ForwardRange>>::value &&
283 boost::mpl::_1, boost::mpl::_1>>::value>* =
nullptr)
294 template <
typename ForwardRange>
303 std::array<char, 4> arr{{0, 0, 0, 0}};
304 boost::range::push_back(m_data, arr);
305 for(
auto&& e : rng) {
306 e.write_to_container(m_data, m_data.end());
308 m_data.push_back(
'\0');
309 auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
310 static_assert(4 ==
size.size(),
"");
312 boost::range::copy(
size, m_data.begin());
318 template <
typename ForwardIterator>
321 std::is_constructible<
basic_document, boost::iterator_range<ForwardIterator>>::value>* =
nullptr)
332 if(boost::distance(m_data) <= static_cast<ptrdiff_t>(
sizeof(int32_t)))
334 return {std::next(m_data.begin(),
sizeof(int32_t)), std::prev(m_data.end())};
343 auto last = std::prev(m_data.end());
353 return std::find_if(
begin(),
end(), [&elem_name](
auto&& elem) {
return elem.name() == elem_name; });
368 auto pos = m_data.erase(it.m_start, std::next(it.m_start, it.m_cur->size()));
370 auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
371 static_assert(4 ==
size.size(),
"");
373 boost::range::copy(
size, m_data.begin());
375 return {pos, std::prev(m_data.end())};
391 template <
typename EContainer>
396 auto pos = m_data.insert(it.m_start, data.begin(), data.end());
398 auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
399 static_assert(4 ==
size.size(),
"");
401 boost::range::copy(
size, m_data.begin());
403 return {pos, std::prev(m_data.end())};
423 auto pos = m_data.insert(it.m_start, data.begin(), data.end());
425 auto size = jbson::detail::native_to_little_endian(static_cast<int32_t>(m_data.size()));
426 static_assert(4 ==
size.size(),
"");
428 boost::range::copy(
size, m_data.begin());
430 return {pos, std::prev(m_data.end())};
442 int32_t
size() const noexcept {
return boost::distance(m_data); }
455 return std::move(m_data);
471 ret =
static_cast<ptrdiff_t
>(boost::distance(m_data)) > static_cast<ptrdiff_t>(
sizeof(int32_t));
473 ret =
static_cast<ptrdiff_t
>(boost::distance(m_data)) ==
474 detail::little_endian_to_native<int32_t>(m_data.begin(), m_data.end());
478 for(
auto&& e : *
this) {
480 ret = get<jbson::element_type::document_element>(e).valid(lvl, recurse);
482 ret = get<jbson::element_type::array_element>(e).valid(lvl, recurse);
484 decltype(get<jbson::element_type::document_element>(e)) scope;
485 std::tie(std::ignore, scope) = get<jbson::element_type::scoped_javascript_element>(e);
486 ret = scope.valid(lvl, recurse);
493 std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> u8to32{};
494 auto str = get<jbson::element_type::string_element>(e);
495 u8to32.from_bytes(str.data(), str.data() + str.size());
511 for(
auto&& e : *
this) {
514 return std::move(set);
519 return boost::equal(m_data, other.m_data);
525 swap(m_data, other.m_data);
531 template <
typename,
typename>
friend class basic_array;
536 template <
typename,
typename>
friend class basic_array;
554 template <
typename OtherContainer>
556 std::enable_if_t<std::is_constructible<container_type, OtherContainer>::value>* =
557 nullptr) noexcept(std::is_nothrow_constructible<container_type, OtherContainer>::value)
558 :
base(other.m_data) {}
560 template <
typename Arg>
563 std::enable_if_t<std::is_constructible<base, Arg&&>::value && !std::is_convertible<Arg&&, base>::value>* =
564 nullptr) noexcept(std::is_nothrow_constructible<base, Arg&&>::value)
565 :
base(std::forward<Arg>(arg)) {}
567 template <
typename Arg1,
typename Arg2>
569 std::enable_if_t<std::is_constructible<base, Arg1&&, Arg2&&>::value>* =
570 nullptr) noexcept(std::is_nothrow_constructible<base, Arg1&&, Arg2&&>::value)
571 :
base(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2)) {}
576 bool ret = base::valid(lvl, recurse);
580 for(
auto&& e : *
this) {
581 if(!(ret = (e.name() == std::to_string(count))))
594 template <
typename SequenceContainer,
595 typename = std::enable_if_t<detail::container_has_push_back<SequenceContainer>::value>,
596 typename = std::enable_if_t<
598 explicit operator SequenceContainer()
const {
599 auto fun = [](
auto&& a,
auto&& b) {
602 return strverscmp(a.name().data(), b.name().data());
604 return a.name().compare(b.name());
607 auto vec = SequenceContainer{};
608 for(
auto&& e : *
this)
610 boost::range::sort(vec, [fun](
auto&& e1,
auto&& e2) {
return fun(e1, e2) < 0; });
611 assert(std::unique(vec.begin(), vec.end(), [fun](
auto&& e1,
auto&& e2) {
return fun(e1, e2) == 0; }) ==
613 return std::move(vec);
618 template <
typename Container,
typename EContainer>
626 template <
typename Container,
typename EContainer>
633 template <
typename Container,
typename SetContainer>
634 void value_set(basic_element<Container>& c,
const basic_document_set<SetContainer>& val) {
640 #endif // JBSON_DOCUMENT_HPP
Type trait to determine whether a type is a basic_document (or basic_array)
bool operator>(validity_level a, validity_level b)
Allow more than (>) comparison for validity_level.
Forward traversal iterator type for traversing basic_document and basic_array.
Tests that array elements are integers named consecutively from 0 (zero). Includes above except unico...
const_iterator emplace(const const_iterator &it, Args &&...args)
Inserts an element in-place directly before another.
const_iterator find(boost::string_ref elem_name) const
Find an element with the specified name.
std::string or boost::string_ref (string_type)
bool operator==(const basic_document< C, EC > &other) const
Determines equality with another basic_document.
const_iterator iterator
Forward iterator for traversal of elements.
Basic test for the size of the data.
document_iter(const document_iter< OtherValue, OtherIt > &other, std::enable_if_t< std::is_convertible< OtherValue, element_type >::value &&std::is_convertible< OtherIt, BaseIterator >::value > *=nullptr)
Copy constructor.
std::multiset< basic_element< Container >, detail::elem_compare > basic_document_set
BSON document in the form of a std::set for ease of manipulation.
void init_empty(Container &c)
Initialises a container or range for a valid empty basic_document or basic_array. ...
document_iter(BaseIterator it1, BaseIterator it2)
Construct document_iter from pair of BaseIterators.
document_iter()=default
Default constructor. Results in an invalid iterator.
void swap(basic_document &other) noexcept
Swaps contents with another basic_document.
validity_level operator&(validity_level a, validity_level b)
Allow bitwise-and (&) for validity_level.
basic_document(SomeType &&c, std::enable_if_t< std::is_same< container_type, std::decay_t< SomeType >>::value > *=nullptr)
Constructs a document with an existing container of data.
bool valid(const validity_level lvl=validity_level::bson_size, const bool recurse=true) const
Validates document/array according to a validity_level.
Basic test for the equality of the data size with that reported by the data. Includes above...
is_range_of_value< RangeT, mpl::bind2< mpl::quote2< std::is_same >, ElementT, mpl::_1 >> is_range_of_same_value
Type trait to determine equivalence of the value_type of a Range.
validity_level
Level of validity to test for with basic_document::valid.
const_iterator end() const noexcept
is_range_of< RangeT, ElementTrait, mpl::quote1< boost::range_mutable_iterator >> is_range_of_iterator
Type trait to apply a unary metafunction trait to the iterator type of a Range.
Variadic version of boost::mpl::quoteN.
container_type && data()&&noexcept(std::is_nothrow_move_constructible< container_type >::value)
const container_type & data() const &noexcept
Type trait to determine whether a type is a boost::iterator_range.
typename mpl::eval_if< has_iterator< std::decay_t< Container >>, container_has_push_back_impl< std::decay_t< Container >>, std::false_type >::type container_has_push_back
Type trait to determine if type is a container with a push_back() function.
Tests that each element can be constructed without error. Includes above.
element_type & dereference() const
Dereferences currently held basic_element.
Tests for valid unicode in elements with type element_type::string_element. Includes above...
const_iterator insert(const const_iterator &it, const basic_element< EContainer > &el)
Inserts an element directly before another.
int32_t size() const noexcept
Returns data's size in bytes.
basic_element< ElementContainer > element_type
Type of constituent elements.
is_range_of< RangeT, ElementTrait, mpl::quote1< boost::range_value >> is_range_of_value
Type trait to apply a unary metafunction trait to the value_type of a Range.
Type trait to determine if a type is nothrow/noexcept swappable.
validity_level operator|(validity_level a, validity_level b)
Allow bitwise-or (|) for validity_level.
basic_document(const basic_document< OtherContainer > &other, std::enable_if_t<!std::is_constructible< container_type, OtherContainer >::value &&detail::container_has_push_back< container_type >::value &&std::is_constructible< container_type, typename OtherContainer::const_iterator, typename OtherContainer::const_iterator >::value > *=nullptr)
Copy constructor from a basic_document with a different container_type.
const_iterator erase(const const_iterator &it)
Removes the element at it.
Exception type. Base class of all exceptions thrown directly by jbson.
basic_document(ForwardRange &&rng, std::enable_if_t< boost::mpl::and_< detail::is_range_of_value< ForwardRange, boost::mpl::quote1< detail::is_element >>, detail::container_has_push_back< container_type >, boost::mpl::not_< detail::is_document< std::decay_t< ForwardRange >>>>::type::value > *=nullptr)
Constructs a document from a range of basic_element.
basic_document> (document_type)
basic_document(ForwardIterator first, ForwardIterator last, std::enable_if_t< std::is_constructible< basic_document, boost::iterator_range< ForwardIterator >>::value > *=nullptr)
Constructs a document from two iterators. Forwards to the range-based constructors.
Value element_type
Alias to Value.
basic_document< std::vector< char >> document
Default basic_document type alias for owned BSON data.
Type trait to determine whether a type is a basic_element.
static void write_to_container(container_type &, typename container_type::const_iterator, boost::string_ref, element_type)
Constructs a BSON element without data, in-place into a container.
typename detail::document_iter< const element_type, typename container_type::const_iterator > const_iterator
Forward iterator for traversal of elements.
Exception thrown when an document's data size differs from that reported.
std::decay_t< Container > container_type
Type of underlying storage container/range.
const_iterator begin() const
Returns an iterator to the first element of the container.
basic_document()
Default constructor.
bool operator<(validity_level a, validity_level b)
Allow less than (<) comparison for validity_level.
basic_document(ForwardRange &&rng, std::enable_if_t< detail::is_range_of_same_value< ForwardRange, char >::value > *=nullptr, std::enable_if_t< std::conditional_t< detail::is_iterator_range< container_type >::value, detail::is_range_of_iterator< ForwardRange, boost::mpl::bind< detail::quote< std::is_constructible >, typename container_type::iterator, boost::mpl::_1 >>, std::true_type >::value > *=nullptr, std::enable_if_t<!std::is_same< container_type, std::decay_t< ForwardRange >>::value &&detail::is_range_of_iterator< ForwardRange, boost::mpl::bind< detail::quote< std::is_constructible >, container_type, boost::mpl::_1, boost::mpl::_1 >>::value > *=nullptr)
Constructs a document from a range of char.
basic_document(const basic_document< OtherContainer > &other, std::enable_if_t< std::is_constructible< container_type, OtherContainer >::value > *=nullptr) noexcept(std::is_nothrow_constructible< container_type, OtherContainer >::value)
Copy constructor from a basic_document with a different container_type.
container_type data() const &&noexcept(std::is_nothrow_copy_constructible< container_type >::value)