Faster accessors for dynamic values
authorPavlo Kushnir <pavlo@fb.com>
Sat, 20 Sep 2014 00:02:35 +0000 (17:02 -0700)
committerAnton Likhtarov <alikhtarov@fb.com>
Fri, 26 Sep 2014 22:20:00 +0000 (15:20 -0700)
Summary:
Sometimes it is useful to have a fast accessor to a value stored in dynamic, without any type conversion. These still do the type check, but compiler has more room for optimization.

Test Plan: build folly, will add unit tests/benchmarks if this makes sense at all.

Reviewed By: njormrod@fb.com

Subscribers: jdelong, njormrod

FB internal diff: D1564867

folly/dynamic-inl.h
folly/dynamic.h
folly/test/DynamicTest.cpp

index 35378bd519fabd6421ff2c2ca300b6affe49cdaf..d4550a2ca6ba112d34d63531e33f0e8daaf93d9d 100644 (file)
@@ -392,8 +392,19 @@ 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 {
index 0e03f8f4791b1b472a59d509b3de5dff8f87a037..ecdcdaf16829f225793177bfe8f4b0396f313647 100644 (file)
@@ -278,6 +278,20 @@ public:
   int64_t  asInt() const;
   bool     asBool() const;
 
+  /*
+   * Extract the value stored in this dynamic without type conversion.
+   *
+   * These will throw a TypeError if the dynamic has a different type.
+   */
+  const fbstring& getString() const;
+  double          getDouble() const;
+  int64_t         getInt() const;
+  bool            getBool() const;
+  fbstring& getString();
+  double&   getDouble();
+  int64_t&  getInt();
+  bool&     getBool();
+
   /*
    * It is occasionally useful to access a string's internal pointer
    * directly, without the type conversion of `asString()`.
@@ -286,6 +300,7 @@ public:
    */
   const char* data()  const;
   const char* c_str() const;
+  StringPiece stringPiece() const;
 
   /*
    * Returns: true if this dynamic is null, an empty array, an empty
index 36afc07b7ac530aa5fac0296de21264de7b6c16a..4701a1bf93228de65840b769f223f4f353cb642f 100644 (file)
@@ -24,6 +24,7 @@
 #include <folly/json.h>
 
 using folly::dynamic;
+using folly::TypeError;
 
 TEST(Dynamic, ObjectBasics) {
   dynamic obj = dynamic::object("a", false);
@@ -226,12 +227,20 @@ TEST(Dynamic, Conversions) {
 TEST(Dynamic, StringPtrs) {
   dynamic str = "12.0";
   dynamic num = 12.0;
+  dynamic nullStr = folly::parseJson("\"foo\\u0000bar\"");
 
   EXPECT_EQ(0, strcmp(str.c_str(), "12.0"));
   EXPECT_EQ(0, strncmp(str.data(), "12.0", str.asString().length()));
+  EXPECT_EQ(str.stringPiece(), "12.0");
 
-  EXPECT_ANY_THROW(num.c_str());
-  EXPECT_ANY_THROW(num.data());
+  EXPECT_THROW(num.c_str(), TypeError);
+  EXPECT_THROW(num.data(), TypeError);
+  EXPECT_THROW(num.stringPiece(), TypeError);
+
+  EXPECT_EQ(nullStr.stringPiece(), folly::StringPiece("foo\0bar", 7));
+
+  nullStr.getString()[3] = '|';
+  EXPECT_EQ(nullStr.stringPiece(), "foo|bar");
 }
 
 TEST(Dynamic, FormattedIO) {
@@ -313,6 +322,46 @@ TEST(Dynamic, ArrayGenerator) {
   EXPECT_EQ(from(arr) | take(3) | member(&dynamic::asInt) | sum, 6);
 }
 
+TEST(Dynamic, Getters) {
+  dynamic dStr = folly::parseJson("\"foo\\u0000bar\"");
+  dynamic dInt = 1;
+  dynamic dDouble = 0.5;
+  dynamic dBool = true;
+
+  EXPECT_EQ(dStr.getString(), std::string("foo\0bar", 7));
+  EXPECT_EQ(dInt.getInt(), 1);
+  EXPECT_EQ(dDouble.getDouble(), 0.5);
+  EXPECT_EQ(dBool.getBool(), true);
+
+  dStr.getString()[3] = '|';
+  EXPECT_EQ(dStr.getString(), "foo|bar");
+
+  dInt.getInt() = 2;
+  EXPECT_EQ(dInt.getInt(), 2);
+
+  dDouble.getDouble() = 0.7;
+  EXPECT_EQ(dDouble.getDouble(), 0.7);
+
+  dBool.getBool() = false;
+  EXPECT_EQ(dBool.getBool(), false);
+
+  EXPECT_THROW(dStr.getInt(), TypeError);
+  EXPECT_THROW(dStr.getDouble(), TypeError);
+  EXPECT_THROW(dStr.getBool(), TypeError);
+
+  EXPECT_THROW(dInt.getString(), TypeError);
+  EXPECT_THROW(dInt.getDouble(), TypeError);
+  EXPECT_THROW(dInt.getBool(), TypeError);
+
+  EXPECT_THROW(dDouble.getString(), TypeError);
+  EXPECT_THROW(dDouble.getInt(), TypeError);
+  EXPECT_THROW(dDouble.getBool(), TypeError);
+
+  EXPECT_THROW(dBool.getString(), TypeError);
+  EXPECT_THROW(dBool.getInt(), TypeError);
+  EXPECT_THROW(dBool.getDouble(), TypeError);
+}
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   gflags::ParseCommandLineFlags(&argc, &argv, true);