common: ++stability of the font cache

Use the font name as the cache key for
precise comparison instead of the file path.
This commit is contained in:
Hermet Park 2025-03-06 15:56:05 +09:00 committed by Hermet Park
parent 384fa7f2cb
commit 9e6a514022
8 changed files with 88 additions and 58 deletions

View file

@ -206,6 +206,7 @@ error:
return 0.0f; return 0.0f;
} }
char* duplicate(const char *str, size_t n) char* duplicate(const char *str, size_t n)
{ {
auto len = strlen(str); auto len = strlen(str);
@ -217,6 +218,7 @@ char* duplicate(const char *str, size_t n)
return (char*)memcpy(ret, str, n); return (char*)memcpy(ret, str, n);
} }
char* append(char* lhs, const char* rhs, size_t n) char* append(char* lhs, const char* rhs, size_t n)
{ {
if (!rhs) return lhs; if (!rhs) return lhs;
@ -225,20 +227,36 @@ char* append(char* lhs, const char* rhs, size_t n)
return strncat(lhs, rhs, n); return strncat(lhs, rhs, n);
} }
char* dirname(const char* path) char* dirname(const char* path)
{ {
const char *ptr = strrchr(path, '/'); auto ptr = strrchr(path, '/');
#ifdef _WIN32 #ifdef _WIN32
if (ptr) ptr = strrchr(ptr + 1, '\\'); ptr = strrchr(ptr ? ptr : path, '\\');
#endif #endif
int len = int(ptr + 1 - path); // +1 to include '/' auto len = ptr ? size_t(ptr - path + 1) : SIZE_MAX;
return duplicate(path, len); return duplicate(path, len);
} }
const char* fileext(const char* filename) char* filename(const char* path)
{ {
auto ext = filename; const char* ptr = strrchr(path, '/');
#ifdef _WIN32
auto ptr2 = strrchr(ptr ? ptr : path, '\\');
if (ptr2) ptr = ptr2;
#endif
if (ptr) ++ptr;
else ptr = path;
auto dot = fileext(ptr);
auto len = (dot > ptr) ? (size_t)(dot - ptr - 1) : strlen(ptr);
return duplicate(ptr, len);
}
const char* fileext(const char* path)
{
auto ext = path;
while (ext) { while (ext) {
auto p = strchr(ext, '.'); auto p = strchr(ext, '.');
if (!p) break; if (!p) break;

View file

@ -37,7 +37,8 @@ float toFloat(const char *str, char **end); //convert to floa
char* duplicate(const char *str, size_t n = SIZE_MAX); //copy the string char* duplicate(const char *str, size_t n = SIZE_MAX); //copy the string
char* append(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs char* append(char* lhs, const char* rhs, size_t n); //append the rhs to the lhs
char* dirname(const char* path); //return the full directory name char* dirname(const char* path); //return the full directory name
const char* fileext(const char* filename); //return the file extension name char* filename(const char* path); //return the file name without extension
const char* fileext(const char* path); //return the file extension name
} }
#endif //_TVG_STR_H_ #endif //_TVG_STR_H_

View file

@ -21,6 +21,7 @@
*/ */
#include "tvgStr.h"
#include "tvgTtfLoader.h" #include "tvgTtfLoader.h"
#if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) #if defined(_WIN32) && (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
@ -208,6 +209,9 @@ void TtfLoader::clear()
_unmap(this); _unmap(this);
#endif #endif
} }
tvg::free(name);
name = nullptr;
shape = nullptr; shape = nullptr;
} }
@ -247,6 +251,9 @@ bool TtfLoader::open(const char* path)
#ifdef THORVG_FILE_IO_SUPPORT #ifdef THORVG_FILE_IO_SUPPORT
clear(); clear();
if (!_map(this, path)) return false; if (!_map(this, path)) return false;
name = tvg::filename(path);
return reader.header(); return reader.header();
#else #else
return false; return false;

View file

@ -33,20 +33,30 @@ struct LoadModule
INLIST_ITEM(LoadModule); INLIST_ITEM(LoadModule);
//Use either hashkey(data) or hashpath(path) //Use either hashkey(data) or hashpath(path)
union { uintptr_t hashkey = 0;
uintptr_t hashkey;
char* hashpath = nullptr; char* hashpath = nullptr;
};
FileType type; //current loader file type FileType type; //current loader file type
uint16_t sharing = 0; //reference count uint16_t sharing = 0; //reference count
bool readied = false; //read done already. bool readied = false; //read done already.
bool pathcache = false; //cached by path bool cached = false; //cached for sharing
LoadModule(FileType type) : type(type) {} LoadModule(FileType type) : type(type) {}
virtual ~LoadModule() virtual ~LoadModule()
{ {
if (pathcache) tvg::free(hashpath); tvg::free(hashpath);
}
void cache(uintptr_t data)
{
hashkey = data;
cached = true;
}
void cache(char* data)
{
hashpath = data;
cached = true;
} }
virtual bool open(const char* path) { return false; } virtual bool open(const char* path) { return false; }
@ -61,12 +71,6 @@ struct LoadModule
return true; return true;
} }
bool cached()
{
if (hashkey) return true;
return false;
}
virtual bool close() virtual bool close()
{ {
if (sharing == 0) return true; if (sharing == 0) return true;
@ -99,6 +103,7 @@ struct ImageLoader : LoadModule
struct FontLoader : LoadModule struct FontLoader : LoadModule
{ {
float scale = 1.0f; float scale = 1.0f;
char* name = nullptr;
FontLoader(FileType type) : LoadModule(type) {} FontLoader(FileType type) : LoadModule(type) {}

View file

@ -205,7 +205,7 @@ static LoadModule* _findFromCache(const char* filename)
ScopedLock lock(key); ScopedLock lock(key);
INLIST_FOREACH(_activeLoaders, loader) { INLIST_FOREACH(_activeLoaders, loader) {
if (loader->pathcache && !strcmp(loader->hashpath, filename)) { if (loader->cached && !strcmp(loader->hashpath, filename)) {
++loader->sharing; ++loader->sharing;
return loader; return loader;
} }
@ -260,7 +260,7 @@ bool LoaderMgr::retrieve(LoadModule* loader)
{ {
if (!loader) return false; if (!loader) return false;
if (loader->close()) { if (loader->close()) {
if (loader->cached()) { if (loader->cached) {
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.remove(loader); _activeLoaders.remove(loader);
} }
@ -287,8 +287,7 @@ LoadModule* LoaderMgr::loader(const char* filename, bool* invalid)
if (auto loader = _findByPath(filename)) { if (auto loader = _findByPath(filename)) {
if (loader->open(filename)) { if (loader->open(filename)) {
if (allowCache) { if (allowCache) {
loader->hashpath = duplicate(filename); loader->cache(duplicate(filename));
loader->pathcache = true;
{ {
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
@ -303,8 +302,7 @@ LoadModule* LoaderMgr::loader(const char* filename, bool* invalid)
if (auto loader = _find(static_cast<FileType>(i))) { if (auto loader = _find(static_cast<FileType>(i))) {
if (loader->open(filename)) { if (loader->open(filename)) {
if (allowCache) { if (allowCache) {
loader->hashpath = duplicate(filename); loader->cache(duplicate(filename));
loader->pathcache = true;
{ {
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
@ -327,30 +325,6 @@ bool LoaderMgr::retrieve(const char* filename)
} }
LoadModule* LoaderMgr::loader(const char* key)
{
INLIST_FOREACH(_activeLoaders, loader) {
if (loader->pathcache && strstr(loader->hashpath, key)) {
++loader->sharing;
return loader;
}
}
return nullptr;
}
LoadModule* LoaderMgr::anyfont()
{
INLIST_FOREACH(_activeLoaders, loader) {
if ((loader->type == FileType::Ttf) && loader->pathcache) {
++loader->sharing;
return loader;
}
}
return nullptr;
}
LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy) LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy)
{ {
//Note that users could use the same data pointer with the different content. //Note that users could use the same data pointer with the different content.
@ -372,7 +346,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeT
if (auto loader = _findByType(mimeType)) { if (auto loader = _findByType(mimeType)) {
if (loader->open(data, size, rpath, copy)) { if (loader->open(data, size, rpath, copy)) {
if (allowCache) { if (allowCache) {
loader->hashkey = HASH_KEY(data); loader->cache(HASH_KEY(data));
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
@ -389,7 +363,7 @@ LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const char* mimeT
if (loader) { if (loader) {
if (loader->open(data, size, rpath, copy)) { if (loader->open(data, size, rpath, copy)) {
if (allowCache) { if (allowCache) {
loader->hashkey = HASH_KEY(data); loader->cache(HASH_KEY(data));
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
@ -415,7 +389,7 @@ LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, Colo
auto loader = new RawLoader; auto loader = new RawLoader;
if (loader->open(data, w, h, cs, copy)) { if (loader->open(data, w, h, cs, copy)) {
if (!copy) { if (!copy) {
loader->hashkey = HASH_KEY((const char*)data); loader->cache(HASH_KEY((const char*)data));
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
} }
@ -431,13 +405,13 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size,
{ {
#ifdef THORVG_TTF_LOADER_SUPPORT #ifdef THORVG_TTF_LOADER_SUPPORT
//TODO: add check for mimetype ? //TODO: add check for mimetype ?
if (auto loader = _findFromCache(name)) return loader; if (auto loader = font(name)) return loader;
//function is dedicated for ttf loader (the only supported font loader) //function is dedicated for ttf loader (the only supported font loader)
auto loader = new TtfLoader; auto loader = new TtfLoader;
if (loader->open(data, size, "", copy)) { if (loader->open(data, size, "", copy)) {
loader->hashpath = duplicate(name); loader->name = duplicate(name);
loader->pathcache = true; loader->cached = true; //force it.
ScopedLock lock(key); ScopedLock lock(key);
_activeLoaders.back(loader); _activeLoaders.back(loader);
return loader; return loader;
@ -448,3 +422,28 @@ LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size,
#endif #endif
return nullptr; return nullptr;
} }
LoadModule* LoaderMgr::font(const char* name)
{
INLIST_FOREACH(_activeLoaders, loader) {
if (loader->type != FileType::Ttf) continue;
if (loader->cached && tvg::equal(name, static_cast<FontLoader*>(loader)->name)) {
++loader->sharing;
return loader;
}
}
return nullptr;
}
LoadModule* LoaderMgr::anyfont()
{
INLIST_FOREACH(_activeLoaders, loader) {
if (loader->cached && loader->type == FileType::Ttf) {
++loader->sharing;
return loader;
}
}
return nullptr;
}

View file

@ -33,7 +33,7 @@ struct LoaderMgr
static LoadModule* loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy); static LoadModule* loader(const char* data, uint32_t size, const char* mimeType, const char* rpath, bool copy);
static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy); static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, ColorSpace cs, bool copy);
static LoadModule* loader(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy); static LoadModule* loader(const char* name, const char* data, uint32_t size, const char* mimeType, bool copy);
static LoadModule* loader(const char* key); static LoadModule* font(const char* name);
static LoadModule* anyfont(); static LoadModule* anyfont();
static bool retrieve(const char* filename); static bool retrieve(const char* filename);
static bool retrieve(LoadModule* loader); static bool retrieve(LoadModule* loader);

View file

@ -67,7 +67,7 @@ Result Text::load(const char* name, const char* data, uint32_t size, const char*
//unload font //unload font
if (!data) { if (!data) {
if (LoaderMgr::retrieve(name)) return Result::Success; if (LoaderMgr::retrieve(LoaderMgr::font(name))) return Result::Success;
return Result::InsufficientCondition; return Result::InsufficientCondition;
} }

View file

@ -62,7 +62,7 @@ struct Text::Impl : Paint::Impl
Result font(const char* name, float size, const char* style) Result font(const char* name, float size, const char* style)
{ {
auto loader = name ? LoaderMgr::loader(name) : LoaderMgr::anyfont(); auto loader = name ? LoaderMgr::font(name) : LoaderMgr::anyfont();
if (!loader) return Result::InsufficientCondition; if (!loader) return Result::InsufficientCondition;
if (style && strstr(style, "italic")) italic = true; if (style && strstr(style, "italic")) italic = true;