ref: 415235df1b955940ce85401a076f882e8717ef7b
dir: /src/cache/ftcchunk.c/
/***************************************************************************/ /* */ /* ftcchunk.c */ /* */ /* FreeType chunk cache cache (body). */ /* */ /* Copyright 2000-2001 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ #include <ft2build.h> #include FT_CACHE_H #include FT_CACHE_INTERNAL_CHUNK_H #include FT_LIST_H #include FT_ERRORS_H #include FT_INTERNAL_OBJECTS_H #include "ftcerror.h" /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** GLYPH NODES *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ /* 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_Cache cache = cset->cache; FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root ); FT_Error error = 0; 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 ); if ( alloc ) { /* allocate elements array */ FT_Memory memory; memory = cache->root.memory; error = MEM_Alloc( node->elements, cset->element_size * cset->element_count ); } return error; } FT_EXPORT_DEF( void ) FTC_ChunkNode_Destroy( FTC_ChunkNode node ) { FTC_ChunkSet cset = node->cset; /* remove from parent set table */ cset->chunks[node->cset_index] = 0; /* destroy the node */ cset->clazz->destroy_node( node ); } 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 }; /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** CHUNK SETS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_EXPORT_DEF( FT_Error ) FTC_ChunkSet_New( FTC_Chunk_Cache cache, FT_Pointer type, FTC_ChunkSet *aset ) { FT_Error error; FT_Memory memory = cache->root.memory; FTC_Manager manager = cache->root.manager; FTC_ChunkSet cset = 0; FTC_Chunk_Cache_Class* ccache_class; FTC_ChunkSet_Class* clazz; 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; } FT_EXPORT_DEF( void ) FTC_ChunkSet_Destroy( 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; /* for each bucket, free the list of glyph nodes */ for ( ; bucket < bucket_limit; bucket++ ) { 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; } } 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 cache = cset->cache; FTC_Manager manager = cache->root.manager; FT_Error error = 0; 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; } 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 ) { 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 ); if ( !error ) { /* good, now set the set index within the set object */ cset->cset_index = (FT_UInt)( node - lru->nodes ); node->root.data = cset; } 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; } /* END */