2 * Copyright 2016 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>
18 #include <folly/Hash.h>
22 //////////////////////////////////////////////////////////////////////
24 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
25 constexpr const char* dynamic::TypeInfo<T>::name; \
26 constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
29 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
30 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
31 FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
32 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
35 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
37 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
39 const char* dynamic::typeName() const {
40 return typeName(type_);
43 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
44 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
45 "type `", expected, '\'', ", but had type `",
46 dynamic::typeName(actual), '\''))
49 TypeError::TypeError(const std::string& expected,
50 dynamic::Type actual1, dynamic::Type actual2)
51 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
52 "types `", expected, '\'', ", but had types `",
53 dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
57 TypeError::~TypeError() = default;
59 // This is a higher-order preprocessor macro to aid going from runtime
60 // types to the compile time type system.
61 #define FB_DYNAMIC_APPLY(type, apply) \
91 bool dynamic::operator<(dynamic const& o) const {
92 if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
93 throw TypeError("object", type_);
95 if (type_ != o.type_) {
96 return type_ < o.type_;
99 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), \
101 FB_DYNAMIC_APPLY(type_, FB_X);
105 bool dynamic::operator==(dynamic const& o) const {
106 if (type() != o.type()) {
107 if (isNumber() && o.isNumber()) {
108 auto& integ = isInt() ? *this : o;
109 auto& doubl = isInt() ? o : *this;
110 return integ.asInt() == doubl.asDouble();
115 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
116 FB_DYNAMIC_APPLY(type_, FB_X);
120 dynamic& dynamic::operator=(dynamic const& o) {
122 if (type_ == o.type_) {
123 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
124 FB_DYNAMIC_APPLY(type_, FB_X);
128 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
129 FB_DYNAMIC_APPLY(o.type_, FB_X);
137 dynamic& dynamic::operator=(dynamic&& o) noexcept {
139 if (type_ == o.type_) {
140 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
141 FB_DYNAMIC_APPLY(type_, FB_X);
145 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
146 FB_DYNAMIC_APPLY(o.type_, FB_X);
154 dynamic& dynamic::operator[](dynamic const& k) & {
155 if (!isObject() && !isArray()) {
156 throw TypeError("object/array", type());
161 auto& obj = get<ObjectImpl>();
162 auto ret = obj.insert({k, nullptr});
163 return ret.first->second;
166 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
167 auto& obj = get<ObjectImpl>();
168 auto it = obj.find(k);
169 return it == obj.end() ? v : it->second;
172 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
173 auto& obj = get<ObjectImpl>();
174 auto it = obj.find(k);
175 // Avoid clang bug with ternary
176 if (it == obj.end()) {
183 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
184 auto& obj = get<ObjectImpl>();
185 auto it = obj.find(k);
186 // Avoid clang bug with ternary
187 if (it == obj.end()) {
190 return std::move(it->second);
194 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
195 auto& obj = get<ObjectImpl>();
196 auto it = obj.find(k);
197 return std::move(it == obj.end() ? v : it->second);
200 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
201 if (auto* parray = get_nothrow<Array>()) {
203 throw TypeError("int64", idx.type());
205 if (idx < 0 || idx >= parray->size()) {
208 return &(*parray)[idx.asInt()];
209 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
210 auto it = pobject->find(idx);
211 if (it == pobject->end()) {
216 throw TypeError("object/array", type());
220 dynamic const& dynamic::at(dynamic const& idx) const& {
221 if (auto* parray = get_nothrow<Array>()) {
223 throw TypeError("int64", idx.type());
225 if (idx < 0 || idx >= parray->size()) {
226 throw std::out_of_range("out of range in dynamic array");
228 return (*parray)[idx.asInt()];
229 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
230 auto it = pobject->find(idx);
231 if (it == pobject->end()) {
232 throw std::out_of_range(to<std::string>(
233 "couldn't find key ", idx.asString(), " in dynamic object"));
237 throw TypeError("object/array", type());
241 std::size_t dynamic::size() const {
242 if (auto* ar = get_nothrow<Array>()) {
245 if (auto* obj = get_nothrow<ObjectImpl>()) {
248 if (auto* str = get_nothrow<std::string>()) {
251 throw TypeError("array/object", type());
254 dynamic::const_iterator
255 dynamic::erase(const_iterator first, const_iterator last) {
256 auto& arr = get<Array>();
257 return get<Array>().erase(
258 arr.begin() + (first - arr.begin()),
259 arr.begin() + (last - arr.begin()));
262 std::size_t dynamic::hash() const {
267 throw TypeError("not null/object/array", type());
269 return std::hash<int64_t>()(getInt());
271 return std::hash<double>()(getDouble());
273 return std::hash<bool>()(getBool());
275 // keep it compatible with FBString
276 const auto& str = getString();
277 return ::folly::hash::fnv32_buf(str.data(), str.size());
284 char const* dynamic::typeName(Type t) {
285 #define FB_X(T) return TypeInfo<T>::name
286 FB_DYNAMIC_APPLY(t, FB_X);
290 void dynamic::destroy() noexcept {
291 // This short-circuit speeds up some microbenchmarks.
292 if (type_ == NULLT) return;
294 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
295 FB_DYNAMIC_APPLY(type_, FB_X);
301 //////////////////////////////////////////////////////////////////////