2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/dynamic.h>
21 //////////////////////////////////////////////////////////////////////
23 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
24 constexpr const char* dynamic::TypeInfo<T>::name; \
25 constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
28 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
29 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
30 FOLLY_DYNAMIC_DEF_TYPEINFO(fbstring)
31 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
32 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
36 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
38 const char* dynamic::typeName() const {
39 return typeName(type_);
42 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
43 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
44 "type `", expected, '\'', ", but had type `",
45 dynamic::typeName(actual), '\''))
48 TypeError::TypeError(const std::string& expected,
49 dynamic::Type actual1, dynamic::Type actual2)
50 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
51 "types `", expected, '\'', ", but had types `",
52 dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
56 TypeError::~TypeError() = default;
58 // This is a higher-order preprocessor macro to aid going from runtime
59 // types to the compile time type system.
60 #define FB_DYNAMIC_APPLY(type, apply) do { \
62 case NULLT: apply(void*); break; \
63 case ARRAY: apply(Array); break; \
64 case BOOL: apply(bool); break; \
65 case DOUBLE: apply(double); break; \
66 case INT64: apply(int64_t); break; \
67 case OBJECT: apply(ObjectImpl); break; \
68 case STRING: apply(fbstring); break; \
69 default: CHECK(0); abort(); \
73 bool dynamic::operator<(dynamic const& o) const {
74 if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
75 throw TypeError("object", type_);
77 if (type_ != o.type_) {
78 return type_ < o.type_;
81 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), \
83 FB_DYNAMIC_APPLY(type_, FB_X);
87 bool dynamic::operator==(dynamic const& o) const {
88 if (type() != o.type()) {
89 if (isNumber() && o.isNumber()) {
90 auto& integ = isInt() ? *this : o;
91 auto& doubl = isInt() ? o : *this;
92 return integ.asInt() == doubl.asDouble();
97 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
98 FB_DYNAMIC_APPLY(type_, FB_X);
102 dynamic& dynamic::operator=(dynamic const& o) {
104 if (type_ == o.type_) {
105 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
106 FB_DYNAMIC_APPLY(type_, FB_X);
110 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
111 FB_DYNAMIC_APPLY(o.type_, FB_X);
119 dynamic& dynamic::operator=(dynamic&& o) noexcept {
121 if (type_ == o.type_) {
122 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
123 FB_DYNAMIC_APPLY(type_, FB_X);
127 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
128 FB_DYNAMIC_APPLY(o.type_, FB_X);
136 dynamic& dynamic::operator[](dynamic const& k) & {
137 if (!isObject() && !isArray()) {
138 throw TypeError("object/array", type());
143 auto& obj = get<ObjectImpl>();
144 auto ret = obj.insert({k, nullptr});
145 return ret.first->second;
148 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
149 auto& obj = get<ObjectImpl>();
150 auto it = obj.find(k);
151 return it == obj.end() ? v : it->second;
154 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
155 auto& obj = get<ObjectImpl>();
156 auto it = obj.find(k);
157 // Avoid clang bug with ternary
158 if (it == obj.end()) {
165 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
166 auto& obj = get<ObjectImpl>();
167 auto it = obj.find(k);
168 // Avoid clang bug with ternary
169 if (it == obj.end()) {
172 return std::move(it->second);
176 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
177 auto& obj = get<ObjectImpl>();
178 auto it = obj.find(k);
179 return std::move(it == obj.end() ? v : it->second);
182 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
183 if (auto* parray = get_nothrow<Array>()) {
185 throw TypeError("int64", idx.type());
187 if (idx < 0 || idx >= parray->size()) {
190 return &(*parray)[idx.asInt()];
191 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
192 auto it = pobject->find(idx);
193 if (it == pobject->end()) {
198 throw TypeError("object/array", type());
202 dynamic const& dynamic::at(dynamic const& idx) const& {
203 if (auto* parray = get_nothrow<Array>()) {
205 throw TypeError("int64", idx.type());
207 if (idx < 0 || idx >= parray->size()) {
208 throw std::out_of_range("out of range in dynamic array");
210 return (*parray)[idx.asInt()];
211 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
212 auto it = pobject->find(idx);
213 if (it == pobject->end()) {
214 throw std::out_of_range(to<std::string>(
215 "couldn't find key ", idx.asString(), " in dynamic object"));
219 throw TypeError("object/array", type());
223 std::size_t dynamic::size() const {
224 if (auto* ar = get_nothrow<Array>()) {
227 if (auto* obj = get_nothrow<ObjectImpl>()) {
230 if (auto* str = get_nothrow<fbstring>()) {
233 throw TypeError("array/object", type());
236 dynamic::const_iterator
237 dynamic::erase(const_iterator first, const_iterator last) {
238 auto& arr = get<Array>();
239 return get<Array>().erase(
240 arr.begin() + (first - arr.begin()),
241 arr.begin() + (last - arr.begin()));
244 std::size_t dynamic::hash() const {
249 throw TypeError("not null/object/array", type());
251 return std::hash<int64_t>()(asInt());
253 return std::hash<double>()(asDouble());
255 return std::hash<bool>()(asBool());
257 return std::hash<fbstring>()(asString());
263 char const* dynamic::typeName(Type t) {
264 #define FB_X(T) return TypeInfo<T>::name
265 FB_DYNAMIC_APPLY(t, FB_X);
269 void dynamic::destroy() noexcept {
270 // This short-circuit speeds up some microbenchmarks.
271 if (type_ == NULLT) return;
273 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
274 FB_DYNAMIC_APPLY(type_, FB_X);
280 //////////////////////////////////////////////////////////////////////