X-Git-Url: https://git.wh0rd.org/?a=blobdiff_plain;f=src%2Ffccache.c;h=af2c68fa88ebcd3348db24f9b270fc985edd7c5a;hb=bc5e487f2a1ad9946aa5c6e19cd75794fc38d530;hp=a67725341a1a442e5eb82532fcf8c0b5aff13b45;hpb=af180c40376690b7ced5262156fbe13c9ebba1e2;p=fontconfig.git diff --git a/src/fccache.c b/src/fccache.c index a677253..af2c68f 100644 --- a/src/fccache.c +++ b/src/fccache.c @@ -1,6 +1,4 @@ /* - * $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 * @@ -37,30 +35,10 @@ # include #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); - struct MD5Context { FcChar32 buf[4]; FcChar32 bits[2]; @@ -72,73 +50,6 @@ static void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len); static void MD5Final(unsigned char digest[16], struct MD5Context *ctx); static void MD5Transform(FcChar32 buf[4], FcChar32 in[16]); -#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; - - file = FcDirCacheOpen (config, dir, NULL); - - if (file == NULL) - return FcFalse; - fclose (file); - - return FcTrue; -} - #define CACHEBASE_LEN (1 + 32 + 1 + sizeof (FC_ARCHITECTURE) + sizeof (FC_CACHE_SUFFIX)) static const char bin2hex[] = { '0', '1', '2', '3', @@ -201,299 +112,340 @@ FcDirCacheUnlink (const FcChar8 *dir, FcConfig *config) } static int -FcCacheReadDirs (FcConfig * config, - FcStrList *list, FcFontSet * set, FcStrSet *processed_dirs) +FcDirCacheOpenFile (const FcChar8 *cache_file, struct stat *file_stat) { - int ret = 0; - FcChar8 *dir; - FcStrSet *subdirs; - FcStrList *sublist; + int fd; - /* - * Read in the results from 'list'. - */ - while ((dir = FcStrListNext (list))) + fd = open((char *) cache_file, O_RDONLY | O_BINARY); + if (fd < 0) + return fd; + if (fstat (fd, file_stat) < 0) { - if (!FcConfigAcceptFilename (config, dir)) - continue; - - /* Skip this directory if already updated - * to avoid the looped directories via symlinks - * Clearly a dir not in fonts.conf shouldn't be globally cached. - */ - - if (FcStrSetMember (processed_dirs, dir)) - continue; - if (!FcStrSetAdd (processed_dirs, dir)) - continue; - - subdirs = FcStrSetCreate (); - if (!subdirs) - { - fprintf (stderr, "Can't create directory set\n"); - ret++; - continue; - } - - FcDirScanConfig (set, subdirs, - config->blanks, dir, FcFalse, config); - - sublist = FcStrListCreate (subdirs); - FcStrSetDestroy (subdirs); - if (!sublist) - { - fprintf (stderr, "Can't create subdir list in \"%s\"\n", dir); - ret++; - continue; - } - ret += FcCacheReadDirs (config, sublist, set, processed_dirs); + close (fd); + return -1; } - FcStrListDone (list); - return ret; -} - -FcFontSet * -FcCacheRead (FcConfig *config) -{ - FcFontSet *s = FcFontSetCreate(); - FcStrSet *processed_dirs; - - if (!s) - return 0; - - processed_dirs = FcStrSetCreate(); - if (!processed_dirs) - goto bail; - - if (FcCacheReadDirs (config, FcConfigGetConfigDirs (config), s, processed_dirs)) - goto bail1; - - FcStrSetDestroy (processed_dirs); - return s; - - bail1: - FcStrSetDestroy (processed_dirs); - bail: - FcFontSetDestroy (s); - return 0; + return fd; } -/* Opens the cache file for 'dir' for reading. - * This searches the list of cache dirs for the relevant cache file, - * returning the first one found. +/* + * Look for a cache file for the specified dir. Attempt + * to use each one we find, stopping when the callback + * indicates success */ -static FILE * -FcDirCacheOpen (FcConfig *config, const FcChar8 *dir, FcChar8 **cache_path) +static FcBool +FcDirCacheProcess (FcConfig *config, const FcChar8 *dir, + FcBool (*callback) (int fd, off_t size, void *closure), + void *closure, FcChar8 **cache_file_ret) { - 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; + FcBool ret = FcFalse; if (stat ((char *) dir, &dir_stat) < 0) - return NULL; + return FcFalse; FcDirCacheBasename (dir, cache_base); list = FcStrListCreate (config->cacheDirs); if (!list) - return NULL; + return FcFalse; 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 && - dir_stat.st_mtime <= file_stat.st_mtime) + fd = FcDirCacheOpenFile (cache_hashed, &file_stat); + if (fd >= 0) { + if (dir_stat.st_mtime <= file_stat.st_mtime) { - break; + ret = (*callback) (fd, file_stat.st_size, closure); + if (ret) + { + if (cache_file_ret) + *cache_file_ret = cache_hashed; + else + FcStrFree (cache_hashed); + close (fd); + break; + } } - fclose (file); - file = NULL; + close (fd); } - FcStrFree (cache_hashed); - cache_hashed = NULL; + FcStrFree (cache_hashed); } FcStrListDone (list); - - if (file == NULL) - return NULL; - if (cache_path) - *cache_path = cache_hashed; - else - FcStrFree (cache_hashed); - - return file; + return ret; } -/* read serialized state from the cache file */ -FcBool -FcDirCacheConsume (FILE *file, FcFontSet *set, FcStrSet *dirs, - const FcChar8 *dir, char *dirname) -{ - 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); +#define FC_CACHE_MIN_MMAP 1024 - for (i = 0; i < metadata.subdirs; i++) { - if (!FcCacheReadString (file, subdir_name, sizeof (subdir_name))) - goto bail; - FcStrSetAdd (dirs, (FcChar8 *)subdir_name); - } +/* + * Map a cache file into memory + */ +static FcCache * +FcDirCacheMapFd (int fd, off_t size) +{ + FcCache *cache = NULL; + FcBool allocated = FcFalse; - if (metadata.count) + if (size < sizeof (FcCache)) + return NULL; + /* + * For small cache files, just read them into memory + */ + if (size >= FC_CACHE_MIN_MMAP) { - int fd = fileno (file); #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; - - current_dir_block = MapViewOfFile (hFileMap, FILE_MAP_READ, 0, metadata.pos, metadata.count); - if (current_dir_block == NULL) + 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); - goto bail; } } -#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_MMAP || + cache->version != FC_CACHE_CONTENT_VERSION || + 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; + /* Mark allocated caches so they're freed rather than unmapped */ + if (allocated) + cache->magic = FC_CACHE_MAGIC_ALLOC; + + return cache; +} - bail: - return FcFalse; +void +FcDirCacheUnload (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; + } } -FcBool -FcDirCacheRead (FcFontSet * set, FcStrSet * dirs, - const FcChar8 *dir, FcConfig *config) +static FcBool +FcDirCacheMapHelper (int fd, off_t size, void *closure) { - FILE *file; + FcCache *cache = FcDirCacheMapFd (fd, size); - file = FcDirCacheOpen (config, dir, NULL); - if (file == NULL) - goto bail; + if (!cache) + return FcFalse; + *((FcCache **) closure) = cache; + return FcTrue; +} - if (!FcDirCacheConsume (file, set, dirs, dir, NULL)) - goto bail1; +FcCache * +FcDirCacheLoad (const FcChar8 *dir, FcConfig *config, FcChar8 **cache_file) +{ + FcCache *cache = NULL; - if (config) - FcConfigAddFontDir (config, (FcChar8 *)dir); - - fclose (file); - return FcTrue; + if (!FcDirCacheProcess (config, dir, + FcDirCacheMapHelper, + &cache, cache_file)) + return NULL; + return cache; +} -bail1: - fclose (file); -bail: - return FcFalse; +FcCache * +FcDirCacheLoadFile (const FcChar8 *cache_file, struct stat *file_stat) +{ + int fd; + FcCache *cache; + + fd = FcDirCacheOpenFile (cache_file, file_stat); + if (fd < 0) + return NULL; + cache = FcDirCacheMapFd (fd, file_stat->st_size); + close (fd); + return cache; } + +/* + * Validate a cache file by reading the header and checking + * the magic number and the size field + */ +static FcBool +FcDirCacheValidateHelper (int fd, off_t size, void *closure) +{ + FcBool ret = FcTrue; + FcCache c; + struct stat file_stat; + if (read (fd, &c, sizeof (FcCache)) != sizeof (FcCache)) + ret = FcFalse; + else if (c.magic != FC_CACHE_MAGIC_MMAP) + ret = FcFalse; + else if (c.version != FC_CACHE_CONTENT_VERSION) + ret = FcFalse; + else if (fstat (fd, &file_stat) < 0) + ret = FcFalse; + else if (file_stat.st_size != c.size) + ret = FcFalse; + return ret; +} -static void * -FcDirCacheProduce (FcFontSet *set, FcCache *metadata) +static FcBool +FcDirCacheValidConfig (const FcChar8 *dir, FcConfig *config) { - void * current_dir_block, * final_dir_block; - static unsigned int rand_state = 0; - int bank, needed_bytes_no_align; + return FcDirCacheProcess (config, dir, + FcDirCacheValidateHelper, + NULL, NULL); +} -#if defined (HAVE_RAND_R) - if (!rand_state) - rand_state = time(0L); - bank = rand_r(&rand_state); +FcBool +FcDirCacheValid (const FcChar8 *dir) +{ + FcConfig *config; + + config = FcConfigGetCurrent (); + if (!config) + return FcFalse; - while (FcCacheHaveBank(bank)) - bank = rand_r(&rand_state); -#else - if (!rand_state) - { - rand_state = 1; - srand (time (0L)); - } - bank = rand(); + return FcDirCacheValidConfig (dir, config); +} - while (FcCacheHaveBank(bank)) - bank = rand(); -#endif +/* + * Build a cache structure from the given contents + */ +FcCache * +FcDirCacheBuild (FcFontSet *set, const FcChar8 *dir, FcStrSet *dirs) +{ + 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; - 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; + /* + * 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); - if (!needed_bytes_no_align) /* not a failure, no fonts to write */ - { - /* no, you don't really need to write any bytes at all. */ - metadata->count = 0; - return 0; - } + serialize->linear = cache; - 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); + cache->magic = FC_CACHE_MAGIC_ALLOC; + cache->version = FC_CACHE_CONTENT_VERSION; + cache->size = serialize->size; - if ((void *)((char *)current_dir_block+metadata->count) < final_dir_block) - goto bail; - - if (!FcFontSetSerialize (bank, set)) - goto bail; + /* + * 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++) + { + 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); - 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 @@ -520,18 +472,18 @@ FcMakeDirectory (const FcChar8 *dir) /* write serialized state to the cache file */ FcBool -FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *config) +FcDirCacheWrite (FcCache *cache, FcConfig *config) { + FcChar8 *dir = FcCacheDir (cache); FcChar8 cache_base[CACHEBASE_LEN]; FcChar8 *cache_hashed; - int fd, i; + int fd; FcAtomic *atomic; - FcCache metadata; - void *current_dir_block = 0; FcStrList *list; FcChar8 *cache_dir = NULL; FcChar8 *test_dir; - off_t header_len; + int magic; + int written; /* * Write it to the first directory in the list which is writable @@ -563,16 +515,12 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *c 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); - - if (metadata.count && !current_dir_block) - goto bail1; - if (FcDebug () & FC_DBG_CACHE) printf ("FcDirCacheWriteDir dir \"%s\" file \"%s\"\n", dir, cache_hashed); @@ -582,151 +530,51 @@ FcDirCacheWrite (FcFontSet *set, FcStrSet *dirs, const FcChar8 *dir, FcConfig *c goto bail1; 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; + /* Temporarily switch magic to MMAP while writing to file */ + magic = cache->magic; + if (magic != FC_CACHE_MAGIC_MMAP) + cache->magic = FC_CACHE_MAGIC_MMAP; /* - * Write out the header + * Write cache contents to file */ - if (write (fd, &metadata, sizeof(FcCache)) != sizeof(FcCache)) - { - perror("write metadata"); - goto bail5; - } + written = write (fd, cache, cache->size); - FcCacheWriteString (fd, (char *) dir); + /* Switch magic back */ + if (magic != FC_CACHE_MAGIC_MMAP) + cache->magic = magic; - for (i = 0; i < dirs->size; i++) - FcCacheWriteString (fd, (char *)dirs->strs[i]); - - if (metadata.count) + if (written != cache->size) { - 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; + perror ("write cache"); + goto bail5; } close(fd); if (!FcAtomicReplaceOrig(atomic)) - goto bail3; - FcStrFree ((FcChar8 *)cache_hashed); + goto bail4; + FcStrFree (cache_hashed); FcAtomicUnlock (atomic); FcAtomicDestroy (atomic); return FcTrue; bail5: close (fd); - bail3: + bail4: FcAtomicUnlock (atomic); - bail2: + bail3: FcAtomicDestroy (atomic); bail1: - FcStrFree ((FcChar8 *)cache_hashed); - if (current_dir_block) - free (current_dir_block); + FcStrFree (cache_hashed); 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