Improve FontSelectionAlgorithm, including moving from IntegerHasher to Hasher
authordarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 03:50:06 +0000 (03:50 +0000)
committerdarin@apple.com <darin@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Dec 2017 03:50:06 +0000 (03:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=180340

Reviewed by Dan Bates.

* css/CSSFontFaceSet.h: Moved FontSelectionRequestKey and FontSelectionRequestKeyHash
here to be private members, and used a std::optional instead of a class for this.
Also use the new Hasher to compute the hash. Also added FontSelectionRequestKeyHashTraits.

* platform/graphics/FontDescription.cpp:
(WebCore::FontDescription::FontDescription): Updated since FontSelectionRequest
does not always have a constructor any more.

* platform/graphics/FontSelectionAlgorithm.h: Tweaked comments. Used "using" instead
of typedef. Formatted some trivial functions as single lines. Stopped using
NeverDestroyed for simple classes like FontSelectionValue; it's not needed unless
there is a destructor. Got rid of some incorrect use of const. Replaced some member
functions with non-member functions. Moved some function bodies out of class definitions.
Used a lot of constexpr functions.
(WebCore::FontSelectionRequest::tied const): Added so we can easily write both == and the
hash function without listing the data members.
(WebCore::add): Added an overload so we can hash things that include FontSelectionRequest.
(WebCore::FontSelectionRequestKey::FontSelectionRequestKey): Changed this class to
use std::optional instead of a separate boolean for deleted values.
(WebCore::FontSelectionRequestKey::isHashTableDeletedValue const): Ditto.
(WebCore::FontSelectionRequestKey::operator== const): Ditto.
(WebCore::FontSelectionRequestKeyHash::hash): Ditto.
(WebCore::FontSelectionRequestKeyHash::equal): Ditto.
(WebCore::FontSelectionCapabilities::tied const): Added so we can easily write both ==
and the hash function without listing the data members.
(WebCore::FontSelectionSpecifiedCapabilities::tied const): Ditto.
(WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm): Use make_unique instead of new.

* platform/graphics/cocoa/FontCacheCoreText.cpp:
(WebCore::findClosestFont): Move in the vector instead of copying it when creating a
FontSelectionAlgorithm object.

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

Source/WebCore/ChangeLog
Source/WebCore/css/CSSFontFaceSet.h
Source/WebCore/platform/graphics/FontDescription.cpp
Source/WebCore/platform/graphics/FontSelectionAlgorithm.cpp
Source/WebCore/platform/graphics/FontSelectionAlgorithm.h

index 9e4911c..f0fb7d3 100644 (file)
@@ -1,3 +1,42 @@
+2017-12-11  Darin Adler  <darin@apple.com>
+
+        Improve FontSelectionAlgorithm, including moving from IntegerHasher to Hasher
+        https://bugs.webkit.org/show_bug.cgi?id=180340
+
+        Reviewed by Dan Bates.
+
+        * css/CSSFontFaceSet.h: Moved FontSelectionRequestKey and FontSelectionRequestKeyHash
+        here to be private members, and used a std::optional instead of a class for this.
+        Also use the new Hasher to compute the hash. Also added FontSelectionRequestKeyHashTraits.
+
+        * platform/graphics/FontDescription.cpp:
+        (WebCore::FontDescription::FontDescription): Updated since FontSelectionRequest
+        does not always have a constructor any more.
+
+        * platform/graphics/FontSelectionAlgorithm.h: Tweaked comments. Used "using" instead
+        of typedef. Formatted some trivial functions as single lines. Stopped using
+        NeverDestroyed for simple classes like FontSelectionValue; it's not needed unless
+        there is a destructor. Got rid of some incorrect use of const. Replaced some member
+        functions with non-member functions. Moved some function bodies out of class definitions.
+        Used a lot of constexpr functions.
+        (WebCore::FontSelectionRequest::tied const): Added so we can easily write both == and the
+        hash function without listing the data members.
+        (WebCore::add): Added an overload so we can hash things that include FontSelectionRequest.
+        (WebCore::FontSelectionRequestKey::FontSelectionRequestKey): Changed this class to
+        use std::optional instead of a separate boolean for deleted values.
+        (WebCore::FontSelectionRequestKey::isHashTableDeletedValue const): Ditto.
+        (WebCore::FontSelectionRequestKey::operator== const): Ditto.
+        (WebCore::FontSelectionRequestKeyHash::hash): Ditto.
+        (WebCore::FontSelectionRequestKeyHash::equal): Ditto.
+        (WebCore::FontSelectionCapabilities::tied const): Added so we can easily write both ==
+        and the hash function without listing the data members.
+        (WebCore::FontSelectionSpecifiedCapabilities::tied const): Ditto.
+        (WebCore::FontSelectionAlgorithm::FontSelectionAlgorithm): Use make_unique instead of new.
+
+        * platform/graphics/cocoa/FontCacheCoreText.cpp:
+        (WebCore::findClosestFont): Move in the vector instead of copying it when creating a
+        FontSelectionAlgorithm object.
+
 2017-12-11  Jer Noble  <jer.noble@apple.com>
 
         [EME] support update() for FairPlayStreaming in Modern EME API
index 7b54bed..337feab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * 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
@@ -97,11 +97,24 @@ private:
 
     static String familyNameFromPrimitive(const CSSPrimitiveValue&);
 
+    using FontSelectionKey = std::optional<FontSelectionRequest>;
+    struct FontSelectionKeyHash {
+        static unsigned hash(const FontSelectionKey& key) { return computeHash(key); }
+        static bool equal(const FontSelectionKey& a, const FontSelectionKey& b) { return a == b; }
+        static const bool safeToCompareToEmptyOrDeleted = true;
+    };
+    struct FontSelectionKeyHashTraits : SimpleClassHashTraits<FontSelectionKey> {
+        static const bool emptyValueIsZero = false;
+        static FontSelectionKey emptyValue() { return FontSelectionRequest { }; }
+        static void constructDeletedValue(FontSelectionKey& slot) { slot = std::nullopt; }
+        static bool isDeletedValue(const FontSelectionKey& value) { return !value; }
+    };
+    using FontSelectionHashMap = HashMap<FontSelectionKey, RefPtr<CSSSegmentedFontFace>, FontSelectionKeyHash, FontSelectionKeyHashTraits>;
+
     // m_faces should hold all the same fonts as the ones inside inside m_facesLookupTable.
     Vector<Ref<CSSFontFace>> m_faces; // We should investigate moving m_faces to FontFaceSet and making it reference FontFaces. This may clean up the font loading design.
     HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_facesLookupTable;
     HashMap<String, Vector<Ref<CSSFontFace>>, ASCIICaseInsensitiveHash> m_locallyInstalledFacesLookupTable;
-    typedef HashMap<FontSelectionRequestKey, RefPtr<CSSSegmentedFontFace>, FontSelectionRequestKeyHash, WTF::SimpleClassHashTraits<FontSelectionRequestKey>> FontSelectionHashMap;
     HashMap<String, FontSelectionHashMap, ASCIICaseInsensitiveHash> m_cache;
     HashMap<StyleRuleFontFace*, CSSFontFace*> m_constituentCSSConnections;
     size_t m_facesPartitionIndex { 0 }; // All entries in m_faces before this index are CSS-connected.
index 1d9d785..df4a9cc 100644 (file)
@@ -55,7 +55,7 @@ struct SameSizeAsFontCascadeDescription {
 COMPILE_ASSERT(sizeof(FontCascadeDescription) == sizeof(SameSizeAsFontCascadeDescription), FontCascadeDescription_should_stay_small);
 
 FontDescription::FontDescription()
-    : m_fontSelectionRequest(FontCascadeDescription::initialWeight(), FontCascadeDescription::initialStretch(), FontCascadeDescription::initialItalic())
+    : m_fontSelectionRequest { FontCascadeDescription::initialWeight(), FontCascadeDescription::initialStretch(), FontCascadeDescription::initialItalic() }
     , m_orientation(Horizontal)
     , m_nonCJKGlyphOrientation(static_cast<unsigned>(NonCJKGlyphOrientation::Mixed))
     , m_widthVariant(RegularWidth)
index f2b8dc3..9979aca 100644 (file)
 
 namespace WebCore {
 
-auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+FontSelectionAlgorithm::FontSelectionAlgorithm(FontSelectionRequest request, const Vector<Capabilities>& capabilities, std::optional<Capabilities> bounds)
+    : m_request(request)
+    , m_capabilities(capabilities)
+{
+    ASSERT(!m_capabilities.isEmpty());
+    if (bounds)
+        m_capabilitiesBounds = bounds.value();
+    else {
+        for (auto& capabilities : m_capabilities)
+            m_capabilitiesBounds.expand(capabilities);
+    }
+}
+
+auto FontSelectionAlgorithm::stretchDistance(Capabilities capabilities) const -> DistanceResult
 {
     auto width = capabilities.width;
     ASSERT(width.isValid());
@@ -50,7 +63,7 @@ auto FontSelectionAlgorithm::stretchDistance(FontSelectionCapabilities capabilit
     return { width.minimum - threshold, width.minimum };
 }
 
-auto FontSelectionAlgorithm::styleDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+auto FontSelectionAlgorithm::styleDistance(Capabilities capabilities) const -> DistanceResult
 {
     auto slope = capabilities.slope;
     ASSERT(slope.isValid());
@@ -92,7 +105,7 @@ auto FontSelectionAlgorithm::styleDistance(FontSelectionCapabilities capabilitie
     return { slope.minimum - threshold, slope.minimum };
 }
 
-auto FontSelectionAlgorithm::weightDistance(FontSelectionCapabilities capabilities) const -> DistanceResult
+auto FontSelectionAlgorithm::weightDistance(Capabilities capabilities) const -> DistanceResult
 {
     auto weight = capabilities.weight;
     ASSERT(weight.isValid());
@@ -123,35 +136,35 @@ auto FontSelectionAlgorithm::weightDistance(FontSelectionCapabilities capabiliti
     return { threshold - weight.maximum, weight.maximum };
 }
 
-void FontSelectionAlgorithm::filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange)
+FontSelectionValue FontSelectionAlgorithm::bestValue(const bool eliminated[], DistanceFunction computeDistance) const
 {
-    std::optional<FontSelectionValue> smallestDistance;
-    FontSelectionValue closestValue;
-    iterateActiveCapabilities([&](FontSelectionCapabilities capabilities, size_t) {
-        auto distanceResult = (this->*computeDistance)(capabilities);
-        if (!smallestDistance || distanceResult.distance < smallestDistance.value()) {
-            smallestDistance = distanceResult.distance;
-            closestValue = distanceResult.value;
-        }
-    });
-    ASSERT(smallestDistance);
-    iterateActiveCapabilities([&](auto& capabilities, size_t i) {
-        if (!(capabilities.*inclusionRange).includes(closestValue))
-            m_filter[i] = false;
-    });
+    std::optional<DistanceResult> smallestDistance;
+    for (size_t i = 0, size = m_capabilities.size(); i < size; ++i) {
+        if (eliminated[i])
+            continue;
+        auto distanceResult = (this->*computeDistance)(m_capabilities[i]);
+        if (!smallestDistance || distanceResult.distance < smallestDistance.value().distance)
+            smallestDistance = distanceResult;
+    }
+    return smallestDistance.value().value;
+}
+
+void FontSelectionAlgorithm::filterCapability(bool eliminated[], DistanceFunction computeDistance, CapabilitiesRange inclusionRange)
+{
+    auto value = bestValue(eliminated, computeDistance);
+    for (size_t i = 0, size = m_capabilities.size(); i < size; ++i) {
+        eliminated[i] = eliminated[i]
+            || !(m_capabilities[i].*inclusionRange).includes(value);
+    }
 }
 
 size_t FontSelectionAlgorithm::indexOfBestCapabilities()
 {
-    filterCapability(&FontSelectionAlgorithm::stretchDistance, &FontSelectionCapabilities::width);
-    filterCapability(&FontSelectionAlgorithm::styleDistance, &FontSelectionCapabilities::slope);
-    filterCapability(&FontSelectionAlgorithm::weightDistance, &FontSelectionCapabilities::weight);
-
-    auto result = iterateActiveCapabilitiesWithReturn<size_t>([](FontSelectionCapabilities, size_t i) {
-        return i;
-    });
-    ASSERT(result);
-    return result.value_or(0);
+    Vector<bool, 256> eliminated(m_capabilities.size(), false);
+    filterCapability(eliminated.data(), &FontSelectionAlgorithm::stretchDistance, &Capabilities::width);
+    filterCapability(eliminated.data(), &FontSelectionAlgorithm::styleDistance, &Capabilities::slope);
+    filterCapability(eliminated.data(), &FontSelectionAlgorithm::weightDistance, &Capabilities::weight);
+    return eliminated.find(false);
 }
 
 }
index b71f917..66289b7 100644 (file)
 #pragma once
 
 #include "TextFlags.h"
-#include <wtf/Function.h>
-#include <wtf/GetPtr.h>
+#include <algorithm>
+#include <tuple>
 #include <wtf/Hasher.h>
-#include <wtf/NeverDestroyed.h>
 #include <wtf/Optional.h>
-#include <wtf/Vector.h>
 
 namespace WebCore {
 
 // Unclamped, unchecked, signed fixed-point number representing a value used for font variations.
-// Sixteen bits in total, one sign bit, two fractional bits, means the smallest positive representable value is 0.25,
-// the maximum representable value is 8191.75, and the minimum representable value is -8192.
+// Sixteen bits in total, one sign bit, two fractional bits, smallest positive value is 0.25,
+// maximum value is 8191.75, and minimum value is -8192.
 class FontSelectionValue {
 public:
-    typedef int16_t BackingType;
+    using BackingType = int16_t;
 
     FontSelectionValue() = default;
 
-    // Explicit because it is lossy.
-    explicit FontSelectionValue(int x)
-        : m_backing(x * fractionalEntropy)
-    {
-    }
+    // Explicit because it won't work correctly for values outside the representable range.
+    explicit constexpr FontSelectionValue(int);
 
-    // Explicit because it is lossy.
-    explicit FontSelectionValue(float x)
-        : m_backing(x * fractionalEntropy)
-    {
-    }
+    // Explicit because it won't work correctly for values outside the representable range and because precision can be lost.
+    explicit constexpr FontSelectionValue(float);
 
-    operator float() const
-    {
-        // floats have 23 fractional bits, but only 14 fractional bits are necessary, so every value can be represented losslessly.
-        return m_backing / static_cast<float>(fractionalEntropy);
-    }
+    // Precision can be lost, but value will be clamped to the representable range.
+    static constexpr FontSelectionValue clampFloat(float);
 
-    FontSelectionValue operator+(const FontSelectionValue other) const;
-    FontSelectionValue operator-(const FontSelectionValue other) const;
-    FontSelectionValue operator*(const FontSelectionValue other) const;
-    FontSelectionValue operator/(const FontSelectionValue other) const;
-    FontSelectionValue operator-() const;
-    bool operator==(const FontSelectionValue other) const;
-    bool operator!=(const FontSelectionValue other) const;
-    bool operator<(const FontSelectionValue other) const;
-    bool operator<=(const FontSelectionValue other) const;
-    bool operator>(const FontSelectionValue other) const;
-    bool operator>=(const FontSelectionValue other) const;
-
-    BackingType rawValue() const
-    {
-        return m_backing;
-    }
+    // Since floats have 23 mantissa bits, every value can be represented losslessly.
+    constexpr operator float() const;
 
-    static FontSelectionValue maximumValue()
-    {
-        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<BackingType>::max(), RawTag::RawTag);
-        return result.get();
-    }
+    static constexpr FontSelectionValue maximumValue();
+    static constexpr FontSelectionValue minimumValue();
 
-    static FontSelectionValue minimumValue()
-    {
-        static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(std::numeric_limits<BackingType>::min(), RawTag::RawTag);
-        return result.get();
-    }
+    friend constexpr FontSelectionValue operator+(FontSelectionValue, FontSelectionValue);
+    friend constexpr FontSelectionValue operator-(FontSelectionValue, FontSelectionValue);
+    friend constexpr FontSelectionValue operator*(FontSelectionValue, FontSelectionValue);
+    friend constexpr FontSelectionValue operator/(FontSelectionValue, FontSelectionValue);
+    friend constexpr FontSelectionValue operator-(FontSelectionValue);
 
-    static FontSelectionValue clampFloat(float value)
-    {
-        if (value < static_cast<float>(FontSelectionValue::minimumValue()))
-            return FontSelectionValue::minimumValue();
-        if (value > static_cast<float>(FontSelectionValue::maximumValue()))
-            return FontSelectionValue::maximumValue();
-        return FontSelectionValue(value);
-    }
+    constexpr BackingType rawValue() const { return m_backing; }
 
 private:
     enum class RawTag { RawTag };
-
-    FontSelectionValue(BackingType rawValue, RawTag)
-        : m_backing(rawValue)
-    {
-    }
+    constexpr FontSelectionValue(int, RawTag);
 
     static constexpr int fractionalEntropy = 4;
     BackingType m_backing { 0 };
 };
 
-inline FontSelectionValue FontSelectionValue::operator+(const FontSelectionValue other) const
+constexpr FontSelectionValue::FontSelectionValue(int x)
+    : m_backing(x * fractionalEntropy)
+{
+    // FIXME: Should we assert the passed in value was in range?
+}
+
+constexpr FontSelectionValue::FontSelectionValue(float x)
+    : m_backing(x * fractionalEntropy)
 {
-    return FontSelectionValue(m_backing + other.m_backing, RawTag::RawTag);
+    // FIXME: Should we assert the passed in value was in range?
 }
 
-inline FontSelectionValue FontSelectionValue::operator-(const FontSelectionValue other) const
+constexpr FontSelectionValue::operator float() const
 {
-    return FontSelectionValue(m_backing - other.m_backing, RawTag::RawTag);
+    return m_backing / static_cast<float>(fractionalEntropy);
 }
 
-inline FontSelectionValue FontSelectionValue::operator*(const FontSelectionValue other) const
+constexpr FontSelectionValue FontSelectionValue::maximumValue()
 {
-    return FontSelectionValue(static_cast<int32_t>(m_backing) * other.m_backing / fractionalEntropy, RawTag::RawTag);
+    return { std::numeric_limits<BackingType>::max(), RawTag::RawTag };
 }
 
-inline FontSelectionValue FontSelectionValue::operator/(const FontSelectionValue other) const
+constexpr FontSelectionValue FontSelectionValue::minimumValue()
 {
-    return FontSelectionValue(static_cast<int32_t>(m_backing) / other.m_backing * fractionalEntropy, RawTag::RawTag);
+    return { std::numeric_limits<BackingType>::min(), RawTag::RawTag };
 }
 
-inline FontSelectionValue FontSelectionValue::operator-() const
+constexpr FontSelectionValue FontSelectionValue::clampFloat(float value)
 {
-    return FontSelectionValue(-m_backing, RawTag::RawTag);
+    return FontSelectionValue { std::max<float>(minimumValue(), std::min<float>(value, maximumValue())) };
 }
 
-inline bool FontSelectionValue::operator==(const FontSelectionValue other) const
+constexpr FontSelectionValue::FontSelectionValue(int rawValue, RawTag)
+    : m_backing(rawValue)
 {
-    return m_backing == other.m_backing;
 }
 
-inline bool FontSelectionValue::operator!=(const FontSelectionValue other) const
+constexpr FontSelectionValue operator+(FontSelectionValue a, FontSelectionValue b)
 {
-    return !operator==(other);
+    return { a.m_backing + b.m_backing, FontSelectionValue::RawTag::RawTag };
 }
 
-inline bool FontSelectionValue::operator<(const FontSelectionValue other) const
+constexpr FontSelectionValue operator-(FontSelectionValue a, FontSelectionValue b)
 {
-    return m_backing < other.m_backing;
+    return { a.m_backing - b.m_backing, FontSelectionValue::RawTag::RawTag };
 }
 
-inline bool FontSelectionValue::operator<=(const FontSelectionValue other) const
+constexpr FontSelectionValue operator*(FontSelectionValue a, FontSelectionValue b)
 {
-    return m_backing <= other.m_backing;
+    return { a.m_backing * b.m_backing / FontSelectionValue::fractionalEntropy, FontSelectionValue::RawTag::RawTag };
 }
 
-inline bool FontSelectionValue::operator>(const FontSelectionValue other) const
+constexpr FontSelectionValue operator/(FontSelectionValue a, FontSelectionValue b)
 {
-    return m_backing > other.m_backing;
+    return { a.m_backing * FontSelectionValue::fractionalEntropy / b.m_backing, FontSelectionValue::RawTag::RawTag };
 }
 
-inline bool FontSelectionValue::operator>=(const FontSelectionValue other) const
+constexpr FontSelectionValue operator-(FontSelectionValue value)
 {
-    return m_backing >= other.m_backing;
+    return { -value.m_backing, FontSelectionValue::RawTag::RawTag };
 }
 
-static inline FontSelectionValue italicThreshold()
+constexpr bool operator==(FontSelectionValue a, FontSelectionValue b)
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(20);
-    return result.get();
+    return a.rawValue() == b.rawValue();
 }
 
-static inline bool isItalic(FontSelectionValue fontWeight)
+constexpr bool operator!=(FontSelectionValue a, FontSelectionValue b)
+{
+    return a.rawValue() != b.rawValue();
+}
+
+constexpr bool operator<(FontSelectionValue a, FontSelectionValue b)
+{
+    return a.rawValue() < b.rawValue();
+}
+
+constexpr bool operator<=(FontSelectionValue a, FontSelectionValue b)
+{
+    return a.rawValue() <= b.rawValue();
+}
+
+constexpr bool operator>(FontSelectionValue a, FontSelectionValue b)
+{
+    return a.rawValue() > b.rawValue();
+}
+
+constexpr bool operator>=(FontSelectionValue a, FontSelectionValue b)
+{
+    return a.rawValue() >= b.rawValue();
+}
+
+constexpr FontSelectionValue italicThreshold()
+{
+    return FontSelectionValue { 20 };
+}
+
+constexpr bool isItalic(FontSelectionValue fontWeight)
 {
     return fontWeight >= italicThreshold();
 }
 
-static inline FontSelectionValue normalItalicValue()
+constexpr FontSelectionValue normalItalicValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue();
-    return result.get();
+    return FontSelectionValue { 0 };
 }
 
-static inline FontSelectionValue italicValue()
+constexpr FontSelectionValue italicValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(20);
-    return result.get();
+    return FontSelectionValue { 20 };
 }
 
-static inline FontSelectionValue boldThreshold()
+constexpr FontSelectionValue boldThreshold()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(600);
-    return result.get();
+    return FontSelectionValue { 600 };
 }
 
-static inline FontSelectionValue boldWeightValue()
+constexpr FontSelectionValue boldWeightValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(700);
-    return result.get();
+    return FontSelectionValue { 700 };
 }
 
-static inline FontSelectionValue normalWeightValue()
+constexpr FontSelectionValue normalWeightValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(400);
-    return result.get();
+    return FontSelectionValue { 400 };
 }
 
-static inline FontSelectionValue lightWeightValue()
+constexpr FontSelectionValue lightWeightValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(200);
-    return result.get();
+    return FontSelectionValue { 200 };
 }
 
-static inline bool isFontWeightBold(FontSelectionValue fontWeight)
+constexpr bool isFontWeightBold(FontSelectionValue fontWeight)
 {
     return fontWeight >= boldThreshold();
 }
 
-static inline FontSelectionValue lowerWeightSearchThreshold()
+constexpr FontSelectionValue lowerWeightSearchThreshold()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(400);
-    return result.get();
+    return FontSelectionValue { 400 };
 }
 
-static inline FontSelectionValue upperWeightSearchThreshold()
+constexpr FontSelectionValue upperWeightSearchThreshold()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(500);
-    return result.get();
+    return FontSelectionValue { 500 };
 }
 
-static inline FontSelectionValue ultraCondensedStretchValue()
+constexpr FontSelectionValue ultraCondensedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(50);
-    return result.get();
+    return FontSelectionValue { 50 };
 }
 
-static inline FontSelectionValue extraCondensedStretchValue()
+constexpr FontSelectionValue extraCondensedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(62.5f);
-    return result.get();
+    return FontSelectionValue { 62.5f };
 }
 
-static inline FontSelectionValue condensedStretchValue()
+constexpr FontSelectionValue condensedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(75);
-    return result.get();
+    return FontSelectionValue { 75 };
 }
 
-static inline FontSelectionValue semiCondensedStretchValue()
+constexpr FontSelectionValue semiCondensedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(87.5f);
-    return result.get();
+    return FontSelectionValue { 87.5f };
 }
 
-static inline FontSelectionValue normalStretchValue()
+constexpr FontSelectionValue normalStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(100);
-    return result.get();
+    return FontSelectionValue { 100 };
 }
 
-static inline FontSelectionValue semiExpandedStretchValue()
+constexpr FontSelectionValue semiExpandedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(112.5f);
-    return result.get();
+    return FontSelectionValue { 112.5f };
 }
 
-static inline FontSelectionValue expandedStretchValue()
+constexpr FontSelectionValue expandedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(125);
-    return result.get();
+    return FontSelectionValue { 125 };
 }
 
-static inline FontSelectionValue extraExpandedStretchValue()
+constexpr FontSelectionValue extraExpandedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(150);
-    return result.get();
+    return FontSelectionValue { 150 };
 }
 
-static inline FontSelectionValue ultraExpandedStretchValue()
+constexpr FontSelectionValue ultraExpandedStretchValue()
 {
-    static NeverDestroyed<FontSelectionValue> result = FontSelectionValue(200);
-    return result.get();
+    return FontSelectionValue { 200 };
 }
 
 // [Inclusive, Inclusive]
 struct FontSelectionRange {
-    FontSelectionRange(FontSelectionValue minimum, FontSelectionValue maximum)
+    using Value = FontSelectionValue;
+
+    constexpr FontSelectionRange(Value minimum, Value maximum)
         : minimum(minimum)
         , maximum(maximum)
     {
     }
 
-    bool operator==(const FontSelectionRange& other) const
+    explicit constexpr FontSelectionRange(Value value)
+        : minimum(value)
+        , maximum(value)
     {
-        return minimum == other.minimum
-            && maximum == other.maximum;
     }
 
-    bool isValid() const
+    constexpr bool operator==(const FontSelectionRange& other) const
+    {
+        return std::tie(minimum, maximum) == std::tie(other.minimum, other.maximum);
+    }
+
+    constexpr bool isValid() const
     {
         return minimum <= maximum;
     }
@@ -316,107 +303,75 @@ struct FontSelectionRange {
         ASSERT(isValid());
     }
 
-    bool includes(FontSelectionValue target) const
+    constexpr bool includes(Value target) const
     {
         return target >= minimum && target <= maximum;
     }
 
-    uint32_t uniqueValue() const
+    // FIXME: This name is not so great. Move this into the add function below
+    // once we move FontPlatformDataCacheKeyHash from IntegerHasher to Hasher,
+    // and then it doesn't need to have a name.
+    constexpr uint32_t uniqueValue() const
     {
         return minimum.rawValue() << 16 | maximum.rawValue();
     }
 
-    FontSelectionValue minimum { FontSelectionValue(1) };
-    FontSelectionValue maximum { FontSelectionValue(0) };
+    Value minimum { 1 };
+    Value maximum { 0 };
 };
 
-struct FontSelectionRequest {
-    FontSelectionRequest() = default;
-
-    FontSelectionRequest(FontSelectionValue weight, FontSelectionValue width, FontSelectionValue slope)
-        : weight(weight)
-        , width(width)
-        , slope(slope)
-    {
-    }
-
-    bool operator==(const FontSelectionRequest& other) const
-    {
-        return weight == other.weight
-            && width == other.width
-            && slope == other.slope;
-    }
-
-    bool operator!=(const FontSelectionRequest& other) const
-    {
-        return !operator==(other);
-    }
-
-    FontSelectionValue weight;
-    FontSelectionValue width;
-    FontSelectionValue slope;
-};
+inline void add(Hasher& hasher, const FontSelectionRange& range)
+{
+    add(hasher, range.uniqueValue());
+}
 
-// Only used for HashMaps. We don't want to put the bool into FontSelectionRequest
-// because FontSelectionRequest needs to be as small as possible because it's inside
-// every FontDescription.
-struct FontSelectionRequestKey {
-    FontSelectionRequestKey() = default;
+struct FontSelectionRequest {
+    using Value = FontSelectionValue;
 
-    FontSelectionRequestKey(FontSelectionRequest request)
-        : request(request)
-    {
-    }
+    Value weight;
+    Value width;
+    Value slope;
 
-    explicit FontSelectionRequestKey(WTF::HashTableDeletedValueType)
-        : isDeletedValue(true)
+    constexpr std::tuple<Value, Value, Value> tied() const
     {
+        return std::tie(weight, width, slope);
     }
 
-    bool isHashTableDeletedValue() const
-    {
-        return isDeletedValue;
-    }
+#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
+    FontSelectionRequest() = default;
 
-    bool operator==(const FontSelectionRequestKey& other) const
+    constexpr FontSelectionRequest(Value weight, Value width, Value slope)
+        : weight(weight)
+        , width(width)
+        , slope(slope)
     {
-        return request == other.request
-            && isDeletedValue == other.isDeletedValue;
     }
-
-    FontSelectionRequest request;
-    bool isDeletedValue { false };
+#endif
 };
 
-struct FontSelectionRequestKeyHash {
-    static unsigned hash(const FontSelectionRequestKey& key)
-    {
-        IntegerHasher hasher;
-        hasher.add(key.request.weight.rawValue());
-        hasher.add(key.request.width.rawValue());
-        hasher.add(key.request.slope.rawValue());
-        hasher.add(key.isDeletedValue);
-        return hasher.hash();
-    }
+constexpr bool operator==(const FontSelectionRequest& a, const FontSelectionRequest& b)
+{
+    return a.tied() == b.tied();
+}
 
-    static bool equal(const FontSelectionRequestKey& a, const FontSelectionRequestKey& b)
-    {
-        return a == b;
-    }
+constexpr bool operator!=(const FontSelectionRequest& a, const FontSelectionRequest& b)
+{
+    return !(a == b);
+}
 
-    static const bool safeToCompareToEmptyOrDeleted = true;
-};
+inline void add(Hasher& hasher, const FontSelectionRequest& request)
+{
+    add(hasher, request.tied());
+}
 
 struct FontSelectionCapabilities {
-    FontSelectionCapabilities()
-    {
-    }
+    using Range = FontSelectionRange;
 
-    FontSelectionCapabilities(FontSelectionRange weight, FontSelectionRange width, FontSelectionRange slope)
-        : weight(weight)
-        , width(width)
-        , slope(slope)
+    FontSelectionCapabilities& operator=(const FontSelectionCapabilities&) = default;
+
+    constexpr std::tuple<Range, Range, Range> tied() const
     {
+        return std::tie(weight, width, slope);
     }
 
     void expand(const FontSelectionCapabilities& capabilities)
@@ -426,131 +381,114 @@ struct FontSelectionCapabilities {
         slope.expand(capabilities.slope);
     }
 
-    bool operator==(const FontSelectionCapabilities& other) const
-    {
-        return weight == other.weight
-            && width == other.width
-            && slope == other.slope;
-    }
+    Range weight { normalWeightValue() };
+    Range width { normalStretchValue() };
+    Range slope { normalItalicValue() };
 
-    bool operator!=(const FontSelectionCapabilities& other) const
+#if !COMPILER_SUPPORTS(NSDMI_FOR_AGGREGATES)
+    FontSelectionCapabilities() = default;
+
+    constexpr FontSelectionCapabilities(Range weight, Range width, Range slope)
+        : weight(weight)
+        , width(width)
+        , slope(slope)
     {
-        return !(*this == other);
     }
-
-    FontSelectionRange weight { normalWeightValue(), normalWeightValue() };
-    FontSelectionRange width { normalStretchValue(), normalStretchValue() };
-    FontSelectionRange slope { normalItalicValue(), normalItalicValue() };
+#endif
 };
 
+constexpr bool operator==(const FontSelectionCapabilities& a, const FontSelectionCapabilities& b)
+{
+    return a.tied() == b.tied();
+}
+
+constexpr bool operator!=(const FontSelectionCapabilities& a, const FontSelectionCapabilities& b)
+{
+    return !(a == b);
+}
+
 struct FontSelectionSpecifiedCapabilities {
-    FontSelectionCapabilities computeFontSelectionCapabilities() const
+    using Capabilities = FontSelectionCapabilities;
+    using Range = FontSelectionRange;
+    using OptionalRange = std::optional<Range>;
+
+    constexpr Capabilities computeFontSelectionCapabilities() const
     {
-        return FontSelectionCapabilities(computeWeight(), computeWidth(), computeSlope());
+        return { computeWeight(), computeWidth(), computeSlope() };
     }
 
-    bool operator==(const FontSelectionSpecifiedCapabilities& other) const
+    constexpr std::tuple<OptionalRange&, OptionalRange&, OptionalRange&> tied()
     {
-        return weight == other.weight
-            && width == other.width
-            && slope == other.slope;
+        return std::tie(weight, width, slope);
     }
 
-    bool operator!=(const FontSelectionSpecifiedCapabilities& other) const
+    constexpr std::tuple<const OptionalRange&, const OptionalRange&, const OptionalRange&> tied() const
     {
-        return !(*this == other);
+        return std::tie(weight, width, slope);
     }
 
-    FontSelectionSpecifiedCapabilities& operator=(const FontSelectionCapabilities& other)
+    FontSelectionSpecifiedCapabilities& operator=(const Capabilities& other)
     {
-        weight = other.weight;
-        width = other.width;
-        slope = other.slope;
+        tied() = other.tied();
         return *this;
     }
 
-    FontSelectionRange computeWeight() const
+    constexpr Range computeWeight() const
     {
-        return weight.value_or(FontSelectionRange({ normalWeightValue(), normalWeightValue() }));
+        return weight.value_or(Range { normalWeightValue() });
     }
 
-    FontSelectionRange computeWidth() const
+    constexpr Range computeWidth() const
     {
-        return width.value_or(FontSelectionRange({ normalStretchValue(), normalStretchValue() }));
+        return width.value_or(Range { normalStretchValue() });
     }
 
-    FontSelectionRange computeSlope() const
+    constexpr Range computeSlope() const
     {
-        return slope.value_or(FontSelectionRange({ normalItalicValue(), normalItalicValue() }));
+        return slope.value_or(Range { normalItalicValue() });
     }
 
-    std::optional<FontSelectionRange> weight;
-    std::optional<FontSelectionRange> width;
-    std::optional<FontSelectionRange> slope;
+    OptionalRange weight;
+    OptionalRange width;
+    OptionalRange slope;
 };
 
+constexpr bool operator==(const FontSelectionSpecifiedCapabilities& a, const FontSelectionSpecifiedCapabilities& b)
+{
+    return a.tied() == b.tied();
+}
+
+constexpr bool operator!=(const FontSelectionSpecifiedCapabilities& a, const FontSelectionSpecifiedCapabilities& b)
+{
+    return !(a == b);
+}
+
 class FontSelectionAlgorithm {
 public:
-    FontSelectionAlgorithm() = delete;
+    using Capabilities = FontSelectionCapabilities;
 
-    FontSelectionAlgorithm(FontSelectionRequest request, const Vector<FontSelectionCapabilities>& capabilities, std::optional<FontSelectionCapabilities> capabilitiesBounds = std::nullopt)
-        : m_request(request)
-        , m_capabilities(capabilities)
-        , m_filter(new bool[m_capabilities.size()])
-    {
-        ASSERT(!m_capabilities.isEmpty());
-        if (capabilitiesBounds)
-            m_capabilitiesBounds = capabilitiesBounds.value();
-        else {
-            for (auto capabilities : m_capabilities)
-                m_capabilitiesBounds.expand(capabilities);
-        }
-        for (size_t i = 0; i < m_capabilities.size(); ++i)
-            m_filter[i] = true;
-    }
+    FontSelectionAlgorithm() = delete;
+    FontSelectionAlgorithm(FontSelectionRequest, const Vector<Capabilities>&, std::optional<Capabilities> capabilitiesBounds = std::nullopt);
 
     struct DistanceResult {
         FontSelectionValue distance;
         FontSelectionValue value;
     };
-
-    DistanceResult stretchDistance(FontSelectionCapabilities) const;
-    DistanceResult styleDistance(FontSelectionCapabilities) const;
-    DistanceResult weightDistance(FontSelectionCapabilities) const;
+    DistanceResult stretchDistance(Capabilities) const;
+    DistanceResult styleDistance(Capabilities) const;
+    DistanceResult weightDistance(Capabilities) const;
 
     size_t indexOfBestCapabilities();
 
 private:
-    template <typename T>
-    using IterateActiveCapabilitiesWithReturnCallback = WTF::Function<std::optional<T>(FontSelectionCapabilities, size_t)>;
-
-    template <typename T>
-    inline std::optional<T> iterateActiveCapabilitiesWithReturn(const IterateActiveCapabilitiesWithReturnCallback<T>& callback)
-    {
-        for (size_t i = 0; i < m_capabilities.size(); ++i) {
-            if (!m_filter[i])
-                continue;
-            if (auto result = callback(m_capabilities[i], i))
-                return result;
-        }
-        return std::nullopt;
-    }
-
-    template <typename T>
-    inline void iterateActiveCapabilities(T callback)
-    {
-        iterateActiveCapabilitiesWithReturn<int>([&](FontSelectionCapabilities capabilities, size_t i) -> std::optional<int> {
-            callback(capabilities, i);
-            return std::nullopt;
-        });
-    }
-
-    void filterCapability(DistanceResult(FontSelectionAlgorithm::*computeDistance)(FontSelectionCapabilities) const, FontSelectionRange FontSelectionCapabilities::*inclusionRange);
+    using DistanceFunction = DistanceResult (FontSelectionAlgorithm::*)(Capabilities) const;
+    using CapabilitiesRange = FontSelectionRange Capabilities::*;
+    FontSelectionValue bestValue(const bool eliminated[], DistanceFunction) const;
+    void filterCapability(bool eliminated[], DistanceFunction, CapabilitiesRange);
 
     FontSelectionRequest m_request;
-    FontSelectionCapabilities m_capabilitiesBounds;
-    const Vector<FontSelectionCapabilities>& m_capabilities;
-    std::unique_ptr<bool[]> m_filter;
+    Capabilities m_capabilitiesBounds;
+    const Vector<Capabilities>& m_capabilities;
 };
 
 }