Harden failure signal handler in the face of memory corruptions
[folly.git] / folly / DynamicConverter.h
1 /*
2  * Copyright 2014 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // @author Nicholas Ormrod <njormrod@fb.com>
18
19 #ifndef DYNAMIC_CONVERTER_H
20 #define DYNAMIC_CONVERTER_H
21
22 #include "folly/dynamic.h"
23 namespace folly {
24   template <typename T> T convertTo(const dynamic&);
25   template <typename T> dynamic toDynamic(const T&);
26 }
27
28 /**
29  * convertTo returns a well-typed representation of the input dynamic.
30  *
31  * Example:
32  *
33  *   dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
34  *   auto vvi = convertTo<fbvector<fbvector<int>>>(d);
35  *
36  * See docs/DynamicConverter.md for supported types and customization
37  */
38
39
40 #include <type_traits>
41 #include <iterator>
42 #include <boost/iterator/iterator_adaptor.hpp>
43 #include <boost/mpl/has_xxx.hpp>
44 #include "folly/Likely.h"
45
46 namespace folly {
47
48 ///////////////////////////////////////////////////////////////////////////////
49 // traits
50
51 namespace dynamicconverter_detail {
52
53 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
54 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
55 BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
56
57 template <typename T> struct class_is_container {
58   typedef std::reverse_iterator<T*> some_iterator;
59   enum { value = has_value_type<T>::value &&
60                  has_iterator<T>::value &&
61               std::is_constructible<T, some_iterator, some_iterator>::value };
62 };
63
64 template <typename T> struct class_is_range {
65   enum { value = has_value_type<T>::value &&
66                  has_iterator<T>::value };
67 };
68
69
70 template <typename T> struct is_container
71   : std::conditional<
72       std::is_class<T>::value,
73       class_is_container<T>,
74       std::false_type
75     >::type {};
76
77 template <typename T> struct is_range
78   : std::conditional<
79       std::is_class<T>::value,
80       class_is_range<T>,
81       std::false_type
82     >::type {};
83
84 template <typename T> struct is_map
85   : std::integral_constant<
86       bool,
87       is_range<T>::value && has_mapped_type<T>::value
88     > {};
89
90 } // namespace dynamicconverter_detail
91
92 ///////////////////////////////////////////////////////////////////////////////
93 // custom iterators
94
95 /**
96  * We have iterators that dereference to dynamics, but need iterators
97  * that dereference to typename T.
98  *
99  * Implementation details:
100  *   1. We cache the value of the dereference operator. This is necessary
101  *      because boost::iterator_adaptor requires *it to return a
102  *      reference.
103  *   2. For const reasons, we cannot call operator= to refresh the
104  *      cache: we must call the destructor then placement new.
105  */
106
107 namespace dynamicconverter_detail {
108
109 template<typename T>
110 struct Dereferencer {
111   static inline void
112   derefToCache(T* mem, const dynamic::const_item_iterator& it) {
113     throw TypeError("array", dynamic::Type::OBJECT);
114   }
115
116   static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
117     new (mem) T(convertTo<T>(*it));
118   }
119 };
120
121 template<typename F, typename S>
122 struct Dereferencer<std::pair<F, S>> {
123   static inline void
124   derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
125     new (mem) std::pair<F, S>(
126         convertTo<F>(it->first), convertTo<S>(it->second)
127     );
128   }
129
130   // Intentional duplication of the code in Dereferencer
131   template <typename T>
132   static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
133     new (mem) T(convertTo<T>(*it));
134   }
135 };
136
137 template <typename T, typename It>
138 class Transformer : public boost::iterator_adaptor<
139                              Transformer<T, It>,
140                              It,
141                              typename T::value_type
142                            > {
143   friend class boost::iterator_core_access;
144
145   typedef typename T::value_type ttype;
146
147   mutable ttype cache_;
148   mutable bool valid_;
149
150   void increment() {
151     ++this->base_reference();
152     valid_ = false;
153   }
154
155   ttype& dereference() const {
156     if (LIKELY(!valid_)) {
157       cache_.~ttype();
158       Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
159       valid_ = true;
160     }
161     return cache_;
162   }
163
164 public:
165   explicit Transformer(const It& it)
166     : Transformer::iterator_adaptor_(it), valid_(false) {}
167 };
168
169 // conversion factory
170 template <typename T, typename It>
171 static inline std::move_iterator<Transformer<T, It>>
172 conversionIterator(const It& it) {
173   return std::make_move_iterator(Transformer<T, It>(it));
174 }
175
176 } // namespace dynamicconverter_detail
177
178 ///////////////////////////////////////////////////////////////////////////////
179 // DynamicConverter specializations
180
181 /**
182  * Each specialization of DynamicConverter has the function
183  *     'static T convert(const dynamic&);'
184  */
185
186 // default - intentionally unimplemented
187 template <typename T, typename Enable = void> struct DynamicConverter;
188
189 // boolean
190 template <>
191 struct DynamicConverter<bool> {
192   static bool convert(const dynamic& d) {
193     return d.asBool();
194   }
195 };
196
197 // integrals
198 template <typename T>
199 struct DynamicConverter<T,
200     typename std::enable_if<std::is_integral<T>::value &&
201                             !std::is_same<T, bool>::value>::type> {
202   static T convert(const dynamic& d) {
203     return folly::to<T>(d.asInt());
204   }
205 };
206
207 // floating point
208 template <typename T>
209 struct DynamicConverter<T,
210     typename std::enable_if<std::is_floating_point<T>::value>::type> {
211   static T convert(const dynamic& d) {
212     return folly::to<T>(d.asDouble());
213   }
214 };
215
216 // fbstring
217 template <>
218 struct DynamicConverter<folly::fbstring> {
219   static folly::fbstring convert(const dynamic& d) {
220     return d.asString();
221   }
222 };
223
224 // std::string
225 template <>
226 struct DynamicConverter<std::string> {
227   static std::string convert(const dynamic& d) {
228     return d.asString().toStdString();
229   }
230 };
231
232 // std::pair
233 template <typename F, typename S>
234 struct DynamicConverter<std::pair<F,S>> {
235   static std::pair<F, S> convert(const dynamic& d) {
236     if (d.isArray() && d.size() == 2) {
237       return std::make_pair(convertTo<F>(d[0]), convertTo<S>(d[1]));
238     } else if (d.isObject() && d.size() == 1) {
239       auto it = d.items().begin();
240       return std::make_pair(convertTo<F>(it->first), convertTo<S>(it->second));
241     } else {
242       throw TypeError("array (size 2) or object (size 1)", d.type());
243     }
244   }
245 };
246
247 // containers
248 template <typename C>
249 struct DynamicConverter<C,
250     typename std::enable_if<
251       dynamicconverter_detail::is_container<C>::value>::type> {
252   static C convert(const dynamic& d) {
253     if (d.isArray()) {
254       return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
255                dynamicconverter_detail::conversionIterator<C>(d.end()));
256     } else if (d.isObject()) {
257       return C(dynamicconverter_detail::conversionIterator<C>
258                  (d.items().begin()),
259                dynamicconverter_detail::conversionIterator<C>
260                  (d.items().end()));
261     } else {
262       throw TypeError("object or array", d.type());
263     }
264   }
265 };
266
267 ///////////////////////////////////////////////////////////////////////////////
268 // DynamicConstructor specializations
269
270 /**
271  * Each specialization of DynamicConstructor has the function
272  *     'static dynamic construct(const C&);'
273  */
274
275 // default
276 template <typename C, typename Enable = void>
277 struct DynamicConstructor {
278   static dynamic construct(const C& x) {
279     return dynamic(x);
280   }
281 };
282
283 // maps
284 template<typename C>
285 struct DynamicConstructor<C,
286     typename std::enable_if<
287       dynamicconverter_detail::is_map<C>::value>::type> {
288   static dynamic construct(const C& x) {
289     dynamic d = dynamic::object;
290     for (auto& pair : x) {
291       d.insert(toDynamic(pair.first), toDynamic(pair.second));
292     }
293     return d;
294   }
295 };
296
297 // other ranges
298 template<typename C>
299 struct DynamicConstructor<C,
300     typename std::enable_if<
301       !dynamicconverter_detail::is_map<C>::value &&
302       !std::is_constructible<StringPiece, const C&>::value &&
303       dynamicconverter_detail::is_range<C>::value>::type> {
304   static dynamic construct(const C& x) {
305     dynamic d = {};
306     for (auto& item : x) {
307       d.push_back(toDynamic(item));
308     }
309     return d;
310   }
311 };
312
313 // pair
314 template<typename A, typename B>
315 struct DynamicConstructor<std::pair<A, B>, void> {
316   static dynamic construct(const std::pair<A, B>& x) {
317     dynamic d = {};
318     d.push_back(toDynamic(x.first));
319     d.push_back(toDynamic(x.second));
320     return d;
321   }
322 };
323
324 ///////////////////////////////////////////////////////////////////////////////
325 // implementation
326
327 template <typename T>
328 T convertTo(const dynamic& d) {
329   return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
330 }
331
332 template<typename T>
333 dynamic toDynamic(const T& x) {
334   return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
335 }
336
337 } // namespace folly
338
339 #endif // DYNAMIC_CONVERTER_H
340