Modified: trunk/Source/WTF/wtf/Vector.h (266487 => 266488)
--- trunk/Source/WTF/wtf/Vector.h 2020-09-02 21:01:59 UTC (rev 266487)
+++ trunk/Source/WTF/wtf/Vector.h 2020-09-02 21:16:27 UTC (rev 266488)
@@ -1680,6 +1680,71 @@
return Mapper<MapFunction, SourceType>::map(std::forward<SourceType>(source), std::forward<MapFunction>(mapFunction));
}
+template<typename MapFunctionReturnType>
+struct CompactMapTraits {
+ static bool hasValue(const MapFunctionReturnType&);
+ template<typename ItemType>
+ static ItemType extractValue(MapFunctionReturnType&&);
+};
+
+template<typename T>
+struct CompactMapTraits<Optional<T>> {
+ using ItemType = T;
+ static bool hasValue(const Optional<T>& returnValue) { return !!returnValue; }
+ static ItemType extractValue(Optional<T>&& returnValue) { return WTFMove(*returnValue); }
+};
+
+template<typename T>
+struct CompactMapTraits<RefPtr<T>> {
+ using ItemType = Ref<T>;
+ static bool hasValue(const RefPtr<T>& returnValue) { return !!returnValue; }
+ static ItemType extractValue(RefPtr<T>&& returnValue) { return returnValue.releaseNonNull(); }
+};
+
+template<typename MapFunction, typename SourceType, typename Enable = void>
+struct CompactMapper {
+ using SourceItemType = typename CollectionInspector<SourceType>::SourceItemType;
+ using ResultItemType = typename std::result_of<MapFunction(SourceItemType&)>::type;
+ using DestinationItemType = typename CompactMapTraits<ResultItemType>::ItemType;
+
+ static Vector<DestinationItemType> compactMap(SourceType source, const MapFunction& mapFunction)
+ {
+ Vector<DestinationItemType> result;
+ for (auto& item : source) {
+ auto itemResult = mapFunction(item);
+ if (CompactMapTraits<ResultItemType>::hasValue(itemResult))
+ result.append(CompactMapTraits<ResultItemType>::extractValue(WTFMove(itemResult)));
+ }
+ result.shrinkToFit();
+ return result;
+ }
+};
+
+template<typename MapFunction, typename SourceType>
+struct CompactMapper<MapFunction, SourceType, typename std::enable_if<std::is_rvalue_reference<SourceType&&>::value>::type> {
+ using SourceItemType = typename CollectionInspector<SourceType>::SourceItemType;
+ using ResultItemType = typename std::result_of<MapFunction(SourceItemType&&)>::type;
+ using DestinationItemType = typename CompactMapTraits<ResultItemType>::ItemType;
+
+ static Vector<DestinationItemType> compactMap(SourceType source, const MapFunction& mapFunction)
+ {
+ Vector<DestinationItemType> result;
+ for (auto& item : source) {
+ auto itemResult = mapFunction(WTFMove(item));
+ if (CompactMapTraits<ResultItemType>::hasValue(itemResult))
+ result.append(CompactMapTraits<ResultItemType>::extractValue(WTFMove(itemResult)));
+ }
+ result.shrinkToFit();
+ return result;
+ }
+};
+
+template<typename MapFunction, typename SourceType>
+Vector<typename CompactMapper<MapFunction, SourceType>::DestinationItemType> compactMap(SourceType&& source, MapFunction&& mapFunction)
+{
+ return CompactMapper<MapFunction, SourceType>::compactMap(std::forward<SourceType>(source), std::forward<MapFunction>(mapFunction));
+}
+
template<typename DestinationVector, typename Collection>
inline auto copyToVectorSpecialization(const Collection& collection) -> DestinationVector
{
@@ -1715,4 +1780,5 @@
using WTF::copyToVector;
using WTF::copyToVectorOf;
using WTF::copyToVectorSpecialization;
+using WTF::compactMap;
using WTF::removeRepeatedElements;
Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp (266487 => 266488)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp 2020-09-02 21:01:59 UTC (rev 266487)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Vector.cpp 2020-09-02 21:16:27 UTC (rev 266488)
@@ -871,6 +871,253 @@
}
+WTF::Optional<int> evenMultipliedByFive(int input)
+{
+ if (input % 2)
+ return WTF::nullopt;
+ return input * 5;
+}
+
+TEST(WTF_Vector, CompactMapStaticFunctionReturnOptional)
+{
+ Vector<int> vector { 1, 2, 3, 4 };
+
+ auto mapped = WTF::compactMap(vector, evenMultipliedByFive);
+
+ EXPECT_EQ(2U, mapped.size());
+ EXPECT_EQ(10, mapped[0]);
+ EXPECT_EQ(20, mapped[1]);
+}
+
+struct RefCountedObject : public RefCounted<RefCountedObject> {
+public:
+ static Ref<RefCountedObject> create(int value) { return adoptRef(*new RefCountedObject(value)); }
+
+ void ref() const
+ {
+ RefCounted<RefCountedObject>::ref();
+ ++s_totalRefCount;
+ }
+
+ int value { 0 };
+
+ static unsigned s_totalRefCount;
+
+private:
+ RefCountedObject(int value)
+ : value(value)
+ { }
+};
+
+unsigned RefCountedObject::s_totalRefCount = 0;
+
+RefPtr<RefCountedObject> createRefCountedForOdd(int input)
+{
+ if (input % 2)
+ return RefCountedObject::create(input);
+ return nullptr;
+}
+
+TEST(WTF_Vector, CompactMapStaticFunctionReturnRefPtr)
+{
+ Vector<int> vector { 1, 2, 3, 4 };
+
+ RefCountedObject::s_totalRefCount = 0;
+ Vector<Ref<RefCountedObject>> mapped = WTF::compactMap(vector, createRefCountedForOdd);
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(2U, mapped.size());
+ EXPECT_EQ(1, mapped[0]->value);
+ EXPECT_EQ(3, mapped[1]->value);
+}
+
+Optional<Ref<RefCountedObject>> createRefCountedForEven(int input)
+{
+ if (input % 2)
+ return WTF::nullopt;
+ return RefCountedObject::create(input);
+}
+
+TEST(WTF_Vector, CompactMapStaticFunctionReturnOptionalRef)
+{
+ Vector<int> vector { 1, 2, 3, 4 };
+
+ RefCountedObject::s_totalRefCount = 0;
+ auto mapped = WTF::compactMap(vector, createRefCountedForEven);
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(2U, mapped.size());
+ EXPECT_EQ(2, mapped[0]->value);
+ EXPECT_EQ(4, mapped[1]->value);
+}
+
+Optional<RefPtr<RefCountedObject>> createRefCountedWhenDivisibleByThree(int input)
+{
+ if (input % 3)
+ return WTF::nullopt;
+ if (input % 2)
+ return RefPtr<RefCountedObject>();
+ return RefPtr<RefCountedObject>(RefCountedObject::create(input));
+}
+
+TEST(WTF_Vector, CompactMapStaticFunctionReturnOptionalRefPtr)
+{
+ Vector<int> vector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ RefCountedObject::s_totalRefCount = 0;
+ Vector<RefPtr<RefCountedObject>> mapped = WTF::compactMap(vector, createRefCountedWhenDivisibleByThree);
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(3U, mapped.size());
+ EXPECT_EQ(nullptr, mapped[0]);
+ EXPECT_EQ(6, mapped[1]->value);
+ EXPECT_EQ(nullptr, mapped[2]);
+}
+
+TEST(WTF_Vector, CompactMapLambdaReturnOptional)
+{
+ Vector<String> vector { "a", "b", "hello", "ciao", "world", "webkit" };
+
+ auto mapped = WTF::compactMap(vector, [](const String& value) -> Optional<String> {
+ if (value.length() < 5)
+ return WTF::nullopt;
+ return value.convertToASCIIUppercase();
+ });
+
+ EXPECT_EQ(3U, mapped.size());
+ EXPECT_STREQ("HELLO", mapped[0].ascii().data());
+ EXPECT_STREQ("WORLD", mapped[1].ascii().data());
+ EXPECT_STREQ("WEBKIT", mapped[2].ascii().data());
+}
+
+class CountedObject {
+public:
+ explicit CountedObject(int value)
+ : m_value(value)
+ { ++s_count; }
+
+ CountedObject(const CountedObject& other)
+ : m_value(other.m_value)
+ { ++s_count; }
+
+ CountedObject(CountedObject&& other)
+ : m_value(other.m_value)
+ { }
+
+ int value() const { return m_value; }
+
+ static unsigned& count() { return s_count; }
+
+private:
+ int m_value;
+
+ static unsigned s_count;
+};
+
+unsigned CountedObject::s_count = 0;
+
+TEST(WTF_Vector, CompactMapLambdaCopyVectorReturnOptionalCountedObject)
+{
+ Vector<CountedObject> vector { CountedObject(1), CountedObject(2), CountedObject(3), CountedObject(4) };
+
+ CountedObject::count() = 0;
+
+ auto mapped = WTF::compactMap(vector, [](const CountedObject& object) -> Optional<CountedObject> {
+ if (object.value() % 2)
+ return object;
+ return WTF::nullopt;
+ });
+
+ EXPECT_EQ(2U, CountedObject::count());
+
+ EXPECT_EQ(2U, mapped.size());
+ EXPECT_EQ(1, mapped[0].value());
+ EXPECT_EQ(3, mapped[1].value());
+}
+
+TEST(WTF_Vector, CompactMapLambdaMoveVectorReturnOptionalCountedObject)
+{
+ Vector<CountedObject> vector { CountedObject(1), CountedObject(2), CountedObject(3), CountedObject(4) };
+
+ CountedObject::count() = 0;
+
+ RefCountedObject::s_totalRefCount = 0;
+ auto mapped = WTF::compactMap(WTFMove(vector), [](CountedObject&& object) -> Optional<CountedObject> {
+ if (object.value() % 2)
+ return WTFMove(object);
+ return WTF::nullopt;
+ });
+
+ EXPECT_EQ(0U, CountedObject::count());
+
+ EXPECT_EQ(2U, mapped.size());
+ EXPECT_EQ(1, mapped[0].value());
+ EXPECT_EQ(3, mapped[1].value());
+}
+
+TEST(WTF_Vector, CompactMapLambdaReturnRefPtr)
+{
+ Vector<int> vector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ RefCountedObject::s_totalRefCount = 0;
+ Vector<Ref<RefCountedObject>> mapped = WTF::compactMap(vector, [](int value) -> RefPtr<RefCountedObject> {
+ if (value % 3)
+ return nullptr;
+ return RefCountedObject::create(value);
+ });
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(3U, mapped.size());
+ EXPECT_EQ(3, mapped[0]->value);
+ EXPECT_EQ(6, mapped[1]->value);
+ EXPECT_EQ(9, mapped[2]->value);
+}
+
+TEST(WTF_Vector, CompactMapLambdaReturnRefPtrFromMovedRef)
+{
+ Vector<int> vector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ auto countedObjects = WTF::map(vector, [](int value) -> Ref<RefCountedObject> {
+ return RefCountedObject::create(value);
+ });
+
+ RefCountedObject::s_totalRefCount = 0;
+ auto mapped = WTF::compactMap(WTFMove(countedObjects), [](Ref<RefCountedObject>&& object) -> RefPtr<RefCountedObject> {
+ if (object->value % 3)
+ return nullptr;
+ return WTFMove(object);
+ });
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(3U, mapped.size());
+ EXPECT_EQ(3, mapped[0]->value);
+ EXPECT_EQ(6, mapped[1]->value);
+ EXPECT_EQ(9, mapped[2]->value);
+}
+
+TEST(WTF_Vector, CompactMapLambdaReturnOptionalRefPtr)
+{
+ Vector<int> vector { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+ RefCountedObject::s_totalRefCount = 0;
+
+ Vector<RefPtr<RefCountedObject>> mapped = WTF::compactMap(vector, [&](int value) -> Optional<RefPtr<RefCountedObject>> {
+ if (!(value % 2))
+ return WTF::nullopt;
+ if (!(value % 3))
+ return RefPtr<RefCountedObject>();
+ return RefPtr<RefCountedObject>(RefCountedObject::create(value));
+ });
+
+ EXPECT_EQ(0U, RefCountedObject::s_totalRefCount);
+ EXPECT_EQ(5U, mapped.size());
+ EXPECT_EQ(1, mapped[0]->value);
+ EXPECT_EQ(nullptr, mapped[1]);
+ EXPECT_EQ(5, mapped[2]->value);
+ EXPECT_EQ(7, mapped[3]->value);
+ EXPECT_EQ(nullptr, mapped[4]);
+}
+
TEST(WTF_Vector, CopyToVector)
{
HashSet<int> intSet { 1, 2, 3 };