Copyright 2014->2015
[folly.git] / folly / dynamic-inl.h
index 4a7c3c54194fe30ec985a27ea1420cf2a0c14ea0..3bd5689cd4ad40cda792c38ea5ca2d59d0d37c8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2015 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,9 +20,9 @@
 #include <functional>
 #include <boost/iterator/iterator_adaptor.hpp>
 #include <boost/iterator/iterator_facade.hpp>
-#include "folly/Likely.h"
-#include "folly/Conv.h"
-#include "folly/Format.h"
+#include <folly/Likely.h>
+#include <folly/Conv.h>
+#include <folly/Format.h>
 
 //////////////////////////////////////////////////////////////////////
 
@@ -58,6 +58,22 @@ struct hash< ::folly::dynamic> {
 
 namespace folly {
 
+struct TypeError : std::runtime_error {
+  explicit 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), '\''))
+  {}
+  explicit 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),
+        '\''))
+  {}
+};
+
+
 //////////////////////////////////////////////////////////////////////
 
 namespace detail {
@@ -128,21 +144,6 @@ namespace detail {
 
 //////////////////////////////////////////////////////////////////////
 
-struct TypeError : std::runtime_error {
-  explicit 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), '\''))
-  {}
-  explicit 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),
-        '\''))
-  {}
-};
-
 /*
  * We're doing this instead of a simple member typedef to avoid the
  * undefined behavior of parameterizing std::unordered_map<> with an
@@ -196,9 +197,22 @@ private:
   dynamic val_;
 };
 
-template<class... Args>
-inline dynamic::ObjectMaker dynamic::object(Args&&... args) {
-  return dynamic::ObjectMaker(std::forward<Args>(args)...);
+// 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 dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); }
+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);
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -246,6 +260,12 @@ inline dynamic::dynamic(ObjectMaker (*)())
   new (getAddress<ObjectImpl>()) ObjectImpl();
 }
 
+inline dynamic::dynamic(StringPiece s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s.data(), s.size());
+}
+
 inline dynamic::dynamic(char const* s)
   : type_(STRING)
 {
@@ -258,6 +278,18 @@ inline dynamic::dynamic(std::string const& s)
   new (&u_.string) fbstring(s);
 }
 
+inline dynamic::dynamic(fbstring const& s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(s);
+}
+
+inline dynamic::dynamic(fbstring&& s)
+  : type_(STRING)
+{
+  new (&u_.string) fbstring(std::move(s));
+}
+
 inline dynamic::dynamic(std::initializer_list<dynamic> il)
   : type_(ARRAY)
 {
@@ -277,13 +309,13 @@ 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<class T>
 dynamic::dynamic(T t) {
@@ -311,6 +343,7 @@ inline dynamic::const_iterator dynamic::end() const {
 template <class It>
 struct dynamic::IterableProxy {
   typedef It const_iterator;
+  typedef typename It::value_type value_type;
 
   /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { }
 
@@ -359,6 +392,20 @@ inline double   dynamic::asDouble() const { return asImpl<double>(); }
 inline int64_t  dynamic::asInt()    const { return asImpl<int64_t>(); }
 inline bool     dynamic::asBool()   const { return asImpl<bool>(); }
 
+inline const fbstring& dynamic::getString() const { return get<fbstring>(); }
+inline double          dynamic::getDouble() const { return get<double>(); }
+inline int64_t         dynamic::getInt()    const { return get<int64_t>(); }
+inline bool            dynamic::getBool()   const { return get<bool>(); }
+
+inline fbstring& dynamic::getString() { return get<fbstring>(); }
+inline double&   dynamic::getDouble() { return get<double>(); }
+inline int64_t&  dynamic::getInt()    { return get<int64_t>(); }
+inline bool&     dynamic::getBool()   { return get<bool>(); }
+
+inline const char* dynamic::data()  const { return get<fbstring>().data();  }
+inline const char* dynamic::c_str() const { return get<fbstring>().c_str(); }
+inline StringPiece dynamic::stringPiece() const { return get<fbstring>(); }
+
 template<class T>
 struct dynamic::CompareOp {
   static bool comp(T const& a, T const& b) { return a < b; }
@@ -461,7 +508,7 @@ inline dynamic& dynamic::operator=(dynamic const& o) {
   return *this;
 }
 
-inline dynamic& dynamic::operator=(dynamic&& o) {
+inline dynamic& dynamic::operator=(dynamic&& o) noexcept {
   if (&o != this) {
     destroy();
 #define FB_X(T) new (getAddress<T>()) T(std::move(*o.getAddress<T>()))
@@ -510,33 +557,53 @@ template<class K, class V> inline dynamic& dynamic::setDefault(K&& k, V&& v) {
                                    std::forward<V>(v))).first->second;
 }
 
-inline dynamic const& dynamic::at(dynamic const& idx) const {
-  return const_cast<dynamic*>(this)->at(idx);
+inline dynamic* dynamic::get_ptr(dynamic const& idx) {
+  return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx));
 }
 
-inline dynamic& dynamic::at(dynamic const& idx) {
-  if (!isObject() && !isArray()) {
+inline const dynamic* dynamic::get_ptr(dynamic const& idx) const {
+  if (auto* parray = get_nothrow<Array>()) {
+    if (!idx.isInt()) {
+      throw TypeError("int64", idx.type());
+    }
+    if (idx >= parray->size()) {
+      return nullptr;
+    }
+    return &(*parray)[idx.asInt()];
+  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
+    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) {
+  return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx));
+}
 
+inline dynamic const& dynamic::at(dynamic const& idx) const {
   if (auto* parray = get_nothrow<Array>()) {
-    if (idx >= parray->size()) {
-      throw std::out_of_range("out of range in dynamic array");
-    }
     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<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"));
+    }
+    return it->second;
+  } else {
+    throw TypeError("object/array", type());
   }
-
-  auto* pobj = get_nothrow<ObjectImpl>();
-  assert(pobj);
-  auto it = find(idx);
-  if (it == items().end()) {
-    throw std::out_of_range(to<std::string>(
-        "couldn't find key ", idx.asString(), " in dynamic object"));
-  }
-  return const_cast<dynamic&>(it->second);
 }
 
 inline bool dynamic::empty() const {
@@ -569,15 +636,8 @@ inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const {
 
 template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
   auto& obj = get<ObjectImpl>();
-  auto rv = obj.insert(std::make_pair(std::forward<K>(key),
-                                      std::forward<V>(val)));
-  if (!rv.second) {
-    // note, the second use of std:forward<V>(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<V>(val);
-  }
+  auto rv = obj.insert({ std::forward<K>(key), nullptr });
+  rv.first->second = std::forward<V>(val);
 }
 
 inline std::size_t dynamic::erase(dynamic const& key) {
@@ -586,12 +646,19 @@ inline std::size_t dynamic::erase(dynamic const& key) {
 }
 
 inline dynamic::const_iterator dynamic::erase(const_iterator it) {
-  return get<Array>().erase(it);
+  auto& arr = get<Array>();
+  // 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
+  // non-const iterator before calling erase.
+  return get<Array>().erase(arr.begin() + (it - arr.begin()));
 }
 
 inline dynamic::const_iterator
 dynamic::erase(const_iterator first, const_iterator last) {
-  return get<Array>().erase(first, last);
+  auto& arr = get<Array>();
+  return get<Array>().erase(
+    arr.begin() + (first - arr.begin()),
+    arr.begin() + (last - arr.begin()));
 }
 
 inline dynamic::const_key_iterator dynamic::erase(const_key_iterator it) {
@@ -639,6 +706,11 @@ inline void dynamic::push_back(dynamic&& v) {
   array.push_back(std::move(v));
 }
 
+inline void dynamic::pop_back() {
+  auto& array = get<Array>();
+  array.pop_back();
+}
+
 inline std::size_t dynamic::hash() const {
   switch (type()) {
   case OBJECT:
@@ -665,6 +737,20 @@ template<class T> struct dynamic::TypeInfo {
   static Type const type;
 };
 
+#define FB_DEC_TYPE(T)                                      \
+  template<> char const dynamic::TypeInfo<T>::name[];       \
+  template<> dynamic::Type const dynamic::TypeInfo<T>::type
+
+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);
+
+#undef FB_DEC_TYPE
+
 template<class T>
 T dynamic::asImpl() const {
   switch (type()) {
@@ -679,7 +765,7 @@ T dynamic::asImpl() const {
 
 // Return a T* to our type, or null if we're not that type.
 template<class T>
-T* dynamic::get_nothrow() {
+T* dynamic::get_nothrow() noexcept {
   if (type_ != TypeInfo<T>::type) {
     return nullptr;
   }
@@ -687,40 +773,40 @@ T* dynamic::get_nothrow() {
 }
 
 template<class T>
-T const* dynamic::get_nothrow() const {
+T const* dynamic::get_nothrow() const noexcept {
   return const_cast<dynamic*>(this)->get_nothrow<T>();
 }
 
 // Return T* for where we can put a T, without type checking.  (Memory
 // might be uninitialized, even.)
 template<class T>
-T* dynamic::getAddress() {
+T* dynamic::getAddress() noexcept {
   return GetAddrImpl<T>::get(u_);
 }
 
 template<class T>
-T const* dynamic::getAddress() const {
+T const* dynamic::getAddress() const noexcept {
   return const_cast<dynamic*>(this)->getAddress<T>();
 }
 
 template<class T> struct dynamic::GetAddrImpl {};
 template<> struct dynamic::GetAddrImpl<void*> {
-  static void** get(Data& d) { return &d.nul; }
+  static void** get(Data& d) noexcept { return &d.nul; }
 };
 template<> struct dynamic::GetAddrImpl<dynamic::Array> {
-  static Array* get(Data& d) { return &d.array; }
+  static Array* get(Data& d) noexcept { return &d.array; }
 };
 template<> struct dynamic::GetAddrImpl<bool> {
-  static bool* get(Data& d) { return &d.boolean; }
+  static bool* get(Data& d) noexcept { return &d.boolean; }
 };
 template<> struct dynamic::GetAddrImpl<int64_t> {
-  static int64_t* get(Data& d) { return &d.integer; }
+  static int64_t* get(Data& d) noexcept { return &d.integer; }
 };
 template<> struct dynamic::GetAddrImpl<double> {
-  static double* get(Data& d) { return &d.doubl; }
+  static double* get(Data& d) noexcept { return &d.doubl; }
 };
 template<> struct dynamic::GetAddrImpl<fbstring> {
-  static fbstring* get(Data& d) { return &d.string; }
+  static fbstring* get(Data& d) noexcept { return &d.string; }
 };
 template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
   static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer),
@@ -728,7 +814,7 @@ template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
     " 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<ObjectImpl*>(data);
   }
@@ -753,7 +839,7 @@ inline char const* dynamic::typeName(Type t) {
 #undef FB_X
 }
 
-inline void dynamic::destroy() {
+inline void dynamic::destroy() noexcept {
   // This short-circuit speeds up some microbenchmarks.
   if (type_ == NULLT) return;
 
@@ -843,7 +929,52 @@ class FormatValue<dynamic> {
   const dynamic& val_;
 };
 
-}
+template <class V>
+class FormatValue<detail::DefaultValueWrapper<dynamic, V>> {
+ public:
+  explicit FormatValue(
+      const detail::DefaultValueWrapper<dynamic, V>& val)
+    : val_(val) { }
+
+  template <class FormatCallback>
+  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<dynamic>(c).format(arg, cb);
+      break;
+    case dynamic::ARRAY:
+      {
+        int key = arg.splitIntKey();
+        if (key >= 0 && size_t(key) < c.size()) {
+          FormatValue<dynamic>(c.at(key)).format(arg, cb);
+        } else{
+          FormatValue<V>(val_.defaultValue).format(arg, cb);
+        }
+      }
+      break;
+    case dynamic::OBJECT:
+      {
+        auto pos = c.find(arg.splitKey());
+        if (pos != c.items().end()) {
+          FormatValue<dynamic>(pos->second).format(arg, cb);
+        } else {
+          FormatValue<V>(val_.defaultValue).format(arg, cb);
+        }
+      }
+      break;
+    }
+  }
+
+ private:
+  const detail::DefaultValueWrapper<dynamic, V>& val_;
+};
+
+}  // namespaces
 
 #undef FB_DYNAMIC_APPLY