From 8c62ae77eeadc77735fa31a695c8c6f2ca81f2d2 Mon Sep 17 00:00:00 2001 From: Pavlo Kushnir Date: Fri, 19 Sep 2014 17:02:35 -0700 Subject: [PATCH] Faster accessors for dynamic values 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 | 11 ++++++++ folly/dynamic.h | 15 +++++++++++ folly/test/DynamicTest.cpp | 53 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/folly/dynamic-inl.h b/folly/dynamic-inl.h index 35378bd5..d4550a2c 100644 --- a/folly/dynamic-inl.h +++ b/folly/dynamic-inl.h @@ -392,8 +392,19 @@ 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 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 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 { diff --git a/folly/dynamic.h b/folly/dynamic.h index 0e03f8f4..ecdcdaf1 100644 --- a/folly/dynamic.h +++ b/folly/dynamic.h @@ -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 diff --git a/folly/test/DynamicTest.cpp b/folly/test/DynamicTest.cpp index 36afc07b..4701a1bf 100644 --- a/folly/test/DynamicTest.cpp +++ b/folly/test/DynamicTest.cpp @@ -24,6 +24,7 @@ #include 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); -- 2.34.1