Title: [266488] trunk
Revision
266488
Author
rn...@webkit.org
Date
2020-09-02 14:16:27 -0700 (Wed, 02 Sep 2020)

Log Message

Add a variant of map which filters items to Vector.h
https://bugs.webkit.org/show_bug.cgi?id=215879

Reviewed by Darin Adler and Yusuke Suzuki.

Source/WTF:

This patch adds WTF::compactMap, which calls a function on each item in an iterable object like WTF::map
but also filters the returned value. The mapped function may return Optional<T> or RefPtr<T>. The value
is kept in the result if the returned value is not WTF::nullopt in the case of Optional<T> and not null
in the case of RefPtr<T>. The result will be either Vector<T> for Optional<T> or else Vector<Ref<T>>.

* wtf/Vector.h:
(WTF::CompactMapTraits:): Added. Traits to have a different behavior
(WTF::CompactMapTraits<Optional<T>>::hasValue): Added.
(WTF::CompactMapTraits<Optional<T>>::extractValue): Added.
(WTF::CompactMapTraits<RefPtr<T>>::hasValue): Added.
(WTF::CompactMapTraits<RefPtr<T>>::extractValue): Added.
(WTF::CompactMap): Added. The helper to use WTFMove on the argument to the mapped function if possible.
(WTF::compactMap): Added.

Tools:

Added unit tests.

* TestWebKitAPI/Tests/WTF/Vector.cpp:
(TestWebKitAPI::evenMultipliedByFive):
(WTF_Vector.CompactMapStaticFunctionReturnOptional):
(TestWebKitAPI::RefCountedObject):
(TestWebKitAPI::RefCountedObject::create):
(TestWebKitAPI::RefCountedObject::ref):
(TestWebKitAPI::RefCountedObject::RefCountedObject):
(TestWebKitAPI::createRefCountedForOdd):
(WTF_Vector.CompactMapStaticFunctionReturnRefPtr):
(TestWebKitAPI::createRefCountedForEven):
(WTF_Vector.CompactMapStaticFunctionReturnOptionalRef):
(TestWebKitAPI::createRefCountedWhenDivisibleByThree):
(WTF_Vector.CompactMapStaticFunctionReturnOptionalRefPtr):
(WTF_Vector.CompactMapLambdaReturnOptional):
(TestWebKitAPI::CountedObject):
(TestWebKitAPI::CountedObject::CountedObject):
(TestWebKitAPI::CountedObject::value):
(TestWebKitAPI::CountedObject::count):
(WTF_Vector.CompactMapLambdaCopyVectorReturnOptionalCountedObject):
(WTF_Vector.CompactMapLambdaMoveVectorReturnOptionalCountedObject):
(WTF_Vector.CompactMapLambdaReturnRefPtr):
(WTF_Vector.CompactMapLambdaReturnRefPtrFromMovedRef):
(WTF_Vector.CompactMapLambdaReturnOptionalRefPtr):

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (266487 => 266488)


--- trunk/Source/WTF/ChangeLog	2020-09-02 21:01:59 UTC (rev 266487)
+++ trunk/Source/WTF/ChangeLog	2020-09-02 21:16:27 UTC (rev 266488)
@@ -1,3 +1,24 @@
+2020-09-02  Ryosuke Niwa  <rn...@webkit.org>
+
+        Add a variant of map which filters items to Vector.h
+        https://bugs.webkit.org/show_bug.cgi?id=215879
+
+        Reviewed by Darin Adler and Yusuke Suzuki.
+
+        This patch adds WTF::compactMap, which calls a function on each item in an iterable object like WTF::map
+        but also filters the returned value. The mapped function may return Optional<T> or RefPtr<T>. The value
+        is kept in the result if the returned value is not WTF::nullopt in the case of Optional<T> and not null
+        in the case of RefPtr<T>. The result will be either Vector<T> for Optional<T> or else Vector<Ref<T>>.
+
+        * wtf/Vector.h:
+        (WTF::CompactMapTraits:): Added. Traits to have a different behavior
+        (WTF::CompactMapTraits<Optional<T>>::hasValue): Added.
+        (WTF::CompactMapTraits<Optional<T>>::extractValue): Added.
+        (WTF::CompactMapTraits<RefPtr<T>>::hasValue): Added.
+        (WTF::CompactMapTraits<RefPtr<T>>::extractValue): Added.
+        (WTF::CompactMap): Added. The helper to use WTFMove on the argument to the mapped function if possible.
+        (WTF::compactMap): Added.
+
 2020-09-02  Said Abou-Hallawa  <sabouhall...@apple.com>
 
         Unreviewed, reverting r266449.

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/ChangeLog (266487 => 266488)


--- trunk/Tools/ChangeLog	2020-09-02 21:01:59 UTC (rev 266487)
+++ trunk/Tools/ChangeLog	2020-09-02 21:16:27 UTC (rev 266488)
@@ -1,3 +1,36 @@
+2020-09-02  Ryosuke Niwa  <rn...@webkit.org>
+
+        Add a variant of map which filters items to Vector.h
+        https://bugs.webkit.org/show_bug.cgi?id=215879
+
+        Reviewed by Darin Adler and Yusuke Suzuki.
+
+        Added unit tests.
+
+        * TestWebKitAPI/Tests/WTF/Vector.cpp:
+        (TestWebKitAPI::evenMultipliedByFive):
+        (WTF_Vector.CompactMapStaticFunctionReturnOptional):
+        (TestWebKitAPI::RefCountedObject):
+        (TestWebKitAPI::RefCountedObject::create):
+        (TestWebKitAPI::RefCountedObject::ref):
+        (TestWebKitAPI::RefCountedObject::RefCountedObject):
+        (TestWebKitAPI::createRefCountedForOdd):
+        (WTF_Vector.CompactMapStaticFunctionReturnRefPtr):
+        (TestWebKitAPI::createRefCountedForEven):
+        (WTF_Vector.CompactMapStaticFunctionReturnOptionalRef):
+        (TestWebKitAPI::createRefCountedWhenDivisibleByThree):
+        (WTF_Vector.CompactMapStaticFunctionReturnOptionalRefPtr):
+        (WTF_Vector.CompactMapLambdaReturnOptional):
+        (TestWebKitAPI::CountedObject):
+        (TestWebKitAPI::CountedObject::CountedObject):
+        (TestWebKitAPI::CountedObject::value):
+        (TestWebKitAPI::CountedObject::count):
+        (WTF_Vector.CompactMapLambdaCopyVectorReturnOptionalCountedObject):
+        (WTF_Vector.CompactMapLambdaMoveVectorReturnOptionalCountedObject):
+        (WTF_Vector.CompactMapLambdaReturnRefPtr):
+        (WTF_Vector.CompactMapLambdaReturnRefPtrFromMovedRef):
+        (WTF_Vector.CompactMapLambdaReturnOptionalRefPtr):
+
 2020-09-02  Jonathan Bedard  <jbed...@apple.com>
 
         [webkitcorepy] Add NoAction class

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 };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to