[WTF] Add Markable<T, Traits>
authoryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Sep 2018 18:19:09 +0000 (18:19 +0000)
committeryusukesuzuki@slowstart.org <yusukesuzuki@slowstart.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Sep 2018 18:19:09 +0000 (18:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189231

Reviewed by Sam Weinig.

Source/WebCore:

Use Markable<Seconds> and Markable<WallTime> in ResourceResponseBase.
Since these fields are parsed results from http header fields, Seconds::nan() and WallTime::nan()
can be used as an empty value for these fields. Thus we can use Markable because it uses
these nan values as an empty values (they are configured by Seconds::MarkableTraits and WallTime::MarkableTraits).
This reduces the size of ResourceResponseBase from 448 to 416.

* platform/network/ResourceResponseBase.h:

Source/WTF:

We can represent a value with nullopt by using std::optional<T>. However, std::optional<T> has storage efficiency
problem. It always has a bool indicating that the value is nullopt or not. If we have a following class,

    class A {
        std::optional<WallTime> m_timeA;
        std::optional<WallTime> m_timeB;
        std::optional<WallTime> m_timeC;
    };

This class has significant amount of padding between m_timeA / m_timeB, m_timeB / m_timeC due to the above bool.

If we know that WallTime has a value that represents invalid, we can use it instead and save the storage.
This is very similar problem to our HashTable implementation. In our HashTable implementation, we need Deleted
and Empty value, which can represent Deleted and Empty values without sacrificing storage efficiency.

We should have similar mechanism here. In this patch, we have WTF::Markable<T, Traits>. Traits offers
`Traits::isEmptyValue(value)` and `Traits::emptyValue()`. Then, we use this empty value instead of having bool
flag. This way, we can make `sizeof(WTF::Markable<T>) == sizeof(T)`.

This idea is inspired from https://github.com/akrzemi1/markable. But we would like to have WTF::Markable<T>
here instead of importing it since we would like to have (1) integrated interfaces with std::optional<T> and (2)
aligned function names to HashTraits' `isEmptyValue` and `emptyValue`.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/Markable.h: Added.
(WTF::std::underlying_type<EnumType>::type>::max): EnumMarkableTraits can be used as an MarkableTraits for enum
values. We can specify a constant value as an empty value.
(WTF::IntegralMarkableTraits::isEmptyValue):
(WTF::IntegralMarkableTraits::emptyValue): IntegralMarkableTraits can be used as an MarkableTraits for integral
types including int etc.
(WTF::Markable::Markable):
(WTF::Markable::operator bool const):
(WTF::Markable::reset):
(WTF::Markable::value const):
(WTF::Markable::value):
(WTF::Markable::operator-> const):
(WTF::Markable::operator->):
(WTF::Markable::operator* const):
(WTF::Markable::operator*):
(WTF::Markable::operator std::optional<T>):
(WTF::Markable::operator std::optional<T> const): This operator allows us to cast Markable<T> to
std::optional<T>.
* wtf/MonotonicTime.h:
(WTF::MonotonicTime::MarkableTraits::isEmptyValue):
(WTF::MonotonicTime::MarkableTraits::emptyValue): MarkableTraits for MonotonicTime. MonotonicTime::nan() is used
as an empty value.
* wtf/Seconds.h:
(WTF::Seconds::MarkableTraits::isEmptyValue):
(WTF::Seconds::MarkableTraits::emptyValue): MarkableTraits for Seconds. Seconds::nan() is used as an empty value.
* wtf/WallTime.h:
(WTF::WallTime::nan):
(WTF::WallTime::MarkableTraits::isEmptyValue):
(WTF::WallTime::MarkableTraits::emptyValue): MarkableTraits for WallTime. WallTime::nan() is used as an empty value.

Tools:

Add tests for Markable.

* TestWebKitAPI/CMakeLists.txt:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WTF/Markable.cpp: Added.
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@235852 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WTF/ChangeLog
Source/WTF/WTF.xcodeproj/project.pbxproj
Source/WTF/wtf/CMakeLists.txt
Source/WTF/wtf/Markable.h [new file with mode: 0644]
Source/WTF/wtf/MonotonicTime.h
Source/WTF/wtf/Seconds.h
Source/WTF/wtf/WallTime.h
Source/WebCore/ChangeLog
Source/WebCore/platform/network/ResourceResponseBase.h
Tools/ChangeLog
Tools/TestWebKitAPI/CMakeLists.txt
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WTF/Markable.cpp [new file with mode: 0644]

index fb553bd..6a6977b 100644 (file)
@@ -1,3 +1,65 @@
+2018-09-10  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WTF] Add Markable<T, Traits>
+        https://bugs.webkit.org/show_bug.cgi?id=189231
+
+        Reviewed by Sam Weinig.
+
+        We can represent a value with nullopt by using std::optional<T>. However, std::optional<T> has storage efficiency
+        problem. It always has a bool indicating that the value is nullopt or not. If we have a following class,
+
+            class A {
+                std::optional<WallTime> m_timeA;
+                std::optional<WallTime> m_timeB;
+                std::optional<WallTime> m_timeC;
+            };
+
+        This class has significant amount of padding between m_timeA / m_timeB, m_timeB / m_timeC due to the above bool.
+
+        If we know that WallTime has a value that represents invalid, we can use it instead and save the storage.
+        This is very similar problem to our HashTable implementation. In our HashTable implementation, we need Deleted
+        and Empty value, which can represent Deleted and Empty values without sacrificing storage efficiency.
+
+        We should have similar mechanism here. In this patch, we have WTF::Markable<T, Traits>. Traits offers
+        `Traits::isEmptyValue(value)` and `Traits::emptyValue()`. Then, we use this empty value instead of having bool
+        flag. This way, we can make `sizeof(WTF::Markable<T>) == sizeof(T)`.
+
+        This idea is inspired from https://github.com/akrzemi1/markable. But we would like to have WTF::Markable<T>
+        here instead of importing it since we would like to have (1) integrated interfaces with std::optional<T> and (2)
+        aligned function names to HashTraits' `isEmptyValue` and `emptyValue`.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/Markable.h: Added.
+        (WTF::std::underlying_type<EnumType>::type>::max): EnumMarkableTraits can be used as an MarkableTraits for enum
+        values. We can specify a constant value as an empty value.
+        (WTF::IntegralMarkableTraits::isEmptyValue):
+        (WTF::IntegralMarkableTraits::emptyValue): IntegralMarkableTraits can be used as an MarkableTraits for integral
+        types including int etc.
+        (WTF::Markable::Markable):
+        (WTF::Markable::operator bool const):
+        (WTF::Markable::reset):
+        (WTF::Markable::value const):
+        (WTF::Markable::value):
+        (WTF::Markable::operator-> const):
+        (WTF::Markable::operator->):
+        (WTF::Markable::operator* const):
+        (WTF::Markable::operator*):
+        (WTF::Markable::operator std::optional<T>):
+        (WTF::Markable::operator std::optional<T> const): This operator allows us to cast Markable<T> to
+        std::optional<T>.
+        * wtf/MonotonicTime.h:
+        (WTF::MonotonicTime::MarkableTraits::isEmptyValue):
+        (WTF::MonotonicTime::MarkableTraits::emptyValue): MarkableTraits for MonotonicTime. MonotonicTime::nan() is used
+        as an empty value.
+        * wtf/Seconds.h:
+        (WTF::Seconds::MarkableTraits::isEmptyValue):
+        (WTF::Seconds::MarkableTraits::emptyValue): MarkableTraits for Seconds. Seconds::nan() is used as an empty value.
+        * wtf/WallTime.h:
+        (WTF::WallTime::nan):
+        (WTF::WallTime::MarkableTraits::isEmptyValue):
+        (WTF::WallTime::MarkableTraits::emptyValue): MarkableTraits for WallTime. WallTime::nan() is used as an empty value.
+
 2018-09-09  Fujii Hironori  <Hironori.Fujii@sony.com>
 
         Add specialized template declarations of HashTraits and DefaultHash to detect misuse
index c511ed2..86f4bdb 100644 (file)
                E38D6E261F5522E300A75CC4 /* StringBuilderJSON.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringBuilderJSON.cpp; sourceTree = "<group>"; };
                E3A32BC21FC830E2007D7E76 /* JSValueMalloc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSValueMalloc.cpp; sourceTree = "<group>"; };
                E3A32BC31FC830E2007D7E76 /* JSValueMalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSValueMalloc.h; sourceTree = "<group>"; };
+               304CA4E41375437EBE931D03 /* Markable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Markable.h; sourceTree = "<group>"; };
                E3CF76902115D6BA0091DE48 /* CompactPointerTuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CompactPointerTuple.h; sourceTree = "<group>"; };
                E3E158251EADA53C004A079D /* SystemFree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SystemFree.h; sourceTree = "<group>"; };
                E431CC4A21187ADB000C8A07 /* DispatchSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchSPI.h; sourceTree = "<group>"; };
                                A8A472C6151A825A004123FF /* MainThread.cpp */,
                                A8A472C7151A825B004123FF /* MainThread.h */,
                                1A233C7C17DAA6E300A93ACF /* MallocPtr.h */,
+                               304CA4E41375437EBE931D03 /* Markable.h */,
                                A8A472C9151A825B004123FF /* MathExtras.h */,
                                1447AEC818FCE59400B3D7FF /* mbmalloc.cpp */,
                                A8A472CA151A825B004123FF /* MD5.cpp */,
index 50e62a3..fb348ab 100644 (file)
@@ -122,6 +122,7 @@ set(WTF_PUBLIC_HEADERS
     MachSendRight.h
     MainThread.h
     MallocPtr.h
+    Markable.h
     MathExtras.h
     MediaTime.h
     MemoryFootprint.h
diff --git a/Source/WTF/wtf/Markable.h b/Source/WTF/wtf/Markable.h
new file mode 100644 (file)
index 0000000..aa8ebe1
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+// The idea of Markable<T> is derived from markable<T> at
+// https://github.com/akrzemi1/markable.
+//
+// Copyright (C) 2015-2018 Andrzej Krzemienski.
+//
+// Use, modification, and distribution is subject to the Boost Software
+// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#pragma once
+
+#include <type_traits>
+#include <wtf/Optional.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WTF {
+
+// Example:
+//     enum class Type { Value1, Value2, Value3 };
+//     Markable<Type, EnumMarkableTraits<Type, 42>> optional;
+template<
+    typename EnumType,
+    typename std::underlying_type<EnumType>::type constant = std::numeric_limits<typename std::underlying_type<EnumType>::type>::max()>
+struct EnumMarkableTraits {
+    static_assert(std::is_enum<EnumType>::value, "");
+    using UnderlyingType = typename std::underlying_type<EnumType>::type;
+
+    constexpr static bool isEmptyValue(EnumType value)
+    {
+        return static_cast<UnderlyingType>(value) == constant;
+    }
+
+    constexpr static EnumType emptyValue()
+    {
+        return static_cast<EnumType>(constant);
+    }
+};
+
+template<typename IntegralType, IntegralType constant = 0>
+struct IntegralMarkableTraits {
+    static_assert(std::is_integral<IntegralType>::value, "");
+    constexpr static bool isEmptyValue(IntegralType value)
+    {
+        return value == constant;
+    }
+
+    constexpr static IntegralType emptyValue()
+    {
+        return constant;
+    }
+};
+
+// The goal of Markable is offering std::optional without sacrificing storage efficiency.
+// Markable takes Traits, which should have isEmptyValue and emptyValue functions. By using
+// one value of T as an empty value, we can remove bool flag in std::optional. This strategy is
+// similar to WTF::HashTable, which uses two values of T as an empty value and a deleted value.
+// This class is intended to be used as a member of a class to compact the size of the class.
+// Otherwise, you should use std::optional.
+template<typename T, typename Traits>
+class Markable {
+public:
+    constexpr Markable()
+        : m_value(Traits::emptyValue())
+    { }
+
+    constexpr Markable(std::nullopt_t)
+        : Markable()
+    { }
+
+    constexpr Markable(T&& value)
+        : m_value(WTFMove(value))
+    { }
+
+    constexpr Markable(const T& value)
+        : m_value(value)
+    { }
+
+    template<typename... Args>
+    constexpr explicit Markable(std::in_place_t, Args&&... args)
+        : m_value(std::forward<Args>(args)...)
+    { }
+
+    constexpr Markable(const std::optional<T>& value)
+        : m_value(bool(value) ? *value : Traits::emptyValue())
+    { }
+
+    constexpr Markable(std::optional<T>&& value)
+        : m_value(bool(value) ? WTFMove(*value) : Traits::emptyValue())
+    { }
+
+    constexpr explicit operator bool() const { return !Traits::isEmptyValue(m_value); }
+
+    void reset() { m_value = Traits::emptyValue(); }
+
+    constexpr const T& value() const& { return m_value; }
+    constexpr T& value() & { return m_value; }
+    constexpr T&& value() && { return WTFMove(m_value); }
+
+    constexpr const T* operator->() const { return std::addressof(m_value); }
+    constexpr T* operator->() { return std::addressof(m_value); }
+
+    constexpr const T& operator*() const& { return m_value; }
+    constexpr T& operator*() & { return m_value; }
+
+    operator std::optional<T>() &&
+    {
+        if (bool(*this))
+            return WTFMove(m_value);
+        return std::nullopt;
+    }
+
+    operator std::optional<T>() const&
+    {
+        if (bool(*this))
+            return m_value;
+        return std::nullopt;
+    }
+
+private:
+    T m_value;
+};
+
+} // namespace WTF
+
+using WTF::Markable;
+using WTF::IntegralMarkableTraits;
+using WTF::EnumMarkableTraits;
index 0bbee82..7805195 100644 (file)
@@ -165,6 +165,8 @@ public:
         return true;
     }
 
+    struct MarkableTraits;
+
 private:
     constexpr MonotonicTime(double rawValue)
         : m_value(rawValue)
@@ -174,6 +176,18 @@ private:
     double m_value { 0 };
 };
 
+struct MonotonicTime::MarkableTraits {
+    static bool isEmptyValue(MonotonicTime time)
+    {
+        return std::isnan(time.m_value);
+    }
+
+    static constexpr MonotonicTime emptyValue()
+    {
+        return MonotonicTime::nan();
+    }
+};
+
 } // namespace WTF
 
 namespace std {
index 61086f3..c03e6a7 100644 (file)
@@ -247,12 +247,26 @@ public:
         return true;
     }
 
+    struct MarkableTraits;
+
 private:
     double m_value { 0 };
 };
 
 WTF_EXPORT_PRIVATE void sleep(Seconds);
 
+struct Seconds::MarkableTraits {
+    static bool isEmptyValue(Seconds seconds)
+    {
+        return std::isnan(seconds.value());
+    }
+
+    static constexpr Seconds emptyValue()
+    {
+        return Seconds::nan();
+    }
+};
+
 inline namespace seconds_literals {
 
 constexpr Seconds operator"" _min(long double minutes)
index bf66201..73ef5d7 100644 (file)
@@ -56,6 +56,7 @@ public:
     WTF_EXPORT_PRIVATE static WallTime now();
     
     static constexpr WallTime infinity() { return fromRawSeconds(std::numeric_limits<double>::infinity()); }
+    static constexpr WallTime nan() { return fromRawSeconds(std::numeric_limits<double>::quiet_NaN()); }
     
     constexpr Seconds secondsSinceEpoch() const { return Seconds(m_value); }
     
@@ -132,6 +133,9 @@ public:
     {
         return *this;
     }
+
+    struct MarkableTraits;
+
 private:
     constexpr WallTime(double rawValue)
         : m_value(rawValue)
@@ -141,6 +145,18 @@ private:
     double m_value { 0 };
 };
 
+struct WallTime::MarkableTraits {
+    static bool isEmptyValue(WallTime time)
+    {
+        return std::isnan(time.m_value);
+    }
+
+    static constexpr WallTime emptyValue()
+    {
+        return WallTime::nan();
+    }
+};
+
 WTF_EXPORT_PRIVATE void sleep(WallTime);
 
 } // namespace WTF
index 5fff931..593804b 100644 (file)
@@ -1,3 +1,18 @@
+2018-09-10  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WTF] Add Markable<T, Traits>
+        https://bugs.webkit.org/show_bug.cgi?id=189231
+
+        Reviewed by Sam Weinig.
+
+        Use Markable<Seconds> and Markable<WallTime> in ResourceResponseBase.
+        Since these fields are parsed results from http header fields, Seconds::nan() and WallTime::nan()
+        can be used as an empty value for these fields. Thus we can use Markable because it uses
+        these nan values as an empty values (they are configured by Seconds::MarkableTraits and WallTime::MarkableTraits).
+        This reduces the size of ResourceResponseBase from 448 to 416.
+
+        * platform/network/ResourceResponseBase.h:
+
 2018-09-07  Matt Rajca  <mrajca@apple.com>
 
         Touch Bar displays an active PIP button for audio elements (and it doesn't do anything)
index 2a4e49e..cb46530 100644 (file)
@@ -32,6 +32,7 @@
 #include "NetworkLoadMetrics.h"
 #include "ParsedContentRange.h"
 #include "URL.h"
+#include <wtf/Markable.h>
 #include <wtf/WallTime.h>
 
 namespace WebCore {
@@ -214,10 +215,10 @@ protected:
     mutable std::optional<CertificateInfo> m_certificateInfo;
 
 private:
-    mutable std::optional<Seconds> m_age;
-    mutable std::optional<WallTime> m_date;
-    mutable std::optional<WallTime> m_expires;
-    mutable std::optional<WallTime> m_lastModified;
+    mutable Markable<Seconds, Seconds::MarkableTraits> m_age;
+    mutable Markable<WallTime, WallTime::MarkableTraits> m_date;
+    mutable Markable<WallTime, WallTime::MarkableTraits> m_expires;
+    mutable Markable<WallTime, WallTime::MarkableTraits> m_lastModified;
     mutable ParsedContentRange m_contentRange;
     mutable CacheControlDirectives m_cacheControlDirectives;
 
index fbd43f9..df44f56 100644 (file)
@@ -1,3 +1,17 @@
+2018-09-10  Yusuke Suzuki  <yusukesuzuki@slowstart.org>
+
+        [WTF] Add Markable<T, Traits>
+        https://bugs.webkit.org/show_bug.cgi?id=189231
+
+        Reviewed by Sam Weinig.
+
+        Add tests for Markable.
+
+        * TestWebKitAPI/CMakeLists.txt:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WTF/Markable.cpp: Added.
+        (TestWebKitAPI::TEST):
+
 2018-09-10  Simon Fraser  <simon.fraser@apple.com>
 
         Before tracking Document leaks, clear all caches
index caa85fc..831c1ba 100644 (file)
@@ -123,6 +123,7 @@ set(TestWTF_SOURCES
     ${TESTWEBKITAPI_DIR}/Tests/WTF/Lock.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/Logger.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/MD5.cpp
+    ${TESTWEBKITAPI_DIR}/Tests/WTF/Markable.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/MathExtras.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/MediaTime.cpp
     ${TESTWEBKITAPI_DIR}/Tests/WTF/MetaAllocator.cpp
index 8b4d677..0475724 100644 (file)
@@ -41,6 +41,7 @@
                0F139E791A42457000F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
                0F2C20B81DCD545000542D9E /* Time.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F2C20B71DCD544800542D9E /* Time.cpp */; };
                0F30CB5C1FCE1796004B5323 /* ConcurrentPtrHashSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F30CB5B1FCE1792004B5323 /* ConcurrentPtrHashSet.cpp */; };
+               4909EE3A2D09480C88982D56 /* Markable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC79F168BE454E579E417B05 /* Markable.cpp */; };
                0F3B94A71A77267400DE3272 /* WKWebViewEvaluateJavaScript.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */; };
                0F4FFA9E1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F4FFA9D1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm */; };
                0F5651F71FCE4DDC00310FBC /* NoHistoryItemScrollToFragment.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F5651F61FCE4DDB00310FBC /* NoHistoryItemScrollToFragment.mm */; };
                0F17BBD415AF6C4D007AB753 /* WebCoreStatisticsWithNoWebProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebCoreStatisticsWithNoWebProcess.cpp; sourceTree = "<group>"; };
                0F2C20B71DCD544800542D9E /* Time.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Time.cpp; sourceTree = "<group>"; };
                0F30CB5B1FCE1792004B5323 /* ConcurrentPtrHashSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConcurrentPtrHashSet.cpp; sourceTree = "<group>"; };
+               EC79F168BE454E579E417B05 /* Markable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Markable.cpp; sourceTree = "<group>"; };
                0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewEvaluateJavaScript.mm; sourceTree = "<group>"; };
                0F4FFA9D1ED3AA8500F7111F /* SnapshotViaRenderInContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SnapshotViaRenderInContext.mm; sourceTree = "<group>"; };
                0F4FFAA01ED3D0DE00F7111F /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
                                0FFC45A41B73EBE20085BD62 /* Lock.cpp */,
                                A57D54F41F3395D000A97AA7 /* Logger.cpp */,
                                A57D54F51F3395D000A97AA7 /* Logger.h */,
+                               EC79F168BE454E579E417B05 /* Markable.cpp */,
                                B4039F9C15E6D8B3007255D6 /* MathExtras.cpp */,
                                CD5393C71757BA9700C07123 /* MD5.cpp */,
                                CD5497B315857F0C00B5BC30 /* MediaTime.cpp */,
                                7C83DEE81D0A590C00FEBCF3 /* ListHashSet.cpp in Sources */,
                                7C83DF1D1D0A590C00FEBCF3 /* Lock.cpp in Sources */,
                                A57D54F61F3395D000A97AA7 /* Logger.cpp in Sources */,
+                               4909EE3A2D09480C88982D56 /* Markable.cpp in Sources */,
                                7C83DEED1D0A590C00FEBCF3 /* MathExtras.cpp in Sources */,
                                7C83DEEF1D0A590C00FEBCF3 /* MD5.cpp in Sources */,
                                7C83DEF11D0A590C00FEBCF3 /* MediaTime.cpp in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WTF/Markable.cpp b/Tools/TestWebKitAPI/Tests/WTF/Markable.cpp
new file mode 100644 (file)
index 0000000..ee43848
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2018 Yusuke Suzuki <yusukesuzuki@slowstart.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <wtf/Markable.h>
+
+namespace TestWebKitAPI {
+
+TEST(WTF_Markable, Disengaged)
+{
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional;
+
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { std::nullopt };
+
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+}
+
+TEST(WTF_Markable, Engaged)
+{
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 10 };
+
+        EXPECT_TRUE(static_cast<bool>(optional));
+        EXPECT_EQ(10, optional.value());
+
+        optional = 41;
+        EXPECT_TRUE(static_cast<bool>(optional));
+        EXPECT_EQ(41, optional.value());
+
+        optional = std::nullopt;
+        EXPECT_FALSE(static_cast<bool>(optional));
+
+        optional = 42;
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 42 };
+
+        EXPECT_FALSE(static_cast<bool>(optional));
+
+        optional = 41;
+
+        EXPECT_TRUE(static_cast<bool>(optional));
+        EXPECT_EQ(41, optional.value());
+    }
+}
+
+TEST(WTF_Markable, Destructor)
+{
+    static bool didCallDestructor = false;
+    struct A {
+        explicit A(int value)
+            : m_value(value)
+        { }
+
+        ~A()
+        {
+            EXPECT_FALSE(didCallDestructor);
+            didCallDestructor = true;
+        }
+
+        int m_value { 42 };
+    };
+
+    struct ATraits {
+        static bool isEmptyValue(const A& value)
+        {
+            return value.m_value == 42;
+        }
+        static A emptyValue()
+        {
+            return A(42);
+        }
+    };
+
+    didCallDestructor = false;
+    {
+        Markable<A, ATraits> optional { std::in_place, 20 };
+        EXPECT_TRUE(static_cast<bool>(optional));
+    }
+    EXPECT_TRUE(didCallDestructor);
+
+    didCallDestructor = false;
+    {
+        Markable<A, ATraits> optional { std::in_place, 42 };
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    EXPECT_TRUE(didCallDestructor);
+}
+
+TEST(WTF_Markable, FromOptional)
+{
+    {
+        std::optional<int> from;
+        EXPECT_FALSE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional = from;
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    {
+        std::optional<int> from { 42 };
+        EXPECT_TRUE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional = from;
+        // We convert this to nullopt.
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    {
+        std::optional<int> from { 43 };
+        EXPECT_TRUE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional = from;
+        EXPECT_TRUE(static_cast<bool>(optional));
+        EXPECT_EQ(optional.value(), 43);
+    }
+    {
+        std::optional<int> from;
+        EXPECT_FALSE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { WTFMove(from) };
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    {
+        std::optional<int> from { 42 };
+        EXPECT_TRUE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { WTFMove(from) };
+        // We convert this to nullopt.
+        EXPECT_FALSE(static_cast<bool>(optional));
+    }
+    {
+        std::optional<int> from { 43 };
+        EXPECT_TRUE(static_cast<bool>(from));
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { WTFMove(from) };
+        EXPECT_TRUE(static_cast<bool>(optional));
+        EXPECT_EQ(optional.value(), 43);
+    }
+}
+
+TEST(WTF_Markable, ToOptional)
+{
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional;
+        EXPECT_FALSE(static_cast<bool>(optional));
+        std::optional<int> to = optional;
+        EXPECT_FALSE(static_cast<bool>(to));
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 42 };
+        EXPECT_FALSE(static_cast<bool>(optional));
+        // We convert this to nullopt.
+        std::optional<int> to = optional;
+        EXPECT_FALSE(static_cast<bool>(to));
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 43 };
+        EXPECT_TRUE(static_cast<bool>(optional));
+        std::optional<int> to = optional;
+        EXPECT_TRUE(static_cast<bool>(to));
+        EXPECT_EQ(to.value(), 43);
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional;
+        EXPECT_FALSE(static_cast<bool>(optional));
+        std::optional<int> to { WTFMove(optional) };
+        EXPECT_FALSE(static_cast<bool>(to));
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 42 };
+        EXPECT_FALSE(static_cast<bool>(optional));
+        // We convert this to nullopt.
+        std::optional<int> to { WTFMove(optional) };
+        EXPECT_FALSE(static_cast<bool>(to));
+    }
+    {
+        Markable<int, IntegralMarkableTraits<int, 42>> optional { 43 };
+        EXPECT_TRUE(static_cast<bool>(optional));
+        std::optional<int> to { WTFMove(optional) };
+        EXPECT_TRUE(static_cast<bool>(to));
+        EXPECT_EQ(to.value(), 43);
+    }
+}
+
+TEST(WTF_Markable, MoveOptional)
+{
+    class OnlyMovable {
+    public:
+        constexpr explicit OnlyMovable(int value)
+            : m_value(value)
+        {
+        }
+
+        int value() { return m_value; }
+
+        OnlyMovable& operator=(OnlyMovable&& other)
+        {
+            m_value = other.m_value;
+            other.m_value = 42;
+            return *this;
+        }
+
+        OnlyMovable(OnlyMovable&& other)
+            : m_value(other.m_value)
+        {
+            other.m_value = 42;
+        }
+
+        static OnlyMovable emptyValue()
+        {
+            return OnlyMovable(42);
+        }
+
+        static constexpr bool isEmptyValue(const OnlyMovable& value)
+        {
+            return value.m_value == 42;
+        }
+
+    private:
+        OnlyMovable(const OnlyMovable&) = delete;
+        OnlyMovable& operator=(const OnlyMovable&) = delete;
+
+        int m_value;
+    };
+    {
+        std::optional<OnlyMovable> from { std::in_place, 20 };
+        EXPECT_TRUE(static_cast<bool>(from));
+        EXPECT_EQ(from.value().value(), 20);
+        Markable<OnlyMovable, OnlyMovable> compact = WTFMove(from);
+        EXPECT_TRUE(static_cast<bool>(compact));
+        EXPECT_EQ(compact.value().value(), 20);
+        std::optional<OnlyMovable> to = WTFMove(compact);
+        EXPECT_TRUE(static_cast<bool>(to));
+        EXPECT_EQ(to.value().value(), 20);
+    }
+}
+
+} // namespace TestWebKitAPI