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/Hash.h>
18 #include <folly/dynamic.h>
19 #include <folly/portability/BitsFunctexcept.h>
23 //////////////////////////////////////////////////////////////////////
25 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
26 constexpr const char* dynamic::TypeInfo<T>::name; \
27 constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
30 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
31 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
32 FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
35 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
36 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
38 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
40 const char* dynamic::typeName() const {
41 return typeName(type_);
44 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
45 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
46 "type `", expected, '\'', ", but had type `",
47 dynamic::typeName(actual), '\''))
50 TypeError::TypeError(const std::string& expected,
51 dynamic::Type actual1, dynamic::Type actual2)
52 : std::runtime_error(to<std::string>("TypeError: expected dynamic "
53 "types `", expected, '\'', ", but had types `",
54 dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
58 TypeError::~TypeError() = default;
60 // This is a higher-order preprocessor macro to aid going from runtime
61 // types to the compile time type system.
62 #define FB_DYNAMIC_APPLY(type, apply) \
92 bool dynamic::operator<(dynamic const& o) const {
93 if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
94 throw TypeError("object", type_);
96 if (type_ != o.type_) {
97 return type_ < o.type_;
100 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), \
102 FB_DYNAMIC_APPLY(type_, FB_X);
106 bool dynamic::operator==(dynamic const& o) const {
107 if (type() != o.type()) {
108 if (isNumber() && o.isNumber()) {
109 auto& integ = isInt() ? *this : o;
110 auto& doubl = isInt() ? o : *this;
111 return integ.asInt() == doubl.asDouble();
116 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
117 FB_DYNAMIC_APPLY(type_, FB_X);
121 dynamic& dynamic::operator=(dynamic const& o) {
123 if (type_ == o.type_) {
124 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
125 FB_DYNAMIC_APPLY(type_, FB_X);
129 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
130 FB_DYNAMIC_APPLY(o.type_, FB_X);
138 dynamic& dynamic::operator=(dynamic&& o) noexcept {
140 if (type_ == o.type_) {
141 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
142 FB_DYNAMIC_APPLY(type_, FB_X);
146 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
147 FB_DYNAMIC_APPLY(o.type_, FB_X);
155 dynamic& dynamic::operator[](dynamic const& k) & {
156 if (!isObject() && !isArray()) {
157 throw TypeError("object/array", type());
162 auto& obj = get<ObjectImpl>();
163 auto ret = obj.insert({k, nullptr});
164 return ret.first->second;
167 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
168 auto& obj = get<ObjectImpl>();
169 auto it = obj.find(k);
170 return it == obj.end() ? v : it->second;
173 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
174 auto& obj = get<ObjectImpl>();
175 auto it = obj.find(k);
176 // Avoid clang bug with ternary
177 if (it == obj.end()) {
184 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
185 auto& obj = get<ObjectImpl>();
186 auto it = obj.find(k);
187 // Avoid clang bug with ternary
188 if (it == obj.end()) {
191 return std::move(it->second);
195 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
196 auto& obj = get<ObjectImpl>();
197 auto it = obj.find(k);
198 return std::move(it == obj.end() ? v : it->second);
201 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
202 if (auto* parray = get_nothrow<Array>()) {
204 throw TypeError("int64", idx.type());
206 if (idx < 0 || idx >= parray->size()) {
209 return &(*parray)[idx.asInt()];
210 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
211 auto it = pobject->find(idx);
212 if (it == pobject->end()) {
217 throw TypeError("object/array", type());
221 dynamic const& dynamic::at(dynamic const& idx) const& {
222 if (auto* parray = get_nothrow<Array>()) {
224 throw TypeError("int64", idx.type());
226 if (idx < 0 || idx >= parray->size()) {
227 std::__throw_out_of_range("out of range in dynamic array");
229 return (*parray)[idx.asInt()];
230 } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
231 auto it = pobject->find(idx);
232 if (it == pobject->end()) {
233 throw std::out_of_range(to<std::string>(
234 "couldn't find key ", idx.asString(), " in dynamic object"));
238 throw TypeError("object/array", type());
242 std::size_t dynamic::size() const {
243 if (auto* ar = get_nothrow<Array>()) {
246 if (auto* obj = get_nothrow<ObjectImpl>()) {
249 if (auto* str = get_nothrow<std::string>()) {
252 throw TypeError("array/object", type());
255 dynamic::const_iterator
256 dynamic::erase(const_iterator first, const_iterator last) {
257 auto& arr = get<Array>();
258 return get<Array>().erase(
259 arr.begin() + (first - arr.begin()),
260 arr.begin() + (last - arr.begin()));
263 std::size_t dynamic::hash() const {
268 throw TypeError("not null/object/array", type());
270 return std::hash<int64_t>()(getInt());
272 return std::hash<double>()(getDouble());
274 return std::hash<bool>()(getBool());
276 // keep it compatible with FBString
277 const auto& str = getString();
278 return ::folly::hash::fnv32_buf(str.data(), str.size());
285 char const* dynamic::typeName(Type t) {
286 #define FB_X(T) return TypeInfo<T>::name
287 FB_DYNAMIC_APPLY(t, FB_X);
291 void dynamic::destroy() noexcept {
292 // This short-circuit speeds up some microbenchmarks.
293 if (type_ == NULLT) return;
295 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
296 FB_DYNAMIC_APPLY(type_, FB_X);
302 //////////////////////////////////////////////////////////////////////