Remove superfluous std::move
[folly.git] / folly / dynamic.cpp
1 /*
2  * Copyright 2017 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 #include <folly/Assume.h>
20 #include <folly/Hash.h>
21 #include <folly/portability/BitsFunctexcept.h>
22
23 namespace folly {
24
25 //////////////////////////////////////////////////////////////////////
26
27 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
28   constexpr const char* dynamic::TypeInfo<T>::name; \
29   constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
30   //
31
32 FOLLY_DYNAMIC_DEF_TYPEINFO(void*)
33 FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
34 FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
35 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
36 FOLLY_DYNAMIC_DEF_TYPEINFO(double)
37 FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
38 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
39
40 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
41
42 const char* dynamic::typeName() const {
43   return typeName(type_);
44 }
45
46 TypeError::TypeError(const std::string& expected, dynamic::Type actual)
47   : std::runtime_error(to<std::string>("TypeError: expected dynamic "
48       "type `", expected, '\'', ", but had type `",
49       dynamic::typeName(actual), '\''))
50 {}
51
52 TypeError::TypeError(const std::string& expected,
53     dynamic::Type actual1, dynamic::Type actual2)
54   : std::runtime_error(to<std::string>("TypeError: expected dynamic "
55       "types `", expected, '\'', ", but had types `",
56       dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
57       '\''))
58 {}
59
60 TypeError::~TypeError() = default;
61
62 // This is a higher-order preprocessor macro to aid going from runtime
63 // types to the compile time type system.
64 #define FB_DYNAMIC_APPLY(type, apply) \
65   do {                                \
66     switch ((type)) {                 \
67       case NULLT:                     \
68         apply(void*);                 \
69         break;                        \
70       case ARRAY:                     \
71         apply(Array);                 \
72         break;                        \
73       case BOOL:                      \
74         apply(bool);                  \
75         break;                        \
76       case DOUBLE:                    \
77         apply(double);                \
78         break;                        \
79       case INT64:                     \
80         apply(int64_t);               \
81         break;                        \
82       case OBJECT:                    \
83         apply(ObjectImpl);            \
84         break;                        \
85       case STRING:                    \
86         apply(std::string);           \
87         break;                        \
88       default:                        \
89         CHECK(0);                     \
90         abort();                      \
91     }                                 \
92   } while (0)
93
94 bool dynamic::operator<(dynamic const& o) const {
95   if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
96     throw TypeError("object", type_);
97   }
98   if (type_ != o.type_) {
99     return type_ < o.type_;
100   }
101
102 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(),   \
103                                           *o.getAddress<T>())
104   FB_DYNAMIC_APPLY(type_, FB_X);
105 #undef FB_X
106 }
107
108 bool dynamic::operator==(dynamic const& o) const {
109   if (type() != o.type()) {
110     if (isNumber() && o.isNumber()) {
111       auto& integ = isInt() ? *this : o;
112       auto& doubl = isInt() ? o     : *this;
113       return integ.asInt() == doubl.asDouble();
114     }
115     return false;
116   }
117
118 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
119   FB_DYNAMIC_APPLY(type_, FB_X);
120 #undef FB_X
121 }
122
123 dynamic& dynamic::operator=(dynamic const& o) {
124   if (&o != this) {
125     if (type_ == o.type_) {
126 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
127       FB_DYNAMIC_APPLY(type_, FB_X);
128 #undef FB_X
129     } else {
130       destroy();
131 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
132       FB_DYNAMIC_APPLY(o.type_, FB_X);
133 #undef FB_X
134       type_ = o.type_;
135     }
136   }
137   return *this;
138 }
139
140 dynamic& dynamic::operator=(dynamic&& o) noexcept {
141   if (&o != this) {
142     if (type_ == o.type_) {
143 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
144       FB_DYNAMIC_APPLY(type_, FB_X);
145 #undef FB_X
146     } else {
147       destroy();
148 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
149       FB_DYNAMIC_APPLY(o.type_, FB_X);
150 #undef FB_X
151       type_ = o.type_;
152     }
153   }
154   return *this;
155 }
156
157 dynamic& dynamic::operator[](dynamic const& k) & {
158   if (!isObject() && !isArray()) {
159     throw TypeError("object/array", type());
160   }
161   if (isArray()) {
162     return at(k);
163   }
164   auto& obj = get<ObjectImpl>();
165   auto ret = obj.insert({k, nullptr});
166   return ret.first->second;
167 }
168
169 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
170   auto& obj = get<ObjectImpl>();
171   auto it = obj.find(k);
172   return it == obj.end() ? v : it->second;
173 }
174
175 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
176   auto& obj = get<ObjectImpl>();
177   auto it = obj.find(k);
178   // Avoid clang bug with ternary
179   if (it == obj.end()) {
180     return std::move(v);
181   } else {
182     return it->second;
183   }
184 }
185
186 dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && {
187   auto& obj = get<ObjectImpl>();
188   auto it = obj.find(k);
189   // Avoid clang bug with ternary
190   if (it == obj.end()) {
191     return v;
192   } else {
193     return std::move(it->second);
194   }
195 }
196
197 dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
198   auto& obj = get<ObjectImpl>();
199   auto it = obj.find(k);
200   return std::move(it == obj.end() ? v : it->second);
201 }
202
203 const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
204   if (auto* parray = get_nothrow<Array>()) {
205     if (!idx.isInt()) {
206       throw TypeError("int64", idx.type());
207     }
208     if (idx < 0 || idx >= parray->size()) {
209       return nullptr;
210     }
211     return &(*parray)[size_t(idx.asInt())];
212   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
213     auto it = pobject->find(idx);
214     if (it == pobject->end()) {
215       return nullptr;
216     }
217     return &it->second;
218   } else {
219     throw TypeError("object/array", type());
220   }
221 }
222
223 dynamic const& dynamic::at(dynamic const& idx) const& {
224   if (auto* parray = get_nothrow<Array>()) {
225     if (!idx.isInt()) {
226       throw TypeError("int64", idx.type());
227     }
228     if (idx < 0 || idx >= parray->size()) {
229       std::__throw_out_of_range("out of range in dynamic array");
230     }
231     return (*parray)[size_t(idx.asInt())];
232   } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
233     auto it = pobject->find(idx);
234     if (it == pobject->end()) {
235       throw std::out_of_range(to<std::string>(
236           "couldn't find key ", idx.asString(), " in dynamic object"));
237     }
238     return it->second;
239   } else {
240     throw TypeError("object/array", type());
241   }
242 }
243
244 std::size_t dynamic::size() const {
245   if (auto* ar = get_nothrow<Array>()) {
246     return ar->size();
247   }
248   if (auto* obj = get_nothrow<ObjectImpl>()) {
249     return obj->size();
250   }
251   if (auto* str = get_nothrow<std::string>()) {
252     return str->size();
253   }
254   throw TypeError("array/object", type());
255 }
256
257 dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
258   auto& arr = get<Array>();
259   return get<Array>().erase(
260     arr.begin() + (first - arr.begin()),
261     arr.begin() + (last - arr.begin()));
262 }
263
264 std::size_t dynamic::hash() const {
265   switch (type()) {
266   case OBJECT:
267   case ARRAY:
268   case NULLT:
269     throw TypeError("not null/object/array", type());
270   case INT64:
271     return std::hash<int64_t>()(getInt());
272   case DOUBLE:
273     return std::hash<double>()(getDouble());
274   case BOOL:
275     return std::hash<bool>()(getBool());
276   case STRING: {
277     // keep it compatible with FBString
278     const auto& str = getString();
279     return ::folly::hash::fnv32_buf(str.data(), str.size());
280   }
281   }
282   assume_unreachable();
283 }
284
285 char const* dynamic::typeName(Type t) {
286 #define FB_X(T) return TypeInfo<T>::name
287   FB_DYNAMIC_APPLY(t, FB_X);
288 #undef FB_X
289 }
290
291 void dynamic::destroy() noexcept {
292   // This short-circuit speeds up some microbenchmarks.
293   if (type_ == NULLT) return;
294
295 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
296   FB_DYNAMIC_APPLY(type_, FB_X);
297 #undef FB_X
298   type_ = NULLT;
299   u_.nul = nullptr;
300 }
301
302 //////////////////////////////////////////////////////////////////////
303
304 }