2 * Copyright 2017 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>
19 #include <folly/Assume.h>
20 #include <folly/Hash.h>
21 #include <folly/portability/BitsFunctexcept.h>
25 //////////////////////////////////////////////////////////////////////
27 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
28 constexpr const char* dynamic::TypeInfo<T>::name; \
29 constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
32 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
35 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
36 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
37 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
38 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
40 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
42 const char* dynamic::typeName() const {
43 return typeName(type_);
46 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
47 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
48 "type `", expected, '\'', ", but had type `",
49 dynamic::typeName(actual), '\''))
52 TypeError::TypeError(const std::string& expected,
53 dynamic::Type actual1, dynamic::Type actual2)
54 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
55 "types `", expected, '\'', ", but had types `",
56 dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
60 TypeError::~TypeError() = default;
62 // This is a higher-order preprocessor macro to aid going from runtime
63 // types to the compile time type system.
64 #define FB_DYNAMIC_APPLY(type, apply) \
94 bool dynamic::operator<(dynamic const& o) const {
95 if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
96 throw TypeError("object", type_);
98 if (type_ != o.type_) {
99 return type_ < o.type_;
102 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), \
104 FB_DYNAMIC_APPLY(type_, FB_X);
108 bool dynamic::operator==(dynamic const& o) const {
109 if (type() != o.type()) {
110 if (isNumber() && o.isNumber()) {
111 auto& integ = isInt() ? *this : o;
112 auto& doubl = isInt() ? o : *this;
113 return integ.asInt() == doubl.asDouble();
118 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
119 FB_DYNAMIC_APPLY(type_, FB_X);
123 dynamic& dynamic::operator=(dynamic const& o) {
125 if (type_ == o.type_) {
126 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
127 FB_DYNAMIC_APPLY(type_, FB_X);
131 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
132 FB_DYNAMIC_APPLY(o.type_, FB_X);
140 dynamic& dynamic::operator=(dynamic&& o) noexcept {
142 if (type_ == o.type_) {
143 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
144 FB_DYNAMIC_APPLY(type_, FB_X);
148 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
149 FB_DYNAMIC_APPLY(o.type_, FB_X);
157 dynamic& dynamic::operator[](dynamic const& k) & {
158 if (!isObject() && !isArray()) {
159 throw TypeError("object/array", type());
164 auto& obj = get<ObjectImpl>();
165 auto ret = obj.insert({k, nullptr});
166 return ret.first->second;
169 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
170 auto& obj = get<ObjectImpl>();
171 auto it = obj.find(k);
172 return it == obj.end() ? v : it->second;
175 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
176 auto& obj = get<ObjectImpl>();
177 auto it = obj.find(k);
178 // Avoid clang bug with ternary
179 if (it == obj.end()) {
186 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
187 auto& obj = get<ObjectImpl>();
188 auto it = obj.find(k);
189 // Avoid clang bug with ternary
190 if (it == obj.end()) {
193 return std::move(it->second);
197 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
198 auto& obj = get<ObjectImpl>();
199 auto it = obj.find(k);
200 return std::move(it == obj.end() ? v : it->second);
203 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
204 if (auto* parray = get_nothrow<Array>()) {
206 throw TypeError("int64", idx.type());
208 if (idx < 0 || idx >= parray->size()) {
211 return &(*parray)[size_t(idx.asInt())];
212 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
213 auto it = pobject->find(idx);
214 if (it == pobject->end()) {
219 throw TypeError("object/array", type());
223 dynamic const& dynamic::at(dynamic const& idx) const& {
224 if (auto* parray = get_nothrow<Array>()) {
226 throw TypeError("int64", idx.type());
228 if (idx < 0 || idx >= parray->size()) {
229 std::__throw_out_of_range("out of range in dynamic array");
231 return (*parray)[size_t(idx.asInt())];
232 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
233 auto it = pobject->find(idx);
234 if (it == pobject->end()) {
235 throw std::out_of_range(to<std::string>(
236 "couldn't find key ", idx.asString(), " in dynamic object"));
240 throw TypeError("object/array", type());
244 std::size_t dynamic::size() const {
245 if (auto* ar = get_nothrow<Array>()) {
248 if (auto* obj = get_nothrow<ObjectImpl>()) {
251 if (auto* str = get_nothrow<std::string>()) {
254 throw TypeError("array/object", type());
257 dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
258 auto& arr = get<Array>();
259 return get<Array>().erase(
260 arr.begin() + (first - arr.begin()),
261 arr.begin() + (last - arr.begin()));
264 std::size_t dynamic::hash() const {
269 throw TypeError("not null/object/array", type());
271 return std::hash<int64_t>()(getInt());
273 return std::hash<double>()(getDouble());
275 return std::hash<bool>()(getBool());
277 // keep it compatible with FBString
278 const auto& str = getString();
279 return ::folly::hash::fnv32_buf(str.data(), str.size());
282 assume_unreachable();
285 char const* dynamic::typeName(Type t) {
286 #define FB_X(T) return TypeInfo<T>::name
287 FB_DYNAMIC_APPLY(t, FB_X);
291 void dynamic::destroy() noexcept {
292 // This short-circuit speeds up some microbenchmarks.
293 if (type_ == NULLT) return;
295 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
296 FB_DYNAMIC_APPLY(type_, FB_X);
302 //////////////////////////////////////////////////////////////////////