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