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 if (it != obj.end()) {
163 const dynamic* dynamic::get_ptr(dynamic const& idx) const {
164 if (auto* parray = get_nothrow<Array>()) {
166 throw TypeError("int64", idx.type());
168 if (idx < 0 || idx >= parray->size()) {
171 return &(*parray)[idx.asInt()];
172 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
173 auto it = pobject->find(idx);
174 if (it == pobject->end()) {
179 throw TypeError("object/array", type());
183 dynamic const& dynamic::at(dynamic const& idx) const {
184 if (auto* parray = get_nothrow<Array>()) {
186 throw TypeError("int64", idx.type());
188 if (idx < 0 || idx >= parray->size()) {
189 throw std::out_of_range("out of range in dynamic array");
191 return (*parray)[idx.asInt()];
192 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
193 auto it = pobject->find(idx);
194 if (it == pobject->end()) {
195 throw std::out_of_range(to<std::string>(
196 "couldn't find key ", idx.asString(), " in dynamic object"));
200 throw TypeError("object/array", type());
204 std::size_t dynamic::size() const {
205 if (auto* ar = get_nothrow<Array>()) {
208 if (auto* obj = get_nothrow<ObjectImpl>()) {
211 if (auto* str = get_nothrow<fbstring>()) {
214 throw TypeError("array/object", type());
217 dynamic::const_iterator
218 dynamic::erase(const_iterator first, const_iterator last) {
219 auto& arr = get<Array>();
220 return get<Array>().erase(
221 arr.begin() + (first - arr.begin()),
222 arr.begin() + (last - arr.begin()));
225 std::size_t dynamic::hash() const {
230 throw TypeError("not null/object/array", type());
232 return std::hash<int64_t>()(asInt());
234 return std::hash<double>()(asDouble());
236 return std::hash<bool>()(asBool());
238 return std::hash<fbstring>()(asString());
244 char const* dynamic::typeName(Type t) {
245 #define FB_X(T) return TypeInfo<T>::name
246 FB_DYNAMIC_APPLY(t, FB_X);
250 void dynamic::destroy() noexcept {
251 // This short-circuit speeds up some microbenchmarks.
252 if (type_ == NULLT) return;
254 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
255 FB_DYNAMIC_APPLY(type_, FB_X);
261 //////////////////////////////////////////////////////////////////////