ftccache.c

Go to the documentation of this file.
00001 /***************************************************************************/
00002 /*                                                                         */
00003 /*  ftccache.c                                                             */
00004 /*                                                                         */
00005 /*    The FreeType internal cache interface (body).                        */
00006 /*                                                                         */
00007 /*  Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 by       */
00008 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
00009 /*                                                                         */
00010 /*  This file is part of the FreeType project, and may only be used,       */
00011 /*  modified, and distributed under the terms of the FreeType project      */
00012 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
00013 /*  this file you indicate that you have read the license and              */
00014 /*  understand and accept it fully.                                        */
00015 /*                                                                         */
00016 /***************************************************************************/
00017 
00018 
00019 #include <ft2build.h>
00020 #include "ftcmanag.h"
00021 #include FT_INTERNAL_OBJECTS_H
00022 #include FT_INTERNAL_DEBUG_H
00023 
00024 #include "ftccback.h"
00025 #include "ftcerror.h"
00026 
00027 #undef  FT_COMPONENT
00028 #define FT_COMPONENT  trace_cache
00029 
00030 
00031 #define FTC_HASH_MAX_LOAD  2
00032 #define FTC_HASH_MIN_LOAD  1
00033 #define FTC_HASH_SUB_LOAD  ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
00034 
00035 /* this one _must_ be a power of 2! */
00036 #define FTC_HASH_INITIAL_SIZE  8
00037 
00038 
00039   /*************************************************************************/
00040   /*************************************************************************/
00041   /*****                                                               *****/
00042   /*****                   CACHE NODE DEFINITIONS                      *****/
00043   /*****                                                               *****/
00044   /*************************************************************************/
00045   /*************************************************************************/
00046 
00047   /* add a new node to the head of the manager's circular MRU list */
00048   static void
00049   ftc_node_mru_link( FTC_Node     node,
00050                      FTC_Manager  manager )
00051   {
00052     void  *nl = &manager->nodes_list;
00053 
00054 
00055     FTC_MruNode_Prepend( (FTC_MruNode*)nl,
00056                          (FTC_MruNode)node );
00057     manager->num_nodes++;
00058   }
00059 
00060 
00061   /* remove a node from the manager's MRU list */
00062   static void
00063   ftc_node_mru_unlink( FTC_Node     node,
00064                        FTC_Manager  manager )
00065   {
00066     void  *nl = &manager->nodes_list;
00067 
00068 
00069     FTC_MruNode_Remove( (FTC_MruNode*)nl,
00070                         (FTC_MruNode)node );
00071     manager->num_nodes--;
00072   }
00073 
00074 
00075 #ifndef FTC_INLINE
00076 
00077   /* move a node to the head of the manager's MRU list */
00078   static void
00079   ftc_node_mru_up( FTC_Node     node,
00080                    FTC_Manager  manager )
00081   {
00082     FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
00083                     (FTC_MruNode)node );
00084   }
00085 
00086 #endif /* !FTC_INLINE */
00087 
00088 
00089   /* Note that this function cannot fail.  If we cannot re-size the
00090    * buckets array appropriately, we simply degrade the hash table's
00091    * performance!
00092    */
00093   static void
00094   ftc_cache_resize( FTC_Cache  cache )
00095   {
00096     for (;;)
00097     {
00098       FTC_Node  node, *pnode;
00099       FT_UFast  p      = cache->p;
00100       FT_UFast  mask   = cache->mask;
00101       FT_UFast  count  = mask + p + 1;    /* number of buckets */
00102 
00103 
00104       /* do we need to shrink the buckets array? */
00105       if ( cache->slack < 0 )
00106       {
00107         FTC_Node  new_list = NULL;
00108 
00109 
00110         /* try to expand the buckets array _before_ splitting
00111          * the bucket lists
00112          */
00113         if ( p >= mask )
00114         {
00115           FT_Memory  memory = cache->memory;
00116           FT_Error   error;
00117 
00118 
00119           /* if we can't expand the array, leave immediately */
00120           if ( FT_RENEW_ARRAY( cache->buckets, (mask+1)*2, (mask+1)*4 ) )
00121             break;
00122         }
00123 
00124         /* split a single bucket */
00125         pnode = cache->buckets + p;
00126 
00127         for (;;)
00128         {
00129           node = *pnode;
00130           if ( node == NULL )
00131             break;
00132 
00133           if ( node->hash & ( mask + 1 ) )
00134           {
00135             *pnode     = node->link;
00136             node->link = new_list;
00137             new_list   = node;
00138           }
00139           else
00140             pnode = &node->link;
00141         }
00142 
00143         cache->buckets[p + mask + 1] = new_list;
00144 
00145         cache->slack += FTC_HASH_MAX_LOAD;
00146 
00147         if ( p >= mask )
00148         {
00149           cache->mask = 2 * mask + 1;
00150           cache->p    = 0;
00151         }
00152         else
00153           cache->p = p + 1;
00154       }
00155 
00156       /* do we need to expand the buckets array? */
00157       else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
00158       {
00159         FT_UFast   old_index = p + mask;
00160         FTC_Node*  pold;
00161 
00162 
00163         if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
00164           break;
00165 
00166         if ( p == 0 )
00167         {
00168           FT_Memory  memory = cache->memory;
00169           FT_Error   error;
00170 
00171 
00172           /* if we can't shrink the array, leave immediately */
00173           if ( FT_RENEW_ARRAY( cache->buckets,
00174                                ( mask + 1 ) * 2, mask + 1 ) )
00175             break;
00176 
00177           cache->mask >>= 1;
00178           p             = cache->mask;
00179         }
00180         else
00181           p--;
00182 
00183         pnode = cache->buckets + p;
00184         while ( *pnode )
00185           pnode = &(*pnode)->link;
00186 
00187         pold   = cache->buckets + old_index;
00188         *pnode = *pold;
00189         *pold  = NULL;
00190 
00191         cache->slack -= FTC_HASH_MAX_LOAD;
00192         cache->p      = p;
00193       }
00194       else /* the hash table is balanced */
00195         break;
00196     }
00197   }
00198 
00199 
00200   /* remove a node from its cache's hash table */
00201   static void
00202   ftc_node_hash_unlink( FTC_Node   node0,
00203                         FTC_Cache  cache )
00204   {
00205     FTC_Node  *pnode;
00206     FT_UInt    idx;
00207 
00208 
00209     idx = (FT_UInt)( node0->hash & cache->mask );
00210     if ( idx < cache->p )
00211       idx = (FT_UInt)( node0->hash & ( 2 * cache->mask + 1 ) );
00212 
00213     pnode = cache->buckets + idx;
00214 
00215     for (;;)
00216     {
00217       FTC_Node  node = *pnode;
00218 
00219 
00220       if ( node == NULL )
00221       {
00222         FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" ));
00223         return;
00224       }
00225 
00226       if ( node == node0 )
00227         break;
00228 
00229       pnode = &(*pnode)->link;
00230     }
00231 
00232     *pnode      = node0->link;
00233     node0->link = NULL;
00234 
00235     cache->slack++;
00236     ftc_cache_resize( cache );
00237   }
00238 
00239 
00240   /* add a node to the `top' of its cache's hash table */
00241   static void
00242   ftc_node_hash_link( FTC_Node   node,
00243                       FTC_Cache  cache )
00244   {
00245     FTC_Node  *pnode;
00246     FT_UInt    idx;
00247 
00248 
00249     idx = (FT_UInt)( node->hash & cache->mask );
00250     if ( idx < cache->p )
00251       idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) );
00252 
00253     pnode = cache->buckets + idx;
00254 
00255     node->link = *pnode;
00256     *pnode     = node;
00257 
00258     cache->slack--;
00259     ftc_cache_resize( cache );
00260   }
00261 
00262 
00263   /* remove a node from the cache manager */
00264 #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
00265   FT_BASE_DEF( void )
00266 #else
00267   FT_LOCAL_DEF( void )
00268 #endif
00269   ftc_node_destroy( FTC_Node     node,
00270                     FTC_Manager  manager )
00271   {
00272     FTC_Cache  cache;
00273 
00274 
00275 #ifdef FT_DEBUG_ERROR
00276     /* find node's cache */
00277     if ( node->cache_index >= manager->num_caches )
00278     {
00279       FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
00280       return;
00281     }
00282 #endif
00283 
00284     cache = manager->caches[node->cache_index];
00285 
00286 #ifdef FT_DEBUG_ERROR
00287     if ( cache == NULL )
00288     {
00289       FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
00290       return;
00291     }
00292 #endif
00293 
00294     manager->cur_weight -= cache->clazz.node_weight( node, cache );
00295 
00296     /* remove node from mru list */
00297     ftc_node_mru_unlink( node, manager );
00298 
00299     /* remove node from cache's hash table */
00300     ftc_node_hash_unlink( node, cache );
00301 
00302     /* now finalize it */
00303     cache->clazz.node_free( node, cache );
00304 
00305 #if 0
00306     /* check, just in case of general corruption :-) */
00307     if ( manager->num_nodes == 0 )
00308       FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n",
00309                   manager->num_nodes ));
00310 #endif
00311   }
00312 
00313 
00314   /*************************************************************************/
00315   /*************************************************************************/
00316   /*****                                                               *****/
00317   /*****                    ABSTRACT CACHE CLASS                       *****/
00318   /*****                                                               *****/
00319   /*************************************************************************/
00320   /*************************************************************************/
00321 
00322 
00323   FT_LOCAL_DEF( FT_Error )
00324   FTC_Cache_Init( FTC_Cache  cache )
00325   {
00326     return ftc_cache_init( cache );
00327   }
00328 
00329 
00330   FT_LOCAL_DEF( FT_Error )
00331   ftc_cache_init( FTC_Cache  cache )
00332   {
00333     FT_Memory  memory = cache->memory;
00334     FT_Error   error;
00335 
00336 
00337     cache->p     = 0;
00338     cache->mask  = FTC_HASH_INITIAL_SIZE - 1;
00339     cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
00340 
00341     (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
00342     return error;
00343   }
00344 
00345 
00346   static void
00347   FTC_Cache_Clear( FTC_Cache  cache )
00348   {
00349     if ( cache )
00350     {
00351       FTC_Manager  manager = cache->manager;
00352       FT_UFast     i;
00353       FT_UFast     count;
00354 
00355 
00356       count = cache->p + cache->mask + 1;
00357 
00358       for ( i = 0; i < count; i++ )
00359       {
00360         FTC_Node  *pnode = cache->buckets + i, next, node = *pnode;
00361 
00362 
00363         while ( node )
00364         {
00365           next        = node->link;
00366           node->link  = NULL;
00367 
00368           /* remove node from mru list */
00369           ftc_node_mru_unlink( node, manager );
00370 
00371           /* now finalize it */
00372           manager->cur_weight -= cache->clazz.node_weight( node, cache );
00373 
00374           cache->clazz.node_free( node, cache );
00375           node = next;
00376         }
00377         cache->buckets[i] = NULL;
00378       }
00379       ftc_cache_resize( cache );
00380     }
00381   }
00382 
00383 
00384   FT_LOCAL_DEF( void )
00385   ftc_cache_done( FTC_Cache  cache )
00386   {
00387     if ( cache->memory )
00388     {
00389       FT_Memory  memory = cache->memory;
00390 
00391 
00392       FTC_Cache_Clear( cache );
00393 
00394       FT_FREE( cache->buckets );
00395       cache->mask  = 0;
00396       cache->p     = 0;
00397       cache->slack = 0;
00398 
00399       cache->memory = NULL;
00400     }
00401   }
00402 
00403 
00404   FT_LOCAL_DEF( void )
00405   FTC_Cache_Done( FTC_Cache  cache )
00406   {
00407     ftc_cache_done( cache );
00408   }
00409 
00410 
00411   static void
00412   ftc_cache_add( FTC_Cache  cache,
00413                  FT_UInt32  hash,
00414                  FTC_Node   node )
00415   {
00416     node->hash = hash;
00417     node->cache_index = (FT_UInt16) cache->index;
00418     node->ref_count   = 0;
00419 
00420     ftc_node_hash_link( node, cache );
00421     ftc_node_mru_link( node, cache->manager );
00422 
00423     {
00424       FTC_Manager  manager = cache->manager;
00425 
00426 
00427       manager->cur_weight += cache->clazz.node_weight( node, cache );
00428 
00429       if ( manager->cur_weight >= manager->max_weight )
00430       {
00431         node->ref_count++;
00432         FTC_Manager_Compress( manager );
00433         node->ref_count--;
00434       }
00435     }
00436   }
00437 
00438 
00439   FT_LOCAL_DEF( FT_Error )
00440   FTC_Cache_NewNode( FTC_Cache   cache,
00441                      FT_UInt32   hash,
00442                      FT_Pointer  query,
00443                      FTC_Node   *anode )
00444   {
00445     FT_Error  error;
00446     FTC_Node  node;
00447 
00448 
00449     /*
00450      * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
00451      * errors (OOM) correctly, i.e., by flushing the cache progressively
00452      * in order to make more room.
00453      */
00454 
00455     FTC_CACHE_TRYLOOP( cache )
00456     {
00457       error = cache->clazz.node_new( &node, query, cache );
00458     }
00459     FTC_CACHE_TRYLOOP_END();
00460 
00461     if ( error )
00462       node = NULL;
00463     else
00464     {
00465      /* don't assume that the cache has the same number of buckets, since
00466       * our allocation request might have triggered global cache flushing
00467       */
00468       ftc_cache_add( cache, hash, node );
00469     }
00470 
00471     *anode = node;
00472     return error;
00473   }
00474 
00475 
00476 #ifndef FTC_INLINE
00477 
00478   FT_LOCAL_DEF( FT_Error )
00479   FTC_Cache_Lookup( FTC_Cache   cache,
00480                     FT_UInt32   hash,
00481                     FT_Pointer  query,
00482                     FTC_Node   *anode )
00483   {
00484     FT_UFast   idx;
00485     FTC_Node*  bucket;
00486     FTC_Node*  pnode;
00487     FTC_Node   node;
00488     FT_Error   error = 0;
00489 
00490     FTC_Node_CompareFunc  compare = cache->clazz.node_compare;
00491 
00492 
00493     if ( cache == NULL || anode == NULL )
00494       return FT_Err_Invalid_Argument;
00495 
00496     idx = hash & cache->mask;
00497     if ( idx < cache->p )
00498       idx = hash & ( cache->mask * 2 + 1 );
00499 
00500     bucket = cache->buckets + idx;
00501     pnode  = bucket;
00502     for (;;)
00503     {
00504       node = *pnode;
00505       if ( node == NULL )
00506         goto NewNode;
00507 
00508       if ( node->hash == hash && compare( node, query, cache ) )
00509         break;
00510 
00511       pnode = &node->link;
00512     }
00513 
00514     if ( node != *bucket )
00515     {
00516       *pnode     = node->link;
00517       node->link = *bucket;
00518       *bucket    = node;
00519     }
00520 
00521     /* move to head of MRU list */
00522     {
00523       FTC_Manager  manager = cache->manager;
00524 
00525 
00526       if ( node != manager->nodes_list )
00527         ftc_node_mru_up( node, manager );
00528     }
00529     *anode = node;
00530     return error;
00531 
00532   NewNode:
00533     return FTC_Cache_NewNode( cache, hash, query, anode );
00534   }
00535 
00536 #endif /* !FTC_INLINE */
00537 
00538 
00539   FT_LOCAL_DEF( void )
00540   FTC_Cache_RemoveFaceID( FTC_Cache   cache,
00541                           FTC_FaceID  face_id )
00542   {
00543     FT_UFast     i, count;
00544     FTC_Manager  manager = cache->manager;
00545     FTC_Node     frees   = NULL;
00546 
00547 
00548     count = cache->p + cache->mask;
00549     for ( i = 0; i < count; i++ )
00550     {
00551       FTC_Node*  bucket = cache->buckets + i;
00552       FTC_Node*  pnode  = bucket;
00553 
00554 
00555       for ( ;; )
00556       {
00557         FTC_Node  node = *pnode;
00558 
00559 
00560         if ( node == NULL )
00561           break;
00562 
00563         if ( cache->clazz.node_remove_faceid( node, face_id, cache ) )
00564         {
00565           *pnode     = node->link;
00566           node->link = frees;
00567           frees      = node;
00568         }
00569         else
00570           pnode = &node->link;
00571       }
00572     }
00573 
00574     /* remove all nodes in the free list */
00575     while ( frees )
00576     {
00577       FTC_Node  node;
00578 
00579 
00580       node  = frees;
00581       frees = node->link;
00582 
00583       manager->cur_weight -= cache->clazz.node_weight( node, cache );
00584       ftc_node_mru_unlink( node, manager );
00585 
00586       cache->clazz.node_free( node, cache );
00587 
00588       cache->slack++;
00589     }
00590 
00591     ftc_cache_resize( cache );
00592   }
00593 
00594 
00595 /* END */

Generated on Tue Jul 5 14:13:45 2011 for ROOT_528-00b_version by  doxygen 1.5.1