2 * Copyright 2015 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 DEF_TYPE(T, str, typen) \
24 template<> char const dynamic::TypeInfo<T>::name[] = str; \
25 template<> dynamic::Type const dynamic::TypeInfo<T>::type = typen
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);
37 const char* dynamic::typeName() const {
38 return typeName(type_);
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), '\''))
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),
55 TypeError::~TypeError() = default;
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 { \
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(); \
72 bool dynamic::operator<(dynamic const& o) const {
73 if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
74 throw TypeError("object", type_);
76 if (type_ != o.type_) {
77 return type_ < o.type_;
80 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), \
82 FB_DYNAMIC_APPLY(type_, FB_X);
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();
96 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
97 FB_DYNAMIC_APPLY(type_, FB_X);
101 dynamic& dynamic::operator=(dynamic const& o) {
103 if (type_ == o.type_) {
104 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
105 FB_DYNAMIC_APPLY(type_, FB_X);
109 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
110 FB_DYNAMIC_APPLY(o.type_, FB_X);
118 dynamic& dynamic::operator=(dynamic&& o) noexcept {
120 if (type_ == o.type_) {
121 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
122 FB_DYNAMIC_APPLY(type_, FB_X);
126 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
127 FB_DYNAMIC_APPLY(o.type_, FB_X);
135 dynamic& dynamic::operator[](dynamic const& k) & {
136 if (!isObject() && !isArray()) {
137 throw TypeError("object/array", type());
142 auto& obj = get<ObjectImpl>();
143 auto ret = obj.insert({k, nullptr});
144 return ret.first->second;
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;
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()) {
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()) {
171 return std::move(it->second);
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);
181 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
182 if (auto* parray = get_nothrow<Array>()) {
184 throw TypeError("int64", idx.type());
186 if (idx < 0 || idx >= parray->size()) {
189 return &(*parray)[idx.asInt()];
190 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
191 auto it = pobject->find(idx);
192 if (it == pobject->end()) {
197 throw TypeError("object/array", type());
201 dynamic const& dynamic::at(dynamic const& idx) const& {
202 if (auto* parray = get_nothrow<Array>()) {
204 throw TypeError("int64", idx.type());
206 if (idx < 0 || idx >= parray->size()) {
207 throw std::out_of_range("out of range in dynamic array");
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"));
218 throw TypeError("object/array", type());
222 std::size_t dynamic::size() const {
223 if (auto* ar = get_nothrow<Array>()) {
226 if (auto* obj = get_nothrow<ObjectImpl>()) {
229 if (auto* str = get_nothrow<fbstring>()) {
232 throw TypeError("array/object", type());
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()));
243 std::size_t dynamic::hash() const {
248 throw TypeError("not null/object/array", type());
250 return std::hash<int64_t>()(asInt());
252 return std::hash<double>()(asDouble());
254 return std::hash<bool>()(asBool());
256 return std::hash<fbstring>()(asString());
262 char const* dynamic::typeName(Type t) {
263 #define FB_X(T) return TypeInfo<T>::name
264 FB_DYNAMIC_APPLY(t, FB_X);
268 void dynamic::destroy() noexcept {
269 // This short-circuit speeds up some microbenchmarks.
270 if (type_ == NULLT) return;
272 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
273 FB_DYNAMIC_APPLY(type_, FB_X);
279 //////////////////////////////////////////////////////////////////////