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