/*
- * $RCSId: xc/lib/fontconfig/src/fccache.c,v 1.12 2002/08/22 07:36:44 keithp Exp $
- *
* Copyright © 2000 Keith Packard
* Copyright © 2005 Patrick Lam
*
# include <windows.h>
#endif
-#define ENDIAN_TEST 0x12345678
-#define MACHINE_SIGNATURE_SIZE (9 + 5*20 + 1)
-/* for when we don't have sysconf: */
-#define FC_HARDCODED_PAGESIZE 8192
-
#ifndef O_BINARY
#define O_BINARY 0
#endif
-static FILE *
-FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path);
-
-static void *
-FcDirCacheProduce (FcFontSet *set, FcCache * metadata);
-
-static off_t
-FcCacheNextOffset(off_t w);
-
-static FcBool
-FcCacheHaveBank (int bank);
-
-static void
-FcCacheAddBankDir (int bank, const char * dir);
+static int
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, off_t *size);
struct MD5Context {
FcChar32 buf[4];
#define FC_DBG_CACHE_REF 1024
-static char *
-FcCacheReadString (FILE *file, char *dest, int len)
-{
- int c;
- char *d = dest;
-
- if (len == 0)
- return 0;
-
- while ((c = getc (file)) != EOF && len > 0) {
- *d++ = c;
- if (c == '\0')
- return dest;
- len--;
- }
- return 0;
-}
-
-static FcBool
-FcCacheWriteString (int fd, const char *chars)
-{
- if (write (fd, chars, strlen(chars)+1) != strlen(chars)+1)
- return FcFalse;
- return FcTrue;
-}
-
-/*
- * Find the next presumably-mmapable offset after the supplied file
- * position.
- */
-static off_t
-FcCacheNextOffset(off_t w)
-{
- static long pagesize = -1;
-
- if (w == -1)
- return w;
-
- if (pagesize == -1)
-#if defined (HAVE_SYSCONF)
- pagesize = sysconf(_SC_PAGESIZE);
-#else
- pagesize = FC_HARDCODED_PAGESIZE;
-#endif
- if (w % pagesize == 0)
- return w;
- else
- return ((w / pagesize)+1)*pagesize;
-}
-
/* Does not check that the cache has the appropriate arch section. */
FcBool
FcDirCacheValid (const FcChar8 *dir, FcConfig *config)
{
- FILE *file;
+ int fd;
- file = FcDirCacheOpen (config, dir, NULL);
+ fd = FcDirCacheOpen (config, dir, NULL);
- if (file == NULL)
+ if (fd < 0)
return FcFalse;
- fclose (file);
+ close (fd);
return FcTrue;
}
* This searches the list of cache dirs for the relevant cache file,
* returning the first one found.
*/
-static FILE *
-FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path)
+static int
+FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, off_t *size)
{
- FILE *file = NULL;
- FcChar8 *cache_hashed = NULL;
+ int fd = -1;
FcChar8 cache_base[CACHEBASE_LEN];
FcStrList *list;
FcChar8 *cache_dir;
struct stat file_stat, dir_stat;
if (stat ((char *) dir, &dir_stat) < 0)
- return NULL;
+ return -1;
FcDirCacheBasename (dir, cache_base);
list = FcStrListCreate (config->cacheDirs);
if (!list)
- return NULL;
+ return -1;
while ((cache_dir = FcStrListNext (list)))
{
- cache_hashed = FcStrPlus (cache_dir, cache_base);
+ FcChar8 *cache_hashed = FcStrPlus (cache_dir, cache_base);
if (!cache_hashed)
break;
- file = fopen((char *) cache_hashed, "rb");
- if (file != NULL) {
- if (fstat (fileno (file), &file_stat) >= 0 &&
+ fd = open((char *) cache_hashed, O_RDONLY | O_BINARY);
+ FcStrFree (cache_hashed);
+ if (fd >= 0) {
+ if (fstat (fd, &file_stat) >= 0 &&
dir_stat.st_mtime <= file_stat.st_mtime)
{
+ if (size)
+ *size = file_stat.st_size;
break;
}
- fclose (file);
- file = NULL;
+ close (fd);
+ fd = -1;
}
- FcStrFree (cache_hashed);
- cache_hashed = NULL;
}
FcStrListDone (list);
-
- if (file == NULL)
- return NULL;
- if (cache_path)
- *cache_path = cache_hashed;
- else
- FcStrFree (cache_hashed);
+ return fd;
+}
- return file;
+#if 0
+static void
+FcDirCacheUnmap (FcCache *cache)
+{
+ if (cache->magic == FC_CACHE_MAGIC_COPY)
+ {
+ free (cache);
+ return;
+ }
+#if defined(HAVE_MMAP) || defined(__CYGWIN__)
+ munmap (cache, cache->size);
+#elif defined(_WIN32)
+ UnmapViewOfFile (cache);
+#endif
}
+#endif
/* read serialized state from the cache file */
-FcBool
-FcDirCacheConsume (FILE *file, FcFontSet *set, FcStrSet *dirs,
- const FcChar8 *dir, char *dirname)
+static FcCache *
+FcDirCacheMap (int fd, off_t size)
{
- FcCache metadata;
- void *current_dir_block;
- char subdir_name[FC_MAX_FILE_LEN + 1 + 12 + 1];
- int i;
-
- if (fread(&metadata, sizeof(FcCache), 1, file) != 1)
- goto bail;
- if (metadata.magic != FC_CACHE_MAGIC)
- goto bail;
-
- /* skip directory name; it's just for fc-cat */
- if (!FcCacheReadString (file, subdir_name, sizeof (subdir_name)))
- goto bail;
-
- if (dirname)
- strcpy (dirname, subdir_name);
+ FcCache *cache;
+ FcBool allocated = FcFalse;
- for (i = 0; i < metadata.subdirs; i++) {
- if (!FcCacheReadString (file, subdir_name, sizeof (subdir_name)))
- goto bail;
- FcStrSetAdd (dirs, (FcChar8 *)subdir_name);
- }
-
- if (metadata.count)
- {
- int fd = fileno (file);
+ if (size < sizeof (FcCache))
+ return NULL;
#if defined(HAVE_MMAP) || defined(__CYGWIN__)
- current_dir_block = mmap (0, metadata.count,
- PROT_READ, MAP_SHARED, fd, metadata.pos);
- if (current_dir_block == MAP_FAILED)
- goto bail;
+ cache = mmap (0, size, PROT_READ, MAP_SHARED, fd, 0);
#elif defined(_WIN32)
- {
- HANDLE hFileMap;
-
- hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL, PAGE_READONLY, 0, 0, NULL);
- if (hFileMap == NULL)
- goto bail;
+ {
+ HANDLE hFileMap;
- current_dir_block = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, metadata.pos, metadata.count);
- if (current_dir_block == NULL)
- {
- CloseHandle (hFileMap);
- goto bail;
- }
+ cache = NULL;
+ hFileMap = CreateFileMapping((HANDLE) _get_osfhandle(fd), NULL,
+ PAGE_READONLY, 0, 0, NULL);
+ if (hFileMap != NULL)
+ {
+ cache = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, 0, size);
+ CloseHandle (hFileMap);
}
-#else
- if (lseek (fd, metatdata.pos, SEEK_SET) == -1)
- goto bail;
-
- current_dir_block = malloc (metadata.count);
- if (!current_dir_block)
- goto bail;
+ }
+#endif
+ if (!cache)
+ {
+ cache = malloc (size);
+ if (!cache)
+ return NULL;
- /* could also use CreateMappedViewOfFile under MinGW... */
- if (read (fd, current_dir_block, metadata.count) != metadata.count)
+ if (read (fd, cache, size) != size)
{
- free (current_dir_block);
- goto bail;
+ free (cache);
+ return NULL;
}
+ allocated = FcTrue;
+ }
+ if (cache->magic != FC_CACHE_MAGIC ||
+ cache->size != size)
+ {
+ if (allocated)
+ free (cache);
+ else
+ {
+#if defined(HAVE_MMAP) || defined(__CYGWIN__)
+ munmap (cache, size);
+#elif defined(_WIN32)
+ UnmapViewOfFile (cache);
#endif
- FcCacheAddBankDir (metadata.bank, (char *) dir);
- if (!FcFontSetUnserialize (&metadata, set, current_dir_block))
- goto bail;
+ }
+ return NULL;
}
- return FcTrue;
-
- bail:
- return FcFalse;
+ /* Mark allocated caches so they're freed rather than unmapped */
+ if (allocated)
+ cache->magic = FC_CACHE_MAGIC_COPY;
+
+ return cache;
}
FcBool
FcDirCacheRead (FcFontSet * set, FcStrSet * dirs,
const FcChar8 *dir, FcConfig *config)
{
- FILE *file;
-
- file = FcDirCacheOpen (config, dir, NULL);
- if (file == NULL)
- goto bail;
+ int fd;
+ FcCache *cache;
+ off_t size;
+ int i;
+ FcFontSet *cache_set;
+ intptr_t *cache_dirs;
+ FcPattern **cache_fonts;
- if (!FcDirCacheConsume (file, set, dirs, dir, NULL))
- goto bail1;
+ fd = FcDirCacheOpen (config, dir, &size);
+ if (fd < 0)
+ return FcFalse;
+ cache = FcDirCacheMap (fd, size);
+
+ if (!cache)
+ {
+ if (FcDebug() & FC_DBG_CACHE)
+ printf ("FcDirCacheRead failed to map cache for %s\n", dir);
+ close (fd);
+ return FcFalse;
+ }
+
+ cache_set = FcCacheSet (cache);
+ cache_fonts = FcFontSetFonts(cache_set);
+ if (FcDebug() & FC_DBG_CACHE)
+ printf ("FcDirCacheRead mapped cache for %s (%d fonts %d subdirs)\n",
+ dir, cache_set->nfont, cache->dirs_count);
+ for (i = 0; i < cache_set->nfont; i++)
+ {
+ FcPattern *font = FcEncodedOffsetToPtr (cache_set,
+ cache_fonts[i],
+ FcPattern);
+ if (FcDebug() & FC_DBG_CACHEV) {
+ printf ("Mapped font %d\n", i);
+ FcPatternPrint (font);
+ }
+ FcFontSetAdd (set, font);
+ }
+
+ cache_dirs = FcCacheDirs (cache);
+ for (i = 0; i < cache->dirs_count; i++)
+ FcStrSetAdd (dirs, FcOffsetToPtr (cache_dirs,
+ cache_dirs[i],
+ FcChar8));
+
if (config)
FcConfigAddFontDir (config, (FcChar8 *)dir);
- fclose (file);
+ close (fd);
return FcTrue;
-
-bail1:
- fclose (file);
-bail:
- return FcFalse;
}
+/*
+ * Cache file is:
+ *
+ * FcCache
+ * dir name
+ * subdirs
+ * FcFontSet
+ */
-static void *
-FcDirCacheProduce (FcFontSet *set, FcCache *metadata)
+static FcCache *
+FcDirCacheProduce (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs)
{
- void * current_dir_block, * final_dir_block;
- static unsigned int rand_state = 0;
- int bank, needed_bytes_no_align;
-
-#if defined (HAVE_RAND_R)
- if (!rand_state)
- rand_state = time(0L);
- bank = rand_r(&rand_state);
+ FcSerialize *serialize = FcSerializeCreate ();
+ FcCache *cache;
+ int i;
+ intptr_t cache_offset;
+ intptr_t dirs_offset;
+ FcChar8 *dir_serialize;
+ intptr_t *dirs_serialize;
+ FcFontSet *set_serialize;
+
+ if (!serialize)
+ return NULL;
+ /*
+ * Space for cache structure
+ */
+ cache_offset = FcSerializeReserve (serialize, sizeof (FcCache));
+ /*
+ * Directory name
+ */
+ if (!FcStrSerializeAlloc (serialize, dir))
+ goto bail1;
+ /*
+ * Subdirs
+ */
+ dirs_offset = FcSerializeAlloc (serialize, dirs, dirs->num * sizeof (FcChar8 *));
+ for (i = 0; i < dirs->num; i++)
+ if (!FcStrSerializeAlloc (serialize, dirs->strs[i]))
+ goto bail1;
- while (FcCacheHaveBank(bank))
- bank = rand_r(&rand_state);
-#else
- if (!rand_state)
- {
- rand_state = 1;
- srand (time (0L));
- }
- bank = rand();
+ /*
+ * Patterns
+ */
+ if (!FcFontSetSerializeAlloc (serialize, set))
+ goto bail1;
+
+ /* Serialize layout complete. Now allocate space and fill it */
+ cache = malloc (serialize->size);
+ if (!cache)
+ goto bail1;
+ /* shut up valgrind */
+ memset (cache, 0, serialize->size);
- while (FcCacheHaveBank(bank))
- bank = rand();
-#endif
+ serialize->linear = cache;
- memset (metadata, 0, sizeof(FcCache));
- FcFontSetNewBank();
- needed_bytes_no_align = FcFontSetNeededBytes (set);
- metadata->count = needed_bytes_no_align +
- FcFontSetNeededBytesAlign ();
- metadata->magic = FC_CACHE_MAGIC;
- metadata->bank = bank;
+ cache->magic = FC_CACHE_MAGIC;
+ cache->size = serialize->size;
- if (!needed_bytes_no_align) /* not a failure, no fonts to write */
+ /*
+ * Serialize directory name
+ */
+ dir_serialize = FcStrSerialize (serialize, dir);
+ if (!dir_serialize)
+ goto bail2;
+ cache->dir = FcPtrToOffset (cache, dir_serialize);
+
+ /*
+ * Serialize sub dirs
+ */
+ dirs_serialize = FcSerializePtr (serialize, dirs);
+ if (!dirs_serialize)
+ goto bail2;
+ cache->dirs = FcPtrToOffset (cache, dirs_serialize);
+ cache->dirs_count = dirs->num;
+ for (i = 0; i < dirs->num; i++)
{
- /* no, you don't really need to write any bytes at all. */
- metadata->count = 0;
- return 0;
+ FcChar8 *d_serialize = FcStrSerialize (serialize, dirs->strs[i]);
+ if (!d_serialize)
+ goto bail2;
+ dirs_serialize[i] = FcPtrToOffset (dirs_serialize, d_serialize);
}
+
+ /*
+ * Serialize font set
+ */
+ set_serialize = FcFontSetSerialize (serialize, set);
+ if (!set_serialize)
+ goto bail2;
+ cache->set = FcPtrToOffset (cache, set_serialize);
- current_dir_block = malloc (metadata->count);
- if (!current_dir_block)
- goto bail;
- /* shut up valgrind */
- memset (current_dir_block, 0, metadata->count);
- final_dir_block = FcFontSetDistributeBytes (metadata, current_dir_block);
-
- if ((void *)((char *)current_dir_block+metadata->count) < final_dir_block)
- goto bail;
-
- if (!FcFontSetSerialize (bank, set))
- goto bail;
-
- return current_dir_block;
+ FcSerializeDestroy (serialize);
+
+ return cache;
- bail:
- free (current_dir_block);
- return 0;
+bail2:
+ free (cache);
+bail1:
+ FcSerializeDestroy (serialize);
+ return NULL;
}
static FcBool
{
FcChar8 cache_base[CACHEBASE_LEN];
FcChar8 *cache_hashed;
- int fd, i;
+ int fd;
FcAtomic *atomic;
- FcCache metadata;
- void *current_dir_block = 0;
+ FcCache *cache;
FcStrList *list;
FcChar8 *cache_dir = NULL;
FcChar8 *test_dir;
- off_t header_len;
/*
* Write it to the first directory in the list which is writable
FcStrListDone (list);
if (!cache_dir)
return FcFalse;
+
FcDirCacheBasename (dir, cache_base);
cache_hashed = FcStrPlus (cache_dir, cache_base);
if (!cache_hashed)
return FcFalse;
- current_dir_block = FcDirCacheProduce (set, &metadata);
+ cache = FcDirCacheProduce (set, dir, dirs);
- if (metadata.count && !current_dir_block)
+ if (!cache)
goto bail1;
if (FcDebug () & FC_DBG_CACHE)
atomic = FcAtomicCreate ((FcChar8 *)cache_hashed);
if (!atomic)
- goto bail1;
+ goto bail2;
if (!FcAtomicLock (atomic))
- goto bail2;
+ goto bail3;
fd = open((char *)FcAtomicNewFile (atomic), O_RDWR | O_CREAT | O_BINARY, 0666);
if (fd == -1)
- goto bail3;
-
- /*
- * Compute file header length -- the FcCache followed by the subdir names
- */
- header_len = sizeof (FcCache);
- header_len += strlen ((char *) dir) + 1;
- for (i = 0; i < dirs->size; i++)
- header_len += strlen ((char *)dirs->strs[i]) + 1;
+ goto bail4;
- metadata.pos = FcCacheNextOffset (lseek (fd, 0, SEEK_CUR) + header_len);
- metadata.subdirs = dirs->size;
-
- /*
- * Write out the header
- */
- if (write (fd, &metadata, sizeof(FcCache)) != sizeof(FcCache))
+ if (write (fd, cache, cache->size) != cache->size)
{
- perror("write metadata");
+ perror ("write cache");
goto bail5;
}
-
- FcCacheWriteString (fd, (char *) dir);
-
- for (i = 0; i < dirs->size; i++)
- FcCacheWriteString (fd, (char *)dirs->strs[i]);
-
- if (metadata.count)
- {
- if (lseek (fd, metadata.pos, SEEK_SET) != metadata.pos)
- perror("lseek");
- else if (write (fd, current_dir_block, metadata.count) !=
- metadata.count)
- perror("write current_dir_block");
- free (current_dir_block);
- current_dir_block = 0;
- }
close(fd);
if (!FcAtomicReplaceOrig(atomic))
- goto bail3;
+ goto bail4;
FcStrFree ((FcChar8 *)cache_hashed);
FcAtomicUnlock (atomic);
FcAtomicDestroy (atomic);
bail5:
close (fd);
- bail3:
+ bail4:
FcAtomicUnlock (atomic);
- bail2:
+ bail3:
FcAtomicDestroy (atomic);
+ bail2:
+ free (cache);
bail1:
FcStrFree ((FcChar8 *)cache_hashed);
- if (current_dir_block)
- free (current_dir_block);
- return FcFalse;
-}
-
-static int banks_ptr = 0, banks_alloc = 0;
-int * _fcBankId = 0, * _fcBankIdx = 0;
-static const char ** bankDirs = 0;
-
-static FcBool
-FcCacheHaveBank (int bank)
-{
- int i;
-
- if (bank < FC_BANK_FIRST)
- return FcTrue;
-
- for (i = 0; i < banks_ptr; i++)
- if (_fcBankId[i] == bank)
- return FcTrue;
-
return FcFalse;
}
-int
-FcCacheBankToIndexMTF (int bank)
-{
- int i, j;
-
- for (i = 0; i < banks_ptr; i++)
- if (_fcBankId[_fcBankIdx[i]] == bank)
- {
- int t = _fcBankIdx[i];
-
- for (j = i; j > 0; j--)
- _fcBankIdx[j] = _fcBankIdx[j-1];
- _fcBankIdx[0] = t;
- return t;
- }
-
- if (banks_ptr >= banks_alloc)
- {
- int * b, * bidx;
- const char ** bds;
-
- b = realloc (_fcBankId, (banks_alloc + 4) * sizeof(int));
- if (!b)
- return -1;
- _fcBankId = b;
-
- bidx = realloc (_fcBankIdx, (banks_alloc + 4) * sizeof(int));
- if (!bidx)
- return -1;
- _fcBankIdx = bidx;
-
- bds = realloc (bankDirs, (banks_alloc + 4) * sizeof (char *));
- if (!bds)
- return -1;
- bankDirs = bds;
-
- banks_alloc += 4;
- }
-
- i = banks_ptr++;
- _fcBankId[i] = bank;
- _fcBankIdx[i] = i;
- return i;
-}
-
-static void
-FcCacheAddBankDir (int bank, const char * dir)
-{
- int bi = FcCacheBankToIndexMTF (bank);
-
- if (bi < 0)
- return;
-
- bankDirs[bi] = (const char *)FcStrCopy ((FcChar8 *)dir);
-}
-
-const char *
-FcCacheFindBankDir (int bank)
-{
- int bi = FcCacheBankToIndex (bank);
- return bankDirs[bi];
-}
-
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was