summaryrefslogtreecommitdiff
path: root/src/fontcache/fontcache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fontcache/fontcache.c')
-rw-r--r--src/fontcache/fontcache.c1019
1 files changed, 1019 insertions, 0 deletions
diff --git a/src/fontcache/fontcache.c b/src/fontcache/fontcache.c
new file mode 100644
index 0000000..0fec03b
--- /dev/null
+++ b/src/fontcache/fontcache.c
@@ -0,0 +1,1019 @@
+/*-
+ * Copyright (c) 1998-1999 Shunsuke Akiyama <akiyama@jp.FreeBSD.org>.
+ * All rights reserved.
+ * Copyright (c) 1998-1999 X-TrueType Server Project, All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Id: fontcache.c,v 1.19 1999/01/31 13:06:00 akiyama Exp $
+ */
+/* $XFree86: xc/lib/font/fontcache/fontcache.c,v 1.5 2001/10/28 03:32:45 tsi Exp $ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fontcache.h"
+
+#define LOW_MARK 0
+#define HI_MARK 1
+
+#define PURGE_ENTRY 1
+#define PURGE_BITMAP 2
+
+typedef struct {
+ long hiMark; /* Cache hi water mark */
+ long lowMark; /* Cache low water mark */
+ long allocated; /* Cache allocated size */
+ long used; /* Cache used size */
+} FontCacheSize_t;
+
+static int CacheInitialized = 0;
+
+static TAILQ_HEAD(FcInUseQueue, cache_entry) InUseQueueHead, *InUseQueue;
+static TAILQ_HEAD(FcFreeQueue, cache_entry) FreeQueueHead, *FreeQueue;
+static FCBCB FreeBitmapHead, *FreeBitmap;
+
+static long CacheHiMark;
+static long CacheLowMark;
+static int CacheBalance;
+static FontCacheSize_t HashSize;
+static FontCacheSize_t AllocSize;
+static int NeedPurgeCache;
+static FontCacheStatistics CacheStatistics;
+
+static void fc_assign_cache(void);
+static int fc_assign_entry(void);
+static void fc_flush_cache(void);
+static int fc_get_bitmap_area(FontCacheEntryPtr, int);
+static void fc_free_bitmap_area(FontCacheBitmapPtr);
+static int fc_check_size(int);
+static void fc_purge_cache(void);
+static void fc_purge_bitmap(void);
+static void fc_flush_cache_bitmap(void);
+static void fc_flush_cache_inuse(void);
+static void fc_flush_cache_free(void);
+static void fc_purge_cache_entry(void);
+static void fc_purge_cache_entry_pool(void);
+static void fc_purge_bitmap_pool(void);
+
+
+/*
+ * FontCacheInitialize()
+ *
+ * Initialize cache work area.
+ */
+
+int
+FontCacheInitialize()
+{
+#ifdef FONTCACHE
+ int i;
+
+ if (!CacheInitialized) {
+ /*
+ * first time initialization
+ */
+#if defined(HASH_DEBUG) || defined(DEBUG)
+ fprintf(stderr, "FontCacheInitialize: initializing cache\n");
+#endif
+ InUseQueue = &InUseQueueHead;
+ TAILQ_INIT(InUseQueue);
+
+ FreeQueue = &FreeQueueHead;
+ TAILQ_INIT(FreeQueue);
+
+ FreeBitmap = &FreeBitmapHead;
+ FreeBitmap->index = 0;
+ for (i = 0; i < FC_MEM_HASH_SIZE; i++) {
+ TAILQ_INIT(&FreeBitmap->head[i]);
+ }
+
+ CacheHiMark = FC_DEFAULT_CACHE_SIZE * 1024; /* temporary */
+ CacheLowMark = (CacheHiMark / 4) * 3;
+ CacheBalance = FC_CACHE_BALANCE;
+
+ NeedPurgeCache = 0;
+
+ HashSize.allocated = HashSize.used = 0;
+ AllocSize.allocated = AllocSize.used = 0;
+ fc_assign_cache();
+ fc_assign_entry();
+#if defined(DEBUG)
+ fprintf(stderr, "FontCacheInitialize: hi=%ld, lo=%ld, bal=%d\n",
+ CacheHiMark, CacheLowMark, CacheBalance);
+#endif
+
+ CacheInitialized = 1;
+ } else {
+ /*
+ * second time or later case.
+ * flush and reassign cache.
+ */
+#if defined(HASH_DEBUG) || defined(DEBUG)
+ fprintf(stderr, "FontCacheInitialize: initializing cache, again\n");
+#endif
+ }
+
+ memset(&CacheStatistics, 0, sizeof (CacheStatistics));
+#endif /* FONTCACHE */
+
+ return 0; /* make lint happy */
+}
+
+/*
+ * FontCacheChangeSettings()
+ *
+ * Change cache size and reinitialize work areas.
+ *
+ * Returns 0, if memory allocation failed. Otherwise 1.
+ */
+
+int
+FontCacheChangeSettings(FontCacheSettingsPtr cs)
+{
+ int result;
+
+ if (!CacheInitialized) {
+ FontCacheInitialize();
+ if (!CacheInitialized)
+ return 0;
+ }
+
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr,
+ "FontCahceChangeSettings: hi-mark=%ld, low-mark=%ld, balance=%ld\n",
+ cs->himark, cs->lowmark, cs->balance);
+#endif
+
+ fc_flush_cache();
+
+ CacheHiMark = cs->himark;
+ CacheLowMark = cs->lowmark;
+ CacheBalance = cs->balance;
+
+ fc_assign_cache();
+ result = fc_assign_entry();
+
+ return result;
+}
+
+/*
+ * FontCacheGetSettings()
+ *
+ * Get current cache control parameters.
+ */
+
+void
+FontCacheGetSettings(FontCacheSettingsPtr cs)
+{
+ if (!CacheInitialized) {
+ FontCacheInitialize();
+ if (!CacheInitialized)
+ return;
+ }
+
+ cs->himark = CacheHiMark;
+ cs->lowmark = CacheLowMark;
+ cs->balance = CacheBalance;
+}
+
+/*
+ * FontCacheGetStatistics()
+ *
+ * Get current cache statistics.
+ */
+
+void
+FontCacheGetStatistics(FontCacheStatisticsPtr cs)
+{
+ if (!CacheInitialized) {
+ FontCacheInitialize();
+ if (!CacheInitialized)
+ return;
+ }
+
+ CacheStatistics.purge_stat = NeedPurgeCache;
+ CacheStatistics.balance = CacheBalance;
+ CacheStatistics.f.usage = HashSize.used;
+ CacheStatistics.v.usage = AllocSize.used;
+
+ memcpy(cs, &CacheStatistics, sizeof (CacheStatistics));
+}
+
+/*
+ * FontCacheOpenCache()
+ *
+ * Allocate font cache control block and initialize it.
+ *
+ * Returns pointer to font cache control block. Or returns NULL when
+ * detected illegal parameter or memory allocation failed.
+ */
+
+FCCBPtr
+FontCacheOpenCache(void *arg)
+{
+ int linesize;
+ FCCBPtr this;
+ int size = 0, mask = 0;
+ int i;
+
+ static int sizes[] = { 16, 32, 64, 128, 0 };
+
+ if (!CacheInitialized) {
+ FontCacheInitialize();
+ if (!CacheInitialized)
+ return NULL;
+ }
+
+ linesize = (long)arg;
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "FontCacheOpenCache: line size=%d\n", linesize);
+#endif
+
+ for (i = 0; sizes[i] != 0; i++) {
+ if (sizes[i] == linesize) {
+ size = linesize;
+ mask = linesize - 1;
+ break;
+ }
+ }
+ if (sizes[i] == 0) {
+ return NULL;
+ }
+
+ this = (FCCBPtr) malloc(sizeof (FCCB));
+ if (this != NULL) {
+ memset(this, 0, sizeof (FCCB));
+ this->head = (FontCacheHeadPtr) malloc(sizeof (FontCacheHead) * size);
+ if (this->head == NULL) {
+ free(this);
+ this = NULL;
+ } else {
+ this->size = size;
+ this->mask = mask;
+ for (i = 0; i < size; i++) {
+ TAILQ_INIT(&this->head[i]);
+ }
+ }
+ }
+
+ return this;
+}
+
+/*
+ * FontCacheCloseCache()
+ *
+ * Release font cache control block and all it's related entries.
+ */
+
+void
+FontCacheCloseCache(FCCBPtr this)
+{
+ FontCacheEntryPtr entry, next;
+ int i;
+ int size;
+
+ if (!CacheInitialized) {
+ return;
+ }
+
+ size = this->size;
+ for (i = 0; i < size; i++) {
+ entry = TAILQ_FIRST(&this->head[i]);
+ while (entry != NULL) {
+ /* remove entry from in-use queue, here */
+ TAILQ_REMOVE(InUseQueue, entry, c_lru);
+
+ /* remove entry from the hash */
+ if (entry->bitmapsize > FC_SMALL_BITMAP_SIZE
+ && entry->charInfo.bits != NULL) {
+ fc_free_bitmap_area(entry->bmp);
+ }
+ entry->charInfo.bits = NULL;
+ entry->bitmapsize = 0;
+
+ next = TAILQ_NEXT(entry, c_hash);
+ TAILQ_INSERT_HEAD(FreeQueue, entry, c_lru);
+ HashSize.used -= sizeof (FontCacheEntry);
+ entry = next;
+ }
+ }
+
+ free(this->head);
+ free(this);
+}
+
+/*
+ * FontCacheGetEntry()
+ *
+ * Allocate font cache entry and initialize it.
+ */
+
+FontCacheEntryPtr
+FontCacheGetEntry()
+{
+ FontCacheEntryPtr entry;
+ FontCacheEntryPtr p;
+ long size;
+
+ /* scan in-use queue and purge if required */
+ fc_purge_cache();
+
+ /* allocate hash entry */
+ if (TAILQ_EMPTY(FreeQueue)) {
+ size = sizeof (FontCacheEntry);
+ p = (FontCacheEntryPtr) malloc(size);
+ if (p != NULL) {
+ TAILQ_INSERT_HEAD(FreeQueue, p, c_lru);
+ HashSize.allocated += size;
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "FontCachegetEntry: allocated new entry\n");
+#endif
+ }
+ }
+
+ if (!TAILQ_EMPTY(FreeQueue)) {
+ entry = TAILQ_FIRST(FreeQueue);
+ TAILQ_REMOVE(FreeQueue, entry, c_lru);
+ memset(entry, 0, sizeof (FontCacheEntry));
+ } else {
+ entry = NULL;
+ }
+
+ return entry;
+}
+
+/*
+ * FontCacheGetBitmap()
+ *
+ * Allocate font glyph bitmap area.
+ *
+ * Note:
+ * Allocated area should be cleared.
+ */
+
+int
+FontCacheGetBitmap(FontCacheEntryPtr entry, int size)
+{
+ int oldsize;
+ int result;
+
+ /* XXX */
+ if ((AllocSize.used > AllocSize.hiMark - size) &&
+ (size > FC_SMALL_BITMAP_SIZE)) {
+ fc_purge_bitmap();
+ }
+
+ if (size < 0) /* wrong size */
+ return 0;
+
+ result = 0;
+ oldsize = entry->bitmapsize;
+ if (size <= FC_SMALL_BITMAP_SIZE) {
+ /* use coresponding bitmap area */
+ if (oldsize > FC_SMALL_BITMAP_SIZE) {
+ /* We don't need allocated area anymore */
+ fc_free_bitmap_area(entry->bmp);
+ }
+ entry->bitmapsize = size;
+ if (size > 0) {
+ entry->charInfo.bits = entry->bitmap;
+ memset(entry->charInfo.bits, 0, size);
+ } else
+ entry->charInfo.bits = NULL;
+
+ result = 1;
+ } else {
+ /* need extra bitmap area */
+ if (entry->charInfo.bits == NULL) {
+ /* no any extra bitmap area */
+ if (fc_get_bitmap_area(entry, size)) {
+ entry->bitmapsize = size;
+ memset(entry->charInfo.bits, 0, size);
+ if (fc_check_size(HI_MARK)) {
+ fc_purge_cache();
+ }
+ result = 1;
+ }
+ } else {
+ /* we already have extra bitmap area */
+ if (oldsize == size) {
+ /* same size, reuse it */
+ memset(entry->charInfo.bits, 0, size);
+ result = 1;
+ } else {
+ /* different size */
+ fc_free_bitmap_area(entry->bmp);
+ if (fc_get_bitmap_area(entry, size)) {
+ entry->bitmapsize = size;
+ memset(entry->charInfo.bits, 0, size);
+ if (fc_check_size(HI_MARK)) {
+ fc_purge_cache();
+ }
+ result = 1;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*
+ * FontCacheSearchEntry()
+ *
+ * Search an entry matched with the key from the hash.
+ */
+
+int
+FontCacheSearchEntry(FCCBPtr this, int key, FontCacheEntryPtr *value)
+{
+ FontCacheHeadPtr head;
+ FontCacheEntryPtr entry;
+ int index;
+
+ index = key & this->mask;
+ head = &this->head[index];
+
+ TAILQ_FOREACH(entry, head, c_hash) {
+ if (entry->key == key) {
+ /* found, change position */
+ CacheStatistics.f.hits++;
+
+ TAILQ_REMOVE(InUseQueue, entry, c_lru);
+ TAILQ_INSERT_HEAD(InUseQueue, entry, c_lru);
+
+ TAILQ_REMOVE(head, entry, c_hash);
+ TAILQ_INSERT_HEAD(head, entry, c_hash);
+
+ /* purge least recentrly used cache entirs */
+ fc_purge_cache();
+
+ *value = entry;
+ return 1;
+ }
+ }
+
+ /* purge least recentrly used cache entirs */
+ fc_purge_cache();
+
+ /* not found */
+ CacheStatistics.f.misshits++;
+ *value = NULL;
+ return 0;
+}
+
+/*
+ * FontCacheInsertEntry()
+ *
+ * Insert an entry into the cache pool.
+ */
+
+int
+FontCacheInsertEntry(FCCBPtr this, int key, FontCacheEntryPtr entry)
+{
+ FontCacheHeadPtr head;
+ int index;
+
+ index = key & this->mask;
+ head = &this->head[index];
+
+ entry->key = key;
+ entry->c_head = head;
+ TAILQ_INSERT_HEAD(head, entry, c_hash);
+
+ /* insert entry into in-use queue */
+ TAILQ_INSERT_HEAD(InUseQueue, entry, c_lru);
+
+ /* adjust cache in-use size */
+ HashSize.used += sizeof (FontCacheEntry);
+ if (fc_check_size(HI_MARK)) {
+ fc_purge_cache();
+ }
+
+ return 1;
+}
+
+/*
+ * fc_assign_cache()
+ *
+ * Assign cache size considered with cache balance rate.
+ */
+
+static void
+fc_assign_cache()
+{
+ HashSize.hiMark = (CacheHiMark * CacheBalance) / 100;
+ HashSize.lowMark = (CacheLowMark * CacheBalance) / 100;
+
+ AllocSize.hiMark = (CacheHiMark * (100 - CacheBalance)) / 100;
+ AllocSize.lowMark = (CacheLowMark * (100 - CacheBalance)) / 100;
+}
+
+/*
+ * fc_assign_entry()
+ *
+ * Assign cache entry into free queue.
+ *
+ * Returns 0, when memory allocation failed. Otherwise 1.
+ */
+
+static int
+fc_assign_entry()
+{
+ FontCacheEntryPtr entry;
+ long used;
+ int result = 1;
+
+ used = 0;
+ while ((used + sizeof (FontCacheEntry)) < HashSize.hiMark) {
+ entry = (FontCacheEntryPtr) malloc(sizeof (FontCacheEntry));
+ if (entry == NULL) {
+ fprintf(stderr, "fc_assign_entry: can't allocate memory.\n");
+ result = 0;
+ break;
+ }
+ TAILQ_INSERT_HEAD(FreeQueue, entry, c_lru);
+ used += sizeof (FontCacheEntry);
+ HashSize.allocated += sizeof (FontCacheEntry);
+ }
+
+ return result;
+}
+
+/*
+ * fc_get_bitmap_area()
+ *
+ * Search allocated memory area from free bitmap hash pool. If there
+ * is no entry, then allocate new bitmap area.
+ *
+ * Returns 0, when memory allocation failed, otherwise 1. And some
+ * sort of cache entry structure members were updated.
+ */
+
+static int
+fc_get_bitmap_area(FontCacheEntryPtr this, int size)
+{
+ FontCacheBitmapHeadPtr head;
+ FontCacheBitmapPtr bitmap;
+ int index;
+ int result = 0;
+
+ index = size & FC_MEM_HASH_MASK;
+ head = &FreeBitmap->head[index];
+ TAILQ_FOREACH(bitmap, head, b_hash) {
+ if (bitmap->key == size) {
+ TAILQ_REMOVE(head, bitmap, b_hash);
+ this->bmp = bitmap;
+ this->charInfo.bits = (char *) (bitmap + 1);
+ bitmap->b_entry = this;
+ result = 1;
+ CacheStatistics.v.hits++;
+ AllocSize.used += (size + sizeof (FontCacheBitmap));
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_get_bitmap_area: bitmap entry found in pool\n");
+#endif
+ break;
+ }
+ }
+
+ if (result == 0) {
+ CacheStatistics.v.misshits++;
+ bitmap = (FontCacheBitmapPtr) malloc(size + sizeof (FontCacheBitmap));
+ if (bitmap != NULL) {
+ bitmap->b_entry = this;
+ bitmap->size = size + sizeof (FontCacheBitmap);
+ bitmap->key = size;
+ this->bmp = bitmap;
+ this->charInfo.bits = (char *) (bitmap + 1);
+ AllocSize.allocated += (size + sizeof (FontCacheBitmap));
+ AllocSize.used += (size + sizeof (FontCacheBitmap));
+ result = 1;
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_get_bitmap_area: bitmap entry allocated\n");
+#endif
+ } else {
+ this->bmp = NULL;
+ this->charInfo.bits = NULL;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * fc_free_bitmap_area()
+ *
+ * Release allocated bitmap area into free hash pool.
+ */
+
+static void
+fc_free_bitmap_area(FontCacheBitmapPtr this)
+{
+ FontCacheBitmapHeadPtr head;
+ FontCacheEntryPtr entry;
+ int index;
+
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_free_bitmap_area: bitmap entry returns into pool\n");
+#endif
+
+ index = this->key & FC_MEM_HASH_MASK;
+ head = &FreeBitmap->head[index];
+ TAILQ_INSERT_HEAD(head, this, b_hash);
+
+ AllocSize.used -= this->size;
+
+ entry = this->b_entry;
+ entry->bmp = NULL;
+ entry->bitmapsize = 0;
+}
+
+/*
+ * fc_flush_cache_bitmap()
+ *
+ * Flush all allocated bitmap area from the free hash pool.
+ */
+
+static void
+fc_flush_cache_bitmap()
+{
+ FontCacheBitmapHeadPtr head;
+ FontCacheBitmapPtr bitmap;
+ int i;
+
+ for (i = 0; i < FC_MEM_HASH_SIZE; i++) {
+ head = &FreeBitmap->head[i];
+ while (!TAILQ_EMPTY(head)) {
+ bitmap = TAILQ_FIRST(head);
+ TAILQ_REMOVE(head, bitmap, b_hash);
+
+ AllocSize.allocated -= bitmap->size;
+ free(bitmap);
+ }
+ }
+}
+
+/*
+ * fc_flush_cache_inuse()
+ *
+ * Release all in-use cache entries.
+ */
+
+static void
+fc_flush_cache_inuse()
+{
+ FontCacheEntryPtr entry;
+ FontCacheHeadPtr head;
+
+ while (!TAILQ_EMPTY(InUseQueue)) {
+ /* remove this entry from in-use queue */
+ entry = TAILQ_FIRST(InUseQueue);
+ TAILQ_REMOVE(InUseQueue, entry, c_lru);
+
+ /* remove this entry from hash */
+ head = entry->c_head;
+ TAILQ_REMOVE(head, entry, c_hash);
+
+ /* release bitmap area */
+ if (entry->bitmapsize > FC_SMALL_BITMAP_SIZE
+ && entry->charInfo.bits != NULL) {
+ fc_free_bitmap_area(entry->bmp);
+ }
+ entry->charInfo.bits = NULL;
+ entry->bitmapsize = 0;
+
+ /* release font-specific private area */
+ if ( entry->vfuncs && entry->vfuncs->f_private_dispose )
+ (*entry->vfuncs->f_private_dispose)(entry->f_private);
+ entry->f_private = NULL;
+ entry->vfuncs = NULL;
+
+ /* add this entry to free queue */
+ TAILQ_INSERT_HEAD(FreeQueue, entry, c_lru);
+
+ /* adjust size */
+ HashSize.used -= sizeof (FontCacheEntry);
+ }
+}
+
+/*
+ * fc_flush_cache_free()
+ *
+ * Flush all free cache entries from the free cache queue.
+ */
+
+static void
+fc_flush_cache_free()
+{
+ FontCacheEntryPtr entry;
+
+ /* release entire entries of the free queue */
+ while (!TAILQ_EMPTY(FreeQueue)) {
+ entry = TAILQ_FIRST(FreeQueue);
+ TAILQ_REMOVE(FreeQueue, entry, c_lru);
+ free(entry);
+ HashSize.allocated -= sizeof (FontCacheEntry);
+ }
+}
+
+/*
+ * fc_flush_cache()
+ *
+ * Flush all cache entries and allocated bitmap area from the pool.
+ */
+
+static void
+fc_flush_cache()
+{
+ fc_flush_cache_inuse();
+ fc_flush_cache_bitmap();
+ fc_flush_cache_free();
+
+ memset(&CacheStatistics, 0, sizeof (CacheStatistics));
+}
+
+/*
+ * fc_check_size()
+ *
+ * Check cache size, then return it's result.
+ */
+
+static int
+fc_check_size(int mark)
+{
+ int result = 0;
+
+ if (mark == LOW_MARK) {
+ if (HashSize.used > HashSize.lowMark) {
+ result |= PURGE_ENTRY;
+ }
+ if (AllocSize.used > AllocSize.lowMark) {
+ result |= PURGE_BITMAP;
+ }
+ } else {
+ if (HashSize.used > HashSize.hiMark) {
+ result |= PURGE_ENTRY;
+ }
+ if (AllocSize.used > AllocSize.hiMark) {
+ result |= PURGE_BITMAP;
+ }
+ }
+
+ return result;
+}
+
+/*
+ * fc_purge_cache_entry()
+ *
+ * Purge least recently used cache entry.
+ */
+
+static void
+fc_purge_cache_entry()
+{
+ FontCacheHeadPtr head;
+ FontCacheEntryPtr entry;
+ int i;
+
+ for (i = 0; i < FC_PURGE_PER_SCAN; i++) {
+ /* get least recently used entry */
+ entry = TAILQ_LAST(InUseQueue, FcInUseQueue);
+
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_cache_entry: purged: %p, %d\n",
+ entry, entry->key);
+#endif
+
+ /* remove this entry from in-use queue */
+ TAILQ_REMOVE(InUseQueue, entry, c_lru);
+
+ /* remove this entry from the hash */
+ head = entry->c_head;
+ TAILQ_REMOVE(head, entry, c_hash);
+
+ /* release bitmap area */
+ if (entry->bitmapsize > FC_SMALL_BITMAP_SIZE
+ && entry->charInfo.bits != NULL) {
+ fc_free_bitmap_area(entry->bmp);
+ CacheStatistics.v.purged++;
+ }
+ entry->charInfo.bits = NULL;
+ entry->bitmapsize = 0;
+
+ /* release font-specific private area */
+ if ( entry->vfuncs && entry->vfuncs->f_private_dispose )
+ (*entry->vfuncs->f_private_dispose)(entry->f_private);
+ entry->f_private = NULL;
+ entry->vfuncs = NULL;
+
+ /* add this entry to free queue */
+ TAILQ_INSERT_HEAD(FreeQueue, entry, c_lru);
+
+ HashSize.used -= sizeof (FontCacheEntry);
+ CacheStatistics.f.purged++;
+ }
+}
+
+/*
+ * fc_purge_cache_entry_pool()
+ *
+ * Purge free cache entries, to adjust cache size.
+ */
+
+static void
+fc_purge_cache_entry_pool()
+{
+ FontCacheEntryPtr entry;
+
+ while (!TAILQ_EMPTY(FreeQueue)) {
+ entry = TAILQ_LAST(FreeQueue, FcFreeQueue);
+ TAILQ_REMOVE(FreeQueue, entry, c_lru);
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_cache_entry_pool: purged from free queue: %p\n",
+ entry);
+#endif
+ HashSize.allocated -= sizeof (FontCacheEntry);
+ free(entry);
+ if (HashSize.allocated <= HashSize.hiMark) {
+ break;
+ }
+ }
+}
+
+/*
+ * fc_purge_bitmap()
+ *
+ * Purge least recently used allocated bitmap area.
+ */
+
+static void
+fc_purge_bitmap()
+{
+ FontCacheEntryPtr entry, first;
+ int purged = 0;
+
+ /* release used entry, if required */
+ first = TAILQ_FIRST(InUseQueue);
+ if (first != NULL) {
+ entry = TAILQ_LAST(InUseQueue, FcInUseQueue);
+ while (purged < FC_PURGE_PER_SCAN) {
+ if (entry->bmp != NULL) {
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_bitmap: purged from live queue: %p, %d(%d)\n",
+ entry->bmp, entry->bmp->key, entry->bmp->size);
+#endif
+ fc_free_bitmap_area(entry->bmp);
+ entry->charInfo.bits = NULL;
+ CacheStatistics.v.purged++;
+ purged++;
+ }
+ if (entry == first) {
+ break;
+ }
+ entry = TAILQ_PREV(entry, FcInUseQueue, c_lru);
+ }
+ }
+}
+
+/*
+ * fc_purge_bitmap_pool()
+ *
+ * Purge free bitmap area from pool, to adjust cache size.
+ */
+
+static void
+fc_purge_bitmap_pool()
+{
+ int this, stop, quit;
+ FontCacheBitmapHeadPtr head;
+ FontCacheBitmapPtr bitmap;
+
+ /* release free bitmap entry */
+ this = FreeBitmap->index;
+ stop = this;
+ quit = 0;
+
+ do {
+ head = &FreeBitmap->head[this];
+ while (!TAILQ_EMPTY(head)) {
+ bitmap = TAILQ_LAST(head, fcmem_head);
+ TAILQ_REMOVE(head, bitmap, b_hash);
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_bitmap_pool: purged from pool: %p, %d(%d)\n",
+ bitmap, bitmap->key, bitmap->size);
+#endif
+ AllocSize.allocated -= bitmap->size;
+ free(bitmap);
+ if (AllocSize.allocated <= AllocSize.hiMark) {
+ quit = 1;
+ break;
+ }
+ }
+ this++;
+ this &= FC_MEM_HASH_MASK;
+ } while (this != stop && quit == 0);
+
+ FreeBitmap->index++;
+ FreeBitmap->index &= FC_MEM_HASH_MASK;
+}
+
+/*
+ * fc_purge_cache()
+ *
+ * Purge font cache, if required.
+ */
+
+static void
+fc_purge_cache()
+{
+ int strategy;
+
+ if (NeedPurgeCache) {
+ strategy = fc_check_size(LOW_MARK);
+ switch (strategy) {
+ case PURGE_ENTRY :
+ CacheStatistics.purge_runs++;
+ fc_purge_cache_entry();
+ break;
+ case PURGE_BITMAP :
+ CacheStatistics.purge_runs++;
+ fc_purge_bitmap();
+ break;
+ case (PURGE_ENTRY | PURGE_BITMAP) :
+ CacheStatistics.purge_runs++;
+ fc_purge_cache_entry();
+ fc_purge_bitmap();
+ break;
+ default :
+ NeedPurgeCache = 0;
+ break;
+ }
+ } else {
+ strategy = fc_check_size(HI_MARK);
+ switch (strategy) {
+ case PURGE_ENTRY :
+ if ((CacheBalance + FC_BALANCE_DIFFS) <= FC_BALANCE_HI) {
+ CacheBalance += FC_BALANCE_DIFFS;
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_cache: cache balance changed to %d\n", CacheBalance);
+#endif
+ fc_assign_cache();
+ fc_purge_bitmap_pool();
+ } else {
+ CacheStatistics.purge_runs++;
+ NeedPurgeCache = 1;
+ while (fc_check_size(HI_MARK) & PURGE_ENTRY) {
+ fc_purge_cache_entry();
+ }
+ }
+ break;
+ case PURGE_BITMAP :
+ if ((CacheBalance - FC_BALANCE_DIFFS) >= FC_BALANCE_LOW) {
+ CacheBalance -= FC_BALANCE_DIFFS;
+#if defined(HASH_DEBUG) || defined(DEBUG)
+fprintf(stderr, "fc_purge_cache: cache balance changed to %d\n", CacheBalance);
+#endif
+ fc_assign_cache();
+ fc_purge_cache_entry_pool();
+ } else {
+ CacheStatistics.purge_runs++;
+ NeedPurgeCache = 1;
+ while (fc_check_size(HI_MARK) & PURGE_BITMAP) {
+ fc_purge_bitmap();
+ }
+ }
+ break;
+ case (PURGE_ENTRY | PURGE_BITMAP) :
+ CacheStatistics.purge_runs++;
+ NeedPurgeCache = 1;
+ while (fc_check_size(HI_MARK)) {
+ fc_purge_cache_entry();
+ fc_purge_bitmap();
+ }
+ break;
+ default :
+ break;
+ }
+ }
+}