View on GitHub
jbson
C++11/1y BSON library
get.hpp
1 // Copyright Christian Manning 2013.
2 // Distributed under the Boost Software License, Version 1.0.
3 // (See accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef JBSON_GET_HPP
7 #define JBSON_GET_HPP
8 
9 #include <type_traits>
10 #include <array>
11 #include <chrono>
12 
13 #include "./config.hpp"
14 
15 JBSON_PUSH_DISABLE_DOCUMENTATION_WARNING
16 #include <boost/utility/string_ref.hpp>
17 JBSON_CLANG_POP_WARNINGS
18 
19 #include "traits.hpp"
20 #include "endian.hpp"
21 #include "error.hpp"
22 #include "detect_size.hpp"
23 
24 namespace jbson {
25 
26 template <typename Container>
27 void value_get(const basic_element<Container>&, ...) {
28  static_assert(std::is_void<Container>::value,
29  "A valid overload of value_get must be supplied for user-defined types.");
30 }
31 
32 namespace detail {
33 
34 template <typename StringT> struct make_string {
35  static_assert(!std::is_same<std::decay_t<StringT>, const char*>::value, "");
36  static_assert(!std::is_same<std::decay_t<StringT>, char*>::value, "");
37  template <typename Iterator> static StringT call(const Iterator& first, const Iterator& last) {
38  return StringT{first, last};
39  }
40 };
41 
42 template <> struct make_string<boost::string_ref> {
43  template <typename Iterator> static boost::string_ref call(const Iterator& first, const Iterator& last) {
44  static_assert(is_iterator_pointer<Iterator>::value, "can only use string_ref for raw memory");
45  return boost::string_ref{&*first, static_cast<size_t>(std::distance(first, last))};
46  }
47 };
48 
49 // number
50 template <typename RangeT, typename ArithT>
51 void deserialise(const RangeT& data, ArithT& num, std::enable_if_t<std::is_arithmetic<ArithT>::value>* = nullptr) {
52  if(boost::distance(data) != sizeof(ArithT))
53  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(boost::distance(data))
54  << detail::expected_size(sizeof(ArithT)));
55  num = detail::little_endian_to_native<ArithT>(data.begin(), data.end());
56 }
57 
58 // string
59 template <typename RangeT, typename StringT>
60 void deserialise(const RangeT& data, StringT& str,
61  std::enable_if_t<std::is_convertible<std::decay_t<StringT>, boost::string_ref>::value>* = nullptr) {
62  auto first = data.begin(), last = data.end();
63  if(std::distance(first, last) <= static_cast<ptrdiff_t>(sizeof(int32_t)))
64  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(std::distance(first, last))
65  << detail::expected_size(sizeof(int32_t)));
66  std::advance(first, sizeof(int32_t));
67  const auto length = detail::little_endian_to_native<int32_t>(data.begin(), first) - 1;
68  if(length < 0)
69  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(length));
70  last = std::find(first, last, '\0');
71  if(std::distance(first, last) != length)
72  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(std::distance(first, last))
73  << detail::expected_size(length));
74 
75  str = detail::make_string<StringT>::call(first, last);
76 }
77 
78 // embedded document
79 template <typename RangeT, typename Container, typename EContainer>
80 void deserialise(const RangeT& data, basic_document<Container, EContainer>& doc) {
82 }
83 
84 template <typename RangeT, typename Container, typename EContainer>
85 void deserialise(const RangeT& data, basic_array<Container, EContainer>& arr) {
87 }
88 
89 // binary data
90 template <typename RangeT> void deserialise(const RangeT& data, std::vector<char>& vec) {
91  boost::range::push_back(vec, data);
92 }
93 
94 // ditto
95 template <typename RangeT> void deserialise(const RangeT& data, RangeT& vec) { vec = data; }
96 
97 // oid
98 template <typename RangeT> void deserialise(const RangeT& data, std::array<char, 12>& oid) {
99  if(boost::distance(data) != 12)
100  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(boost::distance(data))
101  << detail::expected_size(12));
102  std::copy(data.begin(), data.end(), oid.data());
103 }
104 
105 // regex
106 template <typename RangeT, typename StringT>
107 void deserialise(const RangeT& data, std::tuple<StringT, StringT>& tuple,
108  std::enable_if_t<std::is_constructible<std::string, std::decay_t<StringT>>::value>* = nullptr) {
109  using string_maker = detail::make_string<std::decay_t<StringT>>;
110 
111  auto first = std::find(data.begin(), data.end(), '\0');
112  if(first == data.end())
113  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(boost::distance(data)));
114  std::get<0>(tuple) = string_maker::call(data.begin(), first);
115 
116  auto last = std::find(++first, data.end(), '\0');
117  if(last == data.end())
118  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(boost::distance(data)));
119  std::get<1>(tuple) = string_maker::call(first, last);
120 }
121 
122 // db pointer
123 template <typename RangeT, typename StringT>
124 void deserialise(const RangeT& data, std::tuple<StringT, std::array<char, 12>>& tuple,
125  std::enable_if_t<std::is_constructible<std::string, std::decay_t<StringT>>::value>* = nullptr) {
126  deserialise(data, std::get<0>(tuple));
127  deserialise(boost::make_iterator_range(std::next(data.begin(), detail::detect_size(element_type::string_element,
128  data.begin(), data.end())),
129  data.end()),
130  std::get<1>(tuple));
131 }
132 
133 // scoped javascript
134 template <typename RangeT, typename StringT, typename DocContainerT, typename DocEContainerT>
135 void deserialise(const RangeT& data, std::tuple<StringT, basic_document<DocContainerT, DocEContainerT>>& tuple,
136  std::enable_if_t<std::is_constructible<std::string, std::decay_t<StringT>>::value>* = nullptr) {
137  int32_t length;
138  auto it = data.begin();
139  deserialise(boost::make_iterator_range(it, std::next(it, 4)), length);
140  if(length != boost::distance(data))
141  BOOST_THROW_EXCEPTION(invalid_element_size{} << detail::actual_size(boost::distance(data))
142  << detail::expected_size(length));
143  std::advance(it, 4);
144  deserialise(boost::make_iterator_range(it, data.end()), std::get<0>(tuple));
145  deserialise(boost::make_iterator_range(
146  std::next(it, detail::detect_size(element_type::string_element, it, data.end())), data.end()),
147  std::get<1>(tuple));
148 }
149 
150 } // namespace detail
151 
152 template <typename Container>
153 void value_get(const basic_element<Container>& elem, std::string& str) {
154  str = std::string(elem.template value<detail::ElementTypeMap<element_type::string_element, Container>>());
155 }
156 
157 } // namespace jbson
158 
159 #endif // JBSON_GET_HPP
std::string or boost::string_ref (string_type)
BSON document.
Definition: document.hpp:191
BSON element.
Definition: element.hpp:69
Exception thrown when an element's data size differs from that reported.
Definition: error.hpp:55
BSON array.
Definition: document.hpp:534
typename mpl::at< typename TypeMap< Container >::map_type, element_type_c< EType >>::type ElementTypeMap
Type alias to perform boost::mpl::at on TypeMap::map_type.
Definition: traits.hpp:137
Trait to determine if an iterator is a pointer, or pointer in disguise.
Definition: traits.hpp:53