* Copyright 2007, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "FontCache.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include <Entry.h>
#include <Path.h>
#include "AutoLocker.h"
using std::nothrow;
FontCache
FontCache::sDefaultInstance;
FontCache::FontCache()
: MultiLocker("FontCache lock")
, fFontCacheEntries()
{
}
FontCache::~FontCache()
{
}
FontCache*
FontCache::Default()
{
return &sDefaultInstance;
}
FontCacheEntry*
FontCache::FontCacheEntryFor(const ServerFont& font, bool forceVector)
{
static const size_t signatureSize = 512;
char signature[signatureSize];
FontCacheEntry::GenerateSignature(signature, signatureSize, font,
forceVector);
AutoReadLocker readLocker(this);
BReference<FontCacheEntry> entry = fFontCacheEntries.Get(signature);
if (entry) {
return entry.Detach();
}
readLocker.Unlock();
AutoWriteLocker locker(this);
if (!locker.IsLocked())
return NULL;
entry = fFontCacheEntries.Get(signature);
if (!entry) {
_ConstrainEntryCount();
entry.SetTo(new (nothrow) FontCacheEntry(), true);
if (!entry || !entry->Init(font, forceVector)
|| fFontCacheEntries.Put(signature, entry) < B_OK) {
fprintf(stderr, "FontCache::FontCacheEntryFor() - "
"out of memory or no font file\n");
return NULL;
}
}
return entry.Detach();
}
void
FontCache::Recycle(FontCacheEntry* entry)
{
if (!entry)
return;
entry->UpdateUsage();
entry->ReleaseReference();
}
static const int32 kMaxEntryCount = 30;
static inline double
usage_index(uint64 useCount, bigtime_t age)
{
return 100.0 * useCount / age;
}
void
FontCache::_ConstrainEntryCount()
{
if (fFontCacheEntries.Size() < kMaxEntryCount)
return;
FontMap::Iterator iterator = fFontCacheEntries.GetIterator();
FontCacheEntry* leastUsedEntry = iterator.Next().value;
bigtime_t now = system_time();
bigtime_t age = now - leastUsedEntry->LastUsed();
uint64 useCount = leastUsedEntry->UsedCount();
double leastUsageIndex = usage_index(useCount, age);
while (iterator.HasNext()) {
FontCacheEntry* entry = iterator.Next().value;
age = now - entry->LastUsed();
useCount = entry->UsedCount();
double usageIndex = usage_index(useCount, age);
if (usageIndex < leastUsageIndex) {
leastUsedEntry = entry;
leastUsageIndex = usageIndex;
}
}
iterator = fFontCacheEntries.GetIterator();
while (iterator.HasNext()) {
if (iterator.Next().value.Get() == leastUsedEntry) {
fFontCacheEntries.Remove(iterator);
break;
}
}
}