Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /***************************************************************************/
  2. /*                                                                         */
  3. /*  ftccache.c                                                             */
  4. /*                                                                         */
  5. /*    The FreeType internal cache interface (body).                        */
  6. /*                                                                         */
  7. /*  Copyright 2000-2007, 2009-2011, 2013 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 "ftcmanag.h"
  21. #include FT_INTERNAL_OBJECTS_H
  22. #include FT_INTERNAL_DEBUG_H
  23.  
  24. #include "ftccback.h"
  25. #include "ftcerror.h"
  26.  
  27. #undef  FT_COMPONENT
  28. #define FT_COMPONENT  trace_cache
  29.  
  30.  
  31. #define FTC_HASH_MAX_LOAD  2
  32. #define FTC_HASH_MIN_LOAD  1
  33. #define FTC_HASH_SUB_LOAD  ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
  34.  
  35.   /* this one _must_ be a power of 2! */
  36. #define FTC_HASH_INITIAL_SIZE  8
  37.  
  38.  
  39.   /*************************************************************************/
  40.   /*************************************************************************/
  41.   /*****                                                               *****/
  42.   /*****                   CACHE NODE DEFINITIONS                      *****/
  43.   /*****                                                               *****/
  44.   /*************************************************************************/
  45.   /*************************************************************************/
  46.  
  47.   /* add a new node to the head of the manager's circular MRU list */
  48.   static void
  49.   ftc_node_mru_link( FTC_Node     node,
  50.                      FTC_Manager  manager )
  51.   {
  52.     void  *nl = &manager->nodes_list;
  53.  
  54.  
  55.     FTC_MruNode_Prepend( (FTC_MruNode*)nl,
  56.                          (FTC_MruNode)node );
  57.     manager->num_nodes++;
  58.   }
  59.  
  60.  
  61.   /* remove a node from the manager's MRU list */
  62.   static void
  63.   ftc_node_mru_unlink( FTC_Node     node,
  64.                        FTC_Manager  manager )
  65.   {
  66.     void  *nl = &manager->nodes_list;
  67.  
  68.  
  69.     FTC_MruNode_Remove( (FTC_MruNode*)nl,
  70.                         (FTC_MruNode)node );
  71.     manager->num_nodes--;
  72.   }
  73.  
  74.  
  75. #ifndef FTC_INLINE
  76.  
  77.   /* move a node to the head of the manager's MRU list */
  78.   static void
  79.   ftc_node_mru_up( FTC_Node     node,
  80.                    FTC_Manager  manager )
  81.   {
  82.     FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
  83.                     (FTC_MruNode)node );
  84.   }
  85.  
  86.  
  87.   /* get a top bucket for specified hash from cache,
  88.    * body for FTC_NODE__TOP_FOR_HASH( cache, hash )
  89.    */
  90.   FT_LOCAL_DEF( FTC_Node* )
  91.   ftc_get_top_node_for_hash( FTC_Cache   cache,
  92.                              FT_PtrDist  hash )
  93.   {
  94.     FTC_Node*  pnode;
  95.     FT_UInt    idx;
  96.  
  97.  
  98.     idx = (FT_UInt)( hash & cache->mask );
  99.     if ( idx < cache->p )
  100.       idx = (FT_UInt)( hash & ( 2 * cache->mask + 1 ) );
  101.     pnode = cache->buckets + idx;
  102.     return pnode;
  103.   }
  104.  
  105. #endif /* !FTC_INLINE */
  106.  
  107.  
  108.   /* Note that this function cannot fail.  If we cannot re-size the
  109.    * buckets array appropriately, we simply degrade the hash table's
  110.    * performance!
  111.    */
  112.   static void
  113.   ftc_cache_resize( FTC_Cache  cache )
  114.   {
  115.     for (;;)
  116.     {
  117.       FTC_Node  node, *pnode;
  118.       FT_UFast  p     = cache->p;
  119.       FT_UFast  mask  = cache->mask;
  120.       FT_UFast  count = mask + p + 1;    /* number of buckets */
  121.  
  122.  
  123.       /* do we need to shrink the buckets array? */
  124.       if ( cache->slack < 0 )
  125.       {
  126.         FTC_Node  new_list = NULL;
  127.  
  128.  
  129.         /* try to expand the buckets array _before_ splitting
  130.          * the bucket lists
  131.          */
  132.         if ( p >= mask )
  133.         {
  134.           FT_Memory  memory = cache->memory;
  135.           FT_Error   error;
  136.  
  137.  
  138.           /* if we can't expand the array, leave immediately */
  139.           if ( FT_RENEW_ARRAY( cache->buckets,
  140.                                ( mask + 1 ) * 2, ( mask + 1 ) * 4 ) )
  141.             break;
  142.         }
  143.  
  144.         /* split a single bucket */
  145.         pnode = cache->buckets + p;
  146.  
  147.         for (;;)
  148.         {
  149.           node = *pnode;
  150.           if ( node == NULL )
  151.             break;
  152.  
  153.           if ( node->hash & ( mask + 1 ) )
  154.           {
  155.             *pnode     = node->link;
  156.             node->link = new_list;
  157.             new_list   = node;
  158.           }
  159.           else
  160.             pnode = &node->link;
  161.         }
  162.  
  163.         cache->buckets[p + mask + 1] = new_list;
  164.  
  165.         cache->slack += FTC_HASH_MAX_LOAD;
  166.  
  167.         if ( p >= mask )
  168.         {
  169.           cache->mask = 2 * mask + 1;
  170.           cache->p    = 0;
  171.         }
  172.         else
  173.           cache->p = p + 1;
  174.       }
  175.  
  176.       /* do we need to expand the buckets array? */
  177.       else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
  178.       {
  179.         FT_UFast   old_index = p + mask;
  180.         FTC_Node*  pold;
  181.  
  182.  
  183.         if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
  184.           break;
  185.  
  186.         if ( p == 0 )
  187.         {
  188.           FT_Memory  memory = cache->memory;
  189.           FT_Error   error;
  190.  
  191.  
  192.           /* if we can't shrink the array, leave immediately */
  193.           if ( FT_RENEW_ARRAY( cache->buckets,
  194.                                ( mask + 1 ) * 2, mask + 1 ) )
  195.             break;
  196.  
  197.           cache->mask >>= 1;
  198.           p             = cache->mask;
  199.         }
  200.         else
  201.           p--;
  202.  
  203.         pnode = cache->buckets + p;
  204.         while ( *pnode )
  205.           pnode = &(*pnode)->link;
  206.  
  207.         pold   = cache->buckets + old_index;
  208.         *pnode = *pold;
  209.         *pold  = NULL;
  210.  
  211.         cache->slack -= FTC_HASH_MAX_LOAD;
  212.         cache->p      = p;
  213.       }
  214.  
  215.       /* otherwise, the hash table is balanced */
  216.       else
  217.         break;
  218.     }
  219.   }
  220.  
  221.  
  222.   /* remove a node from its cache's hash table */
  223.   static void
  224.   ftc_node_hash_unlink( FTC_Node   node0,
  225.                         FTC_Cache  cache )
  226.   {
  227.     FTC_Node  *pnode = FTC_NODE__TOP_FOR_HASH( cache, node0->hash );
  228.  
  229.  
  230.     for (;;)
  231.     {
  232.       FTC_Node  node = *pnode;
  233.  
  234.  
  235.       if ( node == NULL )
  236.       {
  237.         FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" ));
  238.         return;
  239.       }
  240.  
  241.       if ( node == node0 )
  242.         break;
  243.  
  244.       pnode = &(*pnode)->link;
  245.     }
  246.  
  247.     *pnode      = node0->link;
  248.     node0->link = NULL;
  249.  
  250.     cache->slack++;
  251.     ftc_cache_resize( cache );
  252.   }
  253.  
  254.  
  255.   /* add a node to the `top' of its cache's hash table */
  256.   static void
  257.   ftc_node_hash_link( FTC_Node   node,
  258.                       FTC_Cache  cache )
  259.   {
  260.     FTC_Node  *pnode = FTC_NODE__TOP_FOR_HASH( cache, node->hash );
  261.  
  262.  
  263.     node->link = *pnode;
  264.     *pnode     = node;
  265.  
  266.     cache->slack--;
  267.     ftc_cache_resize( cache );
  268.   }
  269.  
  270.  
  271.   /* remove a node from the cache manager */
  272.   FT_LOCAL_DEF( void )
  273.   ftc_node_destroy( FTC_Node     node,
  274.                     FTC_Manager  manager )
  275.   {
  276.     FTC_Cache  cache;
  277.  
  278.  
  279. #ifdef FT_DEBUG_ERROR
  280.     /* find node's cache */
  281.     if ( node->cache_index >= manager->num_caches )
  282.     {
  283.       FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
  284.       return;
  285.     }
  286. #endif
  287.  
  288.     cache = manager->caches[node->cache_index];
  289.  
  290. #ifdef FT_DEBUG_ERROR
  291.     if ( cache == NULL )
  292.     {
  293.       FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
  294.       return;
  295.     }
  296. #endif
  297.  
  298.     manager->cur_weight -= cache->clazz.node_weight( node, cache );
  299.  
  300.     /* remove node from mru list */
  301.     ftc_node_mru_unlink( node, manager );
  302.  
  303.     /* remove node from cache's hash table */
  304.     ftc_node_hash_unlink( node, cache );
  305.  
  306.     /* now finalize it */
  307.     cache->clazz.node_free( node, cache );
  308.  
  309. #if 0
  310.     /* check, just in case of general corruption :-) */
  311.     if ( manager->num_nodes == 0 )
  312.       FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n",
  313.                   manager->num_nodes ));
  314. #endif
  315.   }
  316.  
  317.  
  318.   /*************************************************************************/
  319.   /*************************************************************************/
  320.   /*****                                                               *****/
  321.   /*****                    ABSTRACT CACHE CLASS                       *****/
  322.   /*****                                                               *****/
  323.   /*************************************************************************/
  324.   /*************************************************************************/
  325.  
  326.  
  327.   FT_LOCAL_DEF( FT_Error )
  328.   FTC_Cache_Init( FTC_Cache  cache )
  329.   {
  330.     return ftc_cache_init( cache );
  331.   }
  332.  
  333.  
  334.   FT_LOCAL_DEF( FT_Error )
  335.   ftc_cache_init( FTC_Cache  cache )
  336.   {
  337.     FT_Memory  memory = cache->memory;
  338.     FT_Error   error;
  339.  
  340.  
  341.     cache->p     = 0;
  342.     cache->mask  = FTC_HASH_INITIAL_SIZE - 1;
  343.     cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
  344.  
  345.     (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
  346.     return error;
  347.   }
  348.  
  349.  
  350.   static void
  351.   FTC_Cache_Clear( FTC_Cache  cache )
  352.   {
  353.     if ( cache && cache->buckets )
  354.     {
  355.       FTC_Manager  manager = cache->manager;
  356.       FT_UFast     i;
  357.       FT_UFast     count;
  358.  
  359.  
  360.       count = cache->p + cache->mask + 1;
  361.  
  362.       for ( i = 0; i < count; i++ )
  363.       {
  364.         FTC_Node  *pnode = cache->buckets + i, next, node = *pnode;
  365.  
  366.  
  367.         while ( node )
  368.         {
  369.           next        = node->link;
  370.           node->link  = NULL;
  371.  
  372.           /* remove node from mru list */
  373.           ftc_node_mru_unlink( node, manager );
  374.  
  375.           /* now finalize it */
  376.           manager->cur_weight -= cache->clazz.node_weight( node, cache );
  377.  
  378.           cache->clazz.node_free( node, cache );
  379.           node = next;
  380.         }
  381.         cache->buckets[i] = NULL;
  382.       }
  383.       ftc_cache_resize( cache );
  384.     }
  385.   }
  386.  
  387.  
  388.   FT_LOCAL_DEF( void )
  389.   ftc_cache_done( FTC_Cache  cache )
  390.   {
  391.     if ( cache->memory )
  392.     {
  393.       FT_Memory  memory = cache->memory;
  394.  
  395.  
  396.       FTC_Cache_Clear( cache );
  397.  
  398.       FT_FREE( cache->buckets );
  399.       cache->mask  = 0;
  400.       cache->p     = 0;
  401.       cache->slack = 0;
  402.  
  403.       cache->memory = NULL;
  404.     }
  405.   }
  406.  
  407.  
  408.   FT_LOCAL_DEF( void )
  409.   FTC_Cache_Done( FTC_Cache  cache )
  410.   {
  411.     ftc_cache_done( cache );
  412.   }
  413.  
  414.  
  415.   static void
  416.   ftc_cache_add( FTC_Cache  cache,
  417.                  FT_PtrDist hash,
  418.                  FTC_Node   node )
  419.   {
  420.     node->hash        = hash;
  421.     node->cache_index = (FT_UInt16)cache->index;
  422.     node->ref_count   = 0;
  423.  
  424.     ftc_node_hash_link( node, cache );
  425.     ftc_node_mru_link( node, cache->manager );
  426.  
  427.     {
  428.       FTC_Manager  manager = cache->manager;
  429.  
  430.  
  431.       manager->cur_weight += cache->clazz.node_weight( node, cache );
  432.  
  433.       if ( manager->cur_weight >= manager->max_weight )
  434.       {
  435.         node->ref_count++;
  436.         FTC_Manager_Compress( manager );
  437.         node->ref_count--;
  438.       }
  439.     }
  440.   }
  441.  
  442.  
  443.   FT_LOCAL_DEF( FT_Error )
  444.   FTC_Cache_NewNode( FTC_Cache   cache,
  445.                      FT_PtrDist  hash,
  446.                      FT_Pointer  query,
  447.                      FTC_Node   *anode )
  448.   {
  449.     FT_Error  error;
  450.     FTC_Node  node;
  451.  
  452.  
  453.     /*
  454.      * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
  455.      * errors (OOM) correctly, i.e., by flushing the cache progressively
  456.      * in order to make more room.
  457.      */
  458.  
  459.     FTC_CACHE_TRYLOOP( cache )
  460.     {
  461.       error = cache->clazz.node_new( &node, query, cache );
  462.     }
  463.     FTC_CACHE_TRYLOOP_END( NULL );
  464.  
  465.     if ( error )
  466.       node = NULL;
  467.     else
  468.     {
  469.      /* don't assume that the cache has the same number of buckets, since
  470.       * our allocation request might have triggered global cache flushing
  471.       */
  472.       ftc_cache_add( cache, hash, node );
  473.     }
  474.  
  475.     *anode = node;
  476.     return error;
  477.   }
  478.  
  479.  
  480. #ifndef FTC_INLINE
  481.  
  482.   FT_LOCAL_DEF( FT_Error )
  483.   FTC_Cache_Lookup( FTC_Cache   cache,
  484.                     FT_PtrDist  hash,
  485.                     FT_Pointer  query,
  486.                     FTC_Node   *anode )
  487.   {
  488.     FTC_Node*  bucket;
  489.     FTC_Node*  pnode;
  490.     FTC_Node   node;
  491.     FT_Error   error        = FT_Err_Ok;
  492.     FT_Bool    list_changed = FALSE;
  493.  
  494.     FTC_Node_CompareFunc  compare = cache->clazz.node_compare;
  495.  
  496.  
  497.     if ( cache == NULL || anode == NULL )
  498.       return FT_THROW( Invalid_Argument );
  499.  
  500.     /* Go to the `top' node of the list sharing same masked hash */
  501.     bucket = pnode = FTC_NODE__TOP_FOR_HASH( cache, hash );
  502.  
  503.     /* Lookup a node with exactly same hash and queried properties.  */
  504.     /* NOTE: _nodcomp() may change the linked list to reduce memory. */
  505.     for (;;)
  506.     {
  507.       node = *pnode;
  508.       if ( node == NULL )
  509.         goto NewNode;
  510.  
  511.       if ( node->hash == hash                           &&
  512.            compare( node, query, cache, &list_changed ) )
  513.         break;
  514.  
  515.       pnode = &node->link;
  516.     }
  517.  
  518.     if ( list_changed )
  519.     {
  520.       /* Update bucket by modified linked list */
  521.       bucket = pnode = FTC_NODE__TOP_FOR_HASH( cache, hash );
  522.  
  523.       /* Update pnode by modified linked list */
  524.       while ( *pnode != node )
  525.       {
  526.         if ( *pnode == NULL )
  527.         {
  528.           FT_ERROR(( "FTC_Cache_Lookup: oops!!!  node missing\n" ));
  529.           goto NewNode;
  530.         }
  531.         else
  532.           pnode = &((*pnode)->link);
  533.       }
  534.     }
  535.  
  536.     /* Reorder the list to move the found node to the `top' */
  537.     if ( node != *bucket )
  538.     {
  539.       *pnode     = node->link;
  540.       node->link = *bucket;
  541.       *bucket    = node;
  542.     }
  543.  
  544.     /* move to head of MRU list */
  545.     {
  546.       FTC_Manager  manager = cache->manager;
  547.  
  548.  
  549.       if ( node != manager->nodes_list )
  550.         ftc_node_mru_up( node, manager );
  551.     }
  552.     *anode = node;
  553.  
  554.     return error;
  555.  
  556.   NewNode:
  557.     return FTC_Cache_NewNode( cache, hash, query, anode );
  558.   }
  559.  
  560. #endif /* !FTC_INLINE */
  561.  
  562.  
  563.   FT_LOCAL_DEF( void )
  564.   FTC_Cache_RemoveFaceID( FTC_Cache   cache,
  565.                           FTC_FaceID  face_id )
  566.   {
  567.     FT_UFast     i, count;
  568.     FTC_Manager  manager = cache->manager;
  569.     FTC_Node     frees   = NULL;
  570.  
  571.  
  572.     count = cache->p + cache->mask + 1;
  573.     for ( i = 0; i < count; i++ )
  574.     {
  575.       FTC_Node*  bucket = cache->buckets + i;
  576.       FTC_Node*  pnode  = bucket;
  577.  
  578.  
  579.       for ( ;; )
  580.       {
  581.         FTC_Node  node = *pnode;
  582.         FT_Bool   list_changed = FALSE;
  583.  
  584.  
  585.         if ( node == NULL )
  586.           break;
  587.  
  588.         if ( cache->clazz.node_remove_faceid( node, face_id,
  589.                                               cache, &list_changed ) )
  590.         {
  591.           *pnode     = node->link;
  592.           node->link = frees;
  593.           frees      = node;
  594.         }
  595.         else
  596.           pnode = &node->link;
  597.       }
  598.     }
  599.  
  600.     /* remove all nodes in the free list */
  601.     while ( frees )
  602.     {
  603.       FTC_Node  node;
  604.  
  605.  
  606.       node  = frees;
  607.       frees = node->link;
  608.  
  609.       manager->cur_weight -= cache->clazz.node_weight( node, cache );
  610.       ftc_node_mru_unlink( node, manager );
  611.  
  612.       cache->clazz.node_free( node, cache );
  613.  
  614.       cache->slack++;
  615.     }
  616.  
  617.     ftc_cache_resize( cache );
  618.   }
  619.  
  620.  
  621. /* END */
  622.