dynamic.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #include <numeric>
  17. #include <folly/dynamic.h>
  18. #include <folly/Format.h>
  19. #include <folly/hash/Hash.h>
  20. #include <folly/lang/Assume.h>
  21. #include <folly/lang/Exception.h>
  22. namespace folly {
  23. //////////////////////////////////////////////////////////////////////
  24. #define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
  25. constexpr const char* dynamic::TypeInfo<T>::name; \
  26. constexpr dynamic::Type dynamic::TypeInfo<T>::type; \
  27. //
  28. FOLLY_DYNAMIC_DEF_TYPEINFO(std::nullptr_t)
  29. FOLLY_DYNAMIC_DEF_TYPEINFO(bool)
  30. FOLLY_DYNAMIC_DEF_TYPEINFO(std::string)
  31. FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array)
  32. FOLLY_DYNAMIC_DEF_TYPEINFO(double)
  33. FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t)
  34. FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl)
  35. #undef FOLLY_DYNAMIC_DEF_TYPEINFO
  36. const char* dynamic::typeName() const {
  37. return typeName(type_);
  38. }
  39. TypeError::TypeError(const std::string& expected, dynamic::Type actual)
  40. : std::runtime_error(sformat(
  41. "TypeError: expected dynamic type `{}', but had type `{}'",
  42. expected,
  43. dynamic::typeName(actual))) {}
  44. TypeError::TypeError(
  45. const std::string& expected,
  46. dynamic::Type actual1,
  47. dynamic::Type actual2)
  48. : std::runtime_error(sformat(
  49. "TypeError: expected dynamic types `{}, but had types `{}' and `{}'",
  50. expected,
  51. dynamic::typeName(actual1),
  52. dynamic::typeName(actual2))) {}
  53. TypeError::TypeError(const TypeError&) noexcept(
  54. std::is_nothrow_copy_constructible<std::runtime_error>::value) = default;
  55. TypeError& TypeError::operator=(const TypeError&) noexcept(
  56. std::is_nothrow_copy_assignable<std::runtime_error>::value) = default;
  57. TypeError::TypeError(TypeError&&) noexcept(
  58. std::is_nothrow_move_constructible<std::runtime_error>::value) = default;
  59. TypeError& TypeError::operator=(TypeError&&) noexcept(
  60. std::is_nothrow_move_assignable<std::runtime_error>::value) = default;
  61. TypeError::~TypeError() = default;
  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(std::nullptr_t); \
  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. bool dynamic::operator<(dynamic const& o) const {
  94. if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
  95. throw_exception<TypeError>("object", type_);
  96. }
  97. if (type_ != o.type_) {
  98. return type_ < o.type_;
  99. }
  100. #define FB_X(T) return CompareOp<T>::comp(*getAddress<T>(), *o.getAddress<T>())
  101. FB_DYNAMIC_APPLY(type_, FB_X);
  102. #undef FB_X
  103. }
  104. bool dynamic::operator==(dynamic const& o) const {
  105. if (type() != o.type()) {
  106. if (isNumber() && o.isNumber()) {
  107. auto& integ = isInt() ? *this : o;
  108. auto& doubl = isInt() ? o : *this;
  109. return integ.asInt() == doubl.asDouble();
  110. }
  111. return false;
  112. }
  113. #define FB_X(T) return *getAddress<T>() == *o.getAddress<T>();
  114. FB_DYNAMIC_APPLY(type_, FB_X);
  115. #undef FB_X
  116. }
  117. dynamic& dynamic::operator=(dynamic const& o) {
  118. if (&o != this) {
  119. if (type_ == o.type_) {
  120. #define FB_X(T) *getAddress<T>() = *o.getAddress<T>()
  121. FB_DYNAMIC_APPLY(type_, FB_X);
  122. #undef FB_X
  123. } else {
  124. destroy();
  125. #define FB_X(T) new (getAddress<T>()) T(*o.getAddress<T>())
  126. FB_DYNAMIC_APPLY(o.type_, FB_X);
  127. #undef FB_X
  128. type_ = o.type_;
  129. }
  130. }
  131. return *this;
  132. }
  133. dynamic& dynamic::operator=(dynamic&& o) noexcept {
  134. if (&o != this) {
  135. if (type_ == o.type_) {
  136. #define FB_X(T) *getAddress<T>() = std::move(*o.getAddress<T>())
  137. FB_DYNAMIC_APPLY(type_, FB_X);
  138. #undef FB_X
  139. } else {
  140. destroy();
  141. #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
  142. FB_DYNAMIC_APPLY(o.type_, FB_X);
  143. #undef FB_X
  144. type_ = o.type_;
  145. }
  146. }
  147. return *this;
  148. }
  149. dynamic const& dynamic::atImpl(dynamic const& idx) const& {
  150. if (auto* parray = get_nothrow<Array>()) {
  151. if (!idx.isInt()) {
  152. throw_exception<TypeError>("int64", idx.type());
  153. }
  154. if (idx < 0 || idx >= parray->size()) {
  155. throw_exception<std::out_of_range>("out of range in dynamic array");
  156. }
  157. return (*parray)[size_t(idx.asInt())];
  158. } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
  159. auto it = pobject->find(idx);
  160. if (it == pobject->end()) {
  161. throw_exception<std::out_of_range>(
  162. sformat("couldn't find key {} in dynamic object", idx.asString()));
  163. }
  164. return it->second;
  165. } else {
  166. throw_exception<TypeError>("object/array", type());
  167. }
  168. }
  169. dynamic const& dynamic::at(StringPiece idx) const& {
  170. auto* pobject = get_nothrow<ObjectImpl>();
  171. if (!pobject) {
  172. throw_exception<TypeError>("object", type());
  173. }
  174. auto it = pobject->find(idx);
  175. if (it == pobject->end()) {
  176. throw_exception<std::out_of_range>(
  177. sformat("couldn't find key {} in dynamic object", idx));
  178. }
  179. return it->second;
  180. }
  181. dynamic& dynamic::operator[](StringPiece k) & {
  182. auto& obj = get<ObjectImpl>();
  183. auto ret = obj.emplace(k, nullptr);
  184. return ret.first->second;
  185. }
  186. dynamic dynamic::getDefault(StringPiece k, const dynamic& v) const& {
  187. auto& obj = get<ObjectImpl>();
  188. auto it = obj.find(k);
  189. return it == obj.end() ? v : it->second;
  190. }
  191. dynamic dynamic::getDefault(StringPiece k, dynamic&& v) const& {
  192. auto& obj = get<ObjectImpl>();
  193. auto it = obj.find(k);
  194. // Avoid clang bug with ternary
  195. if (it == obj.end()) {
  196. return std::move(v);
  197. } else {
  198. return it->second;
  199. }
  200. }
  201. dynamic dynamic::getDefault(StringPiece k, const dynamic& v) && {
  202. auto& obj = get<ObjectImpl>();
  203. auto it = obj.find(k);
  204. // Avoid clang bug with ternary
  205. if (it == obj.end()) {
  206. return v;
  207. } else {
  208. return std::move(it->second);
  209. }
  210. }
  211. dynamic dynamic::getDefault(StringPiece k, dynamic&& v) && {
  212. auto& obj = get<ObjectImpl>();
  213. auto it = obj.find(k);
  214. return std::move(it == obj.end() ? v : it->second);
  215. }
  216. const dynamic* dynamic::get_ptrImpl(dynamic const& idx) const& {
  217. if (auto* parray = get_nothrow<Array>()) {
  218. if (!idx.isInt()) {
  219. throw_exception<TypeError>("int64", idx.type());
  220. }
  221. if (idx < 0 || idx >= parray->size()) {
  222. return nullptr;
  223. }
  224. return &(*parray)[size_t(idx.asInt())];
  225. } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
  226. auto it = pobject->find(idx);
  227. if (it == pobject->end()) {
  228. return nullptr;
  229. }
  230. return &it->second;
  231. } else {
  232. throw_exception<TypeError>("object/array", type());
  233. }
  234. }
  235. const dynamic* dynamic::get_ptr(StringPiece idx) const& {
  236. auto* pobject = get_nothrow<ObjectImpl>();
  237. if (!pobject) {
  238. throw_exception<TypeError>("object", type());
  239. }
  240. auto it = pobject->find(idx);
  241. if (it == pobject->end()) {
  242. return nullptr;
  243. }
  244. return &it->second;
  245. }
  246. std::size_t dynamic::size() const {
  247. if (auto* ar = get_nothrow<Array>()) {
  248. return ar->size();
  249. }
  250. if (auto* obj = get_nothrow<ObjectImpl>()) {
  251. return obj->size();
  252. }
  253. if (auto* str = get_nothrow<std::string>()) {
  254. return str->size();
  255. }
  256. throw_exception<TypeError>("array/object/string", type());
  257. }
  258. dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
  259. auto& arr = get<Array>();
  260. return get<Array>().erase(
  261. arr.begin() + (first - arr.begin()), arr.begin() + (last - arr.begin()));
  262. }
  263. std::size_t dynamic::hash() const {
  264. switch (type()) {
  265. case NULLT:
  266. return 0xBAAAAAAD;
  267. case OBJECT: {
  268. // Accumulate using addition instead of using hash_range (as in the ARRAY
  269. // case), as we need a commutative hash operation since unordered_map's
  270. // iteration order is unspecified.
  271. auto h = std::hash<std::pair<dynamic, dynamic>>{};
  272. return std::accumulate(
  273. items().begin(),
  274. items().end(),
  275. size_t{0x0B1EC7},
  276. [&](auto acc, auto item) { return acc + h(item); });
  277. }
  278. case ARRAY:
  279. return folly::hash::hash_range(begin(), end());
  280. case INT64:
  281. return std::hash<int64_t>()(getInt());
  282. case DOUBLE:
  283. return std::hash<double>()(getDouble());
  284. case BOOL:
  285. return std::hash<bool>()(getBool());
  286. case STRING:
  287. // keep consistent with detail::DynamicHasher
  288. return Hash()(getString());
  289. }
  290. assume_unreachable();
  291. }
  292. char const* dynamic::typeName(Type t) {
  293. #define FB_X(T) return TypeInfo<T>::name
  294. FB_DYNAMIC_APPLY(t, FB_X);
  295. #undef FB_X
  296. }
  297. void dynamic::destroy() noexcept {
  298. // This short-circuit speeds up some microbenchmarks.
  299. if (type_ == NULLT) {
  300. return;
  301. }
  302. #define FB_X(T) detail::Destroy::destroy(getAddress<T>())
  303. FB_DYNAMIC_APPLY(type_, FB_X);
  304. #undef FB_X
  305. type_ = NULLT;
  306. u_.nul = nullptr;
  307. }
  308. dynamic dynamic::merge_diff(const dynamic& source, const dynamic& target) {
  309. if (!source.isObject() || source.type() != target.type()) {
  310. return target;
  311. }
  312. dynamic diff = object;
  313. // added/modified keys
  314. for (const auto& pair : target.items()) {
  315. auto it = source.find(pair.first);
  316. if (it == source.items().end()) {
  317. diff[pair.first] = pair.second;
  318. } else {
  319. diff[pair.first] = merge_diff(source[pair.first], target[pair.first]);
  320. }
  321. }
  322. // removed keys
  323. for (const auto& pair : source.items()) {
  324. auto it = target.find(pair.first);
  325. if (it == target.items().end()) {
  326. diff[pair.first] = nullptr;
  327. }
  328. }
  329. return diff;
  330. }
  331. const dynamic* dynamic::get_ptr(json_pointer const& jsonPtr) const& {
  332. auto const& tokens = jsonPtr.tokens();
  333. if (tokens.empty()) {
  334. return this;
  335. }
  336. dynamic const* dyn = this;
  337. for (auto const& token : tokens) {
  338. if (!dyn) {
  339. return nullptr;
  340. }
  341. // special case of parsing "/": lookup key with empty name
  342. if (token.empty()) {
  343. if (dyn->isObject()) {
  344. dyn = dyn->get_ptr("");
  345. continue;
  346. }
  347. throw_exception<TypeError>("object", dyn->type());
  348. }
  349. if (auto* parray = dyn->get_nothrow<dynamic::Array>()) {
  350. if (token.size() > 1 && token.at(0) == '0') {
  351. throw std::invalid_argument(
  352. "Leading zero not allowed when indexing arrays");
  353. }
  354. // special case, always return non-existent
  355. if (token.size() == 1 && token.at(0) == '-') {
  356. dyn = nullptr;
  357. continue;
  358. }
  359. auto const idx = folly::to<size_t>(token);
  360. dyn = idx < parray->size() ? &(*parray)[idx] : nullptr;
  361. continue;
  362. }
  363. if (auto* pobject = dyn->get_nothrow<dynamic::ObjectImpl>()) {
  364. auto const it = pobject->find(token);
  365. dyn = it != pobject->end() ? &it->second : nullptr;
  366. continue;
  367. }
  368. throw_exception<TypeError>("object/array", dyn->type());
  369. }
  370. return dyn;
  371. }
  372. //////////////////////////////////////////////////////////////////////
  373. } // namespace folly