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() {}
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) {
104 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
105 FB_DYNAMIC_APPLY(o.type_, FB_X);
112 dynamic& dynamic::operator=(dynamic&& o) noexcept {
115 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
116 FB_DYNAMIC_APPLY(o.type_, FB_X);
123 dynamic& dynamic::operator[](dynamic const& k) {
124 if (!isObject() && !isArray()) {
125 throw TypeError("object/array", type());
130 auto& obj = get<ObjectImpl>();
131 auto ret = obj.insert({k, nullptr});
132 return ret.first->second;
135 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const {
136 auto& obj = get<ObjectImpl>();
137 auto it = obj.find(k);
138 return it == obj.end() ? v : it->second;
141 dynamic&& dynamic::getDefault(const dynamic& k, dynamic&& v) const {
142 auto& obj = get<ObjectImpl>();
143 auto it = obj.find(k);
144 if (it != obj.end()) {
151 const dynamic* dynamic::get_ptr(dynamic const& idx) const {
152 if (auto* parray = get_nothrow<Array>()) {
154 throw TypeError("int64", idx.type());
156 if (idx >= parray->size()) {
159 return &(*parray)[idx.asInt()];
160 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
161 auto it = pobject->find(idx);
162 if (it == pobject->end()) {
167 throw TypeError("object/array", type());
171 dynamic const& dynamic::at(dynamic const& idx) const {
172 if (auto* parray = get_nothrow<Array>()) {
174 throw TypeError("int64", idx.type());
176 if (idx >= parray->size()) {
177 throw std::out_of_range("out of range in dynamic array");
179 return (*parray)[idx.asInt()];
180 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
181 auto it = pobject->find(idx);
182 if (it == pobject->end()) {
183 throw std::out_of_range(to<std::string>(
184 "couldn't find key ", idx.asString(), " in dynamic object"));
188 throw TypeError("object/array", type());
192 std::size_t dynamic::size() const {
193 if (auto* ar = get_nothrow<Array>()) {
196 if (auto* obj = get_nothrow<ObjectImpl>()) {
199 if (auto* str = get_nothrow<fbstring>()) {
202 throw TypeError("array/object", type());
205 dynamic::const_iterator
206 dynamic::erase(const_iterator first, const_iterator last) {
207 auto& arr = get<Array>();
208 return get<Array>().erase(
209 arr.begin() + (first - arr.begin()),
210 arr.begin() + (last - arr.begin()));
213 std::size_t dynamic::hash() const {
218 throw TypeError("not null/object/array", type());
220 return std::hash<int64_t>()(asInt());
222 return std::hash<double>()(asDouble());
224 return std::hash<bool>()(asBool());
226 return std::hash<fbstring>()(asString());
232 char const* dynamic::typeName(Type t) {
233 #define FB_X(T) return TypeInfo<T>::name
234 FB_DYNAMIC_APPLY(t, FB_X);
238 void dynamic::destroy() noexcept {
239 // This short-circuit speeds up some microbenchmarks.
240 if (type_ == NULLT) return;
242 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
243 FB_DYNAMIC_APPLY(type_, FB_X);
249 //////////////////////////////////////////////////////////////////////