Home | History | Annotate | Download | only in cache
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ftcmanag.c                                                             */
      4 /*                                                                         */
      5 /*    FreeType Cache Manager (body).                                       */
      6 /*                                                                         */
      7 /*  Copyright 2000-2018 by                                                 */
      8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18 
     19 #include <ft2build.h>
     20 #include FT_CACHE_H
     21 #include "ftcmanag.h"
     22 #include FT_INTERNAL_OBJECTS_H
     23 #include FT_INTERNAL_DEBUG_H
     24 #include FT_SIZES_H
     25 
     26 #include "ftccback.h"
     27 #include "ftcerror.h"
     28 
     29 #ifdef FT_CONFIG_OPTION_PIC
     30 #error "cache system does not support PIC yet"
     31 #endif
     32 
     33 
     34 #undef  FT_COMPONENT
     35 #define FT_COMPONENT  trace_cache
     36 
     37 
     38   static FT_Error
     39   ftc_scaler_lookup_size( FTC_Manager  manager,
     40                           FTC_Scaler   scaler,
     41                           FT_Size     *asize )
     42   {
     43     FT_Face   face;
     44     FT_Size   size = NULL;
     45     FT_Error  error;
     46 
     47 
     48     error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
     49     if ( error )
     50       goto Exit;
     51 
     52     error = FT_New_Size( face, &size );
     53     if ( error )
     54       goto Exit;
     55 
     56     FT_Activate_Size( size );
     57 
     58     if ( scaler->pixel )
     59       error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
     60     else
     61       error = FT_Set_Char_Size( face,
     62                                 (FT_F26Dot6)scaler->width,
     63                                 (FT_F26Dot6)scaler->height,
     64                                 scaler->x_res,
     65                                 scaler->y_res );
     66     if ( error )
     67     {
     68       FT_Done_Size( size );
     69       size = NULL;
     70     }
     71 
     72   Exit:
     73     *asize = size;
     74     return error;
     75   }
     76 
     77 
     78   typedef struct  FTC_SizeNodeRec_
     79   {
     80     FTC_MruNodeRec  node;
     81     FT_Size         size;
     82     FTC_ScalerRec   scaler;
     83 
     84   } FTC_SizeNodeRec, *FTC_SizeNode;
     85 
     86 #define FTC_SIZE_NODE( x ) ( (FTC_SizeNode)( x ) )
     87 
     88 
     89   FT_CALLBACK_DEF( void )
     90   ftc_size_node_done( FTC_MruNode  ftcnode,
     91                       FT_Pointer   data )
     92   {
     93     FTC_SizeNode  node = (FTC_SizeNode)ftcnode;
     94     FT_Size       size = node->size;
     95     FT_UNUSED( data );
     96 
     97 
     98     if ( size )
     99       FT_Done_Size( size );
    100   }
    101 
    102 
    103   FT_CALLBACK_DEF( FT_Bool )
    104   ftc_size_node_compare( FTC_MruNode  ftcnode,
    105                          FT_Pointer   ftcscaler )
    106   {
    107     FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    108     FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    109     FTC_Scaler    scaler0 = &node->scaler;
    110 
    111 
    112     if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
    113     {
    114       FT_Activate_Size( node->size );
    115       return 1;
    116     }
    117     return 0;
    118   }
    119 
    120 
    121   FT_CALLBACK_DEF( FT_Error )
    122   ftc_size_node_init( FTC_MruNode  ftcnode,
    123                       FT_Pointer   ftcscaler,
    124                       FT_Pointer   ftcmanager )
    125   {
    126     FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    127     FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    128     FTC_Manager   manager = (FTC_Manager)ftcmanager;
    129 
    130 
    131     node->scaler = scaler[0];
    132 
    133     return ftc_scaler_lookup_size( manager, scaler, &node->size );
    134   }
    135 
    136 
    137   FT_CALLBACK_DEF( FT_Error )
    138   ftc_size_node_reset( FTC_MruNode  ftcnode,
    139                        FT_Pointer   ftcscaler,
    140                        FT_Pointer   ftcmanager )
    141   {
    142     FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    143     FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    144     FTC_Manager   manager = (FTC_Manager)ftcmanager;
    145 
    146 
    147     FT_Done_Size( node->size );
    148 
    149     node->scaler = scaler[0];
    150 
    151     return ftc_scaler_lookup_size( manager, scaler, &node->size );
    152   }
    153 
    154 
    155   static
    156   const FTC_MruListClassRec  ftc_size_list_class =
    157   {
    158     sizeof ( FTC_SizeNodeRec ),
    159 
    160     ftc_size_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
    161     ftc_size_node_init,     /* FTC_MruNode_InitFunc     node_init    */
    162     ftc_size_node_reset,    /* FTC_MruNode_ResetFunc    node_reset   */
    163     ftc_size_node_done      /* FTC_MruNode_DoneFunc     node_done    */
    164   };
    165 
    166 
    167   /* helper function used by ftc_face_node_done */
    168   static FT_Bool
    169   ftc_size_node_compare_faceid( FTC_MruNode  ftcnode,
    170                                 FT_Pointer   ftcface_id )
    171   {
    172     FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    173     FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
    174 
    175 
    176     return FT_BOOL( node->scaler.face_id == face_id );
    177   }
    178 
    179 
    180   /* documentation is in ftcache.h */
    181 
    182   FT_EXPORT_DEF( FT_Error )
    183   FTC_Manager_LookupSize( FTC_Manager  manager,
    184                           FTC_Scaler   scaler,
    185                           FT_Size     *asize )
    186   {
    187     FT_Error     error;
    188     FTC_MruNode  mrunode;
    189 
    190 
    191     if ( !asize || !scaler )
    192       return FT_THROW( Invalid_Argument );
    193 
    194     *asize = NULL;
    195 
    196     if ( !manager )
    197       return FT_THROW( Invalid_Cache_Handle );
    198 
    199 #ifdef FTC_INLINE
    200 
    201     FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
    202                             mrunode, error );
    203 
    204 #else
    205     error = FTC_MruList_Lookup( &manager->sizes, scaler, &mrunode );
    206 #endif
    207 
    208     if ( !error )
    209       *asize = FTC_SIZE_NODE( mrunode )->size;
    210 
    211     return error;
    212   }
    213 
    214 
    215   /*************************************************************************/
    216   /*************************************************************************/
    217   /*****                                                               *****/
    218   /*****                    FACE MRU IMPLEMENTATION                    *****/
    219   /*****                                                               *****/
    220   /*************************************************************************/
    221   /*************************************************************************/
    222 
    223   typedef struct  FTC_FaceNodeRec_
    224   {
    225     FTC_MruNodeRec  node;
    226     FTC_FaceID      face_id;
    227     FT_Face         face;
    228 
    229   } FTC_FaceNodeRec, *FTC_FaceNode;
    230 
    231 #define FTC_FACE_NODE( x ) ( ( FTC_FaceNode )( x ) )
    232 
    233 
    234   FT_CALLBACK_DEF( FT_Error )
    235   ftc_face_node_init( FTC_MruNode  ftcnode,
    236                       FT_Pointer   ftcface_id,
    237                       FT_Pointer   ftcmanager )
    238   {
    239     FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    240     FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
    241     FTC_Manager   manager = (FTC_Manager)ftcmanager;
    242     FT_Error      error;
    243 
    244 
    245     node->face_id = face_id;
    246 
    247     error = manager->request_face( face_id,
    248                                    manager->library,
    249                                    manager->request_data,
    250                                    &node->face );
    251     if ( !error )
    252     {
    253       /* destroy initial size object; it will be re-created later */
    254       if ( node->face->size )
    255         FT_Done_Size( node->face->size );
    256     }
    257 
    258     return error;
    259   }
    260 
    261 
    262   FT_CALLBACK_DEF( void )
    263   ftc_face_node_done( FTC_MruNode  ftcnode,
    264                       FT_Pointer   ftcmanager )
    265   {
    266     FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    267     FTC_Manager   manager = (FTC_Manager)ftcmanager;
    268 
    269 
    270     /* we must begin by removing all scalers for the target face */
    271     /* from the manager's list                                   */
    272     FTC_MruList_RemoveSelection( &manager->sizes,
    273                                  ftc_size_node_compare_faceid,
    274                                  node->face_id );
    275 
    276     /* all right, we can discard the face now */
    277     FT_Done_Face( node->face );
    278     node->face    = NULL;
    279     node->face_id = NULL;
    280   }
    281 
    282 
    283   FT_CALLBACK_DEF( FT_Bool )
    284   ftc_face_node_compare( FTC_MruNode  ftcnode,
    285                          FT_Pointer   ftcface_id )
    286   {
    287     FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    288     FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
    289 
    290 
    291     return FT_BOOL( node->face_id == face_id );
    292   }
    293 
    294 
    295   static
    296   const FTC_MruListClassRec  ftc_face_list_class =
    297   {
    298     sizeof ( FTC_FaceNodeRec),
    299 
    300     ftc_face_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
    301     ftc_face_node_init,     /* FTC_MruNode_InitFunc     node_init    */
    302     NULL,                   /* FTC_MruNode_ResetFunc    node_reset   */
    303     ftc_face_node_done      /* FTC_MruNode_DoneFunc     node_done    */
    304   };
    305 
    306 
    307   /* documentation is in ftcache.h */
    308 
    309   FT_EXPORT_DEF( FT_Error )
    310   FTC_Manager_LookupFace( FTC_Manager  manager,
    311                           FTC_FaceID   face_id,
    312                           FT_Face     *aface )
    313   {
    314     FT_Error     error;
    315     FTC_MruNode  mrunode;
    316 
    317 
    318     if ( !aface )
    319       return FT_THROW( Invalid_Argument );
    320 
    321     *aface = NULL;
    322 
    323     if ( !manager )
    324       return FT_THROW( Invalid_Cache_Handle );
    325 
    326     /* we break encapsulation for the sake of speed */
    327 #ifdef FTC_INLINE
    328 
    329     FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
    330                             mrunode, error );
    331 
    332 #else
    333     error = FTC_MruList_Lookup( &manager->faces, face_id, &mrunode );
    334 #endif
    335 
    336     if ( !error )
    337       *aface = FTC_FACE_NODE( mrunode )->face;
    338 
    339     return error;
    340   }
    341 
    342 
    343   /*************************************************************************/
    344   /*************************************************************************/
    345   /*****                                                               *****/
    346   /*****                    CACHE MANAGER ROUTINES                     *****/
    347   /*****                                                               *****/
    348   /*************************************************************************/
    349   /*************************************************************************/
    350 
    351 
    352   /* documentation is in ftcache.h */
    353 
    354   FT_EXPORT_DEF( FT_Error )
    355   FTC_Manager_New( FT_Library          library,
    356                    FT_UInt             max_faces,
    357                    FT_UInt             max_sizes,
    358                    FT_ULong            max_bytes,
    359                    FTC_Face_Requester  requester,
    360                    FT_Pointer          req_data,
    361                    FTC_Manager        *amanager )
    362   {
    363     FT_Error     error;
    364     FT_Memory    memory;
    365     FTC_Manager  manager = 0;
    366 
    367 
    368     if ( !library )
    369       return FT_THROW( Invalid_Library_Handle );
    370 
    371     if ( !amanager || !requester )
    372       return FT_THROW( Invalid_Argument );
    373 
    374     memory = library->memory;
    375 
    376     if ( FT_NEW( manager ) )
    377       goto Exit;
    378 
    379     if ( max_faces == 0 )
    380       max_faces = FTC_MAX_FACES_DEFAULT;
    381 
    382     if ( max_sizes == 0 )
    383       max_sizes = FTC_MAX_SIZES_DEFAULT;
    384 
    385     if ( max_bytes == 0 )
    386       max_bytes = FTC_MAX_BYTES_DEFAULT;
    387 
    388     manager->library      = library;
    389     manager->memory       = memory;
    390     manager->max_weight   = max_bytes;
    391 
    392     manager->request_face = requester;
    393     manager->request_data = req_data;
    394 
    395     FTC_MruList_Init( &manager->faces,
    396                       &ftc_face_list_class,
    397                       max_faces,
    398                       manager,
    399                       memory );
    400 
    401     FTC_MruList_Init( &manager->sizes,
    402                       &ftc_size_list_class,
    403                       max_sizes,
    404                       manager,
    405                       memory );
    406 
    407     *amanager = manager;
    408 
    409   Exit:
    410     return error;
    411   }
    412 
    413 
    414   /* documentation is in ftcache.h */
    415 
    416   FT_EXPORT_DEF( void )
    417   FTC_Manager_Done( FTC_Manager  manager )
    418   {
    419     FT_Memory  memory;
    420     FT_UInt    idx;
    421 
    422 
    423     if ( !manager || !manager->library )
    424       return;
    425 
    426     memory = manager->memory;
    427 
    428     /* now discard all caches */
    429     for (idx = manager->num_caches; idx-- > 0; )
    430     {
    431       FTC_Cache  cache = manager->caches[idx];
    432 
    433 
    434       if ( cache )
    435       {
    436         cache->clazz.cache_done( cache );
    437         FT_FREE( cache );
    438         manager->caches[idx] = NULL;
    439       }
    440     }
    441     manager->num_caches = 0;
    442 
    443     /* discard faces and sizes */
    444     FTC_MruList_Done( &manager->sizes );
    445     FTC_MruList_Done( &manager->faces );
    446 
    447     manager->library = NULL;
    448     manager->memory  = NULL;
    449 
    450     FT_FREE( manager );
    451   }
    452 
    453 
    454   /* documentation is in ftcache.h */
    455 
    456   FT_EXPORT_DEF( void )
    457   FTC_Manager_Reset( FTC_Manager  manager )
    458   {
    459     if ( !manager )
    460       return;
    461 
    462     FTC_MruList_Reset( &manager->sizes );
    463     FTC_MruList_Reset( &manager->faces );
    464 
    465     FTC_Manager_FlushN( manager, manager->num_nodes );
    466   }
    467 
    468 
    469 #ifdef FT_DEBUG_ERROR
    470 
    471   static void
    472   FTC_Manager_Check( FTC_Manager  manager )
    473   {
    474     FTC_Node  node, first;
    475 
    476 
    477     first = manager->nodes_list;
    478 
    479     /* check node weights */
    480     if ( first )
    481     {
    482       FT_Offset  weight = 0;
    483 
    484 
    485       node = first;
    486 
    487       do
    488       {
    489         FTC_Cache  cache = manager->caches[node->cache_index];
    490 
    491 
    492         if ( (FT_UInt)node->cache_index >= manager->num_caches )
    493           FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
    494                       node->cache_index ));
    495         else
    496           weight += cache->clazz.node_weight( node, cache );
    497 
    498         node = FTC_NODE_NEXT( node );
    499 
    500       } while ( node != first );
    501 
    502       if ( weight != manager->cur_weight )
    503         FT_TRACE0(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
    504                     manager->cur_weight, weight ));
    505     }
    506 
    507     /* check circular list */
    508     if ( first )
    509     {
    510       FT_UFast  count = 0;
    511 
    512 
    513       node = first;
    514       do
    515       {
    516         count++;
    517         node = FTC_NODE_NEXT( node );
    518 
    519       } while ( node != first );
    520 
    521       if ( count != manager->num_nodes )
    522         FT_TRACE0(( "FTC_Manager_Check:"
    523                     " invalid cache node count %d instead of %d\n",
    524                     manager->num_nodes, count ));
    525     }
    526   }
    527 
    528 #endif /* FT_DEBUG_ERROR */
    529 
    530 
    531   /* `Compress' the manager's data, i.e., get rid of old cache nodes */
    532   /* that are not referenced anymore in order to limit the total     */
    533   /* memory used by the cache.                                       */
    534 
    535   /* documentation is in ftcmanag.h */
    536 
    537   FT_LOCAL_DEF( void )
    538   FTC_Manager_Compress( FTC_Manager  manager )
    539   {
    540     FTC_Node   node, first;
    541 
    542 
    543     if ( !manager )
    544       return;
    545 
    546     first = manager->nodes_list;
    547 
    548 #ifdef FT_DEBUG_ERROR
    549     FTC_Manager_Check( manager );
    550 
    551     FT_TRACE0(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
    552                 manager->cur_weight, manager->max_weight,
    553                 manager->num_nodes ));
    554 #endif
    555 
    556     if ( manager->cur_weight < manager->max_weight || !first )
    557       return;
    558 
    559     /* go to last node -- it's a circular list */
    560     node = FTC_NODE_PREV( first );
    561     do
    562     {
    563       FTC_Node  prev;
    564 
    565 
    566       prev = ( node == first ) ? NULL : FTC_NODE_PREV( node );
    567 
    568       if ( node->ref_count <= 0 )
    569         ftc_node_destroy( node, manager );
    570 
    571       node = prev;
    572 
    573     } while ( node && manager->cur_weight > manager->max_weight );
    574   }
    575 
    576 
    577   /* documentation is in ftcmanag.h */
    578 
    579   FT_LOCAL_DEF( FT_Error )
    580   FTC_Manager_RegisterCache( FTC_Manager      manager,
    581                              FTC_CacheClass   clazz,
    582                              FTC_Cache       *acache )
    583   {
    584     FT_Error   error = FT_ERR( Invalid_Argument );
    585     FTC_Cache  cache = NULL;
    586 
    587 
    588     if ( manager && clazz && acache )
    589     {
    590       FT_Memory  memory = manager->memory;
    591 
    592 
    593       if ( manager->num_caches >= FTC_MAX_CACHES )
    594       {
    595         error = FT_THROW( Too_Many_Caches );
    596         FT_ERROR(( "FTC_Manager_RegisterCache:"
    597                    " too many registered caches\n" ));
    598         goto Exit;
    599       }
    600 
    601       if ( !FT_ALLOC( cache, clazz->cache_size ) )
    602       {
    603         cache->manager   = manager;
    604         cache->memory    = memory;
    605         cache->clazz     = clazz[0];
    606         cache->org_class = clazz;
    607 
    608         /* THIS IS VERY IMPORTANT!  IT WILL WRETCH THE MANAGER */
    609         /* IF IT IS NOT SET CORRECTLY                          */
    610         cache->index = manager->num_caches;
    611 
    612         error = clazz->cache_init( cache );
    613         if ( error )
    614         {
    615           clazz->cache_done( cache );
    616           FT_FREE( cache );
    617           goto Exit;
    618         }
    619 
    620         manager->caches[manager->num_caches++] = cache;
    621       }
    622     }
    623 
    624   Exit:
    625     if ( acache )
    626       *acache = cache;
    627     return error;
    628   }
    629 
    630 
    631   FT_LOCAL_DEF( FT_UInt )
    632   FTC_Manager_FlushN( FTC_Manager  manager,
    633                       FT_UInt      count )
    634   {
    635     FTC_Node  first = manager->nodes_list;
    636     FTC_Node  node;
    637     FT_UInt   result;
    638 
    639 
    640     /* try to remove `count' nodes from the list */
    641     if ( !first )  /* empty list! */
    642       return 0;
    643 
    644     /* go to last node - it's a circular list */
    645     node = FTC_NODE_PREV(first);
    646     for ( result = 0; result < count; )
    647     {
    648       FTC_Node  prev = FTC_NODE_PREV( node );
    649 
    650 
    651       /* don't touch locked nodes */
    652       if ( node->ref_count <= 0 )
    653       {
    654         ftc_node_destroy( node, manager );
    655         result++;
    656       }
    657 
    658       if ( node == first )
    659         break;
    660 
    661       node = prev;
    662     }
    663     return  result;
    664   }
    665 
    666 
    667   /* documentation is in ftcache.h */
    668 
    669   FT_EXPORT_DEF( void )
    670   FTC_Manager_RemoveFaceID( FTC_Manager  manager,
    671                             FTC_FaceID   face_id )
    672   {
    673     FT_UInt  nn;
    674 
    675 
    676     if ( !manager )
    677       return;
    678 
    679     /* this will remove all FTC_SizeNode that correspond to
    680      * the face_id as well
    681      */
    682     FTC_MruList_RemoveSelection( &manager->faces,
    683                                  ftc_face_node_compare,
    684                                  face_id );
    685 
    686     for ( nn = 0; nn < manager->num_caches; nn++ )
    687       FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
    688   }
    689 
    690 
    691   /* documentation is in ftcache.h */
    692 
    693   FT_EXPORT_DEF( void )
    694   FTC_Node_Unref( FTC_Node     node,
    695                   FTC_Manager  manager )
    696   {
    697     if ( node                                             &&
    698          manager                                          &&
    699          (FT_UInt)node->cache_index < manager->num_caches )
    700       node->ref_count--;
    701   }
    702 
    703 
    704 /* END */
    705