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