X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2Fdynamic-inl.h;h=fe65cbbc995709e6f8f3bb590624c0c3c25cf0c5;hp=bae776a246f52499b26eea2e7d67d04a35f6b3cb;hb=b1eb5b684447a4a58516d5e8ffbd328aab1e477d;hpb=bbe8ee53ab2e73e900751611e09cd448d58bb07a diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index bae776a2..fe65cbbc 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 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. @@ -14,15 +14,14 @@ * limitations under the License. */ -#ifndef FOLLY_DYNAMIC_INL_H_ -#define FOLLY_DYNAMIC_INL_H_ +#pragma once #include #include #include -#include "folly/Likely.h" -#include "folly/Conv.h" -#include "folly/Format.h" +#include +#include +#include ////////////////////////////////////////////////////////////////////// @@ -41,36 +40,45 @@ struct hash< ::folly::dynamic> { // 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(void*); 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(fbstring); break; \ - default: CHECK(0); abort(); \ - } \ -} while (0) +#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) ////////////////////////////////////////////////////////////////////// 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(); }; @@ -84,41 +92,6 @@ namespace detail { template static void destroy(T* t) { t->~T(); } }; - /* - * The enable_if junk here is necessary to avoid ambiguous - * conversions relating to bool and double when you implicitly - * convert an int or long to a dynamic. - */ - template struct ConversionHelper; - template - struct ConversionHelper< - T, - typename std::enable_if< - std::is_integral::value && !std::is_same::value - >::type - > { - typedef int64_t type; - }; - template - struct ConversionHelper< - T, - typename std::enable_if< - (!std::is_integral::value || std::is_same::value) && - !std::is_same::value - >::type - > { - typedef T type; - }; - template - struct ConversionHelper< - T, - typename std::enable_if< - std::is_same::value - >::type - > { - typedef void* type; - }; - /* * Helper for implementing numeric conversions in operators on * numbers. Just promotes to double when one of the arguments is @@ -162,12 +135,7 @@ struct dynamic::ObjectMaker { friend struct dynamic; explicit ObjectMaker() : val_(dynamic::object) {} - explicit ObjectMaker(dynamic const& key, dynamic val) - : val_(dynamic::object) - { - val_.insert(key, std::move(val)); - } - explicit ObjectMaker(dynamic&& key, dynamic val) + explicit ObjectMaker(dynamic key, dynamic val) : val_(dynamic::object) { val_.insert(std::move(key), std::move(val)); @@ -180,15 +148,10 @@ struct dynamic::ObjectMaker { ObjectMaker& operator=(ObjectMaker const&) = delete; ObjectMaker& operator=(ObjectMaker&&) = delete; - // These return rvalue-references instead of lvalue-references to allow - // constructs like this to moved instead of copied: + // This returns an rvalue-reference instead of an lvalue-reference + // to allow constructs like this to moved instead of copied: // dynamic a = dynamic::object("a", "b")("c", "d") - ObjectMaker&& operator()(dynamic const& key, dynamic val) { - val_.insert(key, std::move(val)); - return std::move(*this); - } - - ObjectMaker&& operator()(dynamic&& key, dynamic val) { + ObjectMaker&& operator()(dynamic key, dynamic val) { val_.insert(std::move(key), std::move(val)); return std::move(*this); } @@ -197,30 +160,56 @@ private: dynamic val_; }; -// 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. +inline void dynamic::array(EmptyArrayTag) {} + +template +inline dynamic dynamic::array(Args&& ...args) { + return dynamic(Array{std::forward(args)...}); +} + inline dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); } -inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic&& b) { +inline dynamic::ObjectMaker dynamic::object(dynamic a, dynamic b) { return ObjectMaker(std::move(a), std::move(b)); } -inline dynamic::ObjectMaker dynamic::object(dynamic const& a, dynamic&& b) { - return ObjectMaker(a, std::move(b)); -} -inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic const& b) { - return ObjectMaker(std::move(a), b); -} -inline dynamic::ObjectMaker -dynamic::object(dynamic const& a, dynamic const& b) { - return ObjectMaker(a, b); -} ////////////////////////////////////////////////////////////////////// +struct dynamic::item_iterator : boost::iterator_adaptor< + dynamic::item_iterator, + dynamic::ObjectImpl::iterator> { + /* implicit */ item_iterator(base_type b) : iterator_adaptor_(b) {} + + using object_type = dynamic::ObjectImpl; + + private: + friend class boost::iterator_core_access; +}; + +struct dynamic::value_iterator : boost::iterator_adaptor< + dynamic::value_iterator, + dynamic::ObjectImpl::iterator, + dynamic> { + /* implicit */ value_iterator(base_type b) : iterator_adaptor_(b) {} + + using object_type = dynamic::ObjectImpl; + + private: + dynamic& dereference() const { + return base_reference()->second; + } + friend class boost::iterator_core_access; +}; + struct dynamic::const_item_iterator : boost::iterator_adaptor { /* implicit */ const_item_iterator(base_type b) : iterator_adaptor_(b) { } + /* implicit */ const_item_iterator(item_iterator i) + : iterator_adaptor_(i.base()) {} + /* implicit */ const_item_iterator(dynamic::ObjectImpl::iterator i) + : iterator_adaptor_(i) {} + + using object_type = dynamic::ObjectImpl const; private: friend class boost::iterator_core_access; @@ -232,6 +221,8 @@ struct dynamic::const_key_iterator dynamic const> { /* implicit */ const_key_iterator(base_type b) : iterator_adaptor_(b) { } + using object_type = dynamic::ObjectImpl const; + private: dynamic const& dereference() const { return base_reference()->first; @@ -244,6 +235,12 @@ struct dynamic::const_value_iterator dynamic::ObjectImpl::const_iterator, dynamic const> { /* implicit */ const_value_iterator(base_type b) : iterator_adaptor_(b) { } + /* implicit */ const_value_iterator(value_iterator i) + : iterator_adaptor_(i.base()) {} + /* implicit */ const_value_iterator(dynamic::ObjectImpl::iterator i) + : iterator_adaptor_(i) {} + + using object_type = dynamic::ObjectImpl const; private: dynamic const& dereference() const { @@ -254,28 +251,38 @@ struct dynamic::const_value_iterator ////////////////////////////////////////////////////////////////////// +inline dynamic::dynamic() : dynamic(nullptr) {} + +inline dynamic::dynamic(std::nullptr_t) : type_(NULLT) {} + +inline dynamic::dynamic(void (*)(EmptyArrayTag)) + : type_(ARRAY) +{ + new (&u_.array) Array(); +} + inline dynamic::dynamic(ObjectMaker (*)()) : type_(OBJECT) { new (getAddress()) ObjectImpl(); } -inline dynamic::dynamic(char const* s) +inline dynamic::dynamic(StringPiece s) : type_(STRING) { - new (&u_.string) fbstring(s); + new (&u_.string) std::string(s.data(), s.size()); } -inline dynamic::dynamic(std::string const& s) +inline dynamic::dynamic(char const* s) : type_(STRING) { - new (&u_.string) fbstring(s); + new (&u_.string) std::string(s); } -inline dynamic::dynamic(std::initializer_list il) - : type_(ARRAY) +inline dynamic::dynamic(std::string s) + : type_(STRING) { - new (&u_.array) Array(il.begin(), il.end()); + new (&u_.string) std::string(std::move(s)); } inline dynamic::dynamic(ObjectMaker&& maker) @@ -291,22 +298,43 @@ inline dynamic::dynamic(dynamic const& o) *this = o; } -inline dynamic::dynamic(dynamic&& o) +inline dynamic::dynamic(dynamic&& o) noexcept : type_(NULLT) { *this = std::move(o); } -inline dynamic::~dynamic() { destroy(); } +inline dynamic::~dynamic() noexcept { destroy(); } -template +// Integral types except bool convert to int64_t, float types to double. +template +struct dynamic::NumericTypeHelper< + T, typename std::enable_if::value>::type> { + static_assert( + !kIsObjC || sizeof(T) > sizeof(char), + "char-sized types are ambiguous in objc; cast to bool or wider type"); + using type = int64_t; +}; +template <> +struct dynamic::NumericTypeHelper { + using type = bool; +}; +template <> +struct dynamic::NumericTypeHelper { + using type = double; +}; +template <> +struct dynamic::NumericTypeHelper { + using type = double; +}; + +template::type */> dynamic::dynamic(T t) { - typedef typename detail::ConversionHelper::type U; - type_ = TypeInfo::type; - new (getAddress()) U(std::move(t)); + type_ = TypeInfo::type; + new (getAddress()) NumericType(NumericType(t)); } -template +template dynamic::dynamic(Iterator first, Iterator last) : type_(ARRAY) { @@ -322,12 +350,20 @@ inline dynamic::const_iterator dynamic::end() const { return get().end(); } +inline dynamic::iterator dynamic::begin() { + return get().begin(); +} +inline dynamic::iterator dynamic::end() { + return get().end(); +} + template struct dynamic::IterableProxy { - typedef It const_iterator; + typedef It iterator; typedef typename It::value_type value_type; + typedef typename It::object_type object_type; - /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { } + /* implicit */ IterableProxy(object_type* o) : o_(o) {} It begin() const { return o_->begin(); @@ -338,7 +374,7 @@ struct dynamic::IterableProxy { } private: - const dynamic::ObjectImpl* o_; + object_type* o_; }; inline dynamic::IterableProxy dynamic::keys() @@ -356,23 +392,86 @@ inline dynamic::IterableProxy dynamic::items() return &(get()); } -inline bool dynamic::isString() const { return get_nothrow(); } -inline bool dynamic::isObject() const { return get_nothrow(); } -inline bool dynamic::isBool() const { return get_nothrow(); } -inline bool dynamic::isArray() const { return get_nothrow(); } -inline bool dynamic::isDouble() const { return get_nothrow(); } -inline bool dynamic::isInt() const { return get_nothrow(); } -inline bool dynamic::isNull() const { return get_nothrow(); } -inline bool dynamic::isNumber() const { return isInt() || isDouble(); } +inline dynamic::IterableProxy dynamic::values() { + return &(get()); +} + +inline dynamic::IterableProxy dynamic::items() { + return &(get()); +} + +inline bool dynamic::isString() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isObject() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isBool() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isArray() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isDouble() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isInt() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isNull() const { + return get_nothrow() != nullptr; +} +inline bool dynamic::isNumber() const { + return isInt() || isDouble(); +} inline dynamic::Type dynamic::type() const { return type_; } -inline fbstring dynamic::asString() const { return asImpl(); } -inline double dynamic::asDouble() const { return asImpl(); } -inline int64_t dynamic::asInt() const { return asImpl(); } -inline bool dynamic::asBool() const { return asImpl(); } +inline std::string dynamic::asString() const { + return asImpl(); +} +inline double dynamic::asDouble() const { + return asImpl(); +} +inline int64_t dynamic::asInt() const { + return asImpl(); +} +inline bool dynamic::asBool() const { + return asImpl(); +} + +inline const std::string& 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 std::string& dynamic::getString()& { + return get(); +} +inline double& dynamic::getDouble() & { return get(); } +inline int64_t& dynamic::getInt() & { return get(); } +inline bool& dynamic::getBool() & { return get(); } + +inline std::string&& 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 StringPiece dynamic::stringPiece() const { + return get(); +} template struct dynamic::CompareOp { @@ -380,44 +479,21 @@ 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; +template<> +struct dynamic::CompareOp { + static bool comp(std::nullptr_t const&, std::nullptr_t const&) { + return true; } - -#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(); + *getAddress() += *o.getAddress(); return *this; } *this = detail::numericOp(*this, o); @@ -465,92 +541,41 @@ 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 const& dynamic::operator[](dynamic const& idx) const& { + return at(idx); } -inline dynamic& dynamic::operator=(dynamic&& o) { - 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& idx) && { + return std::move((*this)[idx]); } -inline dynamic& dynamic::operator[](dynamic const& k) { - if (!isObject() && !isArray()) { - throw TypeError("object/array", type()); - } - if (isArray()) { - return at(k); - } +template inline dynamic& dynamic::setDefault(K&& k, V&& v) { auto& obj = get(); - auto ret = obj.insert({k, nullptr}); - return ret.first->second; -} - -inline dynamic const& dynamic::operator[](dynamic const& idx) const { - return at(idx); + return obj.insert(std::make_pair(std::forward(k), + std::forward(v))).first->second; } -inline dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const { +template inline dynamic& dynamic::setDefault(K&& k, dynamic&& v) { auto& obj = get(); - auto it = obj.find(k); - return it == obj.end() ? v : it->second; + return obj.insert(std::make_pair(std::forward(k), + std::move(v))).first->second; } -inline dynamic&& dynamic::getDefault(const dynamic& k, dynamic&& v) const { +template inline dynamic& dynamic::setDefault(K&& k, const dynamic& v) { auto& obj = get(); - auto it = obj.find(k); - if (it != obj.end()) { - v = it->second; - } - - return std::move(v); + return obj.insert(std::make_pair(std::forward(k), v)).first->second; } -template inline dynamic& dynamic::setDefault(K&& k, V&& v) { - auto& obj = get(); - return obj.insert(std::make_pair(std::forward(k), - std::forward(v))).first->second; +inline dynamic* dynamic::get_ptr(dynamic const& idx) & { + return const_cast(const_cast(this)->get_ptr(idx)); } -inline dynamic const& dynamic::at(dynamic const& idx) const { - return const_cast(this)->at(idx); +inline dynamic& dynamic::at(dynamic const& idx) & { + return const_cast(const_cast(this)->at(idx)); } -inline dynamic& dynamic::at(dynamic const& idx) { - if (!isObject() && !isArray()) { - throw TypeError("object/array", type()); - } - - if (auto* parray = get_nothrow()) { - if (idx >= parray->size()) { - throw std::out_of_range("out of range in dynamic array"); - } - if (!idx.isInt()) { - throw TypeError("int64", idx.type()); - } - return (*parray)[idx.asInt()]; - } - - assert(get_nothrow()); - auto it = find(idx); - if (it == items().end()) { - throw std::out_of_range(to( - "couldn't find key ", idx.asString(), " in dynamic object")); - } - return const_cast(it->second); +inline dynamic&& dynamic::at(dynamic const& idx) && { + return std::move(at(idx)); } inline bool dynamic::empty() const { @@ -560,38 +585,56 @@ 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(); + return find(key) != items().end() ? 1u : 0u; } inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const { return get().find(key); } +inline dynamic::item_iterator dynamic::find(dynamic const& key) { + return get().find(key); +} template inline void dynamic::insert(K&& key, V&& val) { auto& obj = get(); - auto rv = obj.insert(std::make_pair(std::forward(key), - std::forward(val))); - if (!rv.second) { - // note, the second use of std:forward(val) is only correct - // if the first one did not result in a move. obj[key] = val - // would be preferrable but doesn't compile because dynamic - // is (intentionally) not default constructable - rv.first->second = std::forward(val); + auto rv = obj.insert({ std::forward(key), nullptr }); + 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) { @@ -599,7 +642,7 @@ inline std::size_t dynamic::erase(dynamic const& key) { return obj.erase(key); } -inline dynamic::const_iterator dynamic::erase(const_iterator it) { +inline dynamic::iterator dynamic::erase(const_iterator it) { auto& arr = get(); // std::vector doesn't have an erase method that works on const iterators, // even though the standard says it should, so this hack converts to a @@ -607,103 +650,79 @@ 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())); } -inline dynamic::const_key_iterator dynamic::erase(const_key_iterator first, - const_key_iterator last) { +inline dynamic::const_key_iterator dynamic::erase( + const_key_iterator first, + const_key_iterator last) { return const_key_iterator(get().erase(first.base(), last.base())); } -inline dynamic::const_value_iterator dynamic::erase(const_value_iterator it) { - return const_value_iterator(get().erase(it.base())); +inline dynamic::value_iterator dynamic::erase(const_value_iterator it) { + return value_iterator(get().erase(it.base())); } -inline dynamic::const_value_iterator dynamic::erase(const_value_iterator first, - const_value_iterator last) { - return const_value_iterator(get().erase(first.base(), - last.base())); +inline dynamic::value_iterator dynamic::erase( + const_value_iterator first, + const_value_iterator last) { + return value_iterator(get().erase(first.base(), last.base())); } -inline dynamic::const_item_iterator dynamic::erase(const_item_iterator it) { - return const_item_iterator(get().erase(it.base())); +inline dynamic::item_iterator dynamic::erase(const_item_iterator it) { + return item_iterator(get().erase(it.base())); } -inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first, - const_item_iterator last) { - return const_item_iterator(get().erase(first.base(), - last.base())); +inline dynamic::item_iterator dynamic::erase( + const_item_iterator first, + const_item_iterator last) { + return item_iterator(get().erase(first.base(), last.base())); } 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; -}; +inline dynamic::dynamic(Array&& r) : type_(ARRAY) { + new (&u_.array) Array(std::move(r)); +} -#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(std::nullptr_t, "null", dynamic::NULLT) +FOLLY_DYNAMIC_DEC_TYPEINFO(bool, "boolean", dynamic::BOOL) +FOLLY_DYNAMIC_DEC_TYPEINFO(std::string, "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 { @@ -711,7 +730,8 @@ T dynamic::asImpl() const { case INT64: return to(*get_nothrow()); case DOUBLE: return to(*get_nothrow()); case BOOL: return to(*get_nothrow()); - case STRING: return to(*get_nothrow()); + case STRING: + return to(*get_nothrow()); default: throw TypeError("int/double/bool/string", type()); } @@ -719,7 +739,7 @@ T dynamic::asImpl() const { // Return a T* to our type, or null if we're not that type. template -T* dynamic::get_nothrow() { +T* dynamic::get_nothrow() & noexcept { if (type_ != TypeInfo::type) { return nullptr; } @@ -727,40 +747,43 @@ T* dynamic::get_nothrow() { } template -T const* dynamic::get_nothrow() const { +T const* dynamic::get_nothrow() const& noexcept { return const_cast(this)->get_nothrow(); } // Return T* for where we can put a T, without type checking. (Memory // might be uninitialized, even.) template -T* dynamic::getAddress() { +T* dynamic::getAddress() noexcept { return GetAddrImpl::get(u_); } template -T const* dynamic::getAddress() const { +T const* dynamic::getAddress() const noexcept { return const_cast(this)->getAddress(); } template struct dynamic::GetAddrImpl {}; -template<> struct dynamic::GetAddrImpl { - static void** get(Data& d) { return &d.nul; } +template<> struct dynamic::GetAddrImpl { + static std::nullptr_t* get(Data& d) noexcept { return &d.nul; } }; template<> struct dynamic::GetAddrImpl { - static Array* get(Data& d) { return &d.array; } + static Array* get(Data& d) noexcept { return &d.array; } }; template<> struct dynamic::GetAddrImpl { - static bool* get(Data& d) { return &d.boolean; } + static bool* get(Data& d) noexcept { return &d.boolean; } }; template<> struct dynamic::GetAddrImpl { - static int64_t* get(Data& d) { return &d.integer; } + static int64_t* get(Data& d) noexcept { return &d.integer; } }; template<> struct dynamic::GetAddrImpl { - static double* get(Data& d) { return &d.doubl; } + static double* get(Data& d) noexcept { return &d.doubl; } }; -template<> struct dynamic::GetAddrImpl { - static fbstring* get(Data& d) { return &d.string; } +template <> +struct dynamic::GetAddrImpl { + static std::string* get(Data& d) noexcept { + return &d.string; + } }; template<> struct dynamic::GetAddrImpl { static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer), @@ -768,7 +791,7 @@ template<> struct dynamic::GetAddrImpl { " amount of space depending on its template parameters. This is " "weird. Make objectBuffer bigger if you want to compile dynamic."); - static ObjectImpl* get(Data& d) { + static ObjectImpl* get(Data& d) noexcept { void* data = &d.objectBuffer; return static_cast(data); } @@ -787,23 +810,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() { - // 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; -} - ////////////////////////////////////////////////////////////////////// /* @@ -816,6 +822,15 @@ 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, + std::nullptr_t const&) { + out << "null"; + } +}; template<> struct dynamic::PrintImpl { static void print(dynamic const& d, @@ -865,7 +880,7 @@ class FormatValue { FormatValue(val_.asInt()).format(arg, cb); break; case dynamic::STRING: - FormatValue(val_.asString()).format(arg, cb); + FormatValue(val_.asString()).format(arg, cb); break; case dynamic::DOUBLE: FormatValue(val_.asDouble()).format(arg, cb); @@ -874,7 +889,7 @@ class FormatValue { FormatValue(val_.at(arg.splitIntKey())).format(arg, cb); break; case dynamic::OBJECT: - FormatValue(val_.at(arg.splitKey().toFbstring())).format(arg, cb); + FormatValue(val_.at(arg.splitKey().toString())).format(arg, cb); break; } } @@ -883,8 +898,51 @@ class FormatValue { const dynamic& val_; }; -} +template +class FormatValue> { + public: + explicit FormatValue( + const detail::DefaultValueWrapper& val) + : val_(val) { } -#undef FB_DYNAMIC_APPLY + template + void format(FormatArg& arg, FormatCallback& cb) const { + auto& c = val_.container; + switch (c.type()) { + case dynamic::NULLT: + case dynamic::BOOL: + case dynamic::INT64: + case dynamic::STRING: + case dynamic::DOUBLE: + FormatValue(c).format(arg, cb); + break; + case dynamic::ARRAY: + { + int key = arg.splitIntKey(); + if (key >= 0 && size_t(key) < c.size()) { + FormatValue(c.at(key)).format(arg, cb); + } else{ + FormatValue(val_.defaultValue).format(arg, cb); + } + } + break; + case dynamic::OBJECT: + { + auto pos = c.find(arg.splitKey()); + if (pos != c.items().end()) { + FormatValue(pos->second).format(arg, cb); + } else { + FormatValue(val_.defaultValue).format(arg, cb); + } + } + break; + } + } + + private: + const detail::DefaultValueWrapper& val_; +}; + +} // namespaces -#endif +#undef FB_DYNAMIC_APPLY