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
ftc_scaler_lookup_size(FTC_Manager manager,FTC_Scaler scaler,FT_Size * asize)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 )
ftc_size_node_done(FTC_MruNode ftcnode,FT_Pointer data)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 )
ftc_size_node_compare(FTC_MruNode ftcnode,FT_Pointer ftcscaler)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 )
ftc_size_node_init(FTC_MruNode ftcnode,FT_Pointer ftcscaler,FT_Pointer ftcmanager)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 )
ftc_size_node_reset(FTC_MruNode ftcnode,FT_Pointer ftcscaler,FT_Pointer ftcmanager)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
ftc_size_node_compare_faceid(FTC_MruNode ftcnode,FT_Pointer ftcface_id)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 )
FTC_Manager_LookupSize(FTC_Manager manager,FTC_Scaler scaler,FT_Size * asize)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 )
ftc_face_node_init(FTC_MruNode ftcnode,FT_Pointer ftcface_id,FT_Pointer ftcmanager)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 )
ftc_face_node_done(FTC_MruNode ftcnode,FT_Pointer ftcmanager)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 )
ftc_face_node_compare(FTC_MruNode ftcnode,FT_Pointer ftcface_id)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 )
FTC_Manager_LookupFace(FTC_Manager manager,FTC_FaceID face_id,FT_Face * aface)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 )
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)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 )
FTC_Manager_Done(FTC_Manager manager)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 )
FTC_Manager_Reset(FTC_Manager manager)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
FTC_Manager_Check(FTC_Manager manager)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 )
FTC_Manager_Compress(FTC_Manager manager)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 )
FTC_Manager_RegisterCache(FTC_Manager manager,FTC_CacheClass clazz,FTC_Cache * acache)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 )
FTC_Manager_FlushN(FTC_Manager manager,FT_UInt count)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 )
FTC_Manager_RemoveFaceID(FTC_Manager manager,FTC_FaceID face_id)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 )
FTC_Node_Unref(FTC_Node node,FTC_Manager manager)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