X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2Fdynamic-inl.h;h=cea7f2830d82c5ebf410480f5a77d7b9f04747e4;hb=06fe0874f2c3a786c79695e5b20c5f4580e05115;hp=c2d48eba36333c69585c18681c812c42cd2bb504;hpb=ed281068685319a31741fcecb7917362463b3fdb;p=folly.git diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index c2d48eba..cea7f283 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 Facebook, Inc. + * Copyright 2016 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -59,18 +59,10 @@ struct hash< ::folly::dynamic> { namespace folly { struct TypeError : std::runtime_error { - explicit TypeError(const std::string& expected, dynamic::Type actual) - : std::runtime_error(to("TypeError: expected dynamic " - "type `", expected, '\'', ", but had type `", - dynamic::typeName(actual), '\'')) - {} + explicit TypeError(const std::string& expected, dynamic::Type actual); explicit TypeError(const std::string& expected, - dynamic::Type actual1, dynamic::Type actual2) - : std::runtime_error(to("TypeError: expected dynamic " - "types `", expected, '\'', ", but had types `", - dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2), - '\'')) - {} + dynamic::Type actual1, dynamic::Type actual2); + ~TypeError(); }; @@ -197,9 +189,20 @@ private: dynamic val_; }; +inline void dynamic::array(EmptyArrayTag) {} + +template +inline dynamic dynamic::array(Args&& ...args) { + return dynamic(std::initializer_list{std::forward(args)...}, + PrivateTag()); +} + // This looks like a case for perfect forwarding, but our use of // std::initializer_list for constructing dynamic arrays makes it less // functional than doing this manually. + +// TODO(ott, 10300209): When the initializer_list constructor is gone, +// simplify this. inline dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); } inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic&& b) { return ObjectMaker(std::move(a), std::move(b)); @@ -254,6 +257,12 @@ struct dynamic::const_value_iterator ////////////////////////////////////////////////////////////////////// +inline dynamic::dynamic(void (*)(EmptyArrayTag)) + : type_(ARRAY) +{ + new (&u_.array) Array(); +} + inline dynamic::dynamic(ObjectMaker (*)()) : type_(OBJECT) { @@ -291,11 +300,20 @@ inline dynamic::dynamic(fbstring&& s) } inline dynamic::dynamic(std::initializer_list il) + : dynamic(std::move(il), PrivateTag()) { +} + +inline dynamic::dynamic(std::initializer_list il, PrivateTag) : type_(ARRAY) { new (&u_.array) Array(il.begin(), il.end()); } +inline dynamic& dynamic::operator=(std::initializer_list il) { + (*this) = dynamic(il, PrivateTag()); + return *this; +} + inline dynamic::dynamic(ObjectMaker&& maker) : type_(OBJECT) { @@ -392,18 +410,23 @@ inline double dynamic::asDouble() const { return asImpl(); } inline int64_t dynamic::asInt() const { return asImpl(); } inline bool dynamic::asBool() const { return asImpl(); } -inline const fbstring& dynamic::getString() const { return get(); } -inline double dynamic::getDouble() const { return get(); } -inline int64_t dynamic::getInt() const { return get(); } -inline bool dynamic::getBool() const { return get(); } +inline const fbstring& dynamic::getString() const& { return get(); } +inline double dynamic::getDouble() const& { return get(); } +inline int64_t dynamic::getInt() const& { return get(); } +inline bool dynamic::getBool() const& { return get(); } + +inline fbstring& dynamic::getString() & { return get(); } +inline double& dynamic::getDouble() & { return get(); } +inline int64_t& dynamic::getInt() & { return get(); } +inline bool& dynamic::getBool() & { return get(); } -inline fbstring& dynamic::getString() { return get(); } -inline double& dynamic::getDouble() { return get(); } -inline int64_t& dynamic::getInt() { return get(); } -inline bool& dynamic::getBool() { return get(); } +inline fbstring dynamic::getString() && { return std::move(get()); } +inline double dynamic::getDouble() && { return get(); } +inline int64_t dynamic::getInt() && { return get(); } +inline bool dynamic::getBool() && { return get(); } -inline const char* dynamic::data() const { return get().data(); } -inline const char* dynamic::c_str() const { return get().c_str(); } +inline const char* dynamic::data() const& { return get().data(); } +inline const char* dynamic::c_str() const& { return get().c_str(); } inline StringPiece dynamic::stringPiece() const { return get(); } template @@ -412,41 +435,12 @@ struct dynamic::CompareOp { }; template<> struct dynamic::CompareOp { - static bool comp(ObjectImpl const& a, ObjectImpl const& b) { + static bool comp(ObjectImpl const&, ObjectImpl const&) { // This code never executes; it is just here for the compiler. return false; } }; -inline bool dynamic::operator<(dynamic const& o) const { - if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) { - throw TypeError("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 -} - -inline 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 -} - inline dynamic& dynamic::operator+=(dynamic const& o) { if (type() == STRING && o.type() == STRING) { *getAddress() += *o.getAddress(); @@ -497,58 +491,12 @@ inline dynamic& dynamic::operator--() { return *this; } -inline dynamic& dynamic::operator=(dynamic const& o) { - if (&o != this) { - 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; -} - -inline dynamic& dynamic::operator=(dynamic&& o) noexcept { - if (&o != this) { - 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; -} - -inline dynamic& dynamic::operator[](dynamic const& k) { - if (!isObject() && !isArray()) { - throw TypeError("object/array", type()); - } - if (isArray()) { - return at(k); - } - auto& obj = get(); - auto ret = obj.insert({k, nullptr}); - return ret.first->second; -} - -inline dynamic const& dynamic::operator[](dynamic const& idx) const { +inline dynamic const& dynamic::operator[](dynamic const& idx) const& { return at(idx); } -inline 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; -} - -inline dynamic&& dynamic::getDefault(const dynamic& k, dynamic&& v) const { - auto& obj = get(); - auto it = obj.find(k); - if (it != obj.end()) { - v = it->second; - } - - return std::move(v); +inline dynamic dynamic::operator[](dynamic const& idx) && { + return std::move((*this)[idx]); } template inline dynamic& dynamic::setDefault(K&& k, V&& v) { @@ -557,53 +505,16 @@ template inline dynamic& dynamic::setDefault(K&& k, V&& v) { std::forward(v))).first->second; } -inline dynamic* dynamic::get_ptr(dynamic const& idx) { +inline dynamic* dynamic::get_ptr(dynamic const& idx) & { return const_cast(const_cast(this)->get_ptr(idx)); } -inline const dynamic* dynamic::get_ptr(dynamic const& idx) const { - if (auto* parray = get_nothrow()) { - if (!idx.isInt()) { - throw TypeError("int64", idx.type()); - } - if (idx >= parray->size()) { - return nullptr; - } - return &(*parray)[idx.asInt()]; - } else if (auto* pobject = get_nothrow()) { - auto it = pobject->find(idx); - if (it == pobject->end()) { - return nullptr; - } - return &it->second; - } else { - throw TypeError("object/array", type()); - } -} - -inline dynamic& dynamic::at(dynamic const& idx) { +inline dynamic& dynamic::at(dynamic const& idx) & { return const_cast(const_cast(this)->at(idx)); } -inline dynamic const& dynamic::at(dynamic const& idx) const { - if (auto* parray = get_nothrow()) { - if (!idx.isInt()) { - throw TypeError("int64", idx.type()); - } - if (idx >= parray->size()) { - throw std::out_of_range("out of range in dynamic array"); - } - return (*parray)[idx.asInt()]; - } else if (auto* pobject = get_nothrow()) { - auto it = pobject->find(idx); - if (it == pobject->end()) { - throw std::out_of_range(to( - "couldn't find key ", idx.asString(), " in dynamic object")); - } - return it->second; - } else { - throw TypeError("object/array", type()); - } +inline dynamic dynamic::at(dynamic const& idx) && { + return std::move(at(idx)); } inline bool dynamic::empty() const { @@ -613,19 +524,6 @@ inline bool dynamic::empty() const { return !size(); } -inline 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(); - } - throw TypeError("array/object", type()); -} - inline std::size_t dynamic::count(dynamic const& key) const { return find(key) != items().end(); } @@ -640,6 +538,41 @@ template inline void dynamic::insert(K&& key, V&& val) { rv.first->second = std::forward(val); } +inline void dynamic::update(const dynamic& mergeObj) { + if (!isObject() || !mergeObj.isObject()) { + throw TypeError("object", type(), mergeObj.type()); + } + + for (const auto& pair : mergeObj.items()) { + (*this)[pair.first] = pair.second; + } +} + +inline void dynamic::update_missing(const dynamic& mergeObj1) { + if (!isObject() || !mergeObj1.isObject()) { + throw TypeError("object", type(), mergeObj1.type()); + } + + // Only add if not already there + for (const auto& pair : mergeObj1.items()) { + if ((*this).find(pair.first) == (*this).items().end()) { + (*this)[pair.first] = pair.second; + } + } +} + +inline dynamic dynamic::merge( + const dynamic& mergeObj1, + const dynamic& mergeObj2) { + + // No checks on type needed here because they are done in update_missing + // Note that we do update_missing here instead of update() because + // it will prevent the extra writes that would occur with update() + auto ret = mergeObj2; + ret.update_missing(mergeObj1); + return ret; +} + inline std::size_t dynamic::erase(dynamic const& key) { auto& obj = get(); return obj.erase(key); @@ -653,14 +586,6 @@ inline dynamic::const_iterator dynamic::erase(const_iterator it) { return get().erase(arr.begin() + (it - arr.begin())); } -inline dynamic::const_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())); -} - inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) { return const_key_iterator(get().erase(it.base())); } @@ -692,64 +617,43 @@ inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first, } inline void dynamic::resize(std::size_t sz, dynamic const& c) { - auto& array = get(); - array.resize(sz, c); + auto& arr = get(); + arr.resize(sz, c); } inline void dynamic::push_back(dynamic const& v) { - auto& array = get(); - array.push_back(v); + auto& arr = get(); + arr.push_back(v); } inline void dynamic::push_back(dynamic&& v) { - auto& array = get(); - array.push_back(std::move(v)); + auto& arr = get(); + arr.push_back(std::move(v)); } inline void dynamic::pop_back() { - auto& array = get(); - array.pop_back(); -} - -inline std::size_t dynamic::hash() const { - switch (type()) { - case OBJECT: - case ARRAY: - case NULLT: - throw TypeError("not null/object/array", type()); - case INT64: - return std::hash()(asInt()); - case DOUBLE: - return std::hash()(asDouble()); - case BOOL: - return std::hash()(asBool()); - case STRING: - return std::hash()(asString()); - default: - CHECK(0); abort(); - } + auto& arr = get(); + arr.pop_back(); } ////////////////////////////////////////////////////////////////////// -template struct dynamic::TypeInfo { - static char const name[]; - static Type const type; -}; - -#define FB_DEC_TYPE(T) \ - template<> char const dynamic::TypeInfo::name[]; \ - template<> dynamic::Type const dynamic::TypeInfo::type +#define FOLLY_DYNAMIC_DEC_TYPEINFO(T, str, val) \ + template <> struct dynamic::TypeInfo { \ + static constexpr const char* name = str; \ + static constexpr dynamic::Type type = val; \ + }; \ + // -FB_DEC_TYPE(void*); -FB_DEC_TYPE(bool); -FB_DEC_TYPE(fbstring); -FB_DEC_TYPE(dynamic::Array); -FB_DEC_TYPE(double); -FB_DEC_TYPE(int64_t); -FB_DEC_TYPE(dynamic::ObjectImpl); +FOLLY_DYNAMIC_DEC_TYPEINFO(void*, "null", dynamic::NULLT) +FOLLY_DYNAMIC_DEC_TYPEINFO(bool, "boolean", dynamic::BOOL) +FOLLY_DYNAMIC_DEC_TYPEINFO(fbstring, "string", dynamic::STRING) +FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::Array, "array", dynamic::ARRAY) +FOLLY_DYNAMIC_DEC_TYPEINFO(double, "double", dynamic::DOUBLE) +FOLLY_DYNAMIC_DEC_TYPEINFO(int64_t, "int64", dynamic::INT64) +FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::ObjectImpl, "object", dynamic::OBJECT) -#undef FB_DEC_TYPE +#undef FOLLY_DYNAMIC_DEC_TYPEINFO template T dynamic::asImpl() const { @@ -765,7 +669,7 @@ T dynamic::asImpl() const { // Return a T* to our type, or null if we're not that type. template -T* dynamic::get_nothrow() noexcept { +T* dynamic::get_nothrow() & noexcept { if (type_ != TypeInfo::type) { return nullptr; } @@ -773,7 +677,7 @@ T* dynamic::get_nothrow() noexcept { } template -T const* dynamic::get_nothrow() const noexcept { +T const* dynamic::get_nothrow() const& noexcept { return const_cast(this)->get_nothrow(); } @@ -833,23 +737,6 @@ T const& dynamic::get() const { return const_cast(this)->get(); } -inline char const* dynamic::typeName(Type t) { -#define FB_X(T) return TypeInfo::name - FB_DYNAMIC_APPLY(t, FB_X); -#undef FB_X -} - -inline 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; -} - ////////////////////////////////////////////////////////////////////// /* @@ -862,6 +749,16 @@ struct dynamic::PrintImpl { out << t; } }; +// Otherwise, null, being (void*)0, would print as 0. +template <> +struct dynamic::PrintImpl { + static void print(dynamic const& /* d */, + std::ostream& out, + void* const& nul) { + DCHECK_EQ((void*)0, nul); + out << "null"; + } +}; template<> struct dynamic::PrintImpl { static void print(dynamic const& d,