Revert D4310312: [Folly] Enable -Wunreachable-code
[folly.git] / folly / dynamic.cpp
1 /*
2  * Copyright 2016 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 #include <folly/Hash.h>
18 #include <folly/dynamic.h>
19 #include <folly/portability/BitsFunctexcept.h>
20
21 namespace folly {
22
23 //////////////////////////////////////////////////////////////////////
24
25 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
26   constexpr const char* dynamic::TypeInfo<T>::name; \
27   constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
28   //
29
30 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
31 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
32 FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
35 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
36 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
37
38 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
39
40 const char* dynamic::typeName() const {
41   return typeName(type_);
42 }
43
44 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
45   : std::runtime_error(to<std::string>("TypeError: expected dynamic "
46       "type `", expected, '\'', ", but had type `",
47       dynamic::typeName(actual), '\''))
48 {}
49
50 TypeError::TypeError(const std::string& expected,
51     dynamic::Type actual1, dynamic::Type actual2)
52   : std::runtime_error(to<std::string>("TypeError: expected dynamic "
53       "types `", expected, '\'', ", but had types `",
54       dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
55       '\''))
56 {}
57
58 TypeError::~TypeError() = default;
59
60 // This is a higher-order preprocessor macro to aid going from runtime
61 // types to the compile time type system.
62 #define FB_DYNAMIC_APPLY(type, apply) \
63   do {                                \
64     switch ((type)) {                 \
65       case NULLT:                     \
66         apply(void*);                 \
67         break;                        \
68       case ARRAY:                     \
69         apply(Array);                 \
70         break;                        \
71       case BOOL:                      \
72         apply(bool);                  \
73         break;                        \
74       case DOUBLE:                    \
75         apply(double);                \
76         break;                        \
77       case INT64:                     \
78         apply(int64_t);               \
79         break;                        \
80       case OBJECT:                    \
81         apply(ObjectImpl);            \
82         break;                        \
83       case STRING:                    \
84         apply(std::string);           \
85         break;                        \
86       default:                        \
87         CHECK(0);                     \
88         abort();                      \
89     }                                 \
90   } while (0)
91
92 bool dynamic::operator<(dynamic const& o) const {
93   if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
94     throw TypeError("object", type_);
95   }
96   if (type_ != o.type_) {
97     return type_ < o.type_;
98   }
99
100 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(),   \
101                                           *o.getAddress<T>())
102   FB_DYNAMIC_APPLY(type_, FB_X);
103 #undef FB_X
104 }
105
106 bool dynamic::operator==(dynamic const& o) const {
107   if (type() != o.type()) {
108     if (isNumber() && o.isNumber()) {
109       auto& integ = isInt() ? *this : o;
110       auto& doubl = isInt() ? o     : *this;
111       return integ.asInt() == doubl.asDouble();
112     }
113     return false;
114   }
115
116 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
117   FB_DYNAMIC_APPLY(type_, FB_X);
118 #undef FB_X
119 }
120
121 dynamic& dynamic::operator=(dynamic const& o) {
122   if (&o != this) {
123     if (type_ == o.type_) {
124 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
125       FB_DYNAMIC_APPLY(type_, FB_X);
126 #undef FB_X
127     } else {
128       destroy();
129 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
130       FB_DYNAMIC_APPLY(o.type_, FB_X);
131 #undef FB_X
132       type_ = o.type_;
133     }
134   }
135   return *this;
136 }
137
138 dynamic& dynamic::operator=(dynamic&& o) noexcept {
139   if (&o != this) {
140     if (type_ == o.type_) {
141 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
142       FB_DYNAMIC_APPLY(type_, FB_X);
143 #undef FB_X
144     } else {
145       destroy();
146 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
147       FB_DYNAMIC_APPLY(o.type_, FB_X);
148 #undef FB_X
149       type_ = o.type_;
150     }
151   }
152   return *this;
153 }
154
155 dynamic& dynamic::operator[](dynamic const& k) & {
156   if (!isObject() && !isArray()) {
157     throw TypeError("object/array", type());
158   }
159   if (isArray()) {
160     return at(k);
161   }
162   auto& obj = get<ObjectImpl>();
163   auto ret = obj.insert({k, nullptr});
164   return ret.first->second;
165 }
166
167 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
168   auto& obj = get<ObjectImpl>();
169   auto it = obj.find(k);
170   return it == obj.end() ? v : it->second;
171 }
172
173 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
174   auto& obj = get<ObjectImpl>();
175   auto it = obj.find(k);
176   // Avoid clang bug with ternary
177   if (it == obj.end()) {
178     return std::move(v);
179   } else {
180     return it->second;
181   }
182 }
183
184 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
185   auto& obj = get<ObjectImpl>();
186   auto it = obj.find(k);
187   // Avoid clang bug with ternary
188   if (it == obj.end()) {
189     return v;
190   } else {
191     return std::move(it->second);
192   }
193 }
194
195 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
196   auto& obj = get<ObjectImpl>();
197   auto it = obj.find(k);
198   return std::move(it == obj.end() ? v : it->second);
199 }
200
201 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
202   if (auto* parray = get_nothrow<Array>()) {
203     if (!idx.isInt()) {
204       throw TypeError("int64", idx.type());
205     }
206     if (idx < 0 || idx >= parray->size()) {
207       return nullptr;
208     }
209     return &(*parray)[idx.asInt()];
210   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
211     auto it = pobject->find(idx);
212     if (it == pobject->end()) {
213       return nullptr;
214     }
215     return &it->second;
216   } else {
217     throw TypeError("object/array", type());
218   }
219 }
220
221 dynamic const& dynamic::at(dynamic const& idx) const& {
222   if (auto* parray = get_nothrow<Array>()) {
223     if (!idx.isInt()) {
224       throw TypeError("int64", idx.type());
225     }
226     if (idx < 0 || idx >= parray->size()) {
227       std::__throw_out_of_range("out of range in dynamic array");
228     }
229     return (*parray)[idx.asInt()];
230   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
231     auto it = pobject->find(idx);
232     if (it == pobject->end()) {
233       throw std::out_of_range(to<std::string>(
234           "couldn't find key ", idx.asString(), " in dynamic object"));
235     }
236     return it->second;
237   } else {
238     throw TypeError("object/array", type());
239   }
240 }
241
242 std::size_t dynamic::size() const {
243   if (auto* ar = get_nothrow<Array>()) {
244     return ar->size();
245   }
246   if (auto* obj = get_nothrow<ObjectImpl>()) {
247     return obj->size();
248   }
249   if (auto* str = get_nothrow<std::string>()) {
250     return str->size();
251   }
252   throw TypeError("array/object", type());
253 }
254
255 dynamic::const_iterator
256 dynamic::erase(const_iterator first, const_iterator last) {
257   auto& arr = get<Array>();
258   return get<Array>().erase(
259     arr.begin() + (first - arr.begin()),
260     arr.begin() + (last - arr.begin()));
261 }
262
263 std::size_t dynamic::hash() const {
264   switch (type()) {
265   case OBJECT:
266   case ARRAY:
267   case NULLT:
268     throw TypeError("not null/object/array", type());
269   case INT64:
270     return std::hash<int64_t>()(getInt());
271   case DOUBLE:
272     return std::hash<double>()(getDouble());
273   case BOOL:
274     return std::hash<bool>()(getBool());
275   case STRING: {
276     // keep it compatible with FBString
277     const auto& str = getString();
278     return ::folly::hash::fnv32_buf(str.data(), str.size());
279   }
280   default:
281     CHECK(0); abort();
282   }
283 }
284
285 char const* dynamic::typeName(Type t) {
286 #define FB_X(T) return TypeInfo<T>::name
287   FB_DYNAMIC_APPLY(t, FB_X);
288 #undef FB_X
289 }
290
291 void dynamic::destroy() noexcept {
292   // This short-circuit speeds up some microbenchmarks.
293   if (type_ == NULLT) return;
294
295 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
296   FB_DYNAMIC_APPLY(type_, FB_X);
297 #undef FB_X
298   type_ = NULLT;
299   u_.nul = nullptr;
300 }
301
302 //////////////////////////////////////////////////////////////////////
303
304 }