[Fontconfig] fontconfig: Branch 'master' - 15 commits

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Aug 26 05:14:17 UTC 2020


 src/fccfg.c   |  682 ++++++++++++++++++++++++++++++++++++++--------------------
 src/fcint.h   |   11 
 src/fcmatch.c |  183 +++++++++++++--
 src/fcstr.c   |  103 ++++++--
 4 files changed, 697 insertions(+), 282 deletions(-)

New commits:
commit ff7d314ab5f04d3ad54d5165b81d87894d7a8dfe
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Aug 25 18:12:07 2020 -0400

    Fix up FC_LIKELY macros
    
    __builtin_expect returns the same type as the expression,
    so enforce that we pass in a boolean expression.
    
    Pointed out by Emmanuele Bassi.

diff --git a/src/fcint.h b/src/fcint.h
index fb173b5..a3b192d 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -61,8 +61,8 @@
 #define FC_LIKELY(expr) (expr)
 #define FC_UNLIKELY(expr) (expr)
 #else
-#define FC_LIKELY(expr) (__builtin_expect (expr, 1))
-#define FC_UNLIKELY(expr) (__builtin_expect (expr, 0))
+#define FC_LIKELY(expr) (__builtin_expect (((expr) ? 1 : 0), 1))
+#define FC_UNLIKELY(expr) (__builtin_expect (((expr) ? 1 : 0), 0))
 #endif
 
 #ifdef _WIN32
commit e117d6768c5cf5ae37405d03ed6d3b076e713dde
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Aug 25 07:26:32 2020 -0400

    Fixup: Handle patterns without family
    
    Pointed out by Akira Tagoh.

diff --git a/src/fcmatch.c b/src/fcmatch.c
index 16d03de..df6db71 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -525,25 +525,28 @@ FcCompareDataInit (FcPattern     *pat,
                                free);
 
     elt = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
-    for (l = FcPatternEltValues(elt), i = 0; l; l = FcValueListNext(l), i++)
+    if (elt)
     {
-        key = FcValueString (&l->value);
-        if (!FcHashTableFind (table, key, (void **)&e))
+        for (l = FcPatternEltValues(elt), i = 0; l; l = FcValueListNext(l), i++)
         {
-            e = malloc (sizeof (FamilyEntry));
-            e->strong_value = 1e99;
-            e->weak_value = 1e99;
-            FcHashTableAdd (table, (void *)key, e);
-        }
-        if (l->binding == FcValueBindingWeak)
-        {
-            if (i < e->weak_value)
-                e->weak_value = i;
-        }
-        else
-        {
-            if (i < e->strong_value)
-                e->strong_value = i;
+            key = FcValueString (&l->value);
+            if (!FcHashTableFind (table, key, (void **)&e))
+            {
+                e = malloc (sizeof (FamilyEntry));
+                e->strong_value = 1e99;
+                e->weak_value = 1e99;
+                FcHashTableAdd (table, (void *)key, e);
+            }
+            if (l->binding == FcValueBindingWeak)
+            {
+                if (i < e->weak_value)
+                    e->weak_value = i;
+            }
+            else
+            {
+                if (i < e->strong_value)
+                    e->strong_value = i;
+            }
         }
     }
 
commit 51d40491fc483787ba66fb76344ff7d4c13a39b3
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Mon Aug 24 07:59:50 2020 -0400

    Use FC_UNLIKELY

diff --git a/src/fcstr.c b/src/fcstr.c
index f1f75dc..8971b71 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -164,7 +164,7 @@ FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
 {
     FcChar8	r;
 
-    if (__builtin_expect (w->read != NULL, 0))
+    if (FC_UNLIKELY (w->read != NULL))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -175,7 +175,7 @@ FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
 	r = *w->src++;
     } while (r != 0 && delims && strchr (delims, r));
 
-    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
+    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
@@ -187,7 +187,7 @@ FcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
 {
     FcChar8	r;
 
-    if (__builtin_expect (w->read != NULL, 0))
+    if (FC_UNLIKELY (w->read != NULL))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -198,7 +198,7 @@ FcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
 	r = *w->src++;
     } while (r == ' ');
 
-    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
+    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
@@ -210,7 +210,7 @@ FcStrCaseWalkerNext (FcCaseWalker *w)
 {
     FcChar8	r;
 
-    if (__builtin_expect (w->read != NULL, 0))
+    if (FC_UNLIKELY (w->read != NULL))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -219,7 +219,7 @@ FcStrCaseWalkerNext (FcCaseWalker *w)
 
     r = *w->src++;
 
-    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
+    if (FC_UNLIKELY ((r & 0xc0) == 0xc0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
commit 76699a4813a05fd89114a29965d2532adc1553b0
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Mon Aug 24 07:59:18 2020 -0400

    Add FC_LIKELY and FC_UNLIKELY macros
    
    These wrap __builtin_expect where it exists.

diff --git a/src/fcint.h b/src/fcint.h
index 08a0cd6..fb173b5 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -57,6 +57,14 @@
 #define FC_CONFIG_PATH "fonts.conf"
 #endif
 
+#ifdef _WIN32
+#define FC_LIKELY(expr) (expr)
+#define FC_UNLIKELY(expr) (expr)
+#else
+#define FC_LIKELY(expr) (__builtin_expect (expr, 1))
+#define FC_UNLIKELY(expr) (__builtin_expect (expr, 0))
+#endif
+
 #ifdef _WIN32
 #  include "fcwindows.h"
 typedef UINT (WINAPI *pfnGetSystemWindowsDirectory)(LPSTR, UINT);
commit 835f9bbdbe7c023bce27f699c8906ace9b62a051
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Sat Aug 22 02:35:24 2020 -0400

    Fixup: Promote ints to ranges when appropriate
    
    Pointed out by Akira Tagoh.

diff --git a/src/fccfg.c b/src/fccfg.c
index 13bd0ff..f86f680 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -939,6 +939,13 @@ FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
     case FcTypeInteger:
         v.type = FcTypeDouble;
         v.u.d = (double) v.u.i;
+        /* Fallthrough */
+    case FcTypeDouble:
+        if (u.type == FcTypeRange && buf)
+        {
+            v.u.r = FcRangePromote (v.u.d, buf);
+            v.type = FcTypeRange;
+        }
         break;
     case FcTypeVoid:
         if (u.type == FcTypeMatrix)
@@ -964,13 +971,6 @@ FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
             v.type = FcTypeLangSet;
         }
         break;
-    case FcTypeDouble:
-        if (u.type == FcTypeRange && buf)
-        {
-            v.u.r = FcRangePromote (v.u.d, buf);
-            v.type = FcTypeRange;
-        }
-        break;
     default:
         break;
     }
commit 148ebf98edb498d786f4c1107ec116e1afdf9541
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Sat Aug 22 00:57:25 2020 -0400

    Use __builtin_expect in a few places
    
    utf8 is extremely rare in the strings we see in
    font configuration, so this seems to be a good
    case for __builtin_expect.

diff --git a/src/fcstr.c b/src/fcstr.c
index dc9940a..f1f75dc 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -164,7 +164,7 @@ FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
 {
     FcChar8	r;
 
-    if (w->read)
+    if (__builtin_expect (w->read != NULL, 0))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -175,7 +175,7 @@ FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
 	r = *w->src++;
     } while (r != 0 && delims && strchr (delims, r));
 
-    if ((r & 0xc0) == 0xc0)
+    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
@@ -187,7 +187,7 @@ FcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
 {
     FcChar8	r;
 
-    if (w->read)
+    if (__builtin_expect (w->read != NULL, 0))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -198,7 +198,7 @@ FcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
 	r = *w->src++;
     } while (r == ' ');
 
-    if ((r & 0xc0) == 0xc0)
+    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
@@ -210,7 +210,7 @@ FcStrCaseWalkerNext (FcCaseWalker *w)
 {
     FcChar8	r;
 
-    if (w->read)
+    if (__builtin_expect (w->read != NULL, 0))
     {
 	if ((r = *w->read++))
 	    return r;
@@ -219,7 +219,7 @@ FcStrCaseWalkerNext (FcCaseWalker *w)
 
     r = *w->src++;
 
-    if ((r & 0xc0) == 0xc0)
+    if (__builtin_expect ((r & 0xc0) == 0xc0, 0))
 	return FcStrCaseWalkerLong (w, r);
     if ('A' <= r && r <= 'Z')
         r = r - 'A' + 'a';
commit 13015a0a6ed15ef2e0241a92ef05233dc51ce23b
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Aug 18 16:33:57 2020 -0400

    Use a hash table for families in FcConfigSubstitute
    
    Use the same approach we used for FcFontMatch, and
    keep a hash table of family names. We only speed
    up the no-match case, for now.

diff --git a/src/fccfg.c b/src/fccfg.c
index 803cd68..13bd0ff 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -1579,12 +1579,128 @@ FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
     return v;
 }
 
+/* The bulk of the time in FcConfigSubstitute is spent walking
+ * lists of family names. We speed this up with a hash table.
+ * Since we need to take the ignore-blanks option into account,
+ * we use two separate hash tables.
+ */
+typedef struct
+{
+  int count;
+} FamilyTableEntry;
+
+
+typedef struct
+{
+  FcHashTable *family_blank_hash;
+  FcHashTable *family_hash;
+} FamilyTable;
+
+static FcBool
+FamilyTableLookup (FamilyTable   *table,
+                   FcOp           _op,
+                   const FcChar8 *s)
+{
+    FamilyTableEntry *fe;
+    int flags = FC_OP_GET_FLAGS (_op);
+    FcHashTable *hash;
+
+    if (flags & FcOpFlagIgnoreBlanks)
+        hash = table->family_blank_hash;
+    else
+        hash = table->family_hash;
+
+    return FcHashTableFind (hash, (const void *)s, (void **)&fe);
+}
+
+static void
+FamilyTableAdd (FamilyTable    *table,
+                FcValueListPtr  values)
+{
+    FcValueListPtr ll;
+    for (ll = values; ll; ll = FcValueListNext (ll))
+        {
+            const FcChar8 *s = FcValueString (&ll->value);
+            FamilyTableEntry *fe;
+
+            if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe))
+            {
+                fe = malloc (sizeof (FamilyTableEntry));
+                fe->count = 0;
+                FcHashTableAdd (table->family_hash, (void *)s, fe);
+            }
+            fe->count++;
+
+            if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe))
+            {
+                fe = malloc (sizeof (FamilyTableEntry));
+                fe->count = 0;
+                FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
+            }
+            fe->count++;
+       }
+}
+
+static void
+FamilyTableDel (FamilyTable   *table,
+                const FcChar8 *s)
+{
+    FamilyTableEntry *fe;
+
+    if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe))
+    {
+        fe->count--;
+        if (fe->count == 0)
+            FcHashTableRemove (table->family_hash, (void *)s);
+    }
+
+    if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe))
+    {
+        fe->count--;
+        if (fe->count == 0)
+            FcHashTableRemove (table->family_blank_hash, (void *)s);
+    }
+}
+
+static void
+FamilyTableInit (FamilyTable *table,
+                 FcPattern *p)
+{
+    FcPatternElt *e;
+
+    table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
+                                          (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          free);
+    table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
+                                          (FcCompareFunc)FcStrCmpIgnoreCase,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          free);
+    e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
+    if (e)
+        FamilyTableAdd (table, FcPatternEltValues (e));
+}
+
+static void
+FamilyTableClear (FamilyTable *table)
+{
+    if (table->family_blank_hash)
+        FcHashTableDestroy (table->family_blank_hash);
+    if (table->family_hash)
+        FcHashTableDestroy (table->family_hash);
+}
+
 static FcValueList *
 FcConfigMatchValueList (FcPattern	*p,
 			FcPattern	*p_pat,
 			FcMatchKind      kind,
 			FcTest		*t,
-			FcValueList	*values)
+			FcValueList	*values,
+                        FamilyTable     *table)
 {
     FcValueList	    *ret = 0;
     FcExpr	    *e = t->expr;
@@ -1605,6 +1721,14 @@ FcConfigMatchValueList (FcPattern	*p,
 	    e = 0;
 	}
 
+        if (t->object == FC_FAMILY_OBJECT && table)
+        {
+            if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
+            {
+                    ret = 0;
+                    goto done;
+            }
+        }
 	for (v = values; v; v = FcValueListNext(v))
 	{
 	    /* Compare the pattern value to the match expression value */
@@ -1624,6 +1748,7 @@ FcConfigMatchValueList (FcPattern	*p,
 		}
 	    }
 	}
+done:
 	FcValueDestroy (value);
     }
     return ret;
@@ -1666,7 +1791,8 @@ FcConfigAdd (FcValueListPtr *head,
 	     FcValueList    *position,
 	     FcBool	    append,
 	     FcValueList    *new,
-	     FcObject        object)
+	     FcObject        object,
+             FamilyTable    *table)
 {
     FcValueListPtr  *prev, l, last, v;
     FcValueBinding  sameBinding;
@@ -1692,6 +1818,11 @@ FcConfigAdd (FcValueListPtr *head,
 	}
     }
 
+    if (object == FC_FAMILY_OBJECT && table)
+    {
+        FamilyTableAdd (table, new);
+    }
+
     if (position)
 	sameBinding = position->binding;
     else
@@ -1758,10 +1889,17 @@ FcConfigAdd (FcValueListPtr *head,
 
 static void
 FcConfigDel (FcValueListPtr *head,
-	     FcValueList    *position)
+	     FcValueList    *position,
+             FcObject        object,
+             FamilyTable    *table)
 {
     FcValueListPtr *prev;
 
+    if (object == FC_FAMILY_OBJECT && table)
+    {
+        FamilyTableDel (table, FcValueString (&position->value));
+    }
+
     for (prev = head; *prev != NULL; prev = &(*prev)->next)
     {
 	if (*prev == position)
@@ -1776,9 +1914,10 @@ FcConfigDel (FcValueListPtr *head,
 
 static void
 FcConfigPatternAdd (FcPattern	*p,
-		    FcObject	object,
+		    FcObject	 object,
 		    FcValueList	*list,
-		    FcBool	append)
+		    FcBool	 append,
+                    FamilyTable *table)
 {
     if (list)
     {
@@ -1786,7 +1925,7 @@ FcConfigPatternAdd (FcPattern	*p,
 
 	if (!e)
 	    return;
-	FcConfigAdd (&e->values, 0, append, list, object);
+	FcConfigAdd (&e->values, 0, append, list, object, table);
     }
 }
 
@@ -1795,13 +1934,14 @@ FcConfigPatternAdd (FcPattern	*p,
  */
 static void
 FcConfigPatternDel (FcPattern	*p,
-		    FcObject	object)
+		    FcObject	 object,
+                    FamilyTable *table)
 {
     FcPatternElt    *e = FcPatternObjectFindElt (p, object);
     if (!e)
 	return;
     while (e->values != NULL)
-	FcConfigDel (&e->values, e->values);
+	FcConfigDel (&e->values, e->values, object, table);
 }
 
 static void
@@ -1834,6 +1974,8 @@ FcConfigSubstituteWithPat (FcConfig    *config,
     int		    i, nobjs;
     FcBool	    retval = FcTrue;
     FcTest	    **tst = NULL;
+    FamilyTable     data;
+    FamilyTable     *table = &data;
 
     if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
 	return FcFalse;
@@ -1931,6 +2073,9 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 	printf ("FcConfigSubstitute ");
 	FcPatternPrint (p);
     }
+
+    FamilyTableInit (&data, p);
+
     FcPtrListIterInit (s, &iter);
     for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter))
     {
@@ -1967,9 +2112,15 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 			FcTestPrint (r->u.test);
 		    }
 		    if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
+                    {
 			m = p_pat;
+                        table = NULL;
+                    }
 		    else
+                    {
 			m = p;
+                        table = &data;
+                    }
 		    if (m)
 			e = FcPatternObjectFindElt (m, r->u.test->object);
 		    else
@@ -2002,7 +2153,7 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 		     * Check to see if there is a match, mark the location
 		     * to apply match-relative edits
 		     */
-		    vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values);
+		    vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values, table);
 		    /* different 'kind' won't be the target of edit */
 		    if (!value[object] && kind == r->u.test->kind)
 			value[object] = vl;
@@ -2026,7 +2177,7 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 		    /*
 		     * Evaluate the list of expressions
 		     */
-		    l = FcConfigValues (p, p_pat,kind,  r->u.edit->expr, r->u.edit->binding);
+		    l = FcConfigValues (p, p_pat, kind, r->u.edit->expr, r->u.edit->binding);
 		    if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
 			elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
 
@@ -2044,12 +2195,12 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 			    /*
 			     * Append the new list of values after the current value
 			     */
-			    FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object);
+			    FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
 			    /*
 			     * Delete the marked value
 			     */
 			    if (thisValue)
-				FcConfigDel (&elt[object]->values, thisValue);
+				FcConfigDel (&elt[object]->values, thisValue, object, table);
 			    /*
 			     * Adjust a pointer into the value list to ensure
 			     * future edits occur at the same place
@@ -2063,8 +2214,8 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 			 * Delete all of the values and insert
 			 * the new set
 			 */
-			FcConfigPatternDel (p, r->u.edit->object);
-			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue);
+			FcConfigPatternDel (p, r->u.edit->object, table);
+			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
 			/*
 			 * Adjust a pointer into the value list as they no
 			 * longer point to anything valid
@@ -2074,33 +2225,33 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 		    case FcOpPrepend:
 			if (value[object])
 			{
-			    FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object);
+			    FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
 			    break;
 			}
 			/* fall through ... */
 		    case FcOpPrependFirst:
-			FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse);
+			FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
 			break;
 		    case FcOpAppend:
 			if (value[object])
 			{
-			    FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object);
+			    FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
 			    break;
 			}
 			/* fall through ... */
 		    case FcOpAppendLast:
-			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue);
+			FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
 			break;
 		    case FcOpDelete:
 			if (value[object])
 			{
-			    FcConfigDel (&elt[object]->values, value[object]);
+			    FcConfigDel (&elt[object]->values, value[object], object, table);
 			    FcValueListDestroy (l);
 			    break;
 			}
 			/* fall through ... */
 		    case FcOpDeleteAll:
-			FcConfigPatternDel (p, r->u.edit->object);
+			FcConfigPatternDel (p, r->u.edit->object, table);
 			FcValueListDestroy (l);
 			break;
 		    default:
@@ -2130,6 +2281,7 @@ FcConfigSubstituteWithPat (FcConfig    *config,
 	FcPatternPrint (p);
     }
 bail1:
+    FamilyTableClear (&data);
     if (elt)
 	free (elt);
     if (value)
commit 7deb07e38e31da787875cb65db2ecb0dc0d5807f
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Aug 21 09:12:35 2020 -0400

    Speed up FcCompareLang and FcCompareBool
    
    Avoid FcCanonicalize here too.

diff --git a/src/fcmatch.c b/src/fcmatch.c
index 289204e..16d03de 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -104,30 +104,27 @@ static double
 FcCompareLang (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
 {
     FcLangResult    result;
-    FcValue value1 = FcValueCanonicalize(v1), value2 = FcValueCanonicalize(v2);
 
-    switch ((int) value1.type) {
+    switch ((int) v1->type) {
     case FcTypeLangSet:
-	switch ((int) value2.type) {
+	switch ((int) v2->type) {
 	case FcTypeLangSet:
-	    result = FcLangSetCompare (value1.u.l, value2.u.l);
+	    result = FcLangSetCompare (FcValueLangSet (v1), FcValueLangSet (v2));
 	    break;
 	case FcTypeString:
-	    result = FcLangSetHasLang (value1.u.l,
-				       value2.u.s);
+	    result = FcLangSetHasLang (FcValueLangSet (v1), FcValueString (v2));
 	    break;
 	default:
 	    return -1.0;
 	}
 	break;
     case FcTypeString:
-	switch ((int) value2.type) {
+	switch ((int) v2->type) {
 	case FcTypeLangSet:
-	    result = FcLangSetHasLang (value2.u.l, value1.u.s);
+	    result = FcLangSetHasLang (FcValueLangSet (v2), FcValueString (v1));
 	    break;
 	case FcTypeString:
-	    result = FcLangCompare (value1.u.s,
-				    value2.u.s);
+	    result = FcLangCompare (FcValueString (v1), FcValueString (v2));
 	    break;
 	default:
 	    return -1.0;
@@ -154,10 +151,11 @@ FcCompareBool (const FcValue *v1, const FcValue *v2, FcValue *bestValue)
     if (v2->type != FcTypeBool || v1->type != FcTypeBool)
 	return -1.0;
 
+    bestValue->type = FcTypeBool;
     if (v2->u.b != FcDontCare)
-	*bestValue = FcValueCanonicalize (v2);
+	bestValue->u.b = v2->u.b;
     else
-	*bestValue = FcValueCanonicalize (v1);
+	bestValue->u.b = v1->u.b;
 
     return (double) ((v2->u.b ^ v1->u.b) == 1);
 }
commit 09729c9032e66da30c24d8268e444b2e6dd0e0d7
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Aug 21 00:49:04 2020 -0400

    Speed up FcConfigCompareValue
    
    Avoid FcValueCanonicalize when we can, to avoid
    unnecessary copying of FcValue structs.

diff --git a/src/fccfg.c b/src/fccfg.c
index 8e7a682..803cd68 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -982,192 +982,215 @@ FcConfigCompareValue (const FcValue	*left_o,
 		      unsigned int      op_,
 		      const FcValue	*right_o)
 {
-    FcValue     left = FcValueCanonicalize(left_o);
-    FcValue     right = FcValueCanonicalize(right_o);
+    FcValue     left;
+    FcValue     right;
     FcBool	ret = FcFalse;
     FcOp	op = FC_OP_GET_OP (op_);
     int		flags = FC_OP_GET_FLAGS (op_);
 
-    if (left.type != right.type)
+    if (left_o->type != right_o->type)
     {
+        left = FcValueCanonicalize(left_o);
+        right = FcValueCanonicalize(right_o);
         FcValuePromotionBuffer buf1, buf2;
         left = FcConfigPromote (left, right, &buf1);
         right = FcConfigPromote (right, left, &buf2);
-        if (left.type != right.type)
+        left_o = &left;
+        right_o = &right;
+        if (left_o->type != right_o->type)
         {
 	    if (op == FcOpNotEqual || op == FcOpNotContains)
 	        ret = FcTrue;
             return ret;
         }
     }
-    switch (left.type) {
+    switch (left_o->type) {
     case FcTypeUnknown:
         break;	/* No way to guess how to compare for this object */
-    case FcTypeInteger:
+    case FcTypeInteger: {
+        int l = left_o->u.i;
+        int r = right_o->u.i;
         switch ((int) op) {
         case FcOpEqual:
         case FcOpContains:
         case FcOpListing:
-            ret = left.u.i == right.u.i;
+            ret = l == r;
             break;
         case FcOpNotEqual:
         case FcOpNotContains:
-            ret = left.u.i != right.u.i;
+            ret = l != r;
             break;
         case FcOpLess:
-            ret = left.u.i < right.u.i;
+            ret = l < r;
             break;
         case FcOpLessEqual:
-            ret = left.u.i <= right.u.i;
+            ret = l <= r;
             break;
         case FcOpMore:
-            ret = left.u.i > right.u.i;
+            ret = l > r;
             break;
         case FcOpMoreEqual:
-            ret = left.u.i >= right.u.i;
+            ret = l >= r;
             break;
         default:
             break;
         }
         break;
-    case FcTypeDouble:
+    }
+    case FcTypeDouble: {
+        double l = left_o->u.d;
+        double r = right_o->u.d;
         switch ((int) op) {
         case FcOpEqual:
         case FcOpContains:
         case FcOpListing:
-            ret = left.u.d == right.u.d;
+            ret = l == r;
             break;
         case FcOpNotEqual:
         case FcOpNotContains:
-            ret = left.u.d != right.u.d;
+            ret = l != r;
             break;
         case FcOpLess:
-            ret = left.u.d < right.u.d;
+            ret = l < r;
             break;
         case FcOpLessEqual:
-            ret = left.u.d <= right.u.d;
+            ret = l <= r;
             break;
         case FcOpMore:
-            ret = left.u.d > right.u.d;
+            ret = l > r;
             break;
         case FcOpMoreEqual:
-            ret = left.u.d >= right.u.d;
+            ret = l >= r;
             break;
         default:
             break;
         }
         break;
-    case FcTypeBool:
+    }
+    case FcTypeBool: {
+        FcBool l = left_o->u.b;
+        FcBool r = right_o->u.b;
         switch ((int) op) {
         case FcOpEqual:
-            ret = left.u.b == right.u.b;
+            ret = l == r;
             break;
         case FcOpContains:
         case FcOpListing:
-            ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
+            ret = l == r || l >= FcDontCare;
             break;
         case FcOpNotEqual:
-            ret = left.u.b != right.u.b;
+            ret = l != r;
             break;
         case FcOpNotContains:
-            ret = !(left.u.b == right.u.b || left.u.b >= FcDontCare);
+            ret = !(l == r || l >= FcDontCare);
             break;
         case FcOpLess:
-            ret = left.u.b != right.u.b && right.u.b >= FcDontCare;
+            ret = l != r && r >= FcDontCare;
             break;
         case FcOpLessEqual:
-            ret = left.u.b == right.u.b || right.u.b >= FcDontCare;
+            ret = l == r || r >= FcDontCare;
             break;
         case FcOpMore:
-            ret = left.u.b != right.u.b && left.u.b >= FcDontCare;
+            ret = l != r && l >= FcDontCare;
             break;
         case FcOpMoreEqual:
-            ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
+            ret = l == r || l >= FcDontCare;
             break;
         default:
             break;
         }
         break;
-    case FcTypeString:
+    }
+    case FcTypeString: {
+        const FcChar8 *l = FcValueString (left_o);
+        const FcChar8 *r = FcValueString (right_o);
         switch ((int) op) {
         case FcOpEqual:
         case FcOpListing:
             if (flags & FcOpFlagIgnoreBlanks)
-                ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
+                ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
             else
-                ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
+                ret = FcStrCmpIgnoreCase (l, r) == 0;
             break;
         case FcOpContains:
-            ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
+            ret = FcStrStrIgnoreCase (l, r) != 0;
             break;
         case FcOpNotEqual:
             if (flags & FcOpFlagIgnoreBlanks)
-                ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
+                ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
             else
-                ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
+                ret = FcStrCmpIgnoreCase (l, r) != 0;
             break;
         case FcOpNotContains:
-            ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
+            ret = FcStrStrIgnoreCase (l, r) == 0;
             break;
         default:
             break;
         }
         break;
-    case FcTypeMatrix:
+    }
+    case FcTypeMatrix: {
         switch ((int) op) {
         case FcOpEqual:
         case FcOpContains:
         case FcOpListing:
-            ret = FcMatrixEqual (left.u.m, right.u.m);
+            ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
             break;
         case FcOpNotEqual:
         case FcOpNotContains:
-            ret = !FcMatrixEqual (left.u.m, right.u.m);
+            ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
             break;
         default:
             break;
         }
         break;
-    case FcTypeCharSet:
+    }
+    case FcTypeCharSet: {
+        const FcCharSet *l = FcValueCharSet (left_o);
+        const FcCharSet *r = FcValueCharSet (right_o);
         switch ((int) op) {
         case FcOpContains:
         case FcOpListing:
             /* left contains right if right is a subset of left */
-            ret = FcCharSetIsSubset (right.u.c, left.u.c);
+            ret = FcCharSetIsSubset (r, l);
             break;
         case FcOpNotContains:
             /* left contains right if right is a subset of left */
-            ret = !FcCharSetIsSubset (right.u.c, left.u.c);
+            ret = !FcCharSetIsSubset (r, l);
             break;
         case FcOpEqual:
-            ret = FcCharSetEqual (left.u.c, right.u.c);
+            ret = FcCharSetEqual (l, r);
             break;
         case FcOpNotEqual:
-            ret = !FcCharSetEqual (left.u.c, right.u.c);
+            ret = !FcCharSetEqual (l, r);
             break;
         default:
             break;
         }
         break;
-    case FcTypeLangSet:
+    }
+    case FcTypeLangSet: {
+        const FcLangSet *l = FcValueLangSet (left_o);
+        const FcLangSet *r = FcValueLangSet (right_o);
         switch ((int) op) {
         case FcOpContains:
         case FcOpListing:
-            ret = FcLangSetContains (left.u.l, right.u.l);
+            ret = FcLangSetContains (l, r);
             break;
         case FcOpNotContains:
-            ret = !FcLangSetContains (left.u.l, right.u.l);
+            ret = !FcLangSetContains (l, r);
             break;
         case FcOpEqual:
-            ret = FcLangSetEqual (left.u.l, right.u.l);
+            ret = FcLangSetEqual (l, r);
             break;
         case FcOpNotEqual:
-            ret = !FcLangSetEqual (left.u.l, right.u.l);
+            ret = !FcLangSetEqual (l, r);
             break;
         default:
             break;
         }
         break;
+    }
     case FcTypeVoid:
         switch ((int) op) {
         case FcOpEqual:
@@ -1184,20 +1207,23 @@ FcConfigCompareValue (const FcValue	*left_o,
         case FcOpEqual:
         case FcOpContains:
         case FcOpListing:
-            ret = left.u.f == right.u.f;
+            ret = left_o->u.f == right_o->u.f;
             break;
         case FcOpNotEqual:
         case FcOpNotContains:
-            ret = left.u.f != right.u.f;
+            ret = left_o->u.f != right_o->u.f;
             break;
         default:
             break;
         }
         break;
-    case FcTypeRange:
-        ret = FcRangeCompare (op, left.u.r, right.u.r);
+    case FcTypeRange: {
+        const FcRange *l = FcValueRange (left_o);
+        const FcRange *r = FcValueRange (right_o);
+        ret = FcRangeCompare (op, l, r);
         break;
     }
+    }
     return ret;
 }
 
commit 9d4e5d0f25dd592316d1728f95f40fe5967b120c
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Thu Aug 20 23:45:19 2020 -0400

    Speed up FcConfigCompareValue
    
    Make FcConfigPromote use a switch instead of
    an if-else cascade, and avoid calling it when
    we can.
    
    Note that we need to add a case for integers
    in FcConfigCompareValue, since we are no longer
    promoting integers to doubles unconditionally.

diff --git a/src/fccfg.c b/src/fccfg.c
index cb4d3e0..8e7a682 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -934,35 +934,45 @@ FcConfigAddRule (FcConfig	*config,
 static FcValue
 FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
 {
-    if (v.type == FcTypeInteger)
-    {
-	v.type = FcTypeDouble;
-	v.u.d = (double) v.u.i;
-    }
-    else if (v.type == FcTypeVoid && u.type == FcTypeMatrix)
-    {
-	v.u.m = &FcIdentityMatrix;
-	v.type = FcTypeMatrix;
-    }
-    else if (buf && v.type == FcTypeString && u.type == FcTypeLangSet)
-    {
-	v.u.l = FcLangSetPromote (v.u.s, buf);
-	v.type = FcTypeLangSet;
-    }
-    else if (buf && v.type == FcTypeVoid && u.type == FcTypeLangSet)
-    {
-	v.u.l = FcLangSetPromote (NULL, buf);
-	v.type = FcTypeLangSet;
-    }
-    else if (buf && v.type == FcTypeVoid && u.type == FcTypeCharSet)
-    {
-	v.u.c = FcCharSetPromote (buf);
-	v.type = FcTypeCharSet;
-    }
-    if (buf && v.type == FcTypeDouble && u.type == FcTypeRange)
-    {
-	v.u.r = FcRangePromote (v.u.d, buf);
-	v.type = FcTypeRange;
+    switch (v.type)
+    {
+    case FcTypeInteger:
+        v.type = FcTypeDouble;
+        v.u.d = (double) v.u.i;
+        break;
+    case FcTypeVoid:
+        if (u.type == FcTypeMatrix)
+        {
+            v.u.m = &FcIdentityMatrix;
+            v.type = FcTypeMatrix;
+        }
+        else if (u.type == FcTypeLangSet && buf)
+        {
+            v.u.l = FcLangSetPromote (NULL, buf);
+            v.type = FcTypeLangSet;
+        }
+        else if (u.type == FcTypeCharSet && buf)
+        {
+            v.u.c = FcCharSetPromote (buf);
+            v.type = FcTypeCharSet;
+        }
+        break;
+    case FcTypeString:
+        if (u.type == FcTypeLangSet && buf)
+        {
+            v.u.l = FcLangSetPromote (v.u.s, buf);
+            v.type = FcTypeLangSet;
+        }
+        break;
+    case FcTypeDouble:
+        if (u.type == FcTypeRange && buf)
+        {
+            v.u.r = FcRangePromote (v.u.d, buf);
+            v.type = FcTypeRange;
+        }
+        break;
+    default:
+        break;
     }
     return v;
 }
@@ -972,195 +982,221 @@ FcConfigCompareValue (const FcValue	*left_o,
 		      unsigned int      op_,
 		      const FcValue	*right_o)
 {
-    FcValue	left = FcValueCanonicalize(left_o);
-    FcValue	right = FcValueCanonicalize(right_o);
+    FcValue     left = FcValueCanonicalize(left_o);
+    FcValue     right = FcValueCanonicalize(right_o);
     FcBool	ret = FcFalse;
     FcOp	op = FC_OP_GET_OP (op_);
     int		flags = FC_OP_GET_FLAGS (op_);
-    FcValuePromotionBuffer buf1, buf2;
 
-    left = FcConfigPromote (left, right, &buf1);
-    right = FcConfigPromote (right, left, &buf2);
-    if (left.type == right.type)
-    {
-	switch (left.type) {
-	case FcTypeUnknown:
-	    break;	/* No way to guess how to compare for this object */
-	case FcTypeInteger:
-	    break;	/* FcConfigPromote prevents this from happening */
-	case FcTypeDouble:
-	    switch ((int) op) {
-	    case FcOpEqual:
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = left.u.d == right.u.d;
-		break;
-	    case FcOpNotEqual:
-	    case FcOpNotContains:
-		ret = left.u.d != right.u.d;
-		break;
-	    case FcOpLess:
-		ret = left.u.d < right.u.d;
-		break;
-	    case FcOpLessEqual:
-		ret = left.u.d <= right.u.d;
-		break;
-	    case FcOpMore:
-		ret = left.u.d > right.u.d;
-		break;
-	    case FcOpMoreEqual:
-		ret = left.u.d >= right.u.d;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeBool:
-	    switch ((int) op) {
-	    case FcOpEqual:
-		ret = left.u.b == right.u.b;
-		break;
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
-		break;
-	    case FcOpNotEqual:
-		ret = left.u.b != right.u.b;
-		break;
-	    case FcOpNotContains:
-		ret = !(left.u.b == right.u.b || left.u.b >= FcDontCare);
-		break;
-	    case FcOpLess:
-		ret = left.u.b != right.u.b && right.u.b >= FcDontCare;
-		break;
-	    case FcOpLessEqual:
-		ret = left.u.b == right.u.b || right.u.b >= FcDontCare;
-		break;
-	    case FcOpMore:
-		ret = left.u.b != right.u.b && left.u.b >= FcDontCare;
-		break;
-	    case FcOpMoreEqual:
-		ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeString:
-	    switch ((int) op) {
-	    case FcOpEqual:
-	    case FcOpListing:
-		if (flags & FcOpFlagIgnoreBlanks)
-		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
-		else
-		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
-		break;
-	    case FcOpContains:
-		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
-		break;
-	    case FcOpNotEqual:
-		if (flags & FcOpFlagIgnoreBlanks)
-		    ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
-		else
-		    ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
-		break;
-	    case FcOpNotContains:
-		ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeMatrix:
-	    switch ((int) op) {
-	    case FcOpEqual:
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = FcMatrixEqual (left.u.m, right.u.m);
-		break;
-	    case FcOpNotEqual:
-	    case FcOpNotContains:
-		ret = !FcMatrixEqual (left.u.m, right.u.m);
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeCharSet:
-	    switch ((int) op) {
-	    case FcOpContains:
-	    case FcOpListing:
-		/* left contains right if right is a subset of left */
-		ret = FcCharSetIsSubset (right.u.c, left.u.c);
-		break;
-	    case FcOpNotContains:
-		/* left contains right if right is a subset of left */
-		ret = !FcCharSetIsSubset (right.u.c, left.u.c);
-		break;
-	    case FcOpEqual:
-		ret = FcCharSetEqual (left.u.c, right.u.c);
-		break;
-	    case FcOpNotEqual:
-		ret = !FcCharSetEqual (left.u.c, right.u.c);
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeLangSet:
-	    switch ((int) op) {
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = FcLangSetContains (left.u.l, right.u.l);
-		break;
-	    case FcOpNotContains:
-		ret = !FcLangSetContains (left.u.l, right.u.l);
-		break;
-	    case FcOpEqual:
-		ret = FcLangSetEqual (left.u.l, right.u.l);
-		break;
-	    case FcOpNotEqual:
-		ret = !FcLangSetEqual (left.u.l, right.u.l);
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeVoid:
-	    switch ((int) op) {
-	    case FcOpEqual:
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = FcTrue;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeFTFace:
-	    switch ((int) op) {
-	    case FcOpEqual:
-	    case FcOpContains:
-	    case FcOpListing:
-		ret = left.u.f == right.u.f;
-		break;
-	    case FcOpNotEqual:
-	    case FcOpNotContains:
-		ret = left.u.f != right.u.f;
-		break;
-	    default:
-		break;
-	    }
-	    break;
-	case FcTypeRange:
-	    ret = FcRangeCompare (op, left.u.r, right.u.r);
-	    break;
-	}
-    }
-    else
-    {
-	if (op == FcOpNotEqual || op == FcOpNotContains)
-	    ret = FcTrue;
+    if (left.type != right.type)
+    {
+        FcValuePromotionBuffer buf1, buf2;
+        left = FcConfigPromote (left, right, &buf1);
+        right = FcConfigPromote (right, left, &buf2);
+        if (left.type != right.type)
+        {
+	    if (op == FcOpNotEqual || op == FcOpNotContains)
+	        ret = FcTrue;
+            return ret;
+        }
+    }
+    switch (left.type) {
+    case FcTypeUnknown:
+        break;	/* No way to guess how to compare for this object */
+    case FcTypeInteger:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpContains:
+        case FcOpListing:
+            ret = left.u.i == right.u.i;
+            break;
+        case FcOpNotEqual:
+        case FcOpNotContains:
+            ret = left.u.i != right.u.i;
+            break;
+        case FcOpLess:
+            ret = left.u.i < right.u.i;
+            break;
+        case FcOpLessEqual:
+            ret = left.u.i <= right.u.i;
+            break;
+        case FcOpMore:
+            ret = left.u.i > right.u.i;
+            break;
+        case FcOpMoreEqual:
+            ret = left.u.i >= right.u.i;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeDouble:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpContains:
+        case FcOpListing:
+            ret = left.u.d == right.u.d;
+            break;
+        case FcOpNotEqual:
+        case FcOpNotContains:
+            ret = left.u.d != right.u.d;
+            break;
+        case FcOpLess:
+            ret = left.u.d < right.u.d;
+            break;
+        case FcOpLessEqual:
+            ret = left.u.d <= right.u.d;
+            break;
+        case FcOpMore:
+            ret = left.u.d > right.u.d;
+            break;
+        case FcOpMoreEqual:
+            ret = left.u.d >= right.u.d;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeBool:
+        switch ((int) op) {
+        case FcOpEqual:
+            ret = left.u.b == right.u.b;
+            break;
+        case FcOpContains:
+        case FcOpListing:
+            ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
+            break;
+        case FcOpNotEqual:
+            ret = left.u.b != right.u.b;
+            break;
+        case FcOpNotContains:
+            ret = !(left.u.b == right.u.b || left.u.b >= FcDontCare);
+            break;
+        case FcOpLess:
+            ret = left.u.b != right.u.b && right.u.b >= FcDontCare;
+            break;
+        case FcOpLessEqual:
+            ret = left.u.b == right.u.b || right.u.b >= FcDontCare;
+            break;
+        case FcOpMore:
+            ret = left.u.b != right.u.b && left.u.b >= FcDontCare;
+            break;
+        case FcOpMoreEqual:
+            ret = left.u.b == right.u.b || left.u.b >= FcDontCare;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeString:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpListing:
+            if (flags & FcOpFlagIgnoreBlanks)
+                ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) == 0;
+            else
+                ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) == 0;
+            break;
+        case FcOpContains:
+            ret = FcStrStrIgnoreCase (left.u.s, right.u.s) != 0;
+            break;
+        case FcOpNotEqual:
+            if (flags & FcOpFlagIgnoreBlanks)
+                ret = FcStrCmpIgnoreBlanksAndCase (left.u.s, right.u.s) != 0;
+            else
+                ret = FcStrCmpIgnoreCase (left.u.s, right.u.s) != 0;
+            break;
+        case FcOpNotContains:
+            ret = FcStrStrIgnoreCase (left.u.s, right.u.s) == 0;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeMatrix:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpContains:
+        case FcOpListing:
+            ret = FcMatrixEqual (left.u.m, right.u.m);
+            break;
+        case FcOpNotEqual:
+        case FcOpNotContains:
+            ret = !FcMatrixEqual (left.u.m, right.u.m);
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeCharSet:
+        switch ((int) op) {
+        case FcOpContains:
+        case FcOpListing:
+            /* left contains right if right is a subset of left */
+            ret = FcCharSetIsSubset (right.u.c, left.u.c);
+            break;
+        case FcOpNotContains:
+            /* left contains right if right is a subset of left */
+            ret = !FcCharSetIsSubset (right.u.c, left.u.c);
+            break;
+        case FcOpEqual:
+            ret = FcCharSetEqual (left.u.c, right.u.c);
+            break;
+        case FcOpNotEqual:
+            ret = !FcCharSetEqual (left.u.c, right.u.c);
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeLangSet:
+        switch ((int) op) {
+        case FcOpContains:
+        case FcOpListing:
+            ret = FcLangSetContains (left.u.l, right.u.l);
+            break;
+        case FcOpNotContains:
+            ret = !FcLangSetContains (left.u.l, right.u.l);
+            break;
+        case FcOpEqual:
+            ret = FcLangSetEqual (left.u.l, right.u.l);
+            break;
+        case FcOpNotEqual:
+            ret = !FcLangSetEqual (left.u.l, right.u.l);
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeVoid:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpContains:
+        case FcOpListing:
+            ret = FcTrue;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeFTFace:
+        switch ((int) op) {
+        case FcOpEqual:
+        case FcOpContains:
+        case FcOpListing:
+            ret = left.u.f == right.u.f;
+            break;
+        case FcOpNotEqual:
+        case FcOpNotContains:
+            ret = left.u.f != right.u.f;
+            break;
+        default:
+            break;
+        }
+        break;
+    case FcTypeRange:
+        ret = FcRangeCompare (op, left.u.r, right.u.r);
+        break;
     }
     return ret;
 }
commit 922168afe8bf48e872dc0a808fadefaa798a42ce
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Aug 21 08:19:13 2020 -0400

    Speed up fonthashint matching
    
    When we don't need to differentiate between weak and strong,
    we can exit the loop in FcCompareValueList once we found a
    best match.
    
    This change helps reducing the amount of list walking we do
    for fonthashint, where careless config files end up creating
    lists with ~100 booleans :( We don't want to walk all those
    to the end, over and over again.
    
    We are already special-casing family, and the only other case
    where weak != strong, PostScript names, doesn't have long lists
    of values, so the limitation to weak == strong doesn't matter
    much in practice.

diff --git a/src/fcmatch.c b/src/fcmatch.c
index 969b855..289204e 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -408,6 +408,7 @@ FcCompareValueList (FcObject	     object,
     FcValueListPtr  v1, v2;
     double    	    v, best, bestStrong, bestWeak;
     int		    j, k, pos = 0;
+    int weak, strong;
 
     if (!match)
     {
@@ -418,11 +419,13 @@ FcCompareValueList (FcObject	     object,
 	return FcTrue;
     }
 
+    weak    = match->weak;
+    strong  = match->strong;
+
     best = 1e99;
     bestStrong = 1e99;
     bestWeak = 1e99;
-    j = 0;
-    for (v1 = v1orig; v1; v1 = FcValueListNext(v1))
+    for (v1 = v1orig, j = 0; v1; v1 = FcValueListNext(v1), j++)
     {
 	for (v2 = v2orig, k = 0; v2; v2 = FcValueListNext(v2), k++)
 	{
@@ -441,7 +444,13 @@ FcCompareValueList (FcObject	     object,
 		best = v;
 		pos = k;
 	    }
-	    if (v1->binding == FcValueBindingStrong)
+            if (weak == strong)
+            {
+                /* found the best possible match */
+                if (best < 1000)
+                    goto done;
+            }
+            else if (v1->binding == FcValueBindingStrong)
 	    {
 		if (v < bestStrong)
 		    bestStrong = v;
@@ -452,8 +461,8 @@ FcCompareValueList (FcObject	     object,
 		    bestWeak = v;
 	    }
 	}
-	j++;
     }
+done:
     if (FcDebug () & FC_DBG_MATCHV)
     {
 	printf (" %s: %g ", FcObjectName (object), best);
@@ -464,8 +473,6 @@ FcCompareValueList (FcObject	     object,
     }
     if (value)
     {
-	int weak    = match->weak;
-	int strong  = match->strong;
 	if (weak == strong)
 	    value[strong] += best;
 	else
commit 1b0cb23adcdf0c2b136b41d805b157f0c6d75ad4
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Aug 18 15:02:21 2020 -0400

    Add a shortcut for FcQualAny matching
    
    When checking whether a test matches a pattern,
    we cut the loop short for FcQualAll when we see
    the first non-matching value, but for FcQualAny
    we were always walking the full list. This patch
    cuts the loop short for FcQualAny when we see the
    first matching value.

diff --git a/src/fccfg.c b/src/fccfg.c
index 74e8746..cb4d3e0 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -1550,6 +1550,8 @@ FcConfigMatchValueList (FcPattern	*p,
 	    {
 		if (!ret)
 		    ret = v;
+                if (t->qual != FcQualAll)
+                    break;
 	    }
 	    else
 	    {
commit 8022ab4aff469a8f095ce3168d879d3e0b3605ef
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Tue Aug 18 10:54:43 2020 -0400

    Use a hash table for family matching
    
    With the way typical font configurations look, matching the lists
    of families is the bottleneck for both FcFontMatch and FcFontSort.
    After installing the Noto fonts on my system, an innocent match
    pattern like "Cantarell 14" turns into a monster with a list of
    300 family names after calling FcConfigSubstitute().
    
    With this setup, every FcFontSort call takes 80-100 ms, which is
    entirely incompatible with using FcFontSort for anything interactive.
    And many font choosers render every font in itself, causing on average
    one FcFontSort call per font.
    
    On my system, it takes more than 20 seconds to open the GTK font
    chooser dialog, with frequent stalls when scrolling.
    
    This patch special-cases font families and replaces the list
    walking for comparison with a hash table lookup. With this
    patch, the FcFontSort time goes to ~10ms per call. Which is
    still not good enough for calling it dozens of times when
    scrolling, but a significant improvement.

diff --git a/src/fcmatch.c b/src/fcmatch.c
index e370e8b..969b855 100644
--- a/src/fcmatch.c
+++ b/src/fcmatch.c
@@ -480,6 +480,109 @@ FcCompareValueList (FcObject	     object,
     return FcTrue;
 }
 
+/* The bulk of the time in FcFontMatch and FcFontSort goes to
+ * walking long lists of family names. We speed this up with a
+ * hash table.
+ */
+typedef struct
+{
+    double strong_value;
+    double weak_value;
+} FamilyEntry;
+
+typedef struct
+{
+    FcHashTable *family_hash;
+} FcCompareData;
+
+static void
+FcCompareDataClear (FcCompareData *data)
+{
+    FcHashTableDestroy (data->family_hash);
+}
+
+static void
+FcCompareDataInit (FcPattern     *pat,
+                   FcCompareData *data)
+{
+    FcHashTable *table;
+    FcPatternElt *elt;
+    FcValueListPtr l;
+    int i;
+    const void *key;
+    FamilyEntry *e;
+
+    table = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
+                               (FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
+                               NULL,
+                               NULL,
+                               NULL,
+                               free);
+
+    elt = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT);
+    for (l = FcPatternEltValues(elt), i = 0; l; l = FcValueListNext(l), i++)
+    {
+        key = FcValueString (&l->value);
+        if (!FcHashTableFind (table, key, (void **)&e))
+        {
+            e = malloc (sizeof (FamilyEntry));
+            e->strong_value = 1e99;
+            e->weak_value = 1e99;
+            FcHashTableAdd (table, (void *)key, e);
+        }
+        if (l->binding == FcValueBindingWeak)
+        {
+            if (i < e->weak_value)
+                e->weak_value = i;
+        }
+        else
+        {
+            if (i < e->strong_value)
+                e->strong_value = i;
+        }
+    }
+
+    data->family_hash = table;
+}
+
+static FcBool
+FcCompareFamilies (FcPattern       *pat,
+                   FcValueListPtr   v1orig,
+                   FcPattern       *fnt,
+                   FcValueListPtr   v2orig,
+                   double          *value,
+                   FcResult        *result,
+                   FcHashTable     *table)
+{
+    FcValueListPtr v2;
+    double strong_value;
+    double weak_value;
+    const void *key;
+    FamilyEntry *e;
+
+    assert (table != NULL);
+
+    strong_value = 1e99;
+    weak_value = 1e99;
+
+    for (v2 = v2orig; v2; v2 = FcValueListNext(v2))
+    {
+        key = FcValueString (&v2->value);
+        if (FcHashTableFind (table, key, (void **)&e))
+        {
+            if (e->strong_value < strong_value)
+                strong_value = e->strong_value;
+            if (e->weak_value < weak_value)
+                weak_value = e->weak_value;
+        }
+    }
+
+    value[PRI_FAMILY_STRONG] = strong_value;
+    value[PRI_FAMILY_WEAK] = weak_value;
+
+    return FcTrue;
+}
+
 /*
  * Return a value indicating the distance between the two lists of
  * values
@@ -489,7 +592,8 @@ static FcBool
 FcCompare (FcPattern	*pat,
 	   FcPattern	*fnt,
 	   double	*value,
-	   FcResult	*result)
+	   FcResult	*result,
+           FcCompareData *data)
 {
     int		    i, i1, i2;
 
@@ -508,8 +612,18 @@ FcCompare (FcPattern	*pat,
 	    i2++;
 	else if (i < 0)
 	    i1++;
-	else
-	{
+	else if (elt_i1->object == FC_FAMILY_OBJECT && data->family_hash)
+        {
+            if (!FcCompareFamilies (pat, FcPatternEltValues(elt_i1),
+                                    fnt, FcPatternEltValues(elt_i2),
+                                    value, result,
+                                    data->family_hash))
+                return FcFalse;
+	    i1++;
+	    i2++;
+        }
+        else
+        {
 	    const FcMatcher *match = FcObjectToMatcher (elt_i1->object, FcFalse);
 	    if (!FcCompareValueList (elt_i1->object, match,
 				     FcPatternEltValues(elt_i1),
@@ -734,6 +848,7 @@ FcFontSetMatchInternal (FcFontSet   **sets,
     FcPattern	    *best;
     int		    i;
     int		    set;
+    FcCompareData   data;
 
     for (i = 0; i < PRI_END; i++)
 	bestscore[i] = 0;
@@ -743,6 +858,9 @@ FcFontSetMatchInternal (FcFontSet   **sets,
 	printf ("Match ");
 	FcPatternPrint (p);
     }
+
+    FcCompareDataInit (p, &data);
+
     for (set = 0; set < nsets; set++)
     {
 	s = sets[set];
@@ -755,8 +873,11 @@ FcFontSetMatchInternal (FcFontSet   **sets,
 		printf ("Font %d ", f);
 		FcPatternPrint (s->fonts[f]);
 	    }
-	    if (!FcCompare (p, s->fonts[f], score, result))
+	    if (!FcCompare (p, s->fonts[f], score, result, &data))
+            {
+                FcCompareDataClear (&data);
 		return 0;
+            }
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
 		printf ("Score");
@@ -780,6 +901,9 @@ FcFontSetMatchInternal (FcFontSet   **sets,
 	    }
 	}
     }
+
+    FcCompareDataClear (&data);
+
     if (FcDebug () & FC_DBG_MATCH)
     {
 	printf ("Best score");
@@ -1015,6 +1139,7 @@ FcFontSetSort (FcConfig	    *config FC_UNUSED,
     int		    nPatternLang;
     FcBool    	    *patternLangSat;
     FcValue	    patternLang;
+    FcCompareData   data;
 
     assert (sets != NULL);
     assert (p != NULL);
@@ -1059,6 +1184,8 @@ FcFontSetSort (FcConfig	    *config FC_UNUSED,
     nodeps = (FcSortNode **) (nodes + nnodes);
     patternLangSat = (FcBool *) (nodeps + nnodes);
 
+    FcCompareDataInit (p, &data);
+
     new = nodes;
     nodep = nodeps;
     for (set = 0; set < nsets; set++)
@@ -1074,7 +1201,7 @@ FcFontSetSort (FcConfig	    *config FC_UNUSED,
 		FcPatternPrint (s->fonts[f]);
 	    }
 	    new->pattern = s->fonts[f];
-	    if (!FcCompare (p, new->pattern, new->score, result))
+	    if (!FcCompare (p, new->pattern, new->score, result, &data))
 		goto bail1;
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
@@ -1091,6 +1218,8 @@ FcFontSetSort (FcConfig	    *config FC_UNUSED,
 	}
     }
 
+    FcCompareDataClear (&data);
+
     nnodes = new - nodes;
 
     qsort (nodeps, nnodes, sizeof (FcSortNode *),
commit 055843631b995f8acc778c94032c949844e8812b
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Aug 21 08:05:18 2020 -0400

    Add a hash function for families
    
    Add a hash function that behaves like family
    comparison: ignoring case and blanks. This
    will be used to replace the list walking for
    finding family matches with a hash table.

diff --git a/src/fcint.h b/src/fcint.h
index 88e0701..08a0cd6 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -1328,6 +1328,9 @@ FcStrLastSlash (const FcChar8  *path);
 FcPrivate FcChar32
 FcStrHashIgnoreCase (const FcChar8 *s);
 
+FcPrivate FcChar32
+FcStrHashIgnoreBlanksAndCase (const FcChar8 *s);
+
 FcPrivate FcChar8 *
 FcStrCanonFilename (const FcChar8 *s);
 
diff --git a/src/fcstr.c b/src/fcstr.c
index 39ecbbb..dc9940a 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -320,6 +320,19 @@ FcStrHashIgnoreCase (const FcChar8 *s)
     return h;
 }
 
+FcChar32
+FcStrHashIgnoreBlanksAndCase (const FcChar8 *s)
+{
+    FcChar32	    h = 0;
+    FcCaseWalker    w;
+    FcChar8	    c;
+
+    FcStrCaseWalkerInit (s, &w);
+    while ((c = FcStrCaseWalkerNextNonBlank (&w)))
+	h = ((h << 3) ^ (h >> 3)) ^ c;
+    return h;
+}
+
 /*
  * Is the head of s1 equal to s2?
  */
commit 46d818df26f3585f9261c735129a606837406875
Author: Matthias Clasen <mclasen at redhat.com>
Date:   Fri Aug 21 00:47:09 2020 -0400

    Special-case some of the string walking code
    
    Make variants of FcStrCaseWalkerNext for the two
    common cases, delim == NULL and delim == " ", to
    speed things up.
    
    These are inner loops, and having the conditions
    as simple as possible helps.

diff --git a/src/fcstr.c b/src/fcstr.c
index 864d4aa..39ecbbb 100644
--- a/src/fcstr.c
+++ b/src/fcstr.c
@@ -160,7 +160,7 @@ FcStrCaseWalkerLong (FcCaseWalker *w, FcChar8 r)
 }
 
 static FcChar8
-FcStrCaseWalkerNext (FcCaseWalker *w, const char *delims)
+FcStrCaseWalkerNextNonDelim (FcCaseWalker *w, const char *delims)
 {
     FcChar8	r;
 
@@ -182,6 +182,50 @@ FcStrCaseWalkerNext (FcCaseWalker *w, const char *delims)
     return r;
 }
 
+static FcChar8
+FcStrCaseWalkerNextNonBlank (FcCaseWalker *w)
+{
+    FcChar8	r;
+
+    if (w->read)
+    {
+	if ((r = *w->read++))
+	    return r;
+	w->read = 0;
+    }
+    do
+    {
+	r = *w->src++;
+    } while (r == ' ');
+
+    if ((r & 0xc0) == 0xc0)
+	return FcStrCaseWalkerLong (w, r);
+    if ('A' <= r && r <= 'Z')
+        r = r - 'A' + 'a';
+    return r;
+}
+
+static FcChar8
+FcStrCaseWalkerNext (FcCaseWalker *w)
+{
+    FcChar8	r;
+
+    if (w->read)
+    {
+	if ((r = *w->read++))
+	    return r;
+	w->read = 0;
+    }
+
+    r = *w->src++;
+
+    if ((r & 0xc0) == 0xc0)
+	return FcStrCaseWalkerLong (w, r);
+    if ('A' <= r && r <= 'Z')
+        r = r - 'A' + 'a';
+    return r;
+}
+
 FcChar8 *
 FcStrDowncase (const FcChar8 *s)
 {
@@ -190,13 +234,13 @@ FcStrDowncase (const FcChar8 *s)
     FcChar8	    *dst, *d;
 
     FcStrCaseWalkerInit (s, &w);
-    while (FcStrCaseWalkerNext (&w, NULL))
+    while (FcStrCaseWalkerNext (&w))
 	len++;
     d = dst = malloc (len + 1);
     if (!d)
 	return 0;
     FcStrCaseWalkerInit (s, &w);
-    while ((*d++ = FcStrCaseWalkerNext (&w, NULL)));
+    while ((*d++ = FcStrCaseWalkerNext (&w)));
     return dst;
 }
 
@@ -213,8 +257,8 @@ FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
 
     for (;;)
     {
-	c1 = FcStrCaseWalkerNext (&w1, NULL);
-	c2 = FcStrCaseWalkerNext (&w2, NULL);
+	c1 = FcStrCaseWalkerNext (&w1);
+	c2 = FcStrCaseWalkerNext (&w2);
 	if (!c1 || (c1 != c2))
 	    break;
     }
@@ -223,12 +267,6 @@ FcStrCmpIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
 
 int
 FcStrCmpIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
-{
-    return FcStrCmpIgnoreCaseAndDelims (s1, s2, (const FcChar8 *)" ");
-}
-
-int
-FcStrCmpIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8 *delims)
 {
     FcCaseWalker    w1, w2;
     FcChar8	    c1, c2;
@@ -240,8 +278,8 @@ FcStrCmpIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcChar8
 
     for (;;)
     {
-	c1 = FcStrCaseWalkerNext (&w1, (const char *)delims);
-	c2 = FcStrCaseWalkerNext (&w2, (const char *)delims);
+	c1 = FcStrCaseWalkerNextNonBlank (&w1);
+	c2 = FcStrCaseWalkerNextNonBlank (&w2);
 	if (!c1 || (c1 != c2))
 	    break;
     }
@@ -277,7 +315,7 @@ FcStrHashIgnoreCase (const FcChar8 *s)
     FcChar8	    c;
 
     FcStrCaseWalkerInit (s, &w);
-    while ((c = FcStrCaseWalkerNext (&w, NULL)))
+    while ((c = FcStrCaseWalkerNext (&w)))
 	h = ((h << 3) ^ (h >> 3)) ^ c;
     return h;
 }
@@ -297,8 +335,8 @@ FcStrIsAtIgnoreBlanksAndCase (const FcChar8 *s1, const FcChar8 *s2)
 
     for (;;)
     {
-	c1 = FcStrCaseWalkerNext (&w1, " ");
-	c2 = FcStrCaseWalkerNext (&w2, " ");
+	c1 = FcStrCaseWalkerNextNonBlank (&w1);
+	c2 = FcStrCaseWalkerNextNonBlank (&w2);
 	if (!c1 || (c1 != c2))
 	    break;
     }
@@ -356,8 +394,8 @@ FcStrIsAtIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
 
     for (;;)
     {
-	c1 = FcStrCaseWalkerNext (&w1, NULL);
-	c2 = FcStrCaseWalkerNext (&w2, NULL);
+	c1 = FcStrCaseWalkerNext (&w1);
+	c2 = FcStrCaseWalkerNext (&w2);
 	if (!c1 || (c1 != c2))
 	    break;
     }
@@ -425,8 +463,8 @@ FcStrMatchIgnoreCaseAndDelims (const FcChar8 *s1, const FcChar8 *s2, const FcCha
 
     for (;;)
     {
-	c1 = FcStrCaseWalkerNext (&w1, (const char *)delims);
-	c2 = FcStrCaseWalkerNext (&w2, (const char *)delims);
+	c1 = FcStrCaseWalkerNextNonDelim (&w1, (const char *)delims);
+	c2 = FcStrCaseWalkerNextNonDelim (&w2, (const char *)delims);
 	if (!c1 || (c1 != c2))
 	    break;
     }
@@ -493,12 +531,12 @@ FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
     FcStrCaseWalkerInit (s1, &w1);
     FcStrCaseWalkerInit (s2, &w2);
 
-    c2 = FcStrCaseWalkerNext (&w2, NULL);
+    c2 = FcStrCaseWalkerNext (&w2);
 
     for (;;)
     {
 	cur = w1.src;
-	c1 = FcStrCaseWalkerNext (&w1, NULL);
+	c1 = FcStrCaseWalkerNext (&w1);
 	if (!c1)
 	    break;
 	if (c1 == c2)
@@ -509,8 +547,8 @@ FcStrStrIgnoreCase (const FcChar8 *s1, const FcChar8 *s2)
 
 	    for (;;)
 	    {
-		c1t = FcStrCaseWalkerNext (&w1t, NULL);
-		c2t = FcStrCaseWalkerNext (&w2t, NULL);
+		c1t = FcStrCaseWalkerNext (&w1t);
+		c2t = FcStrCaseWalkerNext (&w2t);
 
 		if (!c2t)
 		    return cur;


More information about the Fontconfig mailing list