From bd51c8dbebd7ee9de3db64b8d84a814a00639e0a Mon Sep 17 00:00:00 2001 From: Yoni Lavi Date: Sat, 20 Jun 2015 07:45:56 -0700 Subject: [PATCH] new small_vector tests that fail on trunk and uncover a bug in emplace_back on references to memory inside the vector + a fix + perf improvement to const lvalue push_back Summary: emplace_back() on a small_vector applied on data inside the vector doesn't work properly. In standard vectors, this usage is required to work properly, but I'm not sure whether it should in small_vector. Consider fixing / adding a lint rule. Reviewed By: @yfeldblum Differential Revision: D2122931 --- folly/small_vector.h | 32 ++++++++++----- folly/test/small_vector_test.cpp | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/folly/small_vector.h b/folly/small_vector.h index a4af2fc4..318fcbfd 100644 --- a/folly/small_vector.h +++ b/folly/small_vector.h @@ -658,6 +658,17 @@ public: emplaceBack(std::forward(args)...); } + void emplace_back(const value_type& t) { + push_back(t); + } + void emplace_back(value_type& t) { + push_back(t); + } + + void emplace_back(value_type&& t) { + push_back(std::move(t)); + } + void push_back(value_type&& t) { if (capacity() == size()) { makeSize(std::max(size_type(2), 3 * size() / 2), &t, size()); @@ -668,9 +679,17 @@ public: } void push_back(value_type const& t) { - // Make a copy and forward to the rvalue value_type&& overload - // above. - push_back(value_type(t)); + // TODO: we'd like to make use of makeSize (it can be optimized better, + // because it manipulates the internals) + // unfortunately the current implementation only supports moving from + // a supplied rvalue, and doing an extra move just to reuse it is a perf + // net loss + if (size() == capacity()) {// && isInside(&t)) { + value_type tmp(t); + emplaceBack(std::move(tmp)); + } else { + emplaceBack(t); + } } void pop_back() { @@ -809,13 +828,6 @@ private: this->setSize(size() + 1); } - /* - * Special case of emplaceBack for rvalue - */ - void emplaceBack(value_type&& t) { - push_back(std::move(t)); - } - static iterator unconst(const_iterator it) { return const_cast(it); } diff --git a/folly/test/small_vector_test.cpp b/folly/test/small_vector_test.cpp index 860fddc6..a5365393 100644 --- a/folly/test/small_vector_test.cpp +++ b/folly/test/small_vector_test.cpp @@ -733,3 +733,72 @@ TEST(small_vector, SelfInsert) { EXPECT_EQ(vec[i], "abc"); } } + +struct CheckedInt { + static const int DEFAULT_VALUE = (int)0xdeadbeef; + CheckedInt(): value(DEFAULT_VALUE) {} + explicit CheckedInt(int value): value(value) {} + CheckedInt(const CheckedInt& rhs): value(rhs.value) {} + CheckedInt(CheckedInt&& rhs) noexcept: value(rhs.value) { + rhs.value = DEFAULT_VALUE; + } + CheckedInt& operator= (const CheckedInt& rhs) { + value = rhs.value; + return *this; + } + CheckedInt& operator= (CheckedInt&& rhs) noexcept { + value = rhs.value; + rhs.value = DEFAULT_VALUE; + return *this; + } + ~CheckedInt() {} + int value; +}; + +TEST(small_vector, LVEmplaceInsideVector) { + folly::small_vector v; + v.push_back(CheckedInt(1)); + for (int i = 1; i < 20; ++i) { + v.emplace_back(v[0]); + ASSERT_EQ(1, v.back().value); + } +} + +TEST(small_vector, CLVEmplaceInsideVector) { + folly::small_vector v; + const folly::small_vector& cv = v; + v.push_back(CheckedInt(1)); + for (int i = 1; i < 20; ++i) { + v.emplace_back(cv[0]); + ASSERT_EQ(1, v.back().value); + } +} + +TEST(small_vector, RVEmplaceInsideVector) { + folly::small_vector v; + v.push_back(CheckedInt(0)); + for (int i = 1; i < 20; ++i) { + v[0] = CheckedInt(1); + v.emplace_back(std::move(v[0])); + ASSERT_EQ(1, v.back().value); + } +} + +TEST(small_vector, LVPushValueInsideVector) { + folly::small_vector v; + v.push_back(CheckedInt(1)); + for (int i = 1; i < 20; ++i) { + v.push_back(v[0]); + ASSERT_EQ(1, v.back().value); + } +} + +TEST(small_vector, RVPushValueInsideVector) { + folly::small_vector v; + v.push_back(CheckedInt(0)); + for (int i = 1; i < 20; ++i) { + v[0] = CheckedInt(1); + v.push_back(v[0]); + ASSERT_EQ(1, v.back().value); + } +} -- 2.34.1