X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2Fdynamic.cpp;h=8c25017d3e4d85c7a740710fb185161e4b5963d5;hp=825cb775f048038bc5cfa01708831a07b2455fbf;hb=7fb9d9fd95490f2a5fbdd0d7bb8a44e5b95f2c48;hpb=ce64f0f685111ac24c7a321ea56d0c3524621df1 diff --git a/folly/dynamic.cpp b/folly/dynamic.cpp index 825cb775..8c25017d 100644 --- a/folly/dynamic.cpp +++ b/folly/dynamic.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,29 +16,311 @@ #include +#include +#include +#include +#include + namespace folly { ////////////////////////////////////////////////////////////////////// -#define DEF_TYPE(T, str, typen) \ - template<> char const dynamic::TypeInfo::name[] = str; \ - template<> dynamic::Type const dynamic::TypeInfo::type = typen +#define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \ + constexpr const char* dynamic::TypeInfo::name; \ + constexpr dynamic::Type dynamic::TypeInfo::type; \ + // -DEF_TYPE(void*, "null", dynamic::NULLT); -DEF_TYPE(bool, "boolean", dynamic::BOOL); -DEF_TYPE(fbstring, "string", dynamic::STRING); -DEF_TYPE(dynamic::Array, "array", dynamic::ARRAY); -DEF_TYPE(double, "double", dynamic::DOUBLE); -DEF_TYPE(int64_t, "int64", dynamic::INT64); -DEF_TYPE(dynamic::ObjectImpl, "object", dynamic::OBJECT); +FOLLY_DYNAMIC_DEF_TYPEINFO(std::nullptr_t) +FOLLY_DYNAMIC_DEF_TYPEINFO(bool) +FOLLY_DYNAMIC_DEF_TYPEINFO(std::string) +FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::Array) +FOLLY_DYNAMIC_DEF_TYPEINFO(double) +FOLLY_DYNAMIC_DEF_TYPEINFO(int64_t) +FOLLY_DYNAMIC_DEF_TYPEINFO(dynamic::ObjectImpl) -#undef DEF_TYPE +#undef FOLLY_DYNAMIC_DEF_TYPEINFO const char* dynamic::typeName() const { return typeName(type_); } -////////////////////////////////////////////////////////////////////// +TypeError::TypeError(const std::string& expected, dynamic::Type actual) + : std::runtime_error(sformat( + "TypeError: expected dynamic type `{}', but had type `{}'", + expected, + dynamic::typeName(actual))) {} + +TypeError::TypeError( + const std::string& expected, + dynamic::Type actual1, + dynamic::Type actual2) + : std::runtime_error(sformat( + "TypeError: expected dynamic types `{}, but had types `{}' and `{}'", + expected, + dynamic::typeName(actual1), + dynamic::typeName(actual2))) {} + +TypeError::~TypeError() = default; + +[[noreturn]] void throwTypeError_( + std::string const& expected, + dynamic::Type actual) { + throw TypeError(expected, actual); +} + +[[noreturn]] void throwTypeError_( + std::string const& expected, + dynamic::Type actual1, + dynamic::Type actual2) { + throw TypeError(expected, actual1, actual2); +} + +// This is a higher-order preprocessor macro to aid going from runtime +// types to the compile time type system. +#define FB_DYNAMIC_APPLY(type, apply) \ + do { \ + switch ((type)) { \ + case NULLT: \ + apply(std::nullptr_t); \ + break; \ + case ARRAY: \ + apply(Array); \ + break; \ + case BOOL: \ + apply(bool); \ + break; \ + case DOUBLE: \ + apply(double); \ + break; \ + case INT64: \ + apply(int64_t); \ + break; \ + case OBJECT: \ + apply(ObjectImpl); \ + break; \ + case STRING: \ + apply(std::string); \ + break; \ + default: \ + CHECK(0); \ + abort(); \ + } \ + } while (0) + +bool dynamic::operator<(dynamic const& o) const { + if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) { + throwTypeError_("object", type_); + } + if (type_ != o.type_) { + return type_ < o.type_; + } + +#define FB_X(T) return CompareOp::comp(*getAddress(), \ + *o.getAddress()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X +} + +bool dynamic::operator==(dynamic const& o) const { + if (type() != o.type()) { + if (isNumber() && o.isNumber()) { + auto& integ = isInt() ? *this : o; + auto& doubl = isInt() ? o : *this; + return integ.asInt() == doubl.asDouble(); + } + return false; + } + +#define FB_X(T) return *getAddress() == *o.getAddress(); + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X +} + +dynamic& dynamic::operator=(dynamic const& o) { + if (&o != this) { + if (type_ == o.type_) { +#define FB_X(T) *getAddress() = *o.getAddress() + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + } else { + destroy(); +#define FB_X(T) new (getAddress()) T(*o.getAddress()) + FB_DYNAMIC_APPLY(o.type_, FB_X); +#undef FB_X + type_ = o.type_; + } + } + return *this; +} + +dynamic& dynamic::operator=(dynamic&& o) noexcept { + if (&o != this) { + if (type_ == o.type_) { +#define FB_X(T) *getAddress() = std::move(*o.getAddress()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + } else { + destroy(); +#define FB_X(T) new (getAddress()) T(std::move(*o.getAddress())) + FB_DYNAMIC_APPLY(o.type_, FB_X); +#undef FB_X + type_ = o.type_; + } + } + return *this; +} + +dynamic& dynamic::operator[](dynamic const& k) & { + if (!isObject() && !isArray()) { + throwTypeError_("object/array", type()); + } + if (isArray()) { + return at(k); + } + auto& obj = get(); + auto ret = obj.insert({k, nullptr}); + return ret.first->second; +} + +dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& { + auto& obj = get(); + auto it = obj.find(k); + return it == obj.end() ? v : it->second; +} +dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& { + auto& obj = get(); + auto it = obj.find(k); + // Avoid clang bug with ternary + if (it == obj.end()) { + return std::move(v); + } else { + return it->second; + } } +dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) && { + auto& obj = get(); + auto it = obj.find(k); + // Avoid clang bug with ternary + if (it == obj.end()) { + return v; + } else { + return std::move(it->second); + } +} + +dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && { + auto& obj = get(); + auto it = obj.find(k); + return std::move(it == obj.end() ? v : it->second); +} + +const dynamic* dynamic::get_ptr(dynamic const& idx) const& { + if (auto* parray = get_nothrow()) { + if (!idx.isInt()) { + throwTypeError_("int64", idx.type()); + } + if (idx < 0 || idx >= parray->size()) { + return nullptr; + } + return &(*parray)[size_t(idx.asInt())]; + } else if (auto* pobject = get_nothrow()) { + auto it = pobject->find(idx); + if (it == pobject->end()) { + return nullptr; + } + return &it->second; + } else { + throwTypeError_("object/array", type()); + } +} + +[[noreturn]] static void throwOutOfRangeAtMissingKey(dynamic const& idx) { + auto msg = sformat("couldn't find key {} in dynamic object", idx.asString()); + std::__throw_out_of_range(msg.c_str()); +} + +dynamic const& dynamic::at(dynamic const& idx) const& { + if (auto* parray = get_nothrow()) { + if (!idx.isInt()) { + throwTypeError_("int64", idx.type()); + } + if (idx < 0 || idx >= parray->size()) { + std::__throw_out_of_range("out of range in dynamic array"); + } + return (*parray)[size_t(idx.asInt())]; + } else if (auto* pobject = get_nothrow()) { + auto it = pobject->find(idx); + if (it == pobject->end()) { + throwOutOfRangeAtMissingKey(idx); + } + return it->second; + } else { + throwTypeError_("object/array", type()); + } +} + +std::size_t dynamic::size() const { + if (auto* ar = get_nothrow()) { + return ar->size(); + } + if (auto* obj = get_nothrow()) { + return obj->size(); + } + if (auto* str = get_nothrow()) { + return str->size(); + } + throwTypeError_("array/object", type()); +} + +dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) { + auto& arr = get(); + return get().erase( + arr.begin() + (first - arr.begin()), + arr.begin() + (last - arr.begin())); +} + +std::size_t dynamic::hash() const { + switch (type()) { + case OBJECT: + case ARRAY: + case NULLT: + throwTypeError_("not null/object/array", type()); + case INT64: + return std::hash()(getInt()); + case DOUBLE: + return std::hash()(getDouble()); + case BOOL: + return std::hash()(getBool()); + case STRING: { + // keep it compatible with FBString + const auto& str = getString(); + return ::folly::hash::fnv32_buf(str.data(), str.size()); + } + } + assume_unreachable(); +} + +char const* dynamic::typeName(Type t) { +#define FB_X(T) return TypeInfo::name + FB_DYNAMIC_APPLY(t, FB_X); +#undef FB_X +} + +void dynamic::destroy() noexcept { + // This short-circuit speeds up some microbenchmarks. + if (type_ == NULLT) { + return; + } + +#define FB_X(T) detail::Destroy::destroy(getAddress()) + FB_DYNAMIC_APPLY(type_, FB_X); +#undef FB_X + type_ = NULLT; + u_.nul = nullptr; +} + +////////////////////////////////////////////////////////////////////// + +} // namespace folly