proxygen
dynamic.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-present 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 <numeric>
18 
19 #include <folly/dynamic.h>
20 
21 #include <folly/Format.h>
22 #include <folly/hash/Hash.h>
23 #include <folly/lang/Assume.h>
24 #include <folly/lang/Exception.h>
25 
26 namespace folly {
27 
29 
30 #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
31  constexpr const char* dynamic::TypeInfo<T>::name; \
32  constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
33  //
34 
35 FOLLY_DYNAMIC_DEF_TYPEINFO(std::nullptr_t)
38 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
41 FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
42 
43 #undef FOLLY_DYNAMIC_DEF_TYPEINFO
44 
45 const char* dynamic::typeName() const {
46  return typeName(type_);
47 }
48 
50  : std::runtime_error(sformat(
51  "TypeError: expected dynamic type `{}', but had type `{}'",
52  expected,
53  dynamic::typeName(actual))) {}
54 
56  const std::string& expected,
57  dynamic::Type actual1,
58  dynamic::Type actual2)
59  : std::runtime_error(sformat(
60  "TypeError: expected dynamic types `{}, but had types `{}' and `{}'",
61  expected,
62  dynamic::typeName(actual1),
63  dynamic::typeName(actual2))) {}
64 
73 TypeError::~TypeError() = default;
74 
75 // This is a higher-order preprocessor macro to aid going from runtime
76 // types to the compile time type system.
77 #define FB_DYNAMIC_APPLY(type, apply) \
78  do { \
79  switch ((type)) { \
80  case NULLT: \
81  apply(std::nullptr_t); \
82  break; \
83  case ARRAY: \
84  apply(Array); \
85  break; \
86  case BOOL: \
87  apply(bool); \
88  break; \
89  case DOUBLE: \
90  apply(double); \
91  break; \
92  case INT64: \
93  apply(int64_t); \
94  break; \
95  case OBJECT: \
96  apply(ObjectImpl); \
97  break; \
98  case STRING: \
99  apply(std::string); \
100  break; \
101  default: \
102  CHECK(0); \
103  abort(); \
104  } \
105  } while (0)
106 
107 bool dynamic::operator<(dynamic const& o) const {
108  if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
109  throw_exception<TypeError>("object", type_);
110  }
111  if (type_ != o.type_) {
112  return type_ < o.type_;
113  }
114 
115 #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), *o.getAddress<T>())
117 #undef FB_X
118 }
119 
120 bool dynamic::operator==(dynamic const& o) const {
121  if (type() != o.type()) {
122  if (isNumber() && o.isNumber()) {
123  auto& integ = isInt() ? *this : o;
124  auto& doubl = isInt() ? o : *this;
125  return integ.asInt() == doubl.asDouble();
126  }
127  return false;
128  }
129 
130 #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
132 #undef FB_X
133 }
134 
136  if (&o != this) {
137  if (type_ == o.type_) {
138 #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
140 #undef FB_X
141  } else {
142  destroy();
143 #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
145 #undef FB_X
146  type_ = o.type_;
147  }
148  }
149  return *this;
150 }
151 
153  if (&o != this) {
154  if (type_ == o.type_) {
155 #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
157 #undef FB_X
158  } else {
159  destroy();
160 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
161  FB_DYNAMIC_APPLY(o.type_, FB_X);
162 #undef FB_X
163  type_ = o.type_;
164  }
165  }
166  return *this;
167 }
168 
169 dynamic const& dynamic::atImpl(dynamic const& idx) const& {
170  if (auto* parray = get_nothrow<Array>()) {
171  if (!idx.isInt()) {
172  throw_exception<TypeError>("int64", idx.type());
173  }
174  if (idx < 0 || idx >= parray->size()) {
175  throw_exception<std::out_of_range>("out of range in dynamic array");
176  }
177  return (*parray)[size_t(idx.asInt())];
178  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
179  auto it = pobject->find(idx);
180  if (it == pobject->end()) {
181  throw_exception<std::out_of_range>(
182  sformat("couldn't find key {} in dynamic object", idx.asString()));
183  }
184  return it->second;
185  } else {
186  throw_exception<TypeError>("object/array", type());
187  }
188 }
189 
190 dynamic const& dynamic::at(StringPiece idx) const& {
191  auto* pobject = get_nothrow<ObjectImpl>();
192  if (!pobject) {
193  throw_exception<TypeError>("object", type());
194  }
195  auto it = pobject->find(idx);
196  if (it == pobject->end()) {
197  throw_exception<std::out_of_range>(
198  sformat("couldn't find key {} in dynamic object", idx));
199  }
200  return it->second;
201 }
202 
204  auto& obj = get<ObjectImpl>();
205  auto ret = obj.emplace(k, nullptr);
206  return ret.first->second;
207 }
208 
210  auto& obj = get<ObjectImpl>();
211  auto it = obj.find(k);
212  return it == obj.end() ? v : it->second;
213 }
214 
216  auto& obj = get<ObjectImpl>();
217  auto it = obj.find(k);
218  // Avoid clang bug with ternary
219  if (it == obj.end()) {
220  return std::move(v);
221  } else {
222  return it->second;
223  }
224 }
225 
227  auto& obj = get<ObjectImpl>();
228  auto it = obj.find(k);
229  // Avoid clang bug with ternary
230  if (it == obj.end()) {
231  return v;
232  } else {
233  return std::move(it->second);
234  }
235 }
236 
238  auto& obj = get<ObjectImpl>();
239  auto it = obj.find(k);
240  return std::move(it == obj.end() ? v : it->second);
241 }
242 
243 const dynamic* dynamic::get_ptrImpl(dynamic const& idx) const& {
244  if (auto* parray = get_nothrow<Array>()) {
245  if (!idx.isInt()) {
246  throw_exception<TypeError>("int64", idx.type());
247  }
248  if (idx < 0 || idx >= parray->size()) {
249  return nullptr;
250  }
251  return &(*parray)[size_t(idx.asInt())];
252  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
253  auto it = pobject->find(idx);
254  if (it == pobject->end()) {
255  return nullptr;
256  }
257  return &it->second;
258  } else {
259  throw_exception<TypeError>("object/array", type());
260  }
261 }
262 
264  auto* pobject = get_nothrow<ObjectImpl>();
265  if (!pobject) {
266  throw_exception<TypeError>("object", type());
267  }
268  auto it = pobject->find(idx);
269  if (it == pobject->end()) {
270  return nullptr;
271  }
272  return &it->second;
273 }
274 
275 std::size_t dynamic::size() const {
276  if (auto* ar = get_nothrow<Array>()) {
277  return ar->size();
278  }
279  if (auto* obj = get_nothrow<ObjectImpl>()) {
280  return obj->size();
281  }
282  if (auto* str = get_nothrow<std::string>()) {
283  return str->size();
284  }
285  throw_exception<TypeError>("array/object/string", type());
286 }
287 
289  auto& arr = get<Array>();
290  return get<Array>().erase(
291  arr.begin() + (first - arr.begin()), arr.begin() + (last - arr.begin()));
292 }
293 
294 std::size_t dynamic::hash() const {
295  switch (type()) {
296  case NULLT:
297  return 0xBAAAAAAD;
298  case OBJECT: {
299  // Accumulate using addition instead of using hash_range (as in the ARRAY
300  // case), as we need a commutative hash operation since unordered_map's
301  // iteration order is unspecified.
302  auto h = std::hash<std::pair<dynamic, dynamic>>{};
303  return std::accumulate(
304  items().begin(),
305  items().end(),
306  size_t{0x0B1EC7},
307  [&](auto acc, auto item) { return acc + h(item); });
308  }
309  case ARRAY:
310  return folly::hash::hash_range(begin(), end());
311  case INT64:
312  return std::hash<int64_t>()(getInt());
313  case DOUBLE:
314  return std::hash<double>()(getDouble());
315  case BOOL:
316  return std::hash<bool>()(getBool());
317  case STRING:
318  // keep consistent with detail::DynamicHasher
319  return Hash()(getString());
320  }
322 }
323 
324 char const* dynamic::typeName(Type t) {
325 #define FB_X(T) return TypeInfo<T>::name
327 #undef FB_X
328 }
329 
331  // This short-circuit speeds up some microbenchmarks.
332  if (type_ == NULLT) {
333  return;
334  }
335 
336 #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
338 #undef FB_X
339  type_ = NULLT;
340  u_.nul = nullptr;
341 }
342 
343 dynamic dynamic::merge_diff(const dynamic& source, const dynamic& target) {
344  if (!source.isObject() || source.type() != target.type()) {
345  return target;
346  }
347 
348  dynamic diff = object;
349 
350  // added/modified keys
351  for (const auto& pair : target.items()) {
352  auto it = source.find(pair.first);
353  if (it == source.items().end()) {
354  diff[pair.first] = pair.second;
355  } else {
356  diff[pair.first] = merge_diff(source[pair.first], target[pair.first]);
357  }
358  }
359 
360  // removed keys
361  for (const auto& pair : source.items()) {
362  auto it = target.find(pair.first);
363  if (it == target.items().end()) {
364  diff[pair.first] = nullptr;
365  }
366  }
367 
368  return diff;
369 }
370 
371 const dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) const& {
372  auto const& tokens = jsonPtr.tokens();
373  if (tokens.empty()) {
374  return this;
375  }
376  dynamic const* dyn = this;
377  for (auto const& token : tokens) {
378  if (!dyn) {
379  return nullptr;
380  }
381  // special case of parsing "/": lookup key with empty name
382  if (token.empty()) {
383  if (dyn->isObject()) {
384  dyn = dyn->get_ptr("");
385  continue;
386  }
387  throw_exception<TypeError>("object", dyn->type());
388  }
389  if (auto* parray = dyn->get_nothrow<dynamic::Array>()) {
390  if (token.size() > 1 && token.at(0) == '0') {
391  throw std::invalid_argument(
392  "Leading zero not allowed when indexing arrays");
393  }
394  // special case, always return non-existent
395  if (token.size() == 1 && token.at(0) == '-') {
396  dyn = nullptr;
397  continue;
398  }
399  auto const idx = folly::to<size_t>(token);
400  dyn = idx < parray->size() ? &(*parray)[idx] : nullptr;
401  continue;
402  }
403  if (auto* pobject = dyn->get_nothrow<dynamic::ObjectImpl>()) {
404  auto const it = pobject->find(token);
405  dyn = it != pobject->end() ? &it->second : nullptr;
406  continue;
407  }
408  throw_exception<TypeError>("object/array", dyn->type());
409  }
410  return dyn;
411 }
412 
414 
415 } // namespace folly
bool operator<(dynamic const &o) const
Definition: dynamic.cpp:107
std::vector< dynamic > Array
Definition: dynamic.h:90
void * object
Definition: AtFork.cpp:32
*than *hazptr_holder h
Definition: Hazptr.h:116
IfIsNonStringDynamicConvertible< K, dynamic > getDefault(K &&k, const dynamic &v=dynamic::object) const &
Definition: dynamic-inl.h:685
#define FB_DYNAMIC_APPLY(type, apply)
Definition: dynamic.cpp:77
auto v
const char * typeName() const
Definition: dynamic.cpp:45
void accumulate(std::vector< std::size_t > &a, std::vector< std::size_t > const &d)
Definition: F14TestUtil.h:58
dynamic const & atImpl(dynamic const &) const &
Definition: dynamic.cpp:169
std::string sformat(StringPiece fmt, Args &&...args)
Definition: Format.h:280
PskType type
Array::iterator iterator
Definition: dynamic.h:118
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
~TypeError() override
#define FB_X(T)
STL namespace.
auto begin(TestAdlIterable &instance)
Definition: ForeachTest.cpp:56
TypeError & operator=(const TypeError &) noexcept(std::is_nothrow_copy_assignable< std::runtime_error >::value)
void destroy() noexcept
Definition: dynamic.cpp:330
std::size_t hash() const
Definition: dynamic.cpp:294
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
requires E e noexcept(noexcept(s.error(std::move(e))))
#define STRING
bool isNumber() const
Definition: dynamic-inl.h:510
static void destroy()
IfIsNonStringDynamicConvertible< K, dynamic & > operator[](K &&)&
Definition: dynamic-inl.h:657
TypeError(const std::string &expected, dynamic::Type actual)
Definition: dynamic.cpp:49
uint64_t hash_range(Iter begin, Iter end, uint64_t hash=0, Hash hasher=Hash())
Definition: Hash.h:604
const dynamic * get_ptrImpl(dynamic const &) const &
Definition: dynamic.cpp:243
static dynamic merge_diff(const dynamic &source, const dynamic &target)
Definition: dynamic.cpp:343
FOLLY_ALWAYS_INLINE void assume_unreachable()
Definition: Assume.h:59
#define FOLLY_DYNAMIC_DEF_TYPEINFO(T)
Definition: dynamic.cpp:30
bool operator==(dynamic const &o) const
Definition: dynamic.cpp:120
dynamic & operator=(dynamic const &)
Definition: dynamic.cpp:135
Type type_
Definition: JSONSchema.cpp:208
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
int64_t asInt() const
Definition: dynamic-inl.h:524
IfIsNonStringDynamicConvertible< K, const_item_iterator > find(K &&) const
Definition: dynamic-inl.h:824
static const char *const value
Definition: Conv.cpp:50
IfIsNonStringDynamicConvertible< K, std::size_t > erase(K &&)
Definition: dynamic-inl.h:916
uint64_t diff(uint64_t a, uint64_t b)
Definition: FutexTest.cpp:135
IterableProxy< const_item_iterator > items() const
Definition: dynamic-inl.h:476
const char * string
Definition: Conv.cpp:212
std::size_t size() const
Definition: dynamic.cpp:275
const
Definition: upload.py:398
Definition: Traits.h:577
IfIsNonStringDynamicConvertible< K, dynamic const & > at(K &&) const &
Definition: dynamic-inl.h:792
Type type() const
Definition: dynamic-inl.h:514
#define UNLIKELY(x)
Definition: Likely.h:48
Array::const_iterator const_iterator
Definition: dynamic.h:119
KeyT k
bool isObject() const
Definition: dynamic-inl.h:492
T * get_nothrow()&noexcept
Definition: dynamic-inl.h:1027
static const char tokens[256]
Definition: http_parser.c:184
const_iterator end() const
Definition: dynamic-inl.h:435
const dynamic * get_ptr(json_pointer const &) const &
Definition: dynamic.cpp:371
constexpr detail::First first
Definition: Base-inl.h:2553