fix Baton.h typo
[folly.git] / folly / dynamic.cpp
1 /*
2  * Copyright 2015 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 namespace folly {
20
21 //////////////////////////////////////////////////////////////////////
22
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
26
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);
34
35 #undef DEF_TYPE
36
37 const char* dynamic::typeName() const {
38   return typeName(type_);
39 }
40
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), '\''))
45 {}
46
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),
52       '\''))
53 {}
54
55 TypeError::~TypeError() {}
56
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 {         \
60   switch ((type)) {                             \
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();              \
69   }                                             \
70 } while (0)
71
72 bool dynamic::operator<(dynamic const& o) const {
73   if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
74     throw TypeError("object", type_);
75   }
76   if (type_ != o.type_) {
77     return type_ < o.type_;
78   }
79
80 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(),   \
81                                           *o.getAddress<T>())
82   FB_DYNAMIC_APPLY(type_, FB_X);
83 #undef FB_X
84 }
85
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();
92     }
93     return false;
94   }
95
96 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
97   FB_DYNAMIC_APPLY(type_, FB_X);
98 #undef FB_X
99 }
100
101 dynamic& dynamic::operator=(dynamic const& o) {
102   if (&o != this) {
103     destroy();
104 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
105     FB_DYNAMIC_APPLY(o.type_, FB_X);
106 #undef FB_X
107     type_ = o.type_;
108   }
109   return *this;
110 }
111
112 dynamic& dynamic::operator=(dynamic&& o) noexcept {
113   if (&o != this) {
114     destroy();
115 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
116     FB_DYNAMIC_APPLY(o.type_, FB_X);
117 #undef FB_X
118     type_ = o.type_;
119   }
120   return *this;
121 }
122
123 dynamic& dynamic::operator[](dynamic const& k) {
124   if (!isObject() && !isArray()) {
125     throw TypeError("object/array", type());
126   }
127   if (isArray()) {
128     return at(k);
129   }
130   auto& obj = get<ObjectImpl>();
131   auto ret = obj.insert({k, nullptr});
132   return ret.first->second;
133 }
134
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;
139 }
140
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()) {
145     v = it->second;
146   }
147
148   return std::move(v);
149 }
150
151 const dynamic* dynamic::get_ptr(dynamic const& idx) const {
152   if (auto* parray = get_nothrow<Array>()) {
153     if (!idx.isInt()) {
154       throw TypeError("int64", idx.type());
155     }
156     if (idx >= parray->size()) {
157       return nullptr;
158     }
159     return &(*parray)[idx.asInt()];
160   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
161     auto it = pobject->find(idx);
162     if (it == pobject->end()) {
163       return nullptr;
164     }
165     return &it->second;
166   } else {
167     throw TypeError("object/array", type());
168   }
169 }
170
171 dynamic const& dynamic::at(dynamic const& idx) const {
172   if (auto* parray = get_nothrow<Array>()) {
173     if (!idx.isInt()) {
174       throw TypeError("int64", idx.type());
175     }
176     if (idx >= parray->size()) {
177       throw std::out_of_range("out of range in dynamic array");
178     }
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"));
185     }
186     return it->second;
187   } else {
188     throw TypeError("object/array", type());
189   }
190 }
191
192 std::size_t dynamic::size() const {
193   if (auto* ar = get_nothrow<Array>()) {
194     return ar->size();
195   }
196   if (auto* obj = get_nothrow<ObjectImpl>()) {
197     return obj->size();
198   }
199   if (auto* str = get_nothrow<fbstring>()) {
200     return str->size();
201   }
202   throw TypeError("array/object", type());
203 }
204
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()));
211 }
212
213 std::size_t dynamic::hash() const {
214   switch (type()) {
215   case OBJECT:
216   case ARRAY:
217   case NULLT:
218     throw TypeError("not null/object/array", type());
219   case INT64:
220     return std::hash<int64_t>()(asInt());
221   case DOUBLE:
222     return std::hash<double>()(asDouble());
223   case BOOL:
224     return std::hash<bool>()(asBool());
225   case STRING:
226     return std::hash<fbstring>()(asString());
227   default:
228     CHECK(0); abort();
229   }
230 }
231
232 char const* dynamic::typeName(Type t) {
233 #define FB_X(T) return TypeInfo<T>::name
234   FB_DYNAMIC_APPLY(t, FB_X);
235 #undef FB_X
236 }
237
238 void dynamic::destroy() noexcept {
239   // This short-circuit speeds up some microbenchmarks.
240   if (type_ == NULLT) return;
241
242 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
243   FB_DYNAMIC_APPLY(type_, FB_X);
244 #undef FB_X
245   type_ = NULLT;
246   u_.nul = nullptr;
247 }
248
249 //////////////////////////////////////////////////////////////////////
250
251 }