1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
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#pragma once
18
19#include <functional>
20
21#include <folly/CPortability.h>
22#include <folly/Conv.h>
23#include <folly/Format.h>
24#include <folly/Likely.h>
25#include <folly/detail/Iterators.h>
26#include <folly/lang/Exception.h>
27
28namespace folly {
29namespace detail {
30struct DynamicHasher {
31 using is_transparent = void;
32
33 size_t operator()(dynamic const& d) const {
34 return d.hash();
35 }
36
37 template <typename T>
38 std::enable_if_t<std::is_convertible<T, StringPiece>::value, size_t>
39 operator()(T const& val) const {
40 // keep consistent with dynamic::hash() for strings
41 return Hash()(static_cast<StringPiece>(val));
42 }
43};
44
45struct DynamicKeyEqual {
46 using is_transparent = void;
47
48 bool operator()(const dynamic& lhs, const dynamic& rhs) const {
49 return std::equal_to<dynamic>()(lhs, rhs);
50 }
51
52 // Dynamic objects contains a map<dynamic, dynamic>. At least one of the
53 // operands should be a dynamic. Hence, an operator() where both operands are
54 // convertible to StringPiece is unnecessary.
55 template <typename A, typename B>
56 std::enable_if_t<
57 std::is_convertible<A, StringPiece>::value &&
58 std::is_convertible<B, StringPiece>::value,
59 bool>
60 operator()(A const& lhs, B const& rhs) const = delete;
61
62 template <typename A>
63 std::enable_if_t<std::is_convertible<A, StringPiece>::value, bool> operator()(
64 A const& lhs,
65 dynamic const& rhs) const {
66 return FOLLY_LIKELY(rhs.type() == dynamic::Type::STRING) &&
67 std::equal_to<StringPiece>()(lhs, rhs.stringPiece());
68 }
69
70 template <typename B>
71 std::enable_if_t<std::is_convertible<B, StringPiece>::value, bool> operator()(
72 dynamic const& lhs,
73 B const& rhs) const {
74 return FOLLY_LIKELY(lhs.type() == dynamic::Type::STRING) &&
75 std::equal_to<StringPiece>()(lhs.stringPiece(), rhs);
76 }
77};
78} // namespace detail
79} // namespace folly
80
81//////////////////////////////////////////////////////////////////////
82
83namespace std {
84
85template <>
86struct hash<::folly::dynamic> {
87 size_t operator()(::folly::dynamic const& d) const {
88 return d.hash();
89 }
90};
91
92} // namespace std
93
94//////////////////////////////////////////////////////////////////////
95
96// This is a higher-order preprocessor macro to aid going from runtime
97// types to the compile time type system.
98#define FB_DYNAMIC_APPLY(type, apply) \
99 do { \
100 switch ((type)) { \
101 case NULLT: \
102 apply(std::nullptr_t); \
103 break; \
104 case ARRAY: \
105 apply(Array); \
106 break; \
107 case BOOL: \
108 apply(bool); \
109 break; \
110 case DOUBLE: \
111 apply(double); \
112 break; \
113 case INT64: \
114 apply(int64_t); \
115 break; \
116 case OBJECT: \
117 apply(ObjectImpl); \
118 break; \
119 case STRING: \
120 apply(std::string); \
121 break; \
122 default: \
123 abort(); \
124 } \
125 } while (0)
126
127//////////////////////////////////////////////////////////////////////
128
129namespace folly {
130
131struct FOLLY_EXPORT TypeError : std::runtime_error {
132 explicit TypeError(const std::string& expected, dynamic::Type actual);
133 explicit TypeError(
134 const std::string& expected,
135 dynamic::Type actual1,
136 dynamic::Type actual2);
137};
138
139//////////////////////////////////////////////////////////////////////
140
141namespace detail {
142
143// This helper is used in destroy() to be able to run destructors on
144// types like "int64_t" without a compiler error.
145struct Destroy {
146 template <class T>
147 static void destroy(T* t) {
148 t->~T();
149 }
150};
151
152/*
153 * Helper for implementing numeric conversions in operators on
154 * numbers. Just promotes to double when one of the arguments is
155 * double, or throws if either is not a numeric type.
156 */
157template <template <class> class Op>
158dynamic numericOp(dynamic const& a, dynamic const& b) {
159 if (!a.isNumber() || !b.isNumber()) {
160 throw_exception<TypeError>("numeric", a.type(), b.type());
161 }
162 if (a.isDouble() || b.isDouble()) {
163 return Op<double>()(a.asDouble(), b.asDouble());
164 }
165 return Op<int64_t>()(a.asInt(), b.asInt());
166}
167
168} // namespace detail
169
170//////////////////////////////////////////////////////////////////////
171
172/*
173 * We're doing this instead of a simple member typedef to avoid the
174 * undefined behavior of parameterizing F14NodeMap<> with an
175 * incomplete type.
176 *
177 * Note: Later we may add separate order tracking here (a multi-index
178 * type of thing.)
179 */
180struct dynamic::ObjectImpl : F14NodeMap<
181 dynamic,
182 dynamic,
183 detail::DynamicHasher,
184 detail::DynamicKeyEqual> {};
185
186//////////////////////////////////////////////////////////////////////
187
188// Helper object for creating objects conveniently. See object and
189// the dynamic::dynamic(ObjectMaker&&) ctor.
190struct dynamic::ObjectMaker {
191 friend struct dynamic;
192
193 explicit ObjectMaker() : val_(dynamic::object) {}
194 explicit ObjectMaker(dynamic key, dynamic val) : val_(dynamic::object) {
195 val_.insert(std::move(key), std::move(val));
196 }
197
198 // Make sure no one tries to save one of these into an lvalue with
199 // auto or anything like that.
200 ObjectMaker(ObjectMaker&&) = default;
201 ObjectMaker(ObjectMaker const&) = delete;
202 ObjectMaker& operator=(ObjectMaker const&) = delete;
203 ObjectMaker& operator=(ObjectMaker&&) = delete;
204
205 // This returns an rvalue-reference instead of an lvalue-reference
206 // to allow constructs like this to moved instead of copied:
207 // dynamic a = dynamic::object("a", "b")("c", "d")
208 ObjectMaker&& operator()(dynamic key, dynamic val) {
209 val_.insert(std::move(key), std::move(val));
210 return std::move(*this);
211 }
212
213 private:
214 dynamic val_;
215};
216
217inline void dynamic::array(EmptyArrayTag) {}
218
219template <class... Args>
220inline dynamic dynamic::array(Args&&... args) {
221 return dynamic(Array{std::forward<Args>(args)...});
222}
223
224inline dynamic::ObjectMaker dynamic::object() {
225 return ObjectMaker();
226}
227inline dynamic::ObjectMaker dynamic::object(dynamic a, dynamic b) {
228 return ObjectMaker(std::move(a), std::move(b));
229}
230
231//////////////////////////////////////////////////////////////////////
232
233struct dynamic::item_iterator : detail::IteratorAdaptor<
234 dynamic::item_iterator,
235 dynamic::ObjectImpl::iterator,
236 std::pair<dynamic const, dynamic>,
237 std::forward_iterator_tag> {
238 using Super = detail::IteratorAdaptor<
239 dynamic::item_iterator,
240 dynamic::ObjectImpl::iterator,
241 std::pair<dynamic const, dynamic>,
242 std::forward_iterator_tag>;
243 /* implicit */ item_iterator(dynamic::ObjectImpl::iterator b) : Super(b) {}
244
245 using object_type = dynamic::ObjectImpl;
246};
247
248struct dynamic::value_iterator : detail::IteratorAdaptor<
249 dynamic::value_iterator,
250 dynamic::ObjectImpl::iterator,
251 dynamic,
252 std::forward_iterator_tag> {
253 using Super = detail::IteratorAdaptor<
254 dynamic::value_iterator,
255 dynamic::ObjectImpl::iterator,
256 dynamic,
257 std::forward_iterator_tag>;
258 /* implicit */ value_iterator(dynamic::ObjectImpl::iterator b) : Super(b) {}
259
260 using object_type = dynamic::ObjectImpl;
261
262 dynamic& dereference() const {
263 return base()->second;
264 }
265};
266
267struct dynamic::const_item_iterator
268 : detail::IteratorAdaptor<
269 dynamic::const_item_iterator,
270 dynamic::ObjectImpl::const_iterator,
271 std::pair<dynamic const, dynamic> const,
272 std::forward_iterator_tag> {
273 using Super = detail::IteratorAdaptor<
274 dynamic::const_item_iterator,
275 dynamic::ObjectImpl::const_iterator,
276 std::pair<dynamic const, dynamic> const,
277 std::forward_iterator_tag>;
278 /* implicit */ const_item_iterator(dynamic::ObjectImpl::const_iterator b)
279 : Super(b) {}
280 /* implicit */ const_item_iterator(const_item_iterator const& i)
281 : Super(i.base()) {}
282 /* implicit */ const_item_iterator(item_iterator i) : Super(i.base()) {}
283
284 using object_type = dynamic::ObjectImpl const;
285};
286
287struct dynamic::const_key_iterator : detail::IteratorAdaptor<
288 dynamic::const_key_iterator,
289 dynamic::ObjectImpl::const_iterator,
290 dynamic const,
291 std::forward_iterator_tag> {
292 using Super = detail::IteratorAdaptor<
293 dynamic::const_key_iterator,
294 dynamic::ObjectImpl::const_iterator,
295 dynamic const,
296 std::forward_iterator_tag>;
297 /* implicit */ const_key_iterator(dynamic::ObjectImpl::const_iterator b)
298 : Super(b) {}
299
300 using object_type = dynamic::ObjectImpl const;
301
302 dynamic const& dereference() const {
303 return base()->first;
304 }
305};
306
307struct dynamic::const_value_iterator : detail::IteratorAdaptor<
308 dynamic::const_value_iterator,
309 dynamic::ObjectImpl::const_iterator,
310 dynamic const,
311 std::forward_iterator_tag> {
312 using Super = detail::IteratorAdaptor<
313 dynamic::const_value_iterator,
314 dynamic::ObjectImpl::const_iterator,
315 dynamic const,
316 std::forward_iterator_tag>;
317 /* implicit */ const_value_iterator(dynamic::ObjectImpl::const_iterator b)
318 : Super(b) {}
319 /* implicit */ const_value_iterator(value_iterator i) : Super(i.base()) {}
320 /* implicit */ const_value_iterator(dynamic::ObjectImpl::iterator i)
321 : Super(i) {}
322
323 using object_type = dynamic::ObjectImpl const;
324
325 dynamic const& dereference() const {
326 return base()->second;
327 }
328};
329
330//////////////////////////////////////////////////////////////////////
331
332inline dynamic::dynamic() : dynamic(nullptr) {}
333
334inline dynamic::dynamic(std::nullptr_t) : type_(NULLT) {}
335
336inline dynamic::dynamic(void (*)(EmptyArrayTag)) : type_(ARRAY) {
337 new (&u_.array) Array();
338}
339
340inline dynamic::dynamic(ObjectMaker (*)()) : type_(OBJECT) {
341 new (getAddress<ObjectImpl>()) ObjectImpl();
342}
343
344inline dynamic::dynamic(StringPiece s) : type_(STRING) {
345 new (&u_.string) std::string(s.data(), s.size());
346}
347
348inline dynamic::dynamic(char const* s) : type_(STRING) {
349 new (&u_.string) std::string(s);
350}
351
352inline dynamic::dynamic(std::string s) : type_(STRING) {
353 new (&u_.string) std::string(std::move(s));
354}
355
356inline dynamic::dynamic(ObjectMaker&& maker) : type_(OBJECT) {
357 new (getAddress<ObjectImpl>())
358 ObjectImpl(std::move(*maker.val_.getAddress<ObjectImpl>()));
359}
360
361inline dynamic::dynamic(dynamic const& o) : type_(NULLT) {
362 *this = o;
363}
364
365inline dynamic::dynamic(dynamic&& o) noexcept : type_(NULLT) {
366 *this = std::move(o);
367}
368
369inline dynamic::~dynamic() noexcept {
370 destroy();
371}
372
373// Integral types except bool convert to int64_t, float types to double.
374template <class T>
375struct dynamic::NumericTypeHelper<
376 T,
377 typename std::enable_if<std::is_integral<T>::value>::type> {
378 static_assert(
379 !kIsObjC || sizeof(T) > sizeof(char),
380 "char-sized types are ambiguous in objc; cast to bool or wider type");
381 using type = int64_t;
382};
383template <>
384struct dynamic::NumericTypeHelper<bool> {
385 using type = bool;
386};
387template <>
388struct dynamic::NumericTypeHelper<float> {
389 using type = double;
390};
391template <>
392struct dynamic::NumericTypeHelper<double> {
393 using type = double;
394};
395
396inline dynamic::dynamic(std::vector<bool>::reference b)
397 : dynamic(static_cast<bool>(b)) {}
398inline dynamic::dynamic(VectorBoolConstRefCtorType b)
399 : dynamic(static_cast<bool>(b)) {}
400
401template <
402 class T,
403 class NumericType /* = typename NumericTypeHelper<T>::type */>
404dynamic::dynamic(T t) {
405 type_ = TypeInfo<NumericType>::type;
406 new (getAddress<NumericType>()) NumericType(NumericType(t));
407}
408
409template <class Iterator>
410dynamic::dynamic(Iterator first, Iterator last) : type_(ARRAY) {
411 new (&u_.array) Array(first, last);
412}
413
414//////////////////////////////////////////////////////////////////////
415
416inline dynamic::const_iterator dynamic::begin() const {
417 return get<Array>().begin();
418}
419inline dynamic::const_iterator dynamic::end() const {
420 return get<Array>().end();
421}
422
423inline dynamic::iterator dynamic::begin() {
424 return get<Array>().begin();
425}
426inline dynamic::iterator dynamic::end() {
427 return get<Array>().end();
428}
429
430template <class It>
431struct dynamic::IterableProxy {
432 typedef It iterator;
433 typedef typename It::value_type value_type;
434 typedef typename It::object_type object_type;
435
436 /* implicit */ IterableProxy(object_type* o) : o_(o) {}
437
438 It begin() const {
439 return o_->begin();
440 }
441
442 It end() const {
443 return o_->end();
444 }
445
446 private:
447 object_type* o_;
448};
449
450inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys()
451 const {
452 return &(get<ObjectImpl>());
453}
454
455inline dynamic::IterableProxy<dynamic::const_value_iterator> dynamic::values()
456 const {
457 return &(get<ObjectImpl>());
458}
459
460inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items()
461 const {
462 return &(get<ObjectImpl>());
463}
464
465inline dynamic::IterableProxy<dynamic::value_iterator> dynamic::values() {
466 return &(get<ObjectImpl>());
467}
468
469inline dynamic::IterableProxy<dynamic::item_iterator> dynamic::items() {
470 return &(get<ObjectImpl>());
471}
472
473inline bool dynamic::isString() const {
474 return get_nothrow<std::string>() != nullptr;
475}
476inline bool dynamic::isObject() const {
477 return get_nothrow<ObjectImpl>() != nullptr;
478}
479inline bool dynamic::isBool() const {
480 return get_nothrow<bool>() != nullptr;
481}
482inline bool dynamic::isArray() const {
483 return get_nothrow<Array>() != nullptr;
484}
485inline bool dynamic::isDouble() const {
486 return get_nothrow<double>() != nullptr;
487}
488inline bool dynamic::isInt() const {
489 return get_nothrow<int64_t>() != nullptr;
490}
491inline bool dynamic::isNull() const {
492 return get_nothrow<std::nullptr_t>() != nullptr;
493}
494inline bool dynamic::isNumber() const {
495 return isInt() || isDouble();
496}
497
498inline dynamic::Type dynamic::type() const {
499 return type_;
500}
501
502inline std::string dynamic::asString() const {
503 return asImpl<std::string>();
504}
505inline double dynamic::asDouble() const {
506 return asImpl<double>();
507}
508inline int64_t dynamic::asInt() const {
509 return asImpl<int64_t>();
510}
511inline bool dynamic::asBool() const {
512 return asImpl<bool>();
513}
514
515inline const std::string& dynamic::getString() const& {
516 return get<std::string>();
517}
518inline double dynamic::getDouble() const& {
519 return get<double>();
520}
521inline int64_t dynamic::getInt() const& {
522 return get<int64_t>();
523}
524inline bool dynamic::getBool() const& {
525 return get<bool>();
526}
527
528inline std::string& dynamic::getString() & {
529 return get<std::string>();
530}
531inline double& dynamic::getDouble() & {
532 return get<double>();
533}
534inline int64_t& dynamic::getInt() & {
535 return get<int64_t>();
536}
537inline bool& dynamic::getBool() & {
538 return get<bool>();
539}
540
541inline std::string&& dynamic::getString() && {
542 return std::move(get<std::string>());
543}
544inline double dynamic::getDouble() && {
545 return get<double>();
546}
547inline int64_t dynamic::getInt() && {
548 return get<int64_t>();
549}
550inline bool dynamic::getBool() && {
551 return get<bool>();
552}
553
554inline const char* dynamic::data() const& {
555 return get<std::string>().data();
556}
557inline const char* dynamic::c_str() const& {
558 return get<std::string>().c_str();
559}
560inline StringPiece dynamic::stringPiece() const {
561 return get<std::string>();
562}
563
564template <class T>
565struct dynamic::CompareOp {
566 static bool comp(T const& a, T const& b) {
567 return a < b;
568 }
569};
570template <>
571struct dynamic::CompareOp<dynamic::ObjectImpl> {
572 static bool comp(ObjectImpl const&, ObjectImpl const&) {
573 // This code never executes; it is just here for the compiler.
574 return false;
575 }
576};
577template <>
578struct dynamic::CompareOp<std::nullptr_t> {
579 static bool comp(std::nullptr_t const&, std::nullptr_t const&) {
580 return true;
581 }
582};
583
584inline dynamic& dynamic::operator+=(dynamic const& o) {
585 if (type() == STRING && o.type() == STRING) {
586 *getAddress<std::string>() += *o.getAddress<std::string>();
587 return *this;
588 }
589 *this = detail::numericOp<std::plus>(*this, o);
590 return *this;
591}
592
593inline dynamic& dynamic::operator-=(dynamic const& o) {
594 *this = detail::numericOp<std::minus>(*this, o);
595 return *this;
596}
597
598inline dynamic& dynamic::operator*=(dynamic const& o) {
599 *this = detail::numericOp<std::multiplies>(*this, o);
600 return *this;
601}
602
603inline dynamic& dynamic::operator/=(dynamic const& o) {
604 *this = detail::numericOp<std::divides>(*this, o);
605 return *this;
606}
607
608#define FB_DYNAMIC_INTEGER_OP(op) \
609 inline dynamic& dynamic::operator op(dynamic const& o) { \
610 if (!isInt() || !o.isInt()) { \
611 throw_exception<TypeError>("int64", type(), o.type()); \
612 } \
613 *getAddress<int64_t>() op o.asInt(); \
614 return *this; \
615 }
616
617FB_DYNAMIC_INTEGER_OP(%=)
618FB_DYNAMIC_INTEGER_OP(|=)
619FB_DYNAMIC_INTEGER_OP(&=)
620FB_DYNAMIC_INTEGER_OP(^=)
621
622#undef FB_DYNAMIC_INTEGER_OP
623
624inline dynamic& dynamic::operator++() {
625 ++get<int64_t>();
626 return *this;
627}
628
629inline dynamic& dynamic::operator--() {
630 --get<int64_t>();
631 return *this;
632}
633
634template <typename K>
635dynamic::IfIsNonStringDynamicConvertible<K, dynamic const&> dynamic::operator[](
636 K&& idx) const& {
637 return at(std::forward<K>(idx));
638}
639
640template <typename K>
641dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::operator[](
642 K&& idx) & {
643 if (!isObject() && !isArray()) {
644 throw_exception<TypeError>("object/array", type());
645 }
646 if (isArray()) {
647 return at(std::forward<K>(idx));
648 }
649 auto& obj = get<ObjectImpl>();
650 auto ret = obj.emplace(std::forward<K>(idx), nullptr);
651 return ret.first->second;
652}
653
654template <typename K>
655dynamic::IfIsNonStringDynamicConvertible<K, dynamic&&> dynamic::operator[](
656 K&& idx) && {
657 return std::move((*this)[std::forward<K>(idx)]);
658}
659
660inline dynamic const& dynamic::operator[](StringPiece k) const& {
661 return at(k);
662}
663
664inline dynamic&& dynamic::operator[](StringPiece k) && {
665 return std::move((*this)[k]);
666}
667
668template <typename K>
669dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault(
670 K&& k,
671 const dynamic& v) const& {
672 auto& obj = get<ObjectImpl>();
673 auto it = obj.find(std::forward<K>(k));
674 return it == obj.end() ? v : it->second;
675}
676
677template <typename K>
678dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault(
679 K&& k,
680 dynamic&& v) const& {
681 auto& obj = get<ObjectImpl>();
682 auto it = obj.find(std::forward<K>(k));
683 // Avoid clang bug with ternary
684 if (it == obj.end()) {
685 return std::move(v);
686 } else {
687 return it->second;
688 }
689}
690
691template <typename K>
692dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault(
693 K&& k,
694 const dynamic& v) && {
695 auto& obj = get<ObjectImpl>();
696 auto it = obj.find(std::forward<K>(k));
697 // Avoid clang bug with ternary
698 if (it == obj.end()) {
699 return v;
700 } else {
701 return std::move(it->second);
702 }
703}
704
705template <typename K>
706dynamic::IfIsNonStringDynamicConvertible<K, dynamic> dynamic::getDefault(
707 K&& k,
708 dynamic&& v) && {
709 auto& obj = get<ObjectImpl>();
710 auto it = obj.find(std::forward<K>(k));
711 return std::move(it == obj.end() ? v : it->second);
712}
713
714template <typename K, typename V>
715dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault(
716 K&& k,
717 V&& v) {
718 auto& obj = get<ObjectImpl>();
719 return obj.emplace(std::forward<K>(k), std::forward<V>(v)).first->second;
720}
721
722template <typename K>
723dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault(
724 K&& k,
725 dynamic&& v) {
726 auto& obj = get<ObjectImpl>();
727 return obj.emplace(std::forward<K>(k), std::move(v)).first->second;
728}
729
730template <typename K>
731dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::setDefault(
732 K&& k,
733 const dynamic& v) {
734 auto& obj = get<ObjectImpl>();
735 return obj.emplace(std::forward<K>(k), v).first->second;
736}
737
738template <typename V>
739dynamic& dynamic::setDefault(StringPiece k, V&& v) {
740 auto& obj = get<ObjectImpl>();
741 return obj.emplace(k, std::forward<V>(v)).first->second;
742}
743
744inline dynamic& dynamic::setDefault(StringPiece k, dynamic&& v) {
745 auto& obj = get<ObjectImpl>();
746 return obj.emplace(k, std::move(v)).first->second;
747}
748
749inline dynamic& dynamic::setDefault(StringPiece k, const dynamic& v) {
750 auto& obj = get<ObjectImpl>();
751 return obj.emplace(k, v).first->second;
752}
753
754template <typename K>
755dynamic::IfIsNonStringDynamicConvertible<K, dynamic const*> dynamic::get_ptr(
756 K&& k) const& {
757 return get_ptrImpl(std::forward<K>(k));
758}
759
760template <typename K>
761dynamic::IfIsNonStringDynamicConvertible<K, dynamic*> dynamic::get_ptr(
762 K&& idx) & {
763 return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx));
764}
765
766inline dynamic* dynamic::get_ptr(StringPiece idx) & {
767 return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx));
768}
769
770// clang-format off
771inline
772dynamic::resolved_json_pointer<dynamic>
773dynamic::try_get_ptr(json_pointer const& jsonPtr) & {
774 auto ret = const_cast<dynamic const*>(this)->try_get_ptr(jsonPtr);
775 if (ret.hasValue()) {
776 return json_pointer_resolved_value<dynamic>{
777 const_cast<dynamic*>(ret.value().parent),
778 const_cast<dynamic*>(ret.value().value),
779 ret.value().parent_key, ret.value().parent_index};
780 } else {
781 return makeUnexpected(
782 json_pointer_resolution_error<dynamic>{
783 ret.error().error_code,
784 ret.error().index,
785 const_cast<dynamic*>(ret.error().context)}
786 );
787 }
788}
789// clang-format on
790
791inline dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) & {
792 return const_cast<dynamic*>(
793 const_cast<dynamic const*>(this)->get_ptr(jsonPtr));
794}
795
796template <typename K>
797dynamic::IfIsNonStringDynamicConvertible<K, dynamic const&> dynamic::at(
798 K&& k) const& {
799 return atImpl(std::forward<K>(k));
800}
801
802template <typename K>
803dynamic::IfIsNonStringDynamicConvertible<K, dynamic&> dynamic::at(K&& idx) & {
804 return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx));
805}
806
807template <typename K>
808dynamic::IfIsNonStringDynamicConvertible<K, dynamic&&> dynamic::at(K&& idx) && {
809 return std::move(at(idx));
810}
811
812inline dynamic& dynamic::at(StringPiece idx) & {
813 return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx));
814}
815
816inline dynamic&& dynamic::at(StringPiece idx) && {
817 return std::move(at(idx));
818}
819
820inline bool dynamic::empty() const {
821 if (isNull()) {
822 return true;
823 }
824 return !size();
825}
826
827template <typename K>
828dynamic::IfIsNonStringDynamicConvertible<K, dynamic::const_item_iterator>
829dynamic::find(K&& key) const {
830 return get<ObjectImpl>().find(std::forward<K>(key));
831}
832
833template <typename K>
834dynamic::IfIsNonStringDynamicConvertible<K, dynamic::item_iterator>
835dynamic::find(K&& key) {
836 return get<ObjectImpl>().find(std::forward<K>(key));
837}
838
839inline dynamic::const_item_iterator dynamic::find(StringPiece key) const {
840 return get<ObjectImpl>().find(key);
841}
842
843inline dynamic::item_iterator dynamic::find(StringPiece key) {
844 return get<ObjectImpl>().find(key);
845}
846
847template <typename K>
848dynamic::IfIsNonStringDynamicConvertible<K, std::size_t> dynamic::count(
849 K&& key) const {
850 return find(std::forward<K>(key)) != items().end() ? 1u : 0u;
851}
852
853inline std::size_t dynamic::count(StringPiece key) const {
854 return find(key) != items().end() ? 1u : 0u;
855}
856
857template <class K, class V>
858inline dynamic::IfNotIterator<K, void> dynamic::insert(K&& key, V&& val) {
859 auto& obj = get<ObjectImpl>();
860 obj[std::forward<K>(key)] = std::forward<V>(val);
861}
862
863template <class T>
864inline dynamic::iterator dynamic::insert(const_iterator pos, T&& value) {
865 auto& arr = get<Array>();
866 return arr.insert(pos, std::forward<T>(value));
867}
868
869inline void dynamic::update(const dynamic& mergeObj) {
870 if (!isObject() || !mergeObj.isObject()) {
871 throw_exception<TypeError>("object", type(), mergeObj.type());
872 }
873
874 for (const auto& pair : mergeObj.items()) {
875 (*this)[pair.first] = pair.second;
876 }
877}
878
879inline void dynamic::update_missing(const dynamic& mergeObj1) {
880 if (!isObject() || !mergeObj1.isObject()) {
881 throw_exception<TypeError>("object", type(), mergeObj1.type());
882 }
883
884 // Only add if not already there
885 for (const auto& pair : mergeObj1.items()) {
886 if ((*this).find(pair.first) == (*this).items().end()) {
887 (*this)[pair.first] = pair.second;
888 }
889 }
890}
891
892inline void dynamic::merge_patch(const dynamic& patch) {
893 auto& self = *this;
894 if (!patch.isObject()) {
895 self = patch;
896 return;
897 }
898 // if we are not an object, erase all contents, reset to object
899 if (!isObject()) {
900 self = object;
901 }
902 for (const auto& pair : patch.items()) {
903 if (pair.second.isNull()) {
904 // if name could be found in current object, remove it
905 auto it = self.find(pair.first);
906 if (it != self.items().end()) {
907 self.erase(it);
908 }
909 } else {
910 self[pair.first].merge_patch(pair.second);
911 }
912 }
913}
914
915inline dynamic dynamic::merge(
916 const dynamic& mergeObj1,
917 const dynamic& mergeObj2) {
918 // No checks on type needed here because they are done in update_missing
919 // Note that we do update_missing here instead of update() because
920 // it will prevent the extra writes that would occur with update()
921 auto ret = mergeObj2;
922 ret.update_missing(mergeObj1);
923 return ret;
924}
925
926template <typename K>
927dynamic::IfIsNonStringDynamicConvertible<K, std::size_t> dynamic::erase(
928 K&& key) {
929 auto& obj = get<ObjectImpl>();
930 return obj.erase(std::forward<K>(key));
931}
932inline std::size_t dynamic::erase(StringPiece key) {
933 auto& obj = get<ObjectImpl>();
934 return obj.erase(key);
935}
936
937inline dynamic::iterator dynamic::erase(const_iterator it) {
938 auto& arr = get<Array>();
939 // std::vector doesn't have an erase method that works on const iterators,
940 // even though the standard says it should, so this hack converts to a
941 // non-const iterator before calling erase.
942 return get<Array>().erase(arr.begin() + (it - arr.begin()));
943}
944
945inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) {
946 return const_key_iterator(get<ObjectImpl>().erase(it.base()));
947}
948
949inline dynamic::const_key_iterator dynamic::erase(
950 const_key_iterator first,
951 const_key_iterator last) {
952 return const_key_iterator(get<ObjectImpl>().erase(first.base(), last.base()));
953}
954
955inline dynamic::value_iterator dynamic::erase(const_value_iterator it) {
956 return value_iterator(get<ObjectImpl>().erase(it.base()));
957}
958
959inline dynamic::value_iterator dynamic::erase(
960 const_value_iterator first,
961 const_value_iterator last) {
962 return value_iterator(get<ObjectImpl>().erase(first.base(), last.base()));
963}
964
965inline dynamic::item_iterator dynamic::erase(const_item_iterator it) {
966 return item_iterator(get<ObjectImpl>().erase(it.base()));
967}
968
969inline dynamic::item_iterator dynamic::erase(
970 const_item_iterator first,
971 const_item_iterator last) {
972 return item_iterator(get<ObjectImpl>().erase(first.base(), last.base()));
973}
974
975inline void dynamic::resize(std::size_t sz, dynamic const& c) {
976 auto& arr = get<Array>();
977 arr.resize(sz, c);
978}
979
980inline void dynamic::push_back(dynamic const& v) {
981 auto& arr = get<Array>();
982 arr.push_back(v);
983}
984
985inline void dynamic::push_back(dynamic&& v) {
986 auto& arr = get<Array>();
987 arr.push_back(std::move(v));
988}
989
990inline void dynamic::pop_back() {
991 auto& arr = get<Array>();
992 arr.pop_back();
993}
994
995inline const dynamic& dynamic::back() const {
996 auto& arr = get<Array>();
997 return arr.back();
998}
999
1000//////////////////////////////////////////////////////////////////////
1001
1002inline dynamic::dynamic(Array&& r) : type_(ARRAY) {
1003 new (&u_.array) Array(std::move(r));
1004}
1005
1006#define FOLLY_DYNAMIC_DEC_TYPEINFO(T, str, val) \
1007 template <> \
1008 struct dynamic::TypeInfo<T> { \
1009 static constexpr const char* name = str; \
1010 static constexpr dynamic::Type type = val; \
1011 }; \
1012 //
1013
1014FOLLY_DYNAMIC_DEC_TYPEINFO(std::nullptr_t, "null", dynamic::NULLT)
1015FOLLY_DYNAMIC_DEC_TYPEINFO(bool, "boolean", dynamic::BOOL)
1016FOLLY_DYNAMIC_DEC_TYPEINFO(std::string, "string", dynamic::STRING)
1017FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::Array, "array", dynamic::ARRAY)
1018FOLLY_DYNAMIC_DEC_TYPEINFO(double, "double", dynamic::DOUBLE)
1019FOLLY_DYNAMIC_DEC_TYPEINFO(int64_t, "int64", dynamic::INT64)
1020FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::ObjectImpl, "object", dynamic::OBJECT)
1021
1022#undef FOLLY_DYNAMIC_DEC_TYPEINFO
1023
1024template <class T>
1025T dynamic::asImpl() const {
1026 switch (type()) {
1027 case INT64:
1028 return to<T>(*get_nothrow<int64_t>());
1029 case DOUBLE:
1030 return to<T>(*get_nothrow<double>());
1031 case BOOL:
1032 return to<T>(*get_nothrow<bool>());
1033 case STRING:
1034 return to<T>(*get_nothrow<std::string>());
1035 case NULLT:
1036 case ARRAY:
1037 case OBJECT:
1038 default:
1039 throw_exception<TypeError>("int/double/bool/string", type());
1040 }
1041}
1042
1043// Return a T* to our type, or null if we're not that type.
1044// clang-format off
1045template <class T>
1046T* dynamic::get_nothrow() & noexcept {
1047 if (type_ != TypeInfo<T>::type) {
1048 return nullptr;
1049 }
1050 return getAddress<T>();
1051}
1052// clang-format on
1053
1054template <class T>
1055T const* dynamic::get_nothrow() const& noexcept {
1056 return const_cast<dynamic*>(this)->get_nothrow<T>();
1057}
1058
1059// Return T* for where we can put a T, without type checking. (Memory
1060// might be uninitialized, even.)
1061template <class T>
1062T* dynamic::getAddress() noexcept {
1063 return GetAddrImpl<T>::get(u_);
1064}
1065
1066template <class T>
1067T const* dynamic::getAddress() const noexcept {
1068 return const_cast<dynamic*>(this)->getAddress<T>();
1069}
1070
1071template <class T>
1072struct dynamic::GetAddrImpl {};
1073template <>
1074struct dynamic::GetAddrImpl<std::nullptr_t> {
1075 static std::nullptr_t* get(Data& d) noexcept {
1076 return &d.nul;
1077 }
1078};
1079template <>
1080struct dynamic::GetAddrImpl<dynamic::Array> {
1081 static Array* get(Data& d) noexcept {
1082 return &d.array;
1083 }
1084};
1085template <>
1086struct dynamic::GetAddrImpl<bool> {
1087 static bool* get(Data& d) noexcept {
1088 return &d.boolean;
1089 }
1090};
1091template <>
1092struct dynamic::GetAddrImpl<int64_t> {
1093 static int64_t* get(Data& d) noexcept {
1094 return &d.integer;
1095 }
1096};
1097template <>
1098struct dynamic::GetAddrImpl<double> {
1099 static double* get(Data& d) noexcept {
1100 return &d.doubl;
1101 }
1102};
1103template <>
1104struct dynamic::GetAddrImpl<std::string> {
1105 static std::string* get(Data& d) noexcept {
1106 return &d.string;
1107 }
1108};
1109template <>
1110struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
1111 static_assert(
1112 sizeof(ObjectImpl) <= sizeof(Data::objectBuffer),
1113 "In your implementation, F14NodeMap<> apparently takes different"
1114 " amount of space depending on its template parameters. This is "
1115 "weird. Make objectBuffer bigger if you want to compile dynamic.");
1116
1117 static ObjectImpl* get(Data& d) noexcept {
1118 void* data = &d.objectBuffer;
1119 return static_cast<ObjectImpl*>(data);
1120 }
1121};
1122
1123template <class T>
1124T& dynamic::get() {
1125 if (auto* p = get_nothrow<T>()) {
1126 return *p;
1127 }
1128 throw_exception<TypeError>(TypeInfo<T>::name, type());
1129}
1130
1131template <class T>
1132T const& dynamic::get() const {
1133 return const_cast<dynamic*>(this)->get<T>();
1134}
1135
1136//////////////////////////////////////////////////////////////////////
1137
1138/*
1139 * Helper for implementing operator<<. Throws if the type shouldn't
1140 * support it.
1141 */
1142template <class T>
1143struct dynamic::PrintImpl {
1144 static void print(dynamic const&, std::ostream& out, T const& t) {
1145 out << t;
1146 }
1147};
1148// Otherwise, null, being (void*)0, would print as 0.
1149template <>
1150struct dynamic::PrintImpl<std::nullptr_t> {
1151 static void
1152 print(dynamic const& /* d */, std::ostream& out, std::nullptr_t const&) {
1153 out << "null";
1154 }
1155};
1156template <>
1157struct dynamic::PrintImpl<dynamic::ObjectImpl> {
1158 static void
1159 print(dynamic const& d, std::ostream& out, dynamic::ObjectImpl const&) {
1160 d.print_as_pseudo_json(out);
1161 }
1162};
1163template <>
1164struct dynamic::PrintImpl<dynamic::Array> {
1165 static void
1166 print(dynamic const& d, std::ostream& out, dynamic::Array const&) {
1167 d.print_as_pseudo_json(out);
1168 }
1169};
1170
1171inline void dynamic::print(std::ostream& out) const {
1172#define FB_X(T) PrintImpl<T>::print(*this, out, *getAddress<T>())
1173 FB_DYNAMIC_APPLY(type_, FB_X);
1174#undef FB_X
1175}
1176
1177inline std::ostream& operator<<(std::ostream& out, dynamic const& d) {
1178 d.print(out);
1179 return out;
1180}
1181
1182//////////////////////////////////////////////////////////////////////
1183
1184// Secialization of FormatValue so dynamic objects can be formatted
1185template <>
1186class FormatValue<dynamic> {
1187 public:
1188 explicit FormatValue(const dynamic& val) : val_(val) {}
1189
1190 template <class FormatCallback>
1191 void format(FormatArg& arg, FormatCallback& cb) const {
1192 switch (val_.type()) {
1193 case dynamic::NULLT:
1194 FormatValue<std::nullptr_t>(nullptr).format(arg, cb);
1195 break;
1196 case dynamic::BOOL:
1197 FormatValue<bool>(val_.asBool()).format(arg, cb);
1198 break;
1199 case dynamic::INT64:
1200 FormatValue<int64_t>(val_.asInt()).format(arg, cb);
1201 break;
1202 case dynamic::STRING:
1203 FormatValue<std::string>(val_.asString()).format(arg, cb);
1204 break;
1205 case dynamic::DOUBLE:
1206 FormatValue<double>(val_.asDouble()).format(arg, cb);
1207 break;
1208 case dynamic::ARRAY:
1209 FormatValue(val_.at(arg.splitIntKey())).format(arg, cb);
1210 break;
1211 case dynamic::OBJECT:
1212 FormatValue(val_.at(arg.splitKey().toString())).format(arg, cb);
1213 break;
1214 }
1215 }
1216
1217 private:
1218 const dynamic& val_;
1219};
1220
1221template <class V>
1222class FormatValue<detail::DefaultValueWrapper<dynamic, V>> {
1223 public:
1224 explicit FormatValue(const detail::DefaultValueWrapper<dynamic, V>& val)
1225 : val_(val) {}
1226
1227 template <class FormatCallback>
1228 void format(FormatArg& arg, FormatCallback& cb) const {
1229 auto& c = val_.container;
1230 switch (c.type()) {
1231 case dynamic::NULLT:
1232 case dynamic::BOOL:
1233 case dynamic::INT64:
1234 case dynamic::STRING:
1235 case dynamic::DOUBLE:
1236 FormatValue<dynamic>(c).format(arg, cb);
1237 break;
1238 case dynamic::ARRAY: {
1239 int key = arg.splitIntKey();
1240 if (key >= 0 && size_t(key) < c.size()) {
1241 FormatValue<dynamic>(c.at(key)).format(arg, cb);
1242 } else {
1243 FormatValue<V>(val_.defaultValue).format(arg, cb);
1244 }
1245 break;
1246 }
1247 case dynamic::OBJECT: {
1248 auto pos = c.find(arg.splitKey());
1249 if (pos != c.items().end()) {
1250 FormatValue<dynamic>(pos->second).format(arg, cb);
1251 } else {
1252 FormatValue<V>(val_.defaultValue).format(arg, cb);
1253 }
1254 break;
1255 }
1256 }
1257 }
1258
1259 private:
1260 const detail::DefaultValueWrapper<dynamic, V>& val_;
1261};
1262
1263} // namespace folly
1264
1265#undef FB_DYNAMIC_APPLY
1266