Imported Upstream version 2.13.2
[platform/upstream/freetype2.git] / src / cache / ftccache.h
1 /****************************************************************************
2  *
3  * ftccache.h
4  *
5  *   FreeType internal cache interface (specification).
6  *
7  * Copyright (C) 2000-2023 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 #ifndef FTCCACHE_H_
20 #define FTCCACHE_H_
21
22 #include <freetype/internal/compiler-macros.h>
23 #include "ftcmru.h"
24
25 FT_BEGIN_HEADER
26
27 #define FTC_FACE_ID_HASH( i )                                  \
28          ( ( (FT_Offset)(i) >> 3 ) ^ ( (FT_Offset)(i) << 7 ) )
29
30   /* handle to cache object */
31   typedef struct FTC_CacheRec_*  FTC_Cache;
32
33   /* handle to cache class */
34   typedef const struct FTC_CacheClassRec_*  FTC_CacheClass;
35
36
37   /*************************************************************************/
38   /*************************************************************************/
39   /*****                                                               *****/
40   /*****                   CACHE NODE DEFINITIONS                      *****/
41   /*****                                                               *****/
42   /*************************************************************************/
43   /*************************************************************************/
44
45   /**************************************************************************
46    *
47    * Each cache controls one or more cache nodes.  Each node is part of
48    * the global_lru list of the manager.  Its `data' field however is used
49    * as a reference count for now.
50    *
51    * A node can be anything, depending on the type of information held by
52    * the cache.  It can be an individual glyph image, a set of bitmaps
53    * glyphs for a given size, some metrics, etc.
54    *
55    */
56
57   /* structure size should be 20 bytes on 32-bits machines */
58   typedef struct  FTC_NodeRec_
59   {
60     FTC_MruNodeRec  mru;          /* circular mru list pointer           */
61     FTC_Node        link;         /* used for hashing                    */
62     FT_Offset       hash;         /* used for hashing too                */
63     FT_UShort       cache_index;  /* index of cache the node belongs to  */
64     FT_Short        ref_count;    /* reference count for this node       */
65
66   } FTC_NodeRec;
67
68
69 #define FTC_NODE( x )    ( (FTC_Node)(x) )
70 #define FTC_NODE_P( x )  ( (FTC_Node*)(x) )
71
72 #define FTC_NODE_NEXT( x )  FTC_NODE( (x)->mru.next )
73 #define FTC_NODE_PREV( x )  FTC_NODE( (x)->mru.prev )
74
75   /* address the hash table entries */
76 #ifdef FTC_INLINE
77 #define FTC_NODE_TOP_FOR_HASH( cache, hash )                       \
78         ( ( cache )->buckets +                                     \
79             ( ( ( ( hash ) &   ( cache )->mask ) >= ( cache )->p ) \
80               ? ( ( hash ) & ( ( cache )->mask >> 1 ) )            \
81               : ( ( hash ) &   ( cache )->mask ) ) )
82 #else
83   FT_LOCAL( FTC_Node* )
84   ftc_get_top_node_for_hash( FTC_Cache  cache,
85                              FT_Offset  hash );
86 #define FTC_NODE_TOP_FOR_HASH( cache, hash )             \
87         ftc_get_top_node_for_hash( ( cache ), ( hash ) )
88 #endif
89
90
91   /*************************************************************************/
92   /*************************************************************************/
93   /*****                                                               *****/
94   /*****                       CACHE DEFINITIONS                       *****/
95   /*****                                                               *****/
96   /*************************************************************************/
97   /*************************************************************************/
98
99   /* initialize a new cache node */
100   typedef FT_Error
101   (*FTC_Node_NewFunc)( FTC_Node    *pnode,
102                        FT_Pointer   query,
103                        FTC_Cache    cache );
104
105   typedef FT_Offset
106   (*FTC_Node_WeightFunc)( FTC_Node   node,
107                           FTC_Cache  cache );
108
109   /* compare a node to a given key pair */
110   typedef FT_Bool
111   (*FTC_Node_CompareFunc)( FTC_Node    node,
112                            FT_Pointer  key,
113                            FTC_Cache   cache,
114                            FT_Bool*    list_changed );
115
116
117   typedef void
118   (*FTC_Node_FreeFunc)( FTC_Node   node,
119                         FTC_Cache  cache );
120
121   typedef FT_Error
122   (*FTC_Cache_InitFunc)( FTC_Cache  cache );
123
124   typedef void
125   (*FTC_Cache_DoneFunc)( FTC_Cache  cache );
126
127
128   typedef struct  FTC_CacheClassRec_
129   {
130     FTC_Node_NewFunc      node_new;
131     FTC_Node_WeightFunc   node_weight;
132     FTC_Node_CompareFunc  node_compare;
133     FTC_Node_CompareFunc  node_remove_faceid;
134     FTC_Node_FreeFunc     node_free;
135
136     FT_Offset             cache_size;
137     FTC_Cache_InitFunc    cache_init;
138     FTC_Cache_DoneFunc    cache_done;
139
140   } FTC_CacheClassRec;
141
142
143   /* each cache really implements a hash table to manage its nodes    */
144   /* the number of the table entries (buckets) can change dynamically */
145   /* each bucket contains a linked lists of nodes for a given hash    */
146   typedef struct  FTC_CacheRec_
147   {
148     FT_UFast           p;           /* hash table counter     */
149     FT_UFast           mask;        /* hash table index range */
150     FT_Long            slack;
151     FTC_Node*          buckets;
152
153     FTC_CacheClassRec  clazz;       /* local copy, for speed  */
154
155     FTC_Manager        manager;
156     FT_Memory          memory;
157     FT_UInt            index;       /* in manager's table     */
158
159     FTC_CacheClass     org_class;   /* original class pointer */
160
161   } FTC_CacheRec;
162
163
164 #define FTC_CACHE( x )    ( (FTC_Cache)(x) )
165 #define FTC_CACHE_P( x )  ( (FTC_Cache*)(x) )
166
167
168   /* default cache initialize */
169   FT_LOCAL( FT_Error )
170   FTC_Cache_Init( FTC_Cache  cache );
171
172   /* default cache finalizer */
173   FT_LOCAL( void )
174   FTC_Cache_Done( FTC_Cache  cache );
175
176   /* Call this function to look up the cache.  If no corresponding
177    * node is found, a new one is automatically created.  This function
178    * is capable of flushing the cache adequately to make room for the
179    * new cache object.
180    */
181
182 #ifndef FTC_INLINE
183   FT_LOCAL( FT_Error )
184   FTC_Cache_Lookup( FTC_Cache   cache,
185                     FT_Offset   hash,
186                     FT_Pointer  query,
187                     FTC_Node   *anode );
188 #endif
189
190   FT_LOCAL( FT_Error )
191   FTC_Cache_NewNode( FTC_Cache   cache,
192                      FT_Offset   hash,
193                      FT_Pointer  query,
194                      FTC_Node   *anode );
195
196   /* Remove all nodes that relate to a given face_id.  This is useful
197    * when un-installing fonts.  Note that if a cache node relates to
198    * the face_id but is locked (i.e., has `ref_count > 0'), the node
199    * will _not_ be destroyed, but its internal face_id reference will
200    * be modified.
201    *
202    * The final result will be that the node will never come back
203    * in further lookup requests, and will be flushed on demand from
204    * the cache normally when its reference count reaches 0.
205    */
206   FT_LOCAL( void )
207   FTC_Cache_RemoveFaceID( FTC_Cache   cache,
208                           FTC_FaceID  face_id );
209
210
211 #ifdef FTC_INLINE
212
213 #define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \
214   FT_BEGIN_STMNT                                                         \
215     FTC_Node             *_bucket, *_pnode, _node;                       \
216     FTC_Cache             _cache   = FTC_CACHE( cache );                 \
217     FT_Offset             _hash    = (FT_Offset)(hash);                  \
218     FTC_Node_CompareFunc  _nodcomp = (FTC_Node_CompareFunc)(nodecmp);    \
219     FT_Bool               _list_changed = FALSE;                         \
220                                                                          \
221                                                                          \
222     error = FT_Err_Ok;                                                   \
223     node  = NULL;                                                        \
224                                                                          \
225     /* Go to the `top' node of the list sharing same masked hash */      \
226     _bucket = _pnode = FTC_NODE_TOP_FOR_HASH( _cache, _hash );           \
227                                                                          \
228     /* Look up a node with identical hash and queried properties.    */  \
229     /* NOTE: _nodcomp() may change the linked list to reduce memory. */  \
230     for (;;)                                                             \
231     {                                                                    \
232       _node = *_pnode;                                                   \
233       if ( !_node )                                                      \
234         goto NewNode_;                                                   \
235                                                                          \
236       if ( _node->hash == _hash                             &&           \
237            _nodcomp( _node, query, _cache, &_list_changed ) )            \
238         break;                                                           \
239                                                                          \
240       _pnode = &_node->link;                                             \
241     }                                                                    \
242                                                                          \
243     if ( _list_changed )                                                 \
244     {                                                                    \
245       /* Update _bucket by possibly modified linked list */              \
246       _bucket = _pnode = FTC_NODE_TOP_FOR_HASH( _cache, _hash );         \
247                                                                          \
248       /* Update _pnode by possibly modified linked list */               \
249       while ( *_pnode != _node )                                         \
250       {                                                                  \
251         if ( !*_pnode )                                                  \
252         {                                                                \
253           FT_ERROR(( "FTC_CACHE_LOOKUP_CMP: oops!!! node missing\n" ));  \
254           goto NewNode_;                                                 \
255         }                                                                \
256         else                                                             \
257           _pnode = &(*_pnode)->link;                                     \
258       }                                                                  \
259     }                                                                    \
260                                                                          \
261     /* Reorder the list to move the found node to the `top' */           \
262     if ( _node != *_bucket )                                             \
263     {                                                                    \
264       *_pnode     = _node->link;                                         \
265       _node->link = *_bucket;                                            \
266       *_bucket    = _node;                                               \
267     }                                                                    \
268                                                                          \
269     /* Update MRU list */                                                \
270     {                                                                    \
271       FTC_Manager  _manager = _cache->manager;                           \
272       void*        _nl      = &_manager->nodes_list;                     \
273                                                                          \
274                                                                          \
275       if ( _node != _manager->nodes_list )                               \
276         FTC_MruNode_Up( (FTC_MruNode*)_nl,                               \
277                         (FTC_MruNode)_node );                            \
278     }                                                                    \
279     goto Ok_;                                                            \
280                                                                          \
281   NewNode_:                                                              \
282     error = FTC_Cache_NewNode( _cache, _hash, query, &_node );           \
283                                                                          \
284   Ok_:                                                                   \
285     node = _node;                                                        \
286   FT_END_STMNT
287
288 #else /* !FTC_INLINE */
289
290 #define FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error ) \
291   FT_BEGIN_STMNT                                                         \
292     error = FTC_Cache_Lookup( FTC_CACHE( cache ), hash, query,           \
293                               (FTC_Node*)&(node) );                      \
294   FT_END_STMNT
295
296 #endif /* !FTC_INLINE */
297
298
299   /*
300    * This macro, together with FTC_CACHE_TRYLOOP_END, defines a retry
301    * loop to flush the cache repeatedly in case of memory overflows.
302    *
303    * It is used when creating a new cache node, or within a lookup
304    * that needs to allocate data (e.g. the sbit cache lookup).
305    *
306    * Example:
307    *
308    * {
309    *   FTC_CACHE_TRYLOOP( cache )
310    *     error = load_data( ... );
311    *   FTC_CACHE_TRYLOOP_END()
312    * }
313    *
314    */
315 #define FTC_CACHE_TRYLOOP( cache )                           \
316   {                                                          \
317     FTC_Manager  _try_manager = FTC_CACHE( cache )->manager; \
318     FT_UInt      _try_count   = 4;                           \
319                                                              \
320                                                              \
321     for (;;)                                                 \
322     {                                                        \
323       FT_UInt  _try_done;
324
325
326 #define FTC_CACHE_TRYLOOP_END( list_changed )                     \
327       if ( !error || FT_ERR_NEQ( error, Out_Of_Memory ) )         \
328         break;                                                    \
329                                                                   \
330       _try_done = FTC_Manager_FlushN( _try_manager, _try_count ); \
331       if ( _try_done > 0 && list_changed != NULL )                \
332         *(FT_Bool*)( list_changed ) = TRUE;                       \
333                                                                   \
334       if ( _try_done == 0 )                                       \
335         break;                                                    \
336                                                                   \
337       if ( _try_done == _try_count )                              \
338       {                                                           \
339         _try_count *= 2;                                          \
340         if ( _try_count < _try_done              ||               \
341             _try_count > _try_manager->num_nodes )                \
342           _try_count = _try_manager->num_nodes;                   \
343       }                                                           \
344     }                                                             \
345   }
346
347  /* */
348
349 FT_END_HEADER
350
351
352 #endif /* FTCCACHE_H_ */
353
354
355 /* END */