_major_ re-design of the caching sub-system. Still using the same API

though :-)
This commit is contained in:
David Turner 2001-10-26 16:58:27 +00:00
parent ae340bbfec
commit cd605b6302
15 changed files with 1900 additions and 1893 deletions

View File

@ -1,3 +1,12 @@
2001-10-26 David Turner <david@freetype.org>
* include/freetype/ftcache.h, include/freetype/cache/*.h,
src/cache/*.c: Major re-design of the cache sub-system to provide
better performance as well as an "Acquire"/"Release" API..
seems to work well here.. but probably needs a bit more testing..
2001-10-26 Leonard Rosenthol <leonardr@lazerware.com>
* updated Mac OS README (builds/mac/) to reflect my taking over

View File

@ -60,109 +60,57 @@ FT_BEGIN_HEADER
typedef struct FTC_ChunkNodeRec_* FTC_ChunkNode;
typedef struct FTC_ChunkSetRec_* FTC_ChunkSet;
typedef struct FTC_Chunk_CacheRec_* FTC_Chunk_Cache;
typedef struct FTC_ChunkCacheRec_* FTC_ChunkCache;
typedef struct FTC_ChunkNodeRec_
{
FTC_CacheNodeRec root;
FTC_ChunkSet cset;
FT_UShort cset_index;
FT_UShort num_elements;
FT_Byte* elements;
FTC_NodeRec node;
FTC_ChunkSet cset;
FT_UShort item_count;
FT_UShort item_start;
FT_Byte* items;
} FTC_ChunkNodeRec;
#define FTC_CHUNK_NODE(x) ((FTC_ChunkNode)(x))
#define FTC_CHUNKNODE_TO_LRUNODE( x ) ((FT_ListNode)( x ))
#define FTC_LRUNODE_TO_CHUNKNODE( x ) ((FTC_ChunkNode)( x ))
/*************************************************************************/
/* */
/* chunk set methods */
/* */
/* used to set "element_max", "element_count" and "element_size" */
typedef FT_Error
(*FTC_ChunkSet_SizesFunc)( FTC_ChunkSet cset,
FT_Pointer type );
typedef FT_Error
(*FTC_ChunkSet_InitFunc)( FTC_ChunkSet cset,
FT_Pointer type );
typedef void
(*FTC_ChunkSet_DoneFunc)( FTC_ChunkSet cset );
typedef FT_Bool
(*FTC_ChunkSet_CompareFunc)( FTC_ChunkSet cset,
FT_Pointer type );
typedef FT_Error
(*FTC_ChunkSet_NewNodeFunc)( FTC_ChunkSet cset,
FT_UInt index,
FTC_ChunkNode* anode );
typedef void
(*FTC_ChunkSet_DestroyNodeFunc)( FTC_ChunkNode node );
typedef FT_ULong
(*FTC_ChunkSet_SizeNodeFunc)( FTC_ChunkNode node );
typedef struct FTC_ChunkSet_Class_
/* a chunk set is used to categorize chunks of a given type */
typedef struct FTC_ChunkSetRec_
{
FT_UInt cset_byte_size;
FTC_ChunkSet_InitFunc init;
FTC_ChunkSet_DoneFunc done;
FTC_ChunkSet_CompareFunc compare;
FTC_ChunkSet_SizesFunc sizes;
FTC_ChunkSet_NewNodeFunc new_node;
FTC_ChunkSet_SizeNodeFunc size_node;
FTC_ChunkSet_DestroyNodeFunc destroy_node;
} FTC_ChunkSet_Class;
typedef struct FTC_ChunkSetRec_
{
FTC_Chunk_Cache cache;
FTC_Manager manager;
FT_Memory memory;
FTC_ChunkSet_Class* clazz;
FT_UInt cset_index; /* index in parent cache */
FT_UInt element_max; /* maximum number of elements */
FT_UInt element_size; /* element size in bytes */
FT_UInt element_count; /* number of elements per chunk */
FT_UInt num_chunks;
FTC_ChunkNode* chunks;
FT_LruNodeRec lru;
FT_UFast hash;
FTC_ChunkCache ccache;
FT_Fast num_chunks;
FT_UInt item_total; /* total number of glyphs in set */
FT_UInt item_size; /* size of each glyph item in set */
FT_UInt item_count; /* number of glyph items per chunk */
} FTC_ChunkSetRec;
#define FTC_CHUNK_SET(x) ((FTC_ChunkSet)(x))
/* the abstract chunk cache class */
typedef struct FTC_Chunk_Cache_Class_
#define FTC_CHUNK_SET_MEMORY(x) ((x)->ccache->cache.memory)
/* the abstract chunk cache class */
typedef struct FTC_ChunkCacheRec_
{
FTC_Cache_Class root;
FTC_ChunkSet_Class* cset_class;
FTC_CacheRec cache;
FT_LruList cset_lru; /* LRU list of chunk sets */
} FTC_ChunkCacheRec;
} FTC_Chunk_Cache_Class;
#define FTC_CHUNK_CACHE(x) ((FTC_ChunkCache)(x))
/* the abstract chunk cache object */
typedef struct FTC_Chunk_CacheRec_
typedef struct FTC_ChunkQueryRec_
{
FTC_CacheRec root;
FT_Lru csets_lru; /* static chunk set lru list */
FTC_ChunkSet last_cset; /* small cache :-) */
FTC_ChunkSet_CompareFunc compare; /* useful shortcut */
/* input */
FT_UInt gindex; /* glyph index */
} FTC_Chunk_CacheRec;
/* output */
FTC_ChunkSet cset;
} FTC_ChunkQueryRec, *FTC_ChunkQuery;
/*************************************************************************/
@ -173,52 +121,44 @@ FT_BEGIN_HEADER
/* */
FT_EXPORT( FT_Error )
FTC_ChunkNode_Init( FTC_ChunkNode node,
FTC_ChunkSet cset,
FT_UInt index,
FT_Bool alloc );
#define FTC_ChunkNode_Ref( n ) \
FTC_CACHENODE_TO_DATA_P( &(n)->root )->ref_count++
#define FTC_ChunkNode_Unref( n ) \
FTC_CACHENODE_TO_DATA_P( &(n)->root )->ref_count--
ftc_chunk_node_init( FTC_ChunkNode node,
FTC_ChunkSet cset,
FT_UInt index,
FT_Bool alloc );
/* chunk set objects */
FT_EXPORT( void )
FTC_ChunkNode_Destroy( FTC_ChunkNode node );
ftc_chunk_node_done( FTC_ChunkNode node );
FT_EXPORT( FT_Error )
FTC_ChunkSet_New( FTC_Chunk_Cache cache,
FT_Pointer type,
FTC_ChunkSet *aset );
ftc_chunk_set_init( FTC_ChunkSet cset,
FT_UInt item_size,
FT_UInt item_count,
FT_UInt item_total,
FTC_ChunkCache cache );
FT_EXPORT( FT_Error )
FTC_ChunkSet_Lookup_Node( FTC_ChunkSet cset,
FT_UInt glyph_index,
FTC_ChunkNode* anode,
FT_UInt *anindex );
FT_EXPORT( void )
ftc_chunk_set_done( FTC_ChunkSet cset );
/* chunk cache objects */
FT_EXPORT( FT_Error )
FTC_Chunk_Cache_Init( FTC_Chunk_Cache cache );
ftc_chunk_cache_init( FTC_ChunkCache cache,
FT_LruList_Class cset_class );
FT_EXPORT( void )
FTC_Chunk_Cache_Done( FTC_Chunk_Cache cache );
ftc_chunk_cache_done( FTC_ChunkCache cache );
FT_EXPORT( FT_Error )
FTC_Chunk_Cache_Lookup( FTC_Chunk_Cache cache,
FT_Pointer type,
FT_UInt gindex,
FTC_ChunkNode *anode,
FT_UInt *aindex );
ftc_chunk_cache_lookup( FTC_ChunkCache cache,
FTC_ChunkQuery query,
FTC_ChunkNode *anode );
/* */
FT_END_HEADER

View File

@ -64,107 +64,83 @@
FT_BEGIN_HEADER
/* maximum number of glyph sets per glyph cache; must be < 256 */
#define FTC_MAX_GLYPH_SETS 16
#define FTC_GSET_HASH_SIZE_DEFAULT 64
/* each glyph set is caracterized by a "glyph set type" which must be */
/* defined by sub-classes.. */
typedef struct FTC_GlyphSetRec_* FTC_GlyphSet;
typedef struct FTC_GlyphNodeRec_* FTC_GlyphNode;
typedef struct FTC_Glyph_CacheRec_* FTC_Glyph_Cache;
typedef struct FTC_GlyphNodeRec_
/* handle to a glyph cache node */
typedef struct FTC_GlyphNodeRec_* FTC_GlyphNode;
/* a glyph cache, its nodes are all glyph-specific */
typedef struct FTC_GlyphCacheRec_* FTC_GlyphCache;
/* glyph sets class handle */
typedef const struct FTC_GlyphSet_ClassRec_* FTC_GlyphSet_Class;
/* size should be 24 bytes on 32-bit machines */
/* note that the node's hash is ((gset->hash << 16) | glyph_index) */
/* this _must_ be set properly by the glyph node initializer */
/* */
typedef struct FTC_GlyphNodeRec_
{
FTC_CacheNodeRec root;
FTC_GlyphNode gset_next; /* next in glyph set's bucket list */
FT_UShort glyph_index;
FT_UShort gset_index;
FTC_NodeRec node;
FTC_GlyphSet gset;
} FTC_GlyphNodeRec;
#define FTC_GLYPHNODE( x ) ( (FTC_GlyphNode)( x ) )
#define FTC_GLYPHNODE_TO_LRUNODE( n ) ( (FT_ListNode)( n ) )
#define FTC_LRUNODE_TO_GLYPHNODE( n ) ( (FTC_GlyphNode)( n ) )
/*************************************************************************/
/* */
/* Glyph set methods. */
/* */
typedef FT_Error
(*FTC_GlyphSet_InitFunc)( FTC_GlyphSet gset,
FT_Pointer type );
typedef void
(*FTC_GlyphSet_DoneFunc)( FTC_GlyphSet gset );
typedef FT_Bool
(*FTC_GlyphSet_CompareFunc)( FTC_GlyphSet gset,
FT_Pointer type );
typedef FT_Error
(*FTC_GlyphSet_NewNodeFunc)( FTC_GlyphSet gset,
FT_UInt gindex,
FTC_GlyphNode* anode );
typedef void
(*FTC_GlyphSet_DestroyNodeFunc)( FTC_GlyphNode node,
FTC_GlyphSet gset );
typedef FT_ULong
(*FTC_GlyphSet_SizeNodeFunc)( FTC_GlyphNode node,
FTC_GlyphSet gset );
typedef struct FTC_GlyphSet_Class_
{
FT_UInt gset_byte_size;
FTC_GlyphSet_InitFunc init;
FTC_GlyphSet_DoneFunc done;
FTC_GlyphSet_CompareFunc compare;
FTC_GlyphSet_NewNodeFunc new_node;
FTC_GlyphSet_SizeNodeFunc size_node;
FTC_GlyphSet_DestroyNodeFunc destroy_node;
} FTC_GlyphSet_Class;
#define FTC_GLYPH_NODE(x) ((FTC_GlyphNode)(x))
#define FTC_GLYPH_NODE_P(x) ((FTC_GlyphNode*)(x))
/* the glyph set structure. each glyph set is used to model a set of */
/* glyphs of the same "type". The type itself is defined in sub-classes */
/* */
/* for example, the "image cache" uses face_id + character_pixel_sizes + */
/* image_format to characterize glyph sets.. */
/* */
/* a pure "master outlines" cache would only use face_id, etc.. */
/* */
typedef struct FTC_GlyphSetRec_
{
FTC_Glyph_Cache cache;
FTC_Manager manager;
FT_Memory memory;
FTC_GlyphSet_Class* clazz;
FT_UInt hash_size;
FTC_GlyphNode* buckets;
FT_UInt gset_index; /* index in parent cache */
FT_LruNodeRec lru; /* glyph sets are LRU nodes within */
FTC_GlyphCache gcache; /* parent cache.. */
FT_UFast hash; /* must be set by initializer !! */
FT_Fast num_glyphs; /* destroyed when 0.. */
} FTC_GlyphSetRec;
#define FTC_GLYPH_SET(x) ((FTC_GlyphSet)(x))
#define FTC_GLYPH_SET_P(x) ((FTC_GlyphSet*)(x))
/* the abstract glyph cache class */
typedef struct FTC_Glyph_Cache_Class_
{
FTC_Cache_Class root;
FTC_GlyphSet_Class* gset_class;
#define FTC_GLYPH_SET_MEMORY(x) ((x)->gcache->cache.memory)
} FTC_Glyph_Cache_Class;
/* retrieve glyph index of glyph node */
#define FTC_GLYPH_NODE_GINDEX(x) \
((FT_UInt)(FTC_GLYPH_NODE(x)->node.hash & 0xFFFF))
/* the abstract glyph cache object */
typedef struct FTC_Glyph_CacheRec_
typedef struct FTC_GlyphCacheRec_
{
FTC_CacheRec root;
FT_Lru gsets_lru; /* static sets lru list */
FTC_GlyphSet last_gset; /* small cache :-) */
FTC_GlyphSet_CompareFunc compare; /* useful shortcut */
FTC_CacheRec cache;
FT_LruList gset_lru; /* LRU list of glyph sets */
} FTC_Glyph_CacheRec;
} FTC_GlyphCacheRec;
#define FTC_GLYPH_CACHE(x) ((FTC_GlyphCache)(x))
#define FTC_GLYPH_CACHE_P(x) ((FTC_GlyphCache*)(x))
typedef struct FTC_GlyphQueryRec_
{
/* input */
FT_UInt gindex;
/* output */
FTC_GlyphSet gset;
} FTC_GlyphQueryRec, *FTC_GlyphQuery;
/*************************************************************************/
@ -174,44 +150,41 @@ FT_BEGIN_HEADER
/* cache sub-system internals. */
/* */
/* must be called by derived FTC_Node_InitFunc routines */
FT_EXPORT( void )
FTC_GlyphNode_Init( FTC_GlyphNode node,
FTC_GlyphSet gset,
FT_UInt gindex );
#define FTC_GlyphNode_Ref( n ) \
FTC_CACHENODE_TO_DATA_P( &(n)->root )->ref_count++
#define FTC_GlyphNode_Unref( n ) \
FTC_CACHENODE_TO_DATA_P( &(n)->root )->ref_count--
ftc_glyph_node_init( FTC_GlyphNode node,
FT_UInt gindex, /* glyph index for node */
FTC_GlyphSet gset );
/* must be called by derived FTC_Node_DoneFunc routines */
FT_EXPORT( void )
FTC_GlyphNode_Destroy( FTC_GlyphNode node,
FTC_Glyph_Cache cache );
ftc_glyph_node_done( FTC_GlyphNode node );
/* can be used as a FTC_LruNode_InitFunc or called by sub-classes */
FT_EXPORT( FT_Error )
FTC_Glyph_Cache_Init( FTC_Glyph_Cache cache );
ftc_glyph_set_init( FTC_GlyphSet gset,
FT_LruList list );
/* can be used as a FTC_LruNode_DoneFunc or called by sub-classes */
FT_EXPORT( void )
FTC_Glyph_Cache_Done( FTC_Glyph_Cache cache );
ftc_glyph_set_done( FTC_GlyphSet gset );
/* can be used as a FTC_Cache_DoneFunc or called by sub-classes */
FT_EXPORT( void )
ftc_glyph_cache_done( FTC_GlyphCache cache );
/* must be called in a FTC_Cache_InitFunc !! */
FT_EXPORT( FT_Error )
FTC_GlyphSet_New( FTC_Glyph_Cache cache,
FT_Pointer type,
FTC_GlyphSet *aset );
ftc_glyph_cache_init( FTC_GlyphCache cache,
FT_LruList_Class gset_class );
/* can be called directly or from sub-classes */
FT_EXPORT( FT_Error )
FTC_GlyphSet_Lookup_Node( FTC_GlyphSet gset,
FT_UInt glyph_index,
FTC_GlyphNode *anode );
FT_EXPORT( FT_Error )
FTC_Glyph_Cache_Lookup( FTC_Glyph_Cache cache,
FT_Pointer type,
FT_UInt gindex,
FTC_GlyphNode *anode );
ftc_glyph_cache_lookup( FTC_GlyphCache cache,
FTC_GlyphQuery query,
FTC_GlyphNode *anode );
FT_END_HEADER

View File

@ -29,7 +29,6 @@
#include <ft2build.h>
#include FT_CACHE_H
#include FT_CACHE_INTERNAL_GLYPH_H
FT_BEGIN_HEADER
@ -96,6 +95,14 @@ FT_BEGIN_HEADER
} FTC_Image_Desc;
/* */
#define FTC_IMAGE_DESC_COMPARE( d1, d2 ) \
( FTC_FONT_COMPARE( &(d1)->font, &(d2)->font ) && \
(d1)->image_type == (d2)->image_type )
#define FTC_IMAGE_DESC_HASH(d) \
(FT_UFast)( FTC_FONT_HASH(&(d)->font) ^ \
((d)->image_type << 4) )
/*************************************************************************/
/* */

View File

@ -81,12 +81,23 @@ FT_BEGIN_HEADER
#define FTC_MAX_FACES_DEFAULT 2
#define FTC_MAX_SIZES_DEFAULT 4
#define FTC_MAX_BYTES_DEFAULT 200000L /* 200kByte by default! */
#define FTC_MAX_BYTES_DEFAULT 100000L /* 200kByte by default! */
/* maximum number of caches registered in a single manager */
#define FTC_MAX_CACHES 16
/* handle to cache object */
typedef struct FTC_CacheRec_* FTC_Cache;
/* handle to cache class */
typedef const struct FTC_Cache_ClassRec_* FTC_Cache_Class;
/* handle to cache node */
typedef struct FTC_NodeRec_* FTC_Node;
/*************************************************************************/
/* */
/* <Struct> */
@ -125,13 +136,15 @@ FT_BEGIN_HEADER
typedef struct FTC_ManagerRec_
{
FT_Library library;
FT_Lru faces_lru;
FT_Lru sizes_lru;
FT_LruList faces_list;
FT_LruList sizes_list;
FT_ULong max_bytes;
FT_ULong num_bytes;
FT_ULong max_weight;
FT_ULong cur_weight;
FT_UInt num_nodes;
FT_ListRec global_lru;
FTC_Node nodes_list;
FTC_Cache caches[FTC_MAX_CACHES];
FT_Pointer request_data;
@ -185,197 +198,135 @@ FT_BEGIN_HEADER
/* glyphs for a given size, some metrics, etc. */
/* */
typedef FT_ListNodeRec FTC_CacheNodeRec;
typedef FTC_CacheNodeRec* FTC_CacheNode;
/* the field `cachenode.data' is typecast to this type */
typedef struct FTC_CacheNode_Data_
/* structure size should be 20 bytes on 32-bits machines */
typedef struct FTC_NodeRec_
{
FT_UShort cache_index;
FT_Short ref_count;
FTC_Node mru_next; /* circular mru list pointer */
FTC_Node mru_prev; /* circular mru list pointer */
FTC_Node link; /* used for hashing.. */
FT_UInt32 hash; /* used for hashing too.. */
FT_UShort cache_index; /* index of cache this node belongs to */
FT_Short ref_count; /* reference count for this node.. */
} FTC_NodeRec;
} FTC_CacheNode_Data;
#define FTC_NODE(x) ((FTC_Node)(x))
#define FTC_NODE_P(x) ((FTC_Node*)(x))
/* return a pointer to FTC_CacheNode_Data contained in a */
/* CacheNode's `data' field */
#define FTC_CACHENODE_TO_DATA_P( n ) \
( (FTC_CacheNode_Data*)&(n)->data )
#define FTC_LIST_TO_CACHENODE( n ) ( (FTC_CacheNode)(n) )
/*************************************************************************/
/* */
/* <FuncType> */
/* FTC_CacheNode_SizeFunc */
/* */
/* <Description> */
/* A function used to compute the total size in bytes of a given */
/* cache node. It is used by the cache manager to compute the number */
/* of old nodes to flush when the cache is full. */
/* */
/* <Input> */
/* node :: A handle to the target cache node. */
/* */
/* cache_data :: A generic pointer passed to the destructor. */
/* */
/* <Return> */
/* The size of a given cache node in bytes. */
/* */
typedef FT_ULong
(*FTC_CacheNode_SizeFunc)( FTC_CacheNode node,
FT_Pointer cache_data );
/*************************************************************************/
/* */
/* <FuncType> */
/* FTC_CacheNode_DestroyFunc */
/* */
/* <Description> */
/* A function used to destroy a given cache node. It is called by */
/* the manager when the cache is full and old nodes need to be */
/* flushed out. */
/* */
/* <Input> */
/* node :: A handle to the target cache node. */
/* */
/* cache_data :: A generic pointer passed to the destructor. */
/* */
typedef void
(*FTC_CacheNode_DestroyFunc)( FTC_CacheNode node,
FT_Pointer cache_data );
/*************************************************************************/
/* */
/* <Struct> */
/* FTC_CacheNode_Class */
/* */
/* <Description> */
/* A very simple structure used to describe a cache node's class to */
/* the cache manager. */
/* */
/* <Fields> */
/* size_node :: A function used to size the node. */
/* */
/* destroy_node :: A function used to destroy the node. */
/* */
/* <Note> */
/* The cache node class doesn't include a `new_node' function because */
/* the cache manager never allocates cache node directly; it */
/* delegates this task to its cache objects. */
/* */
typedef struct FTC_CacheNode_Class_
{
FTC_CacheNode_SizeFunc size_node;
FTC_CacheNode_DestroyFunc destroy_node;
} FTC_CacheNode_Class;
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** CACHE DEFINITIONS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* */
/* <FuncType> */
/* FTC_Cache_InitFunc */
/* */
/* <Description> */
/* A function used to initialize a given cache object. */
/* */
/* <Input> */
/* cache :: A handle to the new cache. */
/* */
typedef FT_Error
(*FTC_Cache_InitFunc)( FTC_Cache cache );
/*************************************************************************/
/* */
/* <FuncType> */
/* FTC_Cache_DoneFunc */
/* */
/* <Description> */
/* A function to finalize a given cache object. */
/* */
/* <Input> */
/* cache :: A handle to the target cache. */
/* */
typedef void
(*FTC_Cache_DoneFunc)( FTC_Cache cache );
/*************************************************************************/
/* */
/* <Struct> */
/* FTC_Cache_Class */
/* */
/* <Description> */
/* A structure used to describe a given cache object class to the */
/* cache manager. */
/* */
/* <Fields> */
/* cache_byte_size :: The size of the cache object in bytes. */
/* */
/* init_cache :: The cache object initializer. */
/* */
/* done_cache :: The cache object finalizer. */
/* */
struct FTC_Cache_Class_
{
FT_UInt cache_byte_size;
FTC_Cache_InitFunc init_cache;
FTC_Cache_DoneFunc done_cache;
};
/*************************************************************************/
/* */
/* <Struct> */
/* FTC_CacheRec */
/* */
/* <Description> */
/* A structure used to describe an abstract cache object. */
/* */
/* <Fields> */
/* manager :: A handle to the parent cache manager. */
/* */
/* memory :: A handle to the memory manager. */
/* */
/* clazz :: A pointer to the cache class. */
/* */
/* node_clazz :: A pointer to the cache's node class. */
/* */
/* cache_index :: An index of the cache in the manager's table. */
/* */
/* cache_data :: Data passed to the cache node */
/* constructor/finalizer. */
/* */
/* each cache really implements a dynamic hash table to manage its nodes */
typedef struct FTC_CacheRec_
{
FTC_Manager manager;
FT_Memory memory;
FTC_Cache_Class* clazz;
FTC_CacheNode_Class* node_clazz;
FTC_Manager manager;
FT_Memory memory;
FTC_Cache_Class clazz;
FT_UInt cache_index; /* in manager's table */
FT_Pointer cache_data; /* passed to cache node methods */
FT_UInt cache_index; /* in manager's table */
FT_Pointer cache_data; /* used by cache node methods */
FT_UFast nodes;
FT_UFast size;
FTC_Node* buckets;
} FTC_CacheRec;
#define FTC_CACHE(x) ((FTC_Cache)(x))
#define FTC_CACHE_P(x) ((FTC_Cache*)(x))
/* initialize a given cache */
typedef FT_Error
(*FTC_Cache_InitFunc)( FTC_Cache cache );
/* finalize a given cache */
typedef void
(*FTC_Cache_DoneFunc)( FTC_Cache cache );
/* initialize a new cache node */
typedef FT_Error
(*FTC_Node_InitFunc)( FTC_Node node,
FT_Pointer type,
FTC_Cache cache );
/* compute the weight of a given cache node */
typedef FT_ULong
(*FTC_Node_WeightFunc)( FTC_Node node,
FTC_Cache cache );
/* compare a node to a given key pair */
typedef FT_Bool
(*FTC_Node_CompareFunc)( FTC_Node node,
FT_Pointer key,
FTC_Cache cache );
/* finalize a given cache node */
typedef void
(*FTC_Node_DoneFunc)( FTC_Node node,
FTC_Cache cache );
typedef struct FTC_Cache_ClassRec_
{
FT_UInt cache_size;
FTC_Cache_InitFunc cache_init;
FTC_Cache_DoneFunc cache_done;
FT_UInt node_size;
FTC_Node_InitFunc node_init;
FTC_Node_WeightFunc node_weight;
FTC_Node_CompareFunc node_compare;
FTC_Node_DoneFunc node_done;
} FTC_Cache_ClassRec;
/* */
#define FTC_CACHE_RESIZE_TEST(c) \
( (c)->nodes*3 < (c)->size || \
(c)->size*3 < (c)->nodes )
/* this must be used internally for the moment */
FT_EXPORT( FT_Error )
FTC_Manager_Register_Cache( FTC_Manager manager,
FTC_Cache_Class clazz,
FTC_Cache *acache );
/* can be used directory as FTC_Cache_DoneFunc, or called by custom */
/* cache finalizers.. */
FT_EXPORT( void )
ftc_cache_done( FTC_Cache cache );
/* initalize the hash table within the cache */
FT_EXPORT( FT_Error )
ftc_cache_init( FTC_Cache cache );
/* can be used when FTC_CACHE_RESIZE_TEST returns TRUE after a node */
/* insertion.. */
FT_EXPORT(void)
ftc_cache_resize( FTC_Cache cache );
/* can be called when the key's hash value has been computed */
FT_EXPORT(FT_Error)
ftc_cache_lookup_node( FTC_Cache cache,
FT_UFast key_hash,
FT_Pointer key,
FTC_Node *anode );
/* can be called to increment a node's reference count */
FT_EXPORT(void)
ftc_node_ref( FTC_Node node,
FTC_Cache cache );
/* can be called to decrement a node's reference count */
FT_EXPORT(void)
ftc_node_unref( FTC_Node node,
FTC_Cache cache );
/* */
FT_END_HEADER

View File

@ -22,7 +22,6 @@
#include <ft2build.h>
#include FT_CACHE_H
#include FT_CACHE_INTERNAL_CHUNK_H
#include FT_CACHE_IMAGE_H

View File

@ -66,117 +66,130 @@
FT_BEGIN_HEADER
/* generic key type */
/* generic list key type */
typedef FT_Pointer FT_LruKey;
/* a list list handle */
typedef struct FT_LruListRec_* FT_LruList;
/* an lru node -- root.data points to the element */
/* list class handle */
typedef const struct FT_LruList_ClassRec_* FT_LruList_Class;
/* an list node handle */
typedef struct FT_LruNodeRec_* FT_LruNode;
/* the list node structure */
typedef struct FT_LruNodeRec_
{
FT_ListNodeRec root;
FT_LruKey key;
FT_LruNode next;
FT_LruKey key;
} FT_LruNodeRec, *FT_LruNode;
} FT_LruNodeRec;
/* forward declaration */
typedef struct FT_LruRec_* FT_Lru;
/* LRU class */
typedef struct FT_Lru_Class_
/* the list structure */
typedef struct FT_LruListRec_
{
FT_UInt lru_size; /* object size in bytes */
FT_Memory memory;
FT_LruList_Class clazz;
FT_LruNode nodes;
FT_UInt max_nodes;
FT_UInt num_nodes;
FT_Pointer user_data;
/* this method is used to initialize a new list element node */
FT_Error
(*init_element)( FT_Lru lru,
FT_LruNode node );
} FT_LruListRec;
/* this method is used to finalize a given list element node */
void
(*done_element)( FT_Lru lru,
FT_LruNode node );
/* If defined, this method is called when the list if full */
/* during the lookup process -- it is used to change the contents */
/* of a list element node, instead of calling `done_element()', */
/* then `init_element'. Set it to 0 for default behaviour. */
FT_Error
(*flush_element)( FT_Lru lru,
FT_LruNode node,
FT_LruKey new_key );
/* initialize a list list */
typedef FT_Error (*FT_LruList_InitFunc)( FT_LruList list );
/* finalize a list list */
typedef void (*FT_LruList_DoneFunc)( FT_LruList list );
/* If defined, this method is used to compare a list element node */
/* with a given key during a lookup. If set to 0, the `key' */
/* fields will be directly compared instead. */
FT_Bool
(*compare_element)( FT_LruNode node,
FT_LruKey key );
/* this method is used to initialize a new list element node */
typedef FT_Error (*FT_LruNode_InitFunc)( FT_LruNode node,
FT_LruKey key,
FT_LruList list );
} FT_Lru_Class;
/* this method is used to finalize a given list element node */
typedef void (*FT_LruNode_DoneFunc)( FT_LruNode node,
FT_LruList list );
/* If defined, this method is called when the list if full */
/* during the lookup process -- it is used to change the contents */
/* of a list element node, instead of calling `done_element()', */
/* then `init_element'. Set it to 0 for default behaviour. */
typedef FT_Error (*FT_LruNode_FlushFunc)( FT_LruNode node,
FT_LruKey new_key,
FT_LruList list );
/* If defined, this method is used to compare a list element node */
/* with a given key during a lookup. If set to 0, the `key' */
/* fields will be directly compared instead. */
typedef FT_Bool (*FT_LruNode_CompareFunc)( FT_LruNode node,
FT_LruKey key,
FT_LruList list );
/* A selector is used to indicate whether a given list element node */
/* is part of a selection for FT_Lru_Remove_Selection(). The function */
/* is part of a selection for FT_LruList_Remove_Selection(). The function */
/* must return true (i.e., non-null) to indicate that the node is part */
/* of it. */
typedef FT_Bool
(*FT_Lru_Selector)( FT_Lru lru,
FT_LruNode node,
FT_Pointer data );
typedef FT_Bool (*FT_LruNode_SelectFunc)( FT_LruNode node,
FT_Pointer data,
FT_LruList list );
typedef struct FT_LruRec_
/* LRU class */
typedef struct FT_LruList_ClassRec_
{
FT_Lru_Class* clazz;
FT_UInt max_elements;
FT_UInt num_elements;
FT_ListRec elements;
FT_Memory memory;
FT_Pointer user_data;
FT_UInt list_size;
FT_LruList_InitFunc list_init; /* optional */
FT_LruList_DoneFunc list_done; /* optional */
FT_UInt node_size;
FT_LruNode_InitFunc node_init; /* MANDATORY */
FT_LruNode_DoneFunc node_done; /* optional */
FT_LruNode_FlushFunc node_flush; /* optional */
FT_LruNode_CompareFunc node_compare; /* optional */
/* the following fields are only meaningful for static lru containers */
FT_ListRec free_nodes;
FT_LruNode nodes;
} FT_LruList_ClassRec;
} FT_LruRec;
/* the following functions must be exported in the case where applications */
/* would want to write their own cache classes.. */
FT_EXPORT( FT_Error )
FT_LruList_New( FT_LruList_Class clazz,
FT_UInt max_elements,
FT_Pointer user_data,
FT_Memory memory,
FT_LruList *alist );
FT_EXPORT( void )
FT_LruList_Reset( FT_LruList list );
FT_EXPORT( void )
FT_LruList_Destroy ( FT_LruList list );
FT_EXPORT( FT_Error )
FT_Lru_New( const FT_Lru_Class* clazz,
FT_UInt max_elements,
FT_Pointer user_data,
FT_Memory memory,
FT_Bool pre_alloc,
FT_Lru *anlru );
FT_LruList_Lookup( FT_LruList list,
FT_LruKey key,
FT_LruNode *anode );
FT_EXPORT( void )
FT_Lru_Reset( FT_Lru lru );
FT_LruList_Remove( FT_LruList list,
FT_LruNode node );
FT_EXPORT( void )
FT_Lru_Done ( FT_Lru lru );
FT_EXPORT( FT_Error )
FT_Lru_Lookup_Node( FT_Lru lru,
FT_LruKey key,
FT_LruNode *anode );
FT_EXPORT( FT_Error )
FT_Lru_Lookup( FT_Lru lru,
FT_LruKey key,
FT_Pointer *anobject );
FT_EXPORT( void )
FT_Lru_Remove_Node( FT_Lru lru,
FT_LruNode node );
FT_EXPORT( void )
FT_Lru_Remove_Selection( FT_Lru lru,
FT_Lru_Selector selector,
FT_Pointer data );
FT_LruList_Remove_Selection( FT_LruList list,
FT_LruNode_SelectFunc select_func,
FT_Pointer select_data );
/* */
FT_END_HEADER
#endif /* __FTLRU_H__ */

View File

@ -142,6 +142,19 @@ FT_BEGIN_HEADER
} FTC_FontRec;
/* */
#define FTC_FONT_COMPARE(f1,f2) \
( (f1)->face_id == (f2)->face_id && \
(f1)->pix_width == (f2)->pix_width && \
(f1)->pix_height == (f2)->pix_height )
#define FTC_FACE_ID_HASH(i) ((FT_UInt32)(FT_Pointer)(i))
#define FTC_FONT_HASH(f) \
(FT_UInt32)( FTC_FACE_ID_HASH((f)->face_id) ^ \
((f)->pix_width << 8) ^ \
((f)->pix_height) )
/*************************************************************************/
/* */
@ -334,19 +347,6 @@ FT_BEGIN_HEADER
FT_Face *aface,
FT_Size *asize );
/* a cache class is used to describe a unique cache type to the manager */
typedef struct FTC_Cache_Class_ FTC_Cache_Class;
typedef struct FTC_CacheRec_* FTC_Cache;
/* this must be used internally for the moment */
FT_EXPORT( FT_Error )
FTC_Manager_Register_Cache( FTC_Manager manager,
FTC_Cache_Class* clazz,
FTC_Cache *acache );
/* */

View File

@ -1,6 +1,7 @@
#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_MEMORY_H
#include FT_SYSTEM_H
#include FT_ERRORS_H
#include FT_TYPES_H
@ -173,7 +174,7 @@
if ( new_buckets == NULL )
return;
memset( new_buckets, 0, sizeof(FT_MemNode)*new_size );
MEM_Set( new_buckets, 0, sizeof(FT_MemNode)*new_size );
for ( i = 0; i < table->size; i++ )
{
@ -211,7 +212,7 @@
table = memory->alloc( memory, sizeof(*table) );
if ( table == NULL ) goto Exit;
memset( table, 0, sizeof(*table) );
MEM_Set( table, 0, sizeof(*table) );
table->size = FT_MEM_SIZE_MIN;
table->nodes = 0;
@ -226,7 +227,7 @@
table->buckets = memory->alloc( memory, table->size * sizeof(FT_MemNode) );
if ( table->buckets )
memset( table->buckets, 0, sizeof(FT_MemNode)*table->size );
MEM_Set( table->buckets, 0, sizeof(FT_MemNode)*table->size );
else
{
memory->free( memory, table );
@ -408,7 +409,7 @@
/* we simply invert the node's size to indicate that the node */
/* was freed. We also change its content.. */
memset( address, 0xF3, node->size );
MEM_Set( address, 0xF3, node->size );
table->alloc_current -= node->size;
node->size = -node->size;

440
src/cache/ftcchunk.c vendored
View File

@ -34,73 +34,62 @@
/*************************************************************************/
/*************************************************************************/
#define FTC_CSET_HASH(cset,start) \
((FT_UFast)(((cset)->hash << 16) | ((start) & 0xFFFF)))
/* create a new chunk node, setting its cache index and ref count */
FT_EXPORT_DEF( FT_Error )
FTC_ChunkNode_Init( FTC_ChunkNode node,
FTC_ChunkSet cset,
FT_UInt index,
FT_Bool alloc )
ftc_chunk_node_init( FTC_ChunkNode cnode,
FTC_ChunkSet cset,
FT_UInt gindex,
FT_Bool alloc )
{
FTC_Chunk_Cache cache = cset->cache;
FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root );
FT_Error error = 0;
FTC_ChunkCache ccache = cset->ccache;
FT_Error error = 0;
FT_UInt len;
FT_UInt start = (gindex / cset->item_count) * cset->item_count;
cnode->cset = cset;
cnode->node.hash = FTC_CSET_HASH(cset,start);
cnode->item_start = start;
data->cache_index = (FT_UShort)cache->root.cache_index;
data->ref_count = (FT_Short) 0;
node->cset = cset;
node->cset_index = (FT_UShort)index;
node->num_elements = (unsigned short)(
( index + 1 < cset->num_chunks )
? cset->element_count
: cset->element_max - cset->element_count*index );
len = cset->item_total - start;
if ( len > cset->item_count )
len = cset->item_count;
cnode->item_count = len;
if ( alloc )
{
/* allocate elements array */
FT_Memory memory;
memory = cache->root.memory;
error = MEM_Alloc( node->elements,
cset->element_size * cset->element_count );
FT_Memory memory = ccache->cache.memory;
error = MEM_Alloc( cnode->items, cset->item_size * cnode->item_count );
}
if (!error )
cset->num_chunks++;
return error;
}
FT_EXPORT_DEF( void )
FTC_ChunkNode_Destroy( FTC_ChunkNode node )
ftc_chunk_node_done( FTC_ChunkNode cnode )
{
FTC_ChunkSet cset = node->cset;
/* remove from parent set table */
cset->chunks[node->cset_index] = 0;
FTC_ChunkSet cset = cnode->cset;
FT_Memory memory = cset->ccache->cache.memory;
/* destroy the node */
cset->clazz->destroy_node( node );
FREE( cnode->items );
cnode->item_count = 0;
cnode->item_start = 0;
/* remove from parent set table - eventually destroy the set */
if ( --cset->num_chunks <= 0 )
FT_LruList_Remove( cset->ccache->cset_lru, (FT_LruNode) cset );
}
FT_EXPORT_DEF( FT_ULong )
FTC_ChunkNode_Size( FTC_ChunkNode node )
{
FTC_ChunkSet cset = node->cset;
return cset->clazz->size_node( node );
}
FT_CALLBACK_TABLE_DEF
const FTC_CacheNode_Class ftc_chunk_cache_node_class =
{
(FTC_CacheNode_SizeFunc) FTC_ChunkNode_Size,
(FTC_CacheNode_DestroyFunc)FTC_ChunkNode_Destroy
};
/*************************************************************************/
/*************************************************************************/
@ -112,339 +101,88 @@
FT_EXPORT_DEF( FT_Error )
FTC_ChunkSet_New( FTC_Chunk_Cache cache,
FT_Pointer type,
FTC_ChunkSet *aset )
ftc_chunk_set_init( FTC_ChunkSet cset,
FT_UInt item_size,
FT_UInt item_count,
FT_UInt item_total,
FTC_ChunkCache cache )
{
FT_Error error;
FT_Memory memory = cache->root.memory;
FTC_Manager manager = cache->root.manager;
FTC_ChunkSet cset = 0;
cset->ccache = cache;
cset->num_chunks = 0;
FTC_Chunk_Cache_Class* ccache_class;
FTC_ChunkSet_Class* clazz;
cset->item_total = item_total;
cset->item_size = item_size;
cset->item_count = item_count;
ccache_class = (FTC_Chunk_Cache_Class*)cache->root.clazz;
clazz = ccache_class->cset_class;
*aset = 0;
if ( ALLOC( cset, clazz->cset_byte_size ) )
goto Exit;
cset->cache = cache;
cset->manager = manager;
cset->memory = memory;
cset->clazz = clazz;
/* now compute element_max, element_count and element_size */
error = clazz->sizes( cset, type );
if ( error )
goto Exit;
/* compute maximum number of nodes */
cset->num_chunks = ( cset->element_max + cset->element_count - 1 ) /
cset->element_count;
/* allocate chunk pointers table */
if ( ALLOC_ARRAY( cset->chunks, cset->num_chunks, FTC_ChunkNode ) )
goto Exit;
/* initialize set by type if needed */
if ( clazz->init )
{
error = clazz->init( cset, type );
if ( error )
goto Exit;
}
*aset = cset;
Exit:
if ( error && cset )
{
FREE( cset->chunks );
FREE( cset );
}
return error;
return 0;
}
FT_EXPORT_DEF( void )
FTC_ChunkSet_Destroy( FTC_ChunkSet cset )
ftc_chunk_set_done( FTC_ChunkSet cset )
{
FTC_Chunk_Cache cache = cset->cache;
FTC_Manager manager = cache->root.manager;
FT_List glyphs_lru = &manager->global_lru;
FTC_ChunkNode* bucket = cset->chunks;
FTC_ChunkNode* bucket_limit = bucket + cset->num_chunks;
FT_Memory memory = cache->root.memory;
FTC_ChunkSet_Class* clazz = cset->clazz;
/* nothing for now */
FT_UNUSED( cset );
}
/* for each bucket, free the list of glyph nodes */
for ( ; bucket < bucket_limit; bucket++ )
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** CHUNK CACHES *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_DEF( void )
ftc_chunk_cache_done( FTC_ChunkCache ccache )
{
ftc_cache_done( FTC_CACHE(ccache) );
/* simply delete all remaining glyph sets */
if ( ccache->cset_lru )
{
FTC_ChunkNode node = bucket[0];
FT_ListNode lrunode;
if ( node )
{
lrunode = FTC_CHUNKNODE_TO_LRUNODE( node );
manager->num_bytes -= clazz->size_node( node );
manager->num_nodes--;
FT_List_Remove( glyphs_lru, lrunode );
clazz->destroy_node( node );
bucket[0] = 0;
}
FT_LruList_Destroy( ccache->cset_lru );
ccache->cset_lru = NULL;
}
if ( clazz->done )
clazz->done( cset );
FREE( cset->chunks );
FREE( cset );
}
FT_EXPORT_DEF( FT_Error )
FTC_ChunkSet_Lookup_Node( FTC_ChunkSet cset,
FT_UInt glyph_index,
FTC_ChunkNode *anode,
FT_UInt *anindex )
ftc_chunk_cache_init( FTC_ChunkCache ccache,
FT_LruList_Class cset_class )
{
FTC_Chunk_Cache cache = cset->cache;
FTC_Manager manager = cache->root.manager;
FT_Error error = 0;
FT_Error error;
FTC_ChunkSet_Class* clazz = cset->clazz;
*anode = 0;
if ( glyph_index >= cset->element_max )
error = FTC_Err_Invalid_Argument;
else
{
FT_UInt chunk_size = cset->element_count;
FT_UInt chunk_index = glyph_index / chunk_size;
FTC_ChunkNode* pnode = cset->chunks + chunk_index;
FTC_ChunkNode node = *pnode;
if ( !node )
{
/* we didn't found the glyph image; we will now create a new one */
error = clazz->new_node( cset, chunk_index, &node );
if ( error )
goto Exit;
/* store the new chunk in the cset's table */
*pnode = node;
/* insert the node at the start the global LRU glyph list */
FT_List_Insert( &manager->global_lru,
FTC_CHUNKNODE_TO_LRUNODE( node ) );
manager->num_bytes += clazz->size_node( node );
manager->num_nodes++;
if ( manager->num_bytes > manager->max_bytes )
{
FTC_ChunkNode_Ref ( node );
FTC_Manager_Compress( manager );
FTC_ChunkNode_Unref ( node );
}
}
*anode = node;
*anindex = glyph_index - chunk_index * chunk_size;
}
error = ftc_cache_init( FTC_CACHE(ccache) );
if (error) goto Exit;
error = FT_LruList_New( cset_class, 0, ccache,
ccache->cache.memory,
&ccache->cset_lru );
Exit:
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** CHUNK SETS LRU CALLBACKS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
#define FTC_CSET_LRU_GET_CACHE( lru ) \
( (FTC_Chunk_Cache)((lru)->user_data) )
#define FTC_CSET_LRU_GET_MANAGER( lru ) \
FTC_CSET_LRU_GET_CACHE( lru )->manager
#define FTC_LRUNODE_CSET( node ) \
( (FTC_ChunkSet)(node)->root.data )
FT_CALLBACK_DEF( FT_Error )
ftc_chunk_set_lru_init( FT_Lru lru,
FT_LruNode node )
FT_EXPORT_DEF( FT_Error )
ftc_chunk_cache_lookup( FTC_ChunkCache ccache,
FTC_ChunkQuery query,
FTC_ChunkNode *anode )
{
FTC_Chunk_Cache cache = FTC_CSET_LRU_GET_CACHE( lru );
FT_Error error;
FTC_ChunkSet cset;
error = FTC_ChunkSet_New( cache,
(FT_Pointer)node->key,
&cset );
FT_LruNode node;
FT_Error error;
error = FT_LruList_Lookup( ccache->cset_lru, query, &node );
if ( !error )
{
/* good, now set the set index within the set object */
cset->cset_index = (FT_UInt)( node - lru->nodes );
node->root.data = cset;
FTC_ChunkSet cset = FTC_CHUNK_SET(node);
FT_UFast hash = FTC_CSET_HASH( cset, query->gindex );
error = ftc_cache_lookup_node( FTC_CACHE(ccache), hash, query,
FTC_NODE_P(anode) );
}
return error;
}
FT_CALLBACK_DEF( void )
ftc_chunk_set_lru_done( FT_Lru lru,
FT_LruNode node )
{
FTC_ChunkSet cset = FTC_LRUNODE_CSET( node );
FT_UNUSED( lru );
FTC_ChunkSet_Destroy( cset );
}
FT_CALLBACK_DEF( FT_Bool )
ftc_chunk_set_lru_compare( FT_LruNode node,
FT_LruKey key )
{
FTC_ChunkSet cset = FTC_LRUNODE_CSET( node );
return cset->clazz->compare( cset, (FT_Pointer)key );
}
FT_CALLBACK_TABLE_DEF
const FT_Lru_Class ftc_chunk_set_lru_class =
{
sizeof( FT_LruRec ),
ftc_chunk_set_lru_init,
ftc_chunk_set_lru_done,
0, /* no flush */
ftc_chunk_set_lru_compare
};
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** CHUNK CACHE OBJECTS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_DEF( FT_Error )
FTC_Chunk_Cache_Init( FTC_Chunk_Cache cache )
{
FT_Memory memory = cache->root.memory;
FT_Error error;
FTC_Chunk_Cache_Class* ccache_clazz;
/* set up root node_class to be used by manager */
cache->root.node_clazz =
(FTC_CacheNode_Class*)&ftc_chunk_cache_node_class;
/* setup `compare' shortcut */
ccache_clazz = (FTC_Chunk_Cache_Class*)cache->root.clazz;
cache->compare = ccache_clazz->cset_class->compare;
error = FT_Lru_New( &ftc_chunk_set_lru_class,
FTC_MAX_CHUNK_SETS,
cache,
memory,
1, /* pre_alloc == TRUE */
&cache->csets_lru );
return error;
}
FT_EXPORT_DEF( void )
FTC_Chunk_Cache_Done( FTC_Chunk_Cache cache )
{
/* discard glyph sets */
FT_Lru_Done( cache->csets_lru );
}
FT_EXPORT_DEF( FT_Error )
FTC_Chunk_Cache_Lookup( FTC_Chunk_Cache cache,
FT_Pointer type,
FT_UInt gindex,
FTC_ChunkNode *anode,
FT_UInt *aindex )
{
FT_Error error;
FTC_ChunkSet cset;
FTC_ChunkNode node;
FT_UInt cindex;
FTC_Manager manager;
/* check for valid `desc' delayed to FT_Lru_Lookup() */
if ( !cache || !anode || !aindex )
return FTC_Err_Invalid_Argument;
*anode = 0;
*aindex = 0;
cset = cache->last_cset;
if ( !cset || !cache->compare( cset, type ) )
{
error = FT_Lru_Lookup( cache->csets_lru,
(FT_LruKey)type,
(FT_Pointer*)&cset );
cache->last_cset = cset;
if ( error )
goto Exit;
}
error = FTC_ChunkSet_Lookup_Node( cset, gindex, &node, &cindex );
if ( error )
goto Exit;
/* now compress the manager's cache pool if needed */
manager = cache->root.manager;
if ( manager->num_bytes > manager->max_bytes )
{
FTC_ChunkNode_Ref ( node );
FTC_Manager_Compress( manager );
FTC_ChunkNode_Unref ( node );
}
*anode = node;
*aindex = cindex;
Exit:
return error;
}

453
src/cache/ftcglyph.c vendored
View File

@ -35,21 +35,19 @@
/*************************************************************************/
/*************************************************************************/
#define FTC_GSET_HASH(gset,gindex) \
((FT_UFast)(((gset)->hash << 16) | ((gindex) & 0xFFFF)))
/* create a new glyph node, setting its cache index and ref count */
FT_EXPORT_DEF( void )
FTC_GlyphNode_Init( FTC_GlyphNode node,
FTC_GlyphSet gset,
FT_UInt gindex )
ftc_glyph_node_init( FTC_GlyphNode gnode,
FT_UInt gindex,
FTC_GlyphSet gset )
{
FTC_Glyph_Cache cache = gset->cache;
FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root );
data->cache_index = (FT_UShort)cache->root.cache_index;
data->ref_count = (FT_Short) 0;
node->gset_index = (FT_UShort)gset->gset_index;
node->glyph_index = (FT_UShort)gindex;
gnode->gset = gset;
gnode->node.hash = FTC_GSET_HASH( gset, gindex );
gset->num_glyphs++;
}
@ -61,71 +59,15 @@
/* will happen! */
FT_EXPORT_DEF( void )
FTC_GlyphNode_Destroy( FTC_GlyphNode node,
FTC_Glyph_Cache cache )
ftc_glyph_node_done( FTC_GlyphNode gnode )
{
FT_LruNode gset_lru = cache->gsets_lru->nodes + node->gset_index;
FTC_GlyphSet gset = (FTC_GlyphSet)gset_lru->root.data;
FT_UInt hash = node->glyph_index % gset->hash_size;
/* remove the node from its gset's bucket list */
{
FTC_GlyphNode* pnode = gset->buckets + hash;
FTC_GlyphNode cur;
for (;;)
{
cur = *pnode;
if ( !cur )
{
/* this should never happen */
FT_ERROR(( "FTC_GlyphNode_Destroy:"
" trying to delete an unlisted node!" ));
return;
}
if ( cur == node )
{
*pnode = cur->gset_next;
break;
}
pnode = &cur->gset_next;
}
}
/* destroy the node */
gset->clazz->destroy_node( node, gset );
FTC_GlyphSet gset = gnode->gset;
if ( --gset->num_glyphs <= 0 )
FT_LruList_Remove( gset->gcache->gset_lru, (FT_LruNode) gset );
}
/* Important: This function is called from the cache manager to */
/* size a given cache node during `cache compression'. The */
/* second argument is always `cache.user_data'. Thus be */
/* certain that the function FTC_Glyph_Cache_New() does indeed */
/* set its `user_data' field correctly, otherwise bad things */
/* will happen! */
FT_EXPORT_DEF( FT_ULong )
FTC_GlyphNode_Size( FTC_GlyphNode node,
FTC_Glyph_Cache cache )
{
FT_LruNode gset_lru = cache->gsets_lru->nodes + node->gset_index;
FTC_GlyphSet gset = (FTC_GlyphSet)gset_lru->root.data;
return gset->clazz->size_node( node, gset );
}
FT_CALLBACK_TABLE_DEF
const FTC_CacheNode_Class ftc_glyph_cache_node_class =
{
(FTC_CacheNode_SizeFunc) FTC_GlyphNode_Size,
(FTC_CacheNode_DestroyFunc)FTC_GlyphNode_Destroy
};
/*************************************************************************/
/*************************************************************************/
@ -135,347 +77,88 @@
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_DEF( FT_Error )
FTC_GlyphSet_New( FTC_Glyph_Cache cache,
FT_Pointer type,
FTC_GlyphSet *aset )
ftc_glyph_set_init( FTC_GlyphSet gset,
FT_LruList lru )
{
FT_Error error;
FT_Memory memory = cache->root.memory;
FTC_Manager manager = cache->root.manager;
FTC_GlyphSet gset = 0;
FTC_Glyph_Cache_Class* gcache_class;
FTC_GlyphSet_Class* clazz;
gcache_class = (FTC_Glyph_Cache_Class*)cache->root.clazz;
clazz = gcache_class->gset_class;
*aset = 0;
if ( ALLOC( gset, clazz->gset_byte_size ) )
goto Exit;
gset->cache = cache;
gset->manager = manager;
gset->memory = memory;
gset->hash_size = FTC_GSET_HASH_SIZE_DEFAULT;
gset->clazz = clazz;
/* allocate buckets table */
if ( ALLOC_ARRAY( gset->buckets, gset->hash_size, FTC_GlyphNode ) )
goto Exit;
/* initialize gset by type if needed */
if ( clazz->init )
{
error = clazz->init( gset, type );
if ( error )
goto Exit;
}
*aset = gset;
Exit:
if ( error && gset )
{
FREE( gset->buckets );
FREE( gset );
}
return error;
}
FTC_GlyphCache gcache = lru->user_data;
gset->gcache = gcache;
gset->num_glyphs = 0;
return 0;
}
FT_EXPORT_DEF( void )
FTC_GlyphSet_Destroy( FTC_GlyphSet gset )
ftc_glyph_set_done( FTC_GlyphSet gset )
{
FTC_Glyph_Cache cache = gset->cache;
FTC_Manager manager = cache->root.manager;
FT_List glyphs_lru = &manager->global_lru;
FTC_GlyphNode* bucket = gset->buckets;
FTC_GlyphNode* bucket_limit = bucket + gset->hash_size;
FT_Memory memory = cache->root.memory;
FTC_GlyphSet_Class* clazz = gset->clazz;
/* for now, nothing to be done here */
FT_UNUSED(gset);
}
/* for each bucket, free the list of glyph nodes */
for ( ; bucket < bucket_limit; bucket++ )
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH CACHES *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_DEF( void )
ftc_glyph_cache_done( FTC_GlyphCache gcache )
{
/* remove all nodes in the cache */
ftc_cache_done( &gcache->cache );
/* simply delete all remaining glyph sets */
if ( gcache->gset_lru )
{
FTC_GlyphNode node = bucket[0];
FTC_GlyphNode next = 0;
FT_ListNode lrunode;
for ( ; node; node = next )
{
next = node->gset_next;
lrunode = FTC_GLYPHNODE_TO_LRUNODE( node );
manager->num_bytes -= clazz->size_node( node, gset );
manager->num_nodes--;
FT_List_Remove( glyphs_lru, lrunode );
clazz->destroy_node( node, gset );
}
bucket[0] = 0;
FT_LruList_Destroy( gcache->gset_lru );
gcache->gset_lru = NULL;
}
if ( clazz->done )
clazz->done( gset );
FREE( gset->buckets );
FREE( gset );
}
FT_EXPORT_DEF( FT_Error )
FTC_GlyphSet_Lookup_Node( FTC_GlyphSet gset,
FT_UInt glyph_index,
FTC_GlyphNode *anode )
ftc_glyph_cache_init( FTC_GlyphCache gcache,
FT_LruList_Class gset_class )
{
FTC_Glyph_Cache cache = gset->cache;
FTC_Manager manager = cache->root.manager;
FT_UInt hash_index = glyph_index % gset->hash_size;
FTC_GlyphNode* bucket = gset->buckets + hash_index;
FTC_GlyphNode* pnode = bucket;
FTC_GlyphNode node;
FT_Error error;
FTC_GlyphSet_Class* clazz = gset->clazz;
*anode = 0;
for ( ;; )
{
node = *pnode;
if ( !node )
break;
if ( (FT_UInt)node->glyph_index == glyph_index )
{
/* we found it! -- move glyph to start of the lists */
*pnode = node->gset_next;
node->gset_next = bucket[0];
bucket[0] = node;
FT_List_Up( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
*anode = node;
return 0;
}
/* go to next node in bucket */
pnode = &node->gset_next;
}
/* we didn't found the glyph image, we will now create a new one */
error = clazz->new_node( gset, glyph_index, &node );
if ( error )
goto Exit;
/* insert the node at the start of our bucket list */
node->gset_next = bucket[0];
bucket[0] = node;
/* insert the node at the start the global LRU glyph list */
FT_List_Insert( &manager->global_lru, FTC_GLYPHNODE_TO_LRUNODE( node ) );
manager->num_bytes += clazz->size_node( node, gset );
manager->num_nodes++;
if ( manager->num_bytes > manager->max_bytes )
{
FTC_GlyphNode_Ref ( node );
FTC_Manager_Compress( manager );
FTC_GlyphNode_Unref ( node );
}
*anode = node;
FT_Error error;
error = ftc_cache_init( FTC_CACHE(gcache) );
if (error) goto Exit;
error = FT_LruList_New( gset_class, 0, gcache,
gcache->cache.memory,
&gcache->gset_lru );
Exit:
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH SETS LRU CALLBACKS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
#define FTC_GSET_LRU_GET_CACHE( lru ) \
( (FTC_Glyph_Cache)(lru)->user_data )
#define FTC_GSET_LRU_GET_MANAGER( lru ) \
FTC_GSET_LRU_GET_CACHE( lru )->manager
#define FTC_LRUNODE_GSET( node ) \
( (FTC_GlyphSet)(node)->root.data )
FT_CALLBACK_DEF( FT_Error )
ftc_glyph_set_lru_init( FT_Lru lru,
FT_LruNode node )
FT_EXPORT_DEF( FT_Error )
ftc_glyph_cache_lookup( FTC_GlyphCache gcache,
FTC_GlyphQuery query,
FTC_GlyphNode *anode )
{
FTC_Glyph_Cache cache = FTC_GSET_LRU_GET_CACHE( lru );
FT_Error error;
FTC_GlyphSet gset;
error = FTC_GlyphSet_New( cache, (FT_Pointer)node->key, &gset );
FT_LruNode node;
FT_Error error;
error = FT_LruList_Lookup( gcache->gset_lru, query, &node );
if ( !error )
{
/* good, now set the gset index within the gset object */
gset->gset_index = (FT_UInt)( node - lru->nodes );
node->root.data = gset;
FTC_GlyphSet gset = (FTC_GlyphSet) node;
FT_UFast hash = FTC_GSET_HASH( gset, query->gindex );
error = ftc_cache_lookup_node( FTC_CACHE(gcache), hash, query,
FTC_NODE_P(anode) );
}
return error;
}
FT_CALLBACK_DEF( void )
ftc_glyph_set_lru_done( FT_Lru lru,
FT_LruNode node )
{
FTC_GlyphSet gset = FTC_LRUNODE_GSET( node );
FT_UNUSED( lru );
FTC_GlyphSet_Destroy( gset );
}
FT_CALLBACK_DEF( FT_Bool )
ftc_glyph_set_lru_compare( FT_LruNode node,
FT_LruKey key )
{
FTC_GlyphSet gset = FTC_LRUNODE_GSET( node );
return gset->clazz->compare( gset, (FT_Pointer)key );
}
FT_CALLBACK_TABLE_DEF
const FT_Lru_Class ftc_glyph_set_lru_class =
{
sizeof( FT_LruRec ),
ftc_glyph_set_lru_init,
ftc_glyph_set_lru_done,
0, /* no flush */
ftc_glyph_set_lru_compare
};
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** GLYPH CACHE OBJECTS *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
FT_EXPORT_DEF( FT_Error )
FTC_Glyph_Cache_Init( FTC_Glyph_Cache cache )
{
FT_Memory memory = cache->root.memory;
FT_Error error;
FTC_Glyph_Cache_Class* gcache_clazz;
/* set up root node_class to be used by manager */
cache->root.node_clazz =
(FTC_CacheNode_Class*)&ftc_glyph_cache_node_class;
/* setup the `compare' shortcut */
gcache_clazz = (FTC_Glyph_Cache_Class*)cache->root.clazz;
cache->compare = gcache_clazz->gset_class->compare;
/* The following is extremely important for ftc_destroy_glyph_image() */
/* to work properly, as the second parameter that is sent to it */
/* through the cache manager is `cache_data' and must be set to */
/* `cache' here. */
/* */
cache->root.cache_data = cache;
error = FT_Lru_New( &ftc_glyph_set_lru_class,
FTC_MAX_GLYPH_SETS,
cache,
memory,
1, /* pre_alloc == TRUE */
&cache->gsets_lru );
return error;
}
FT_EXPORT_DEF( void )
FTC_Glyph_Cache_Done( FTC_Glyph_Cache cache )
{
/* discard glyph sets */
FT_Lru_Done( cache->gsets_lru );
}
FT_EXPORT_DEF( FT_Error )
FTC_Glyph_Cache_Lookup( FTC_Glyph_Cache cache,
FT_Pointer type,
FT_UInt gindex,
FTC_GlyphNode *anode )
{
FT_Error error;
FTC_GlyphSet gset;
FTC_GlyphNode node;
FTC_Manager manager;
/* check for valid `desc' delayed to FT_Lru_Lookup() */
if ( !cache || !anode )
return FTC_Err_Invalid_Argument;
*anode = 0;
gset = cache->last_gset;
if ( !gset || !cache->compare( gset, type ) )
{
error = FT_Lru_Lookup( cache->gsets_lru,
(FT_LruKey)type,
(FT_Pointer*)&gset );
cache->last_gset = gset;
if ( error )
goto Exit;
}
error = FTC_GlyphSet_Lookup_Node( gset, gindex, &node );
if ( error )
goto Exit;
/* now compress the manager's cache pool if needed */
manager = cache->root.manager;
if ( manager->num_bytes > manager->max_bytes )
{
FTC_GlyphNode_Ref ( node );
FTC_Manager_Compress( manager );
FTC_GlyphNode_Unref ( node );
}
*anode = node;
Exit:
return error;
}
/* END */

252
src/cache/ftcimage.c vendored
View File

@ -19,6 +19,7 @@
#include <ft2build.h>
#include FT_CACHE_H
#include FT_CACHE_IMAGE_H
#include FT_CACHE_INTERNAL_GLYPH_H
#include FT_INTERNAL_MEMORY_H
#include "ftcerror.h"
@ -27,30 +28,36 @@
#include <stdlib.h> /* labs() */
/* the FT_Glyph image `glyph node' type */
typedef struct FTC_GlyphImageRec_
/* the FT_Glyph image node type */
typedef struct FTC_ImageNodeRec_
{
FTC_GlyphNodeRec root;
FT_Glyph ft_glyph;
FTC_GlyphNodeRec gnode;
FT_Glyph glyph;
} FTC_GlyphImageRec, *FTC_GlyphImage;
} FTC_ImageNodeRec, *FTC_ImageNode;
#define FTC_IMAGE_NODE(x) ((FTC_ImageNode)(x))
#define FTC_IMAGE_NODE_GINDEX(x) FTC_GLYPH_NODE_GINDEX(x)
/* the glyph image queue type */
/* the glyph image set type */
typedef struct FTC_ImageSetRec_
{
FTC_GlyphSetRec root;
FTC_GlyphSetRec gset;
FTC_Image_Desc description;
} FTC_ImageSetRec, *FTC_ImageSet;
#define FTC_IMAGE_SET(x) ((FTC_ImageSet)(x))
typedef struct FTC_Image_CacheRec_
#define FTC_IMAGE_SET_MEMORY(x) FTC_GLYPH_SET_MEMORY(&(x)->gset)
typedef struct FTC_ImageQueryRec_
{
FTC_Glyph_CacheRec root;
} FTC_Image_CacheRec;
FTC_GlyphQueryRec glyph;
FTC_Image_Desc desc;
} FTC_ImageQueryRec, *FTC_ImageQuery;
/*************************************************************************/
@ -62,47 +69,41 @@
/*************************************************************************/
/* finalize a given glyph image node */
FT_CALLBACK_DEF( void )
ftc_glyph_image_node_destroy( FTC_GlyphImage node,
FTC_GlyphSet gset )
ftc_image_node_done( FTC_ImageNode inode )
{
FT_Memory memory = gset->memory;
FT_Done_Glyph( node->ft_glyph );
FREE( node );
if (inode->glyph)
{
FT_Done_Glyph( inode->glyph );
inode->glyph = NULL;
}
}
/* initialize a new glyph image node */
FT_CALLBACK_DEF( FT_Error )
ftc_glyph_image_node_new( FTC_GlyphSet gset,
FT_UInt glyph_index,
FTC_GlyphImage *anode )
ftc_image_node_init( FTC_ImageNode inode,
FTC_GlyphQuery query )
{
FT_Memory memory = gset->memory;
FTC_ImageSet imageset = (FTC_ImageSet)gset;
FTC_ImageSet iset = FTC_IMAGE_SET( query->gset );
FT_Memory memory = FTC_IMAGE_SET_MEMORY( iset );
FT_Error error;
FTC_GlyphImage node = 0;
FT_Face face;
FT_Size size;
/* allocate node */
if ( ALLOC( node, sizeof ( *node ) ) )
goto Exit;
/* initialize its inner fields */
FTC_GlyphNode_Init( FTC_GLYPHNODE( node ), gset, glyph_index );
ftc_glyph_node_init( FTC_GLYPH_NODE(inode), query->gindex, query->gset );
/* we will now load the glyph image */
error = FTC_Manager_Lookup_Size( gset->manager,
&imageset->description.font,
error = FTC_Manager_Lookup_Size( iset->gset.gcache->cache.manager,
&iset->description.font,
&face, &size );
if ( !error )
{
FT_UInt gindex = node->root.glyph_index;
FT_UInt gindex = FTC_GLYPH_NODE_GINDEX(inode);
FT_UInt load_flags = FT_LOAD_DEFAULT;
FT_UInt image_type = imageset->description.image_type;
FT_UInt image_type = iset->description.image_type;
if ( FTC_IMAGE_FORMAT( image_type ) == ftc_image_format_bitmap )
@ -142,31 +143,29 @@
error = FT_Get_Glyph( face->glyph, &glyph );
if ( !error )
node->ft_glyph = glyph;
{
inode->glyph = glyph;
goto Exit;
}
}
else
error = FTC_Err_Invalid_Argument;
}
}
/* in case of error */
ftc_glyph_node_done( FTC_GLYPH_NODE(inode) );
Exit:
if ( error && node )
FREE( node );
*anode = node;
return error;
}
/* this function is important because it is both part of */
/* an FTC_GlyphSet_Class and an FTC_CacheNode_Class */
/* */
FT_CALLBACK_DEF( FT_ULong )
ftc_glyph_image_node_size( FTC_GlyphImage node )
ftc_image_node_weight( FTC_ImageNode inode )
{
FT_ULong size = 0;
FT_Glyph glyph = node->ft_glyph;
FT_Glyph glyph = inode->glyph;
switch ( glyph->format )
{
@ -198,11 +197,27 @@
;
}
size += sizeof ( *node );
size += sizeof ( *inode );
return size;
}
/* this function assumes that the desired node's glyph set has been */
/* set by a previous call to ftc_image_set_compare.. */
/* */
FT_CALLBACK_DEF( FT_Bool )
ftc_image_node_compare( FTC_ImageNode inode,
FTC_ImageQuery iquery )
{
FTC_ImageSet iset = FTC_IMAGE_SET(inode->gnode.gset);
/* only if same glyph index and image set description */
return FT_BOOL( iquery->glyph.gindex == FTC_IMAGE_NODE_GINDEX(inode) &&
iquery->glyph.gset == inode->gnode.gset );
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
@ -214,33 +229,47 @@
FT_CALLBACK_DEF( FT_Error )
ftc_image_set_init( FTC_ImageSet iset,
FTC_Image_Desc* type )
FTC_ImageQuery query,
FT_LruList lru )
{
iset->description = *type;
ftc_glyph_set_init( &iset->gset, lru );
iset->description = query->desc;
/* now compute hash from description - this is _very_ important */
iset->gset.hash = FTC_IMAGE_DESC_HASH(&query->desc);
query->glyph.gset = FTC_GLYPH_SET(iset);
return 0;
}
FT_CALLBACK_DEF( FT_Bool )
ftc_image_set_compare( FTC_ImageSet iset,
FTC_Image_Desc* type )
ftc_image_set_compare( FTC_ImageSet iset,
FTC_ImageQuery iquery )
{
return FT_BOOL( !memcmp( &iset->description, type, sizeof ( *type ) ) );
FT_Bool result;
/* we must set iquery.glyph.gset for faster glyph node comparisons */
result = FT_BOOL( FTC_IMAGE_DESC_COMPARE( &iset->description, &iquery->desc ) );
if ( result )
iquery->glyph.gset = &iset->gset;
return result;
}
FT_CALLBACK_TABLE_DEF
const FTC_GlyphSet_Class ftc_glyph_image_set_class =
const FT_LruList_ClassRec ftc_image_set_class =
{
sizeof( FT_LruListRec ),
(FT_LruList_InitFunc) NULL,
(FT_LruList_DoneFunc) NULL,
sizeof( FTC_ImageSetRec ),
(FTC_GlyphSet_InitFunc) ftc_image_set_init,
(FTC_GlyphSet_DoneFunc) 0,
(FTC_GlyphSet_CompareFunc) ftc_image_set_compare,
(FTC_GlyphSet_NewNodeFunc) ftc_glyph_image_node_new,
(FTC_GlyphSet_SizeNodeFunc) ftc_glyph_image_node_size,
(FTC_GlyphSet_DestroyNodeFunc)ftc_glyph_image_node_destroy
(FT_LruNode_InitFunc) ftc_image_set_init,
(FT_LruNode_DoneFunc) ftc_glyph_set_init,
(FT_LruNode_FlushFunc) NULL,
(FT_LruNode_CompareFunc) ftc_image_set_compare
};
@ -253,15 +282,25 @@
/*************************************************************************/
FT_CALLBACK_TABLE_DEF
const FTC_Glyph_Cache_Class ftc_glyph_image_cache_class =
FT_CALLBACK_DEF( FT_Error )
ftc_image_cache_init( FTC_Image_Cache cache )
{
{
sizeof( FTC_Image_CacheRec ),
(FTC_Cache_InitFunc) FTC_Glyph_Cache_Init,
(FTC_Cache_DoneFunc) FTC_Glyph_Cache_Done
},
(FTC_GlyphSet_Class*) &ftc_glyph_image_set_class
return ftc_glyph_cache_init( (FTC_GlyphCache) cache, &ftc_image_set_class );
}
FT_CALLBACK_TABLE_DEF
const FTC_Cache_ClassRec ftc_image_cache_class =
{
sizeof( FTC_GlyphCacheRec ),
(FTC_Cache_InitFunc) ftc_image_cache_init,
(FTC_Cache_DoneFunc) ftc_glyph_cache_done,
sizeof( FTC_ImageNodeRec ),
(FTC_Node_InitFunc) ftc_image_node_init,
(FTC_Node_WeightFunc) ftc_image_node_weight,
(FTC_Node_CompareFunc) ftc_image_node_compare,
(FTC_Node_DoneFunc) ftc_image_node_done
};
@ -273,36 +312,73 @@
{
return FTC_Manager_Register_Cache(
manager,
(FTC_Cache_Class*)&ftc_glyph_image_cache_class,
(FTC_Cache*)acache );
(FTC_Cache_Class) &ftc_image_cache_class,
FTC_CACHE_P(acache) );
}
/* documentation is in ftcimage.h */
FT_EXPORT_DEF( FT_Error )
FTC_Image_Cache_Lookup( FTC_Image_Cache cache,
FTC_Image_Cache_Acquire( FTC_Image_Cache cache,
FTC_Image_Desc* desc,
FT_UInt gindex,
FT_Glyph *aglyph,
FTC_Node *anode )
{
FTC_ImageQueryRec query;
FTC_ImageNode node;
FT_Error error;
/* some argument checks are delayed to ftc_glyph_cache_lookup */
if ( !cache || !desc || !aglyph )
return FTC_Err_Invalid_Argument;
*aglyph = NULL;
if ( anode )
*anode = NULL;
query.glyph.gindex = gindex;
query.glyph.gset = NULL;
query.desc = *desc;
error = ftc_glyph_cache_lookup( FTC_GLYPH_CACHE(cache),
&query.glyph,
(FTC_GlyphNode*) &node );
if (!error)
{
*aglyph = node->glyph;
if (anode)
{
*anode = (FTC_Node) node;
FTC_NODE(node)->ref_count++;
}
}
return error;
}
FT_EXPORT_DEF( void )
FTC_Image_Cache_Release( FTC_Image_Cache icache,
FTC_Node node )
{
ftc_node_unref( node, FTC_CACHE(icache) );
}
FT_EXPORT_DEF( FT_Error )
FTC_Image_Cache_Lookup( FTC_Image_Cache icache,
FTC_Image_Desc* desc,
FT_UInt gindex,
FT_Glyph *aglyph )
{
FT_Error error;
FTC_GlyphNode node;
/* some argument checks are delayed to FTC_Glyph_Cache_Lookup */
if ( !aglyph )
return FTC_Err_Invalid_Argument;
error = FTC_Glyph_Cache_Lookup( (FTC_Glyph_Cache)cache,
desc, gindex, &node );
if ( !error )
*aglyph = ((FTC_GlyphImage)node)->ft_glyph;
return error;
return FTC_Image_Cache_Acquire( icache, desc, gindex, aglyph, NULL );
}
/* END */

895
src/cache/ftcmanag.c vendored

File diff suppressed because it is too large Load Diff

404
src/cache/ftcsbits.c vendored
View File

@ -19,6 +19,7 @@
#include <ft2build.h>
#include FT_CACHE_H
#include FT_CACHE_SMALL_BITMAPS_H
#include FT_CACHE_INTERNAL_CHUNK_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_ERRORS_H
@ -28,23 +29,31 @@
#include <string.h> /* memcmp() */
#define FTC_SBITSET_ELEMENT_COUNT 16
#define FTC_SBIT_ITEMS_PER_NODE 16
/* handle to sbit set */
typedef struct FTC_SBitSetRec_* FTC_SBitSet;
/* sbit set structure */
typedef struct FTC_SBitSetRec_
{
FTC_ChunkSetRec root;
FTC_ChunkSetRec cset;
FTC_Image_Desc desc;
} FTC_SBitSetRec, *FTC_SBitSet;
} FTC_SBitSetRec;
#define FTC_SBIT_SET(x) ((FTC_SBitSet)(x))
#define FTC_SBIT_SET_MEMORY(x) FTC_CHUNK_SET_MEMORY(&(x)->cset)
typedef struct FTC_SBit_CacheRec_
typedef struct FTC_SBitQueryRec_
{
FTC_Chunk_CacheRec root;
} FTC_SBit_CacheRec;
FTC_ChunkQueryRec chunk;
FTC_Image_Desc desc;
} FTC_SBitQueryRec, *FTC_SBitQuery;
/*************************************************************************/
@ -57,77 +66,78 @@
FT_CALLBACK_DEF( void )
ftc_sbit_chunk_node_destroy( FTC_ChunkNode node )
ftc_sbit_node_done( FTC_ChunkNode cnode )
{
FTC_ChunkSet cset = node->cset;
FT_Memory memory = cset->memory;
FT_UInt count = node->num_elements;
FTC_SBit sbit = (FTC_SBit)node->elements;
FTC_ChunkSet cset = cnode->cset;
FT_Memory memory = cset->ccache->cache.memory;
FT_UInt count = cnode->item_count;
FTC_SBit sbit = (FTC_SBit) cnode->items;
if ( sbit )
{
for ( ; count > 0; sbit++, count-- )
FREE( sbit->buffer );
for ( ; count > 0; sbit++, count-- )
FREE( sbit->buffer );
FREE( cnode->items );
}
FREE( node->elements );
FREE( node );
ftc_chunk_node_done( cnode );
}
FT_CALLBACK_DEF( FT_Error )
ftc_bitmap_copy( FT_Memory memory,
FT_Bitmap* source,
FTC_SBit target )
static FT_Error
ftc_sbit_set_bitmap( FTC_SBit sbit,
FT_Bitmap* bitmap,
FT_Memory memory )
{
FT_Error error;
FT_Int pitch = source->pitch;
FT_Int pitch = bitmap->pitch;
FT_ULong size;
if ( pitch < 0 )
pitch = -pitch;
size = (FT_ULong)( pitch * source->rows );
size = (FT_ULong)( pitch * bitmap->rows );
if ( !ALLOC( target->buffer, size ) )
MEM_Copy( target->buffer, source->buffer, size );
if ( !ALLOC( sbit->buffer, size ) )
MEM_Copy( sbit->buffer, bitmap->buffer, size );
return error;
}
FT_CALLBACK_DEF( FT_Error )
ftc_sbit_chunk_node_new( FTC_ChunkSet cset,
FT_UInt index,
FTC_ChunkNode *anode )
static FT_Error
ftc_sbit_node_load( FTC_ChunkNode cnode,
FT_UInt gindex,
FT_ULong *asize )
{
FT_Error error;
FT_Memory memory = cset->memory;
FTC_SBitSet sbitset = (FTC_SBitSet)cset;
FTC_ChunkNode node = 0;
FTC_ChunkSet cset = cnode->cset;
FTC_SBitSet sbitset = FTC_SBIT_SET(cset);
FT_Memory memory = FTC_SBIT_SET_MEMORY(sbitset);
FT_Face face;
FT_Size size;
FTC_SBit sbit;
/* allocate node */
if ( ALLOC( node, sizeof ( *node ) ) )
goto Exit;
if ( gindex < (FT_UInt)cnode->item_start ||
gindex >= (FT_UInt)cnode->item_start + cnode->item_count )
{
FT_ERROR(( "FreeType.cache.sbit_load: invalid glyph index" ));
return FTC_Err_Invalid_Argument;
}
/* initialize its inner fields */
error = FTC_ChunkNode_Init( node, cset, index, 1 );
if ( error )
goto Exit;
sbit = (FTC_SBit)cnode->items + (gindex - cnode->item_start);
/* we will now load all glyph images for this chunk */
error = FTC_Manager_Lookup_Size( cset->manager,
error = FTC_Manager_Lookup_Size( cset->ccache->cache.manager,
&sbitset->desc.font,
&face, &size );
if ( !error )
{
FT_UInt glyph_index = index * cset->element_count;
FT_UInt load_flags = FT_LOAD_DEFAULT;
FT_UInt image_type = sbitset->desc.image_type;
FT_UInt count = node->num_elements;
FTC_SBit sbit = (FTC_SBit)node->elements;
/* determine load flags, depending on the font description's */
@ -144,7 +154,7 @@
}
else
{
FT_ERROR(( "FTC_SBit_Cache: cannot load scalable glyphs in an"
FT_ERROR(( "FreeType.cache.sbit_load: cannot load scalable glyphs in an"
" sbit cache, please check your arguments!\n" ));
error = FTC_Err_Invalid_Argument;
goto Exit;
@ -159,102 +169,126 @@
if ( image_type & ftc_image_flag_autohinted )
load_flags |= FT_LOAD_FORCE_AUTOHINT;
/* load a chunk of small bitmaps in a row */
for ( ; count > 0; count--, glyph_index++, sbit++ )
/* by default, indicates a `missing' glyph */
sbit->buffer = 0;
error = FT_Load_Glyph( face, gindex, load_flags );
if ( !error )
{
/* by default, indicates a `missing' glyph */
sbit->buffer = 0;
error = FT_Load_Glyph( face, glyph_index, load_flags );
if ( !error )
{
FT_Int temp;
FT_GlyphSlot slot = face->glyph;
FT_Bitmap* bitmap = &slot->bitmap;
FT_Int xadvance, yadvance;
FT_Int temp;
FT_GlyphSlot slot = face->glyph;
FT_Bitmap* bitmap = &slot->bitmap;
FT_Int xadvance, yadvance;
/* check that our values fit into 8-bit containers! */
/* If this is not the case, our bitmap is too large */
/* and we will leave it as `missing' with sbit.buffer = 0 */
/* check that our values fit into 8-bit containers! */
/* If this is not the case, our bitmap is too large */
/* and we will leave it as `missing' with sbit.buffer = 0 */
#define CHECK_CHAR( d ) ( temp = (FT_Char)d, temp == d )
#define CHECK_BYTE( d ) ( temp = (FT_Byte)d, temp == d )
/* XXX: FIXME: add support for vertical layouts maybe */
/* XXX: FIXME: add support for vertical layouts maybe */
/* horizontal advance in pixels */
xadvance = ( slot->metrics.horiAdvance + 32 ) >> 6;
yadvance = ( slot->metrics.vertAdvance + 32 ) >> 6;
/* horizontal advance in pixels */
xadvance = ( slot->metrics.horiAdvance + 32 ) >> 6;
yadvance = ( slot->metrics.vertAdvance + 32 ) >> 6;
if ( CHECK_BYTE( bitmap->rows ) &&
CHECK_BYTE( bitmap->width ) &&
CHECK_CHAR( bitmap->pitch ) &&
CHECK_CHAR( slot->bitmap_left ) &&
CHECK_CHAR( slot->bitmap_top ) &&
CHECK_CHAR( xadvance ) &&
CHECK_CHAR( yadvance ) )
if ( CHECK_BYTE( bitmap->rows ) &&
CHECK_BYTE( bitmap->width ) &&
CHECK_CHAR( bitmap->pitch ) &&
CHECK_CHAR( slot->bitmap_left ) &&
CHECK_CHAR( slot->bitmap_top ) &&
CHECK_CHAR( xadvance ) &&
CHECK_CHAR( yadvance ) )
{
sbit->width = (FT_Byte) bitmap->width;
sbit->height = (FT_Byte) bitmap->rows;
sbit->pitch = (FT_Char) bitmap->pitch;
sbit->left = (FT_Char) slot->bitmap_left;
sbit->top = (FT_Char) slot->bitmap_top;
sbit->xadvance = (FT_Char) xadvance;
sbit->yadvance = (FT_Char) yadvance;
sbit->format = (FT_Byte) bitmap->pixel_mode;
/* grab the bitmap when possible - this is a hack !! */
if ( slot->flags & ft_glyph_own_bitmap )
{
sbit->width = (FT_Byte)bitmap->width;
sbit->height = (FT_Byte)bitmap->rows;
sbit->pitch = (FT_Char)bitmap->pitch;
sbit->left = (FT_Char)slot->bitmap_left;
sbit->top = (FT_Char)slot->bitmap_top;
sbit->xadvance = (FT_Char)xadvance;
sbit->yadvance = (FT_Char)yadvance;
sbit->format = (FT_Byte)bitmap->pixel_mode;
/* grab the bitmap when possible */
if ( slot->flags & ft_glyph_own_bitmap )
{
slot->flags &= ~ft_glyph_own_bitmap;
sbit->buffer = bitmap->buffer;
}
else
{
/* copy the bitmap into a new buffer -- ignore error */
ftc_bitmap_copy( memory, bitmap, sbit );
}
slot->flags &= ~ft_glyph_own_bitmap;
sbit->buffer = bitmap->buffer;
}
}
}
else
{
/* copy the bitmap into a new buffer -- ignore error */
error = ftc_sbit_set_bitmap( sbit, bitmap, memory );
}
/* now, compute size */
if ( asize )
*asize = sizeof( FTC_SBitRec ) + ABS(sbit->pitch) * sbit->height;
} /* glyph dimensions ok */
} /* glyph loading successful */
/* ignore the errors that might have occurred -- */
/* we recognize unloaded glyphs with `sbit.buffer == 0' */
error = 0;
/* and 'width == 255', 'height == 0' */
/* */
if ( error )
{
sbit->width = 255;
error = 0;
/* sbit->buffer == NULL too !! */
}
}
Exit:
if ( error && node )
{
FREE( node->elements );
FREE( node );
}
return error;
}
*anode = node;
FT_CALLBACK_DEF( FT_Error )
ftc_sbit_node_init( FTC_ChunkNode cnode,
FTC_ChunkQuery query )
{
FT_Error error;
error = ftc_chunk_node_init( cnode,
query->cset,
query->gindex,
TRUE );
if ( !error )
{
error = ftc_sbit_node_load( cnode, query->gindex, NULL );
if ( error )
ftc_chunk_node_done( cnode );
}
return error;
}
/* this function is important because it is both part of */
/* an FTC_ChunkSet_Class and an FTC_CacheNode_Class */
/* */
FT_CALLBACK_DEF( FT_ULong )
ftc_sbit_chunk_node_size( FTC_ChunkNode node )
ftc_sbit_node_weight( FTC_ChunkNode cnode )
{
FT_ULong size;
FTC_ChunkSet cset = node->cset;
FT_UInt count = node->num_elements;
FTC_ChunkSet cset = cnode->cset;
FT_UInt count = cnode->item_count;
FT_Int pitch;
FTC_SBit sbit = (FTC_SBit)node->elements;
FTC_SBit sbit = (FTC_SBit) cnode->items;
/* the node itself */
size = sizeof ( *node );
size = sizeof ( *cnode );
/* the sbit records */
size += cset->element_count * sizeof ( FTC_SBitRec );
size += cnode->item_count * sizeof ( FTC_SBitRec );
for ( ; count > 0; count--, sbit++ )
{
@ -273,6 +307,34 @@
}
FT_CALLBACK_DEF( FT_Bool )
ftc_sbit_node_compare( FTC_ChunkNode cnode,
FTC_SBitQuery query,
FTC_Cache cache )
{
FTC_ChunkQuery creq = &query->chunk;
FT_UInt gindex = query->chunk.gindex;
FT_UInt offset = (FT_UInt)(gindex - cnode->item_start);
FT_Bool result;
result = FT_BOOL( offset < (FT_UInt)cnode->item_count &&
creq->cset == cnode->cset );
if ( result )
{
/* check if we need to load the glyph bitmap now */
FTC_SBit sbit = (FTC_SBit)cnode->items + offset;
if ( sbit->buffer == NULL && sbit->width != 255 )
{
FT_ULong size;
ftc_sbit_node_load( cnode, gindex, &size );
cache->manager->cur_weight += size;
}
}
return result;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
@ -283,60 +345,74 @@
FT_CALLBACK_DEF( FT_Error )
ftc_sbit_chunk_set_sizes( FTC_ChunkSet cset,
FTC_Image_Desc* desc )
ftc_sbit_set_init( FTC_SBitSet sset,
FTC_SBitQuery query,
FT_LruList lru )
{
FT_Error error;
FT_Face face;
FTC_ChunkCache ccache = lru->user_data;
FTC_Manager manager = ccache->cache.manager;
FT_Error error;
FT_Face face;
sset->desc = query->desc;
cset->element_count = FTC_SBITSET_ELEMENT_COUNT;
cset->element_size = sizeof ( FTC_SBitRec );
/* lookup the FT_Face to obtain the number of glyphs */
error = FTC_Manager_Lookup_Face( cset->manager,
desc->font.face_id, &face );
/* we need to compute "cquery.item_total" now */
error = FTC_Manager_Lookup_Face( manager,
query->desc.font.face_id,
&face );
if ( !error )
cset->element_max = face->num_glyphs;
{
ftc_chunk_set_init( FTC_CHUNK_SET(sset),
sizeof( FTC_SBitRec ),
FTC_SBIT_ITEMS_PER_NODE,
face->num_glyphs,
FTC_CHUNK_CACHE(lru->user_data) );
/* now compute hash from description - this is _very_ important */
/* for good performance.. */
sset->cset.hash = FTC_IMAGE_DESC_HASH( &sset->desc );
query->chunk.cset = &sset->cset;
}
return error;
}
FT_CALLBACK_DEF( FT_Error )
ftc_sbit_chunk_set_init( FTC_SBitSet sset,
FTC_Image_Desc* type )
{
sset->desc = *type;
return 0;
}
FT_CALLBACK_DEF( FT_Bool )
ftc_sbit_chunk_set_compare( FTC_SBitSet sset,
FTC_Image_Desc* type )
ftc_sbit_set_compare( FTC_SBitSet sset,
FTC_SBitQuery query )
{
return FT_BOOL( !memcmp( &sset->desc, type, sizeof ( *type ) ) );
FT_Bool result;
/* we need to set the "cquery.cset" field or our query for */
/* faster glyph comparisons in ftc_sbit_node_compare.. */
/* */
result = FT_BOOL( FTC_IMAGE_DESC_COMPARE( &sset->desc, &query->desc ) );
if ( result )
query->chunk.cset = &sset->cset;
return result;
}
FT_CALLBACK_TABLE_DEF
const FTC_ChunkSet_Class ftc_sbit_chunk_set_class =
const FT_LruList_ClassRec ftc_sbit_set_class =
{
sizeof( FT_LruListRec ),
(FT_LruList_InitFunc) NULL,
(FT_LruList_DoneFunc) NULL,
sizeof( FTC_SBitSetRec ),
(FTC_ChunkSet_InitFunc) ftc_sbit_chunk_set_init,
(FTC_ChunkSet_DoneFunc) 0,
(FTC_ChunkSet_CompareFunc) ftc_sbit_chunk_set_compare,
(FTC_ChunkSet_SizesFunc) ftc_sbit_chunk_set_sizes,
(FTC_ChunkSet_NewNodeFunc) ftc_sbit_chunk_node_new,
(FTC_ChunkSet_SizeNodeFunc) ftc_sbit_chunk_node_size,
(FTC_ChunkSet_DestroyNodeFunc)ftc_sbit_chunk_node_destroy
(FT_LruNode_InitFunc) ftc_sbit_set_init,
(FT_LruNode_DoneFunc) ftc_chunk_set_done,
(FT_LruNode_FlushFunc) NULL,
(FT_LruNode_CompareFunc) ftc_sbit_set_compare,
};
/*************************************************************************/
/*************************************************************************/
/***** *****/
@ -346,15 +422,25 @@
/*************************************************************************/
FT_CALLBACK_TABLE_DEF
const FTC_Chunk_Cache_Class ftc_sbit_cache_class =
FT_CALLBACK_DEF( FT_Error )
ftc_sbit_cache_init( FTC_SBit_Cache scache )
{
{
sizeof( FTC_SBit_CacheRec ),
(FTC_Cache_InitFunc)FTC_Chunk_Cache_Init,
(FTC_Cache_DoneFunc)FTC_Chunk_Cache_Done
},
(FTC_ChunkSet_Class*)&ftc_sbit_chunk_set_class
return ftc_chunk_cache_init( FTC_CHUNK_CACHE(scache),
&ftc_sbit_set_class );
}
FT_CALLBACK_TABLE_DEF
const FTC_Cache_ClassRec ftc_sbit_cache_class =
{
sizeof( FTC_ChunkCacheRec ),
(FTC_Cache_InitFunc) ftc_sbit_cache_init,
(FTC_Cache_DoneFunc) ftc_chunk_cache_done,
sizeof( FTC_ChunkNodeRec ),
(FTC_Node_InitFunc) ftc_sbit_node_init,
(FTC_Node_WeightFunc) ftc_sbit_node_weight,
(FTC_Node_CompareFunc) ftc_sbit_node_compare,
(FTC_Node_DoneFunc) ftc_sbit_node_done
};
@ -366,8 +452,8 @@
{
return FTC_Manager_Register_Cache(
manager,
(FTC_Cache_Class*)&ftc_sbit_cache_class,
(FTC_Cache*)acache );
&ftc_sbit_cache_class,
(FTC_Cache*) acache );
}
@ -379,21 +465,27 @@
FT_UInt gindex,
FTC_SBit *ansbit )
{
FT_Error error;
FTC_ChunkNode node;
FT_UInt cindex;
FT_Error error;
FTC_ChunkCache ccache = (FTC_ChunkCache) cache;
FTC_ChunkNode node;
FTC_SBitQueryRec query;
/* argument checks delayed to FTC_Chunk_Cache_Lookup */
if ( !ansbit )
return FTC_Err_Invalid_Argument;
*ansbit = 0;
error = FTC_Chunk_Cache_Lookup( &cache->root, desc, gindex,
&node, &cindex );
if ( !error )
*ansbit = (FTC_SBit)node->elements + cindex;
*ansbit = NULL;
query.chunk.gindex = gindex;
query.chunk.cset = NULL;
query.desc = *desc;
error = ftc_chunk_cache_lookup( ccache, &query.chunk, &node );
if ( !error )
{
*ansbit = (FTC_SBit) node->items + (gindex - node->item_start);
}
return error;
}

450
src/cache/ftlru.c vendored
View File

@ -25,326 +25,308 @@
#include "ftcerror.h"
static void
lru_build_free_list( FT_LruNode nodes,
FT_UInt count,
FT_List free_list )
{
FT_LruNode node = nodes;
FT_LruNode limit = node + count;
free_list->head = free_list->tail = 0;
for ( ; node < limit; node++ )
FT_List_Add( free_list, (FT_ListNode)node );
}
FT_EXPORT_DEF( FT_Error )
FT_Lru_New( const FT_Lru_Class* clazz,
FT_UInt max_elements,
FT_Pointer user_data,
FT_Memory memory,
FT_Bool pre_alloc,
FT_Lru *anlru )
FT_LruList_New( FT_LruList_Class clazz,
FT_UInt max_nodes,
FT_Pointer user_data,
FT_Memory memory,
FT_LruList *alist )
{
FT_Error error;
FT_Lru lru;
FT_Error error;
FT_LruList list;
if ( !anlru )
if ( !alist || !clazz )
return FTC_Err_Invalid_Argument;
*anlru = 0;
if ( !ALLOC( lru, sizeof ( *lru ) ) )
*alist = NULL;
if ( !ALLOC( list, clazz->list_size ) )
{
if ( pre_alloc )
{
/* allocate static array of lru list nodes */
if ( ALLOC_ARRAY( lru->nodes, max_elements, FT_LruNodeRec ) )
{
FREE( lru );
goto Exit;
}
/* initialize common fields */
list->clazz = clazz;
list->memory = memory;
list->max_nodes = max_nodes;
list->user_data = user_data;
/* build the `free_nodes' list from the array */
lru_build_free_list( lru->nodes, max_elements, &lru->free_nodes );
if ( clazz->list_init )
{
error = clazz->list_init( list );
if ( error )
{
if ( clazz->list_done )
clazz->list_done( list );
FREE( list );
}
}
/* initialize common fields */
lru->clazz = (FT_Lru_Class*)clazz;
lru->max_elements = max_elements;
lru->memory = memory;
lru->user_data = user_data;
*anlru = lru;
*alist = list;
}
Exit:
return error;
}
FT_EXPORT_DEF( void )
FT_Lru_Reset( FT_Lru lru )
FT_LruList_Destroy( FT_LruList list )
{
FT_ListNode node;
FT_Lru_Class* clazz;
FT_Memory memory;
FT_Memory memory;
FT_LruList_Class clazz;
if ( !lru )
if ( !list )
return;
node = lru->elements.head;
clazz = lru->clazz;
memory = lru->memory;
memory = list->memory;
clazz = list->clazz;
while ( node )
{
FT_ListNode next = node->next;
FT_LruList_Reset( list );
if ( clazz->list_done )
clazz->list_done( list );
clazz->done_element( lru, (FT_LruNode)node );
if ( !lru->nodes )
FREE( node );
node = next;
}
/* rebuild free list if necessary */
if ( lru->nodes )
lru_build_free_list( lru->nodes, lru->max_elements, &lru->free_nodes );
lru->elements.head = lru->elements.tail = 0;
lru->num_elements = 0;
FREE( list );
}
FT_EXPORT_DEF( void )
FT_Lru_Done( FT_Lru lru )
FT_LruList_Reset( FT_LruList list )
{
FT_Memory memory;
FT_LruNode node;
FT_LruList_Class clazz;
FT_Memory memory;
if ( !lru )
if ( !list )
return;
memory = lru->memory;
node = list->nodes;
clazz = list->clazz;
memory = list->memory;
FT_Lru_Reset( lru );
while ( node )
{
FT_LruNode next = node->next;
FREE( lru->nodes );
FREE( lru );
if ( clazz->node_done )
clazz->node_done( node, list );
FREE( node );
node = next;
}
list->nodes = NULL;
list->num_nodes = 0;
}
FT_EXPORT_DEF( FT_Error )
FT_Lru_Lookup_Node( FT_Lru lru,
FT_LruKey key,
FT_LruNode *anode )
FT_LruList_Lookup( FT_LruList list,
FT_LruKey key,
FT_LruNode *anode )
{
FT_Error error = 0;
FT_ListNode node;
FT_Lru_Class* clazz;
FT_LruNode found = 0;
FT_Memory memory;
FT_Error error = 0;
FT_LruNode node, *pnode;
FT_LruList_Class clazz;
FT_LruNode* plast;
FT_LruNode result = NULL;
FT_Memory memory;
if ( !lru || !key || !anode )
if ( !list || !key || !anode )
return FTC_Err_Invalid_Argument;
node = lru->elements.head;
clazz = lru->clazz;
memory = lru->memory;
pnode = &list->nodes;
plast = pnode;
node = NULL;
clazz = list->clazz;
memory = list->memory;
if ( clazz->compare_element )
if ( clazz->node_compare )
{
for ( ; node; node = node->next )
if ( clazz->compare_element( (FT_LruNode)node, key ) )
{
found = (FT_LruNode)node;
for (;;)
{
node = *pnode;
if ( node == NULL )
break;
}
if ( clazz->node_compare( node, key, list ) )
break;
plast = pnode;
pnode = &(*pnode)->next;
}
}
else
{
for ( ; node; node = node->next )
if ( ((FT_LruNode)node)->key == key )
{
found = (FT_LruNode)node;
for (;;)
{
node = *pnode;
if ( node == NULL )
break;
}
if ( node->key == key )
break;
plast = pnode;
pnode = &(*pnode)->next;
}
}
if ( found )
if ( node )
{
/* move element to top of list */
FT_List_Up( &lru->elements, node );
}
else
{
/* we haven't found the relevant element. We will now try */
/* to create a new one. */
if ( lru->num_elements >= lru->max_elements )
if ( list->nodes != node )
{
/* this lru list is full; we will now flush */
/* the oldest node */
FT_LruNode lru_node;
*pnode = node->next;
node->next = list->nodes;
list->nodes = node;
}
result = node;
goto Exit;
}
/* we haven't found the relevant element. We will now try */
/* to create a new one. */
/* */
node = lru->elements.tail;
lru_node = (FT_LruNode)node;
found = lru_node;
/* first, check if our list if full, when appropriate */
if ( list->max_nodes > 0 && list->num_nodes >= list->max_nodes )
{
/* this list list is full; we will now flush */
/* the oldest node, if there's one !! */
FT_LruNode last = *plast;
if ( clazz->flush_element )
error = clazz->flush_element( lru, lru_node, key );
if ( last )
{
if ( clazz->node_flush )
{
error = clazz->node_flush( last, key, list );
}
else
{
clazz->done_element( lru, lru_node );
lru_node->key = key;
node->data = 0;
error = clazz->init_element( lru, lru_node );
if ( clazz->node_done )
clazz->node_done( last, list );
last->key = key;
error = clazz->node_init( last, key, list );
}
if ( !error )
{
/* now, move element to top of list */
FT_List_Up( &lru->elements, node );
}
else
{
/* in case of error, the node must be discarded */
FT_List_Remove( &lru->elements, node );
lru->num_elements--;
/* move it to the top of the list */
*plast = NULL;
last->next = list->nodes;
list->nodes = last;
if ( lru->nodes )
FT_List_Insert( &lru->free_nodes, node );
else
FREE( lru_node );
found = 0;
}
}
else
{
FT_LruNode lru_node;
/* create a new lru list node, then the element for it */
if ( lru->nodes )
{
node = lru->free_nodes.head;
lru_node = (FT_LruNode)node;
lru_node->key = key;
error = clazz->init_element( lru, lru_node );
if ( error )
goto Exit;
FT_List_Remove( &lru->free_nodes, node );
}
else
{
if ( ALLOC( lru_node, sizeof ( *lru_node ) ) )
goto Exit;
lru_node->key = key;
error = clazz->init_element( lru, lru_node );
if ( error )
{
FREE( lru_node );
goto Exit;
}
result = last;
goto Exit;
}
found = lru_node;
node = (FT_ListNode)lru_node;
FT_List_Insert( &lru->elements, node );
lru->num_elements++;
/* in case of error during the flush or done/init cycle, */
/* we need to discard the node.. */
if ( clazz->node_done )
clazz->node_done( last, list );
*plast = NULL;
list->num_nodes--;
FREE( last );
goto Exit;
}
}
/* otherwise, simply allocate a new node */
if ( ALLOC( node, clazz->node_size ) )
goto Exit;
node->key = key;
error = clazz->node_init( node, key, list );
if ( error )
{
FREE( node );
goto Exit;
}
result = node;
node->next = list->nodes;
list->nodes = node;
list->num_nodes++;
Exit:
*anode = found;
*anode = result;
return error;
}
FT_EXPORT_DEF( FT_Error )
FT_Lru_Lookup( FT_Lru lru,
FT_LruKey key,
FT_Pointer *anobject )
{
FT_Error error;
FT_LruNode node;
/* check for valid `lru' and `key' delayed to FT_Lru_Lookup_Node() */
if ( !anobject )
return FTC_Err_Invalid_Argument;
*anobject = 0;
error = FT_Lru_Lookup_Node( lru, key, &node );
if ( !error )
*anobject = node->root.data;
return error;
}
FT_EXPORT_DEF( void )
FT_Lru_Remove_Node( FT_Lru lru,
FT_LruNode node )
FT_LruList_Remove( FT_LruList list,
FT_LruNode node )
{
if ( !lru || !node )
FT_LruNode *pnode;
if ( !list || !node )
return;
if ( lru->num_elements > 0 )
pnode = &list->nodes;
for (;;)
{
FT_List_Remove( &lru->elements, (FT_ListNode)node );
lru->clazz->done_element( lru, node );
if ( lru->nodes )
FT_List_Insert( &lru->free_nodes, (FT_ListNode)node );
else
if ( *pnode == node )
{
FT_Memory memory = lru->memory;
FT_Memory memory = list->memory;
FT_LruList_Class clazz = list->clazz;
*pnode = node->next;
node->next = NULL;
if ( clazz->node_done )
clazz->node_done( node, list );
FREE( node );
list->num_nodes--;
break;
}
pnode = &(*pnode)->next;
}
}
FT_EXPORT_DEF( void )
FT_LruList_Remove_Selection( FT_LruList list,
FT_LruNode_SelectFunc select_func,
FT_Pointer select_data )
{
FT_LruNode *pnode, node;
FT_LruList_Class clazz;
FT_Memory memory;
if ( !list || !select_func )
return;
memory = list->memory;
clazz = list->clazz;
pnode = &list->nodes;
for (;;)
{
node = *pnode;
if ( node == NULL )
break;
if ( select_func( node, select_data, list ) )
{
*pnode = node->next;
node->next = NULL;
if ( clazz->node_done )
clazz->node_done( node, list );
FREE( node );
}
lru->num_elements--;
}
}
FT_EXPORT_DEF( void )
FT_Lru_Remove_Selection( FT_Lru lru,
FT_Lru_Selector selector,
FT_Pointer data )
{
if ( !lru || !selector )
return;
if ( lru->num_elements > 0 )
{
FT_ListNode node = lru->elements.head;
FT_ListNode next;
while ( node )
{
next = node->next;
if ( selector( lru, (FT_LruNode)node, data ) )
{
/* remove this element from the list, and destroy it */
FT_Lru_Remove_Node( lru, (FT_LruNode)node );
}
node = next;
}
else
pnode = &(*pnode)->next;
}
}