]> git.wh0rd.org - fontconfig.git/blobdiff - src/fccache.c
Don't use varargs CPP macros in fccache.c. (bug 8733)
[fontconfig.git] / src / fccache.c
index a3a758ebb2450cc3cf0256567f6e3ea918173aab..3185059c4f229246d5bb38652606f6417e7205a9 100644 (file)
@@ -28,6 +28,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <sys/types.h>
+#include <assert.h>
 #if defined(HAVE_MMAP) || defined(__CYGWIN__)
 #  include <unistd.h>
 #  include <sys/mman.h>
@@ -182,97 +183,245 @@ FcDirCacheProcess (FcConfig *config, const FcChar8 *dir,
     return ret;
 }
 
-static void
-FcDirCacheDispose (FcCache *cache)
-{
-    switch (cache->magic) {
-    case FC_CACHE_MAGIC_ALLOC:
-       free (cache);
-       break;
-    case FC_CACHE_MAGIC_MMAP:
-#if defined(HAVE_MMAP) || defined(__CYGWIN__)
-       munmap (cache, cache->size);
-#elif defined(_WIN32)
-       UnmapViewOfFile (cache);
-#endif
-       break;
-    }
-}
-
 #define FC_CACHE_MIN_MMAP   1024
 
-#define FC_CACHE_HASH_SIZE  67
+/*
+ * Skip list element, make sure the 'next' pointer is the last thing
+ * in the structure, it will be allocated large enough to hold all
+ * of the necessary pointers
+ */
 
-typedef struct _FcCacheBucket FcCacheBucket;
+typedef struct _FcCacheSkip FcCacheSkip;
 
-struct _FcCacheBucket {
-    FcCacheBucket   *next;
+struct _FcCacheSkip {
     FcCache        *cache;
+    int                    ref;
+    intptr_t       size;
     dev_t          cache_dev;
     ino_t          cache_ino;
     time_t         cache_mtime;
+    FcCacheSkip            *next[1];
 };
 
-static FcCacheBucket   *FcCacheBuckets[FC_CACHE_HASH_SIZE];
+/*
+ * The head of the skip list; pointers for every possible level
+ * in the skip list, plus the largest level in the list
+ */
 
-static uint32_t
-FcCacheStatHash (struct stat *cache_stat)
-{
-    return ((uint32_t) cache_stat->st_dev ^
-           (uint32_t) cache_stat->st_ino ^
-           (uint32_t) cache_stat->st_mtime);
-}
+#define FC_CACHE_MAX_LEVEL  16
 
-static FcCache *
-FcCacheLookup (struct stat *cache_stat)
+static FcCacheSkip     *fcCacheChains[FC_CACHE_MAX_LEVEL];
+static int             fcCacheMaxLevel;
+
+#if HAVE_RANDOM
+# define FcRandom()  random()
+#else
+# if HAVE_LRAND48
+#  define FcRandom()  lrand48()
+# else
+#  if HAVE_RAND
+#   define FcRandom()  rand()
+#  endif
+# endif
+#endif
+/*
+ * Generate a random level number, distributed
+ * so that each level is 1/4 as likely as the one before
+ *
+ * Note that level numbers run 1 <= level <= MAX_LEVEL
+ */
+static int
+random_level (void)
 {
-    uint32_t       hash = FcCacheStatHash(cache_stat);
-    FcCacheBucket   **bucket = &FcCacheBuckets[hash % FC_CACHE_HASH_SIZE];
-    FcCacheBucket   *b;
+    /* tricky bit -- each bit is '1' 75% of the time */
+    long int   bits = FcRandom () | FcRandom ();
+    int        level = 0;
 
-    for (b = *bucket; b; b = b->next)
-       if (b->cache_dev == cache_stat->st_dev &&
-           b->cache_ino == cache_stat->st_ino &&
-           b->cache_mtime == cache_stat->st_mtime)
-           return b->cache;
-    return NULL;
+    while (++level < FC_CACHE_MAX_LEVEL)
+    {
+       if (bits & 1)
+           break;
+       bits >>= 1;
+    }
+    return level;
 }
 
+/*
+ * Insert cache into the list
+ */
 static FcBool
-FcCacheRecord (FcCache *cache, struct stat *cache_stat)
+FcCacheInsert (FcCache *cache, struct stat *cache_stat)
 {
-    uint32_t       hash = FcCacheStatHash(cache_stat);
-    FcCacheBucket   **bucket = &FcCacheBuckets[hash % FC_CACHE_HASH_SIZE];
-    FcCacheBucket   *b;
+    FcCacheSkip    **update[FC_CACHE_MAX_LEVEL];
+    FcCacheSkip    *s, **next;
+    int                    i, level;
+
+    /*
+     * Find links along each chain
+     */
+    next = fcCacheChains;
+    for (i = fcCacheMaxLevel; --i >= 0; )
+    {
+       for (; (s = next[i]); next = s->next)
+           if (s->cache > cache)
+               break;
+        update[i] = &next[i];
+    }
 
-    b = malloc (sizeof (FcCacheBucket));
-    if (!b)
+    /*
+     * Create new list element
+     */
+    level = random_level ();
+    if (level > fcCacheMaxLevel)
+    {
+       level = fcCacheMaxLevel + 1;
+       update[fcCacheMaxLevel] = &fcCacheChains[fcCacheMaxLevel];
+       fcCacheMaxLevel = level;
+    }
+    
+    s = malloc (sizeof (FcCacheSkip) + (level - 1) * sizeof (FcCacheSkip *));
+    if (!s)
        return FcFalse;
-    b->next = *bucket;
-    b->cache = cache;
-    b->cache_dev = cache_stat->st_dev;
-    b->cache_ino = cache_stat->st_ino;
-    b->cache_mtime = cache_stat->st_mtime;
-    *bucket = b;
+
+    s->cache = cache;
+    s->size = cache->size;
+    s->ref = 1;
+    if (cache_stat)
+    {
+       s->cache_dev = cache_stat->st_dev;
+       s->cache_ino = cache_stat->st_ino;
+       s->cache_mtime = cache_stat->st_mtime;
+    }
+    else
+    {
+       s->cache_dev = 0;
+       s->cache_ino = 0;
+       s->cache_mtime = 0;
+    }
+    
+    /*
+     * Insert into all fcCacheChains
+     */
+    for (i = 0; i < level; i++)
+    {
+       s->next[i] = *update[i];
+       *update[i] = s;
+    }
     return FcTrue;
 }
 
-void
-FcCacheFini (void)
+static FcCacheSkip *
+FcCacheFindByAddr (void *object)
+{
+    int            i;
+    FcCacheSkip    **next = fcCacheChains;
+    FcCacheSkip    *s;
+
+    /*
+     * Walk chain pointers one level at a time
+     */
+    for (i = fcCacheMaxLevel; --i >= 0;)
+       while (next[i] && (char *) object >= ((char *) next[i]->cache + next[i]->size))
+           next = next[i]->next;
+    /*
+     * Here we are
+     */
+    s = next[0];
+    if (s && (char *) object < ((char *) s->cache + s->size))
+       return s;
+    return NULL;
+}
+
+static void
+FcCacheRemove (FcCache *cache)
 {
+    FcCacheSkip            **update[FC_CACHE_MAX_LEVEL];
+    FcCacheSkip            *s, **next;
     int                    i;
-    FcCacheBucket   *b, *next;
 
-    for (i = 0; i < FC_CACHE_HASH_SIZE; i++)
+    /*
+     * Find links along each chain
+     */
+    next = fcCacheChains;
+    for (i = fcCacheMaxLevel; --i >= 0; )
     {
-       for (b = FcCacheBuckets[i]; b; b = next)
+       for (; (s = next[i]); next = s->next)
+           if (s->cache >= cache)
+               break;
+        update[i] = &next[i];
+    }
+    s = next[0];
+    for (i = 0; i < fcCacheMaxLevel && *update[i] == s; i++)
+       *update[i] = s->next[i];
+    while (fcCacheMaxLevel > 0 && fcCacheChains[fcCacheMaxLevel - 1] == NULL)
+       fcCacheMaxLevel--;
+    free (s);
+}
+
+static FcCache *
+FcCacheFindByStat (struct stat *cache_stat)
+{
+    FcCacheSkip            *s;
+
+    for (s = fcCacheChains[0]; s; s = s->next[0])
+       if (s->cache_dev == cache_stat->st_dev &&
+           s->cache_ino == cache_stat->st_ino &&
+           s->cache_mtime == cache_stat->st_mtime)
        {
-           next = b->next;
-           FcDirCacheDispose (b->cache);
-           free (b);
+           s->ref++;
+           return s->cache;
        }
-       FcCacheBuckets[i] = NULL;
+    return NULL;
+}
+
+static void
+FcDirCacheDispose (FcCache *cache)
+{
+    switch (cache->magic) {
+    case FC_CACHE_MAGIC_ALLOC:
+       free (cache);
+       break;
+    case FC_CACHE_MAGIC_MMAP:
+#if defined(HAVE_MMAP) || defined(__CYGWIN__)
+       munmap (cache, cache->size);
+#elif defined(_WIN32)
+       UnmapViewOfFile (cache);
+#endif
+       break;
     }
+    FcCacheRemove (cache);
+}
+
+void
+FcCacheObjectReference (void *object)
+{
+    FcCacheSkip *skip = FcCacheFindByAddr (object);
+
+    if (skip)
+       skip->ref++;
+}
+
+void
+FcCacheObjectDereference (void *object)
+{
+    FcCacheSkip        *skip = FcCacheFindByAddr (object);
+
+    if (skip)
+    {
+       skip->ref--;
+       if (skip->ref <= 0)
+           FcDirCacheDispose (skip->cache);
+    }
+}
+
+void
+FcCacheFini (void)
+{
+    int                    i;
+
+    for (i = 0; i < FC_CACHE_MAX_LEVEL; i++)
+       assert (fcCacheChains[i] == NULL);
+    assert (fcCacheMaxLevel == 0);
 }
 
 /*
@@ -286,7 +435,7 @@ FcDirCacheMapFd (int fd, struct stat *fd_stat)
 
     if (fd_stat->st_size < sizeof (FcCache))
        return NULL;
-    cache = FcCacheLookup (fd_stat);
+    cache = FcCacheFindByStat (fd_stat);
     if (cache)
        return cache;
     /*
@@ -305,7 +454,8 @@ FcDirCacheMapFd (int fd, struct stat *fd_stat)
                                         PAGE_READONLY, 0, 0, NULL);
            if (hFileMap != NULL)
            {
-               cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, size);
+               cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, 
+                                      fd_stat->st_size);
                CloseHandle (hFileMap);
            }
        }
@@ -327,7 +477,7 @@ FcDirCacheMapFd (int fd, struct stat *fd_stat)
     if (cache->magic != FC_CACHE_MAGIC_MMAP || 
        cache->version < FC_CACHE_CONTENT_VERSION ||
        cache->size != fd_stat->st_size ||
-       !FcCacheRecord (cache, fd_stat))
+       !FcCacheInsert (cache, fd_stat))
     {
        if (allocated)
            free (cache);
@@ -349,10 +499,19 @@ FcDirCacheMapFd (int fd, struct stat *fd_stat)
     return cache;
 }
 
+void
+FcDirCacheReference (FcCache *cache, int nref)
+{
+    FcCacheSkip *skip = FcCacheFindByAddr (cache);
+
+    if (skip)
+       skip->ref += nref;
+}
+
 void
 FcDirCacheUnload (FcCache *cache)
 {
-    /* Can't free or unmap the cache as apps may point into it */
+    FcCacheObjectDereference (cache);
 }
 
 static FcBool
@@ -520,6 +679,8 @@ FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs)
 
     FcSerializeDestroy (serialize);
     
+    FcCacheInsert (cache, NULL);
+
     return cache;
 
 bail2:
@@ -529,6 +690,11 @@ bail1:
     return NULL;
 }
 
+
+#ifdef _WIN32
+#define mkdir(path,mode) _mkdir(path)
+#endif
+
 static FcBool
 FcMakeDirectory (const FcChar8 *dir)
 {
@@ -660,16 +826,17 @@ FcDirCacheWrite (FcCache *cache, FcConfig *config)
  * Hokey little macro trick to permit the definitions of C functions
  * with the same name as CPP macros
  */
-#define args(x...)         (x)
+#define args1(x)           (x)
+#define args2(x,y)         (x,y)
 
 const FcChar8 *
-FcCacheDir args(const FcCache *c)
+FcCacheDir args1(const FcCache *c)
 {
     return FcCacheDir (c);
 }
 
 FcFontSet *
-FcCacheCopySet args(const FcCache *c)
+FcCacheCopySet args1(const FcCache *c)
 {
     FcFontSet  *old = FcCacheSet (c);
     FcFontSet  *new = FcFontSetCreate ();
@@ -678,28 +845,33 @@ FcCacheCopySet args(const FcCache *c)
     if (!new)
        return NULL;
     for (i = 0; i < old->nfont; i++)
-       if (!FcFontSetAdd (new, FcFontSetFont (old, i)))
+    {
+       FcPattern   *font = FcFontSetFont (old, i);
+       
+       FcPatternReference (font);
+       if (!FcFontSetAdd (new, font))
        {
            FcFontSetDestroy (new);
            return NULL;
        }
+    }
     return new;
 }
 
 const FcChar8 *
-FcCacheSubdir args(const FcCache *c, int i)
+FcCacheSubdir args2(const FcCache *c, int i)
 {
     return FcCacheSubdir (c, i);
 }
 
 int
-FcCacheNumSubdir args(const FcCache *c)
+FcCacheNumSubdir args1(const FcCache *c)
 {
     return c->dirs_count;
 }
 
 int
-FcCacheNumFont args(const FcCache *c)
+FcCacheNumFont args1(const FcCache *c)
 {
     return FcCacheSet(c)->nfont;
 }
@@ -947,3 +1119,6 @@ static void MD5Transform(FcChar32 buf[4], FcChar32 in[16])
     buf[2] += c;
     buf[3] += d;
 }
+#define __fccache__
+#include "fcaliastail.h"
+#undef __fccache__