1020 lines
23 KiB
C
1020 lines
23 KiB
C
/*-
|
|
* 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;
|
|
}
|
|
}
|
|
}
|