/*
- * Copyright 2015 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.
#include <folly/dynamic.h>
+#include <folly/Assume.h>
+#include <folly/Format.h>
+#include <folly/Hash.h>
+#include <folly/portability/BitsFunctexcept.h>
+
namespace folly {
//////////////////////////////////////////////////////////////////////
-#define DEF_TYPE(T, str, typen) \
- template<> char const dynamic::TypeInfo<T>::name[] = str; \
- template<> dynamic::Type const dynamic::TypeInfo<T>::type = typen
+#define FOLLY_DYNAMIC_DEF_TYPEINFO(T) \
+ constexpr const char* dynamic::TypeInfo<T>::name; \
+ constexpr dynamic::Type dynamic::TypeInfo<T>::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(to<std::string>("TypeError: expected dynamic "
- "type `", expected, '\'', ", but had type `",
- dynamic::typeName(actual), '\''))
-{}
-
-TypeError::TypeError(const std::string& expected,
- dynamic::Type actual1, dynamic::Type actual2)
- : std::runtime_error(to<std::string>("TypeError: expected dynamic "
- "types `", expected, '\'', ", but had types `",
- dynamic::typeName(actual1), "' and `", dynamic::typeName(actual2),
- '\''))
-{}
+ : 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(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)
bool dynamic::operator<(dynamic const& o) const {
if (UNLIKELY(type_ == OBJECT || o.type_ == OBJECT)) {
- throw TypeError("object", type_);
+ throwTypeError_("object", type_);
}
if (type_ != o.type_) {
return type_ < o.type_;
return *this;
}
-dynamic& dynamic::operator[](dynamic const& k) {
+dynamic& dynamic::operator[](dynamic const& k) & {
if (!isObject() && !isArray()) {
- throw TypeError("object/array", type());
+ throwTypeError_("object/array", type());
}
if (isArray()) {
return at(k);
return ret.first->second;
}
-dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const {
+dynamic dynamic::getDefault(const dynamic& k, const dynamic& v) const& {
auto& obj = get<ObjectImpl>();
auto it = obj.find(k);
return it == obj.end() ? v : it->second;
}
-dynamic&& dynamic::getDefault(const dynamic& k, dynamic&& v) const {
+dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) const& {
+ auto& obj = get<ObjectImpl>();
+ 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<ObjectImpl>();
auto it = obj.find(k);
- if (it != obj.end()) {
- v = it->second;
+ // Avoid clang bug with ternary
+ if (it == obj.end()) {
+ return v;
+ } else {
+ return std::move(it->second);
}
+}
- return std::move(v);
+dynamic dynamic::getDefault(const dynamic& k, dynamic&& v) && {
+ auto& obj = get<ObjectImpl>();
+ auto it = obj.find(k);
+ return std::move(it == obj.end() ? v : it->second);
}
-const dynamic* dynamic::get_ptr(dynamic const& idx) const {
+const dynamic* dynamic::get_ptr(dynamic const& idx) const& {
if (auto* parray = get_nothrow<Array>()) {
if (!idx.isInt()) {
- throw TypeError("int64", idx.type());
+ throwTypeError_("int64", idx.type());
}
if (idx < 0 || idx >= parray->size()) {
return nullptr;
}
- return &(*parray)[idx.asInt()];
+ return &(*parray)[size_t(idx.asInt())];
} else if (auto* pobject = get_nothrow<ObjectImpl>()) {
auto it = pobject->find(idx);
if (it == pobject->end()) {
}
return &it->second;
} else {
- throw TypeError("object/array", type());
+ throwTypeError_("object/array", type());
}
}
-dynamic const& dynamic::at(dynamic const& idx) const {
+[[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<Array>()) {
if (!idx.isInt()) {
- throw TypeError("int64", idx.type());
+ throwTypeError_("int64", idx.type());
}
if (idx < 0 || idx >= parray->size()) {
- throw std::out_of_range("out of range in dynamic array");
+ std::__throw_out_of_range("out of range in dynamic array");
}
- return (*parray)[idx.asInt()];
+ return (*parray)[size_t(idx.asInt())];
} else if (auto* pobject = get_nothrow<ObjectImpl>()) {
auto it = pobject->find(idx);
if (it == pobject->end()) {
- throw std::out_of_range(to<std::string>(
- "couldn't find key ", idx.asString(), " in dynamic object"));
+ throwOutOfRangeAtMissingKey(idx);
}
return it->second;
} else {
- throw TypeError("object/array", type());
+ throwTypeError_("object/array", type());
}
}
if (auto* obj = get_nothrow<ObjectImpl>()) {
return obj->size();
}
- if (auto* str = get_nothrow<fbstring>()) {
+ if (auto* str = get_nothrow<std::string>()) {
return str->size();
}
- throw TypeError("array/object", type());
+ throwTypeError_("array/object", type());
}
-dynamic::const_iterator
-dynamic::erase(const_iterator first, const_iterator last) {
+dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
auto& arr = get<Array>();
return get<Array>().erase(
arr.begin() + (first - arr.begin()),
case OBJECT:
case ARRAY:
case NULLT:
- throw TypeError("not null/object/array", type());
+ throwTypeError_("not null/object/array", type());
case INT64:
- return std::hash<int64_t>()(asInt());
+ return std::hash<int64_t>()(getInt());
case DOUBLE:
- return std::hash<double>()(asDouble());
+ return std::hash<double>()(getDouble());
case BOOL:
- return std::hash<bool>()(asBool());
- case STRING:
- return std::hash<fbstring>()(asString());
- default:
- CHECK(0); abort();
+ return std::hash<bool>()(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) {