1 /***************************************************************************/
5 /* The FreeType internal cache interface (body). */
7 /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
21 #include FT_INTERNAL_OBJECTS_H
22 #include FT_INTERNAL_DEBUG_H
28 #define FTC_HASH_MAX_LOAD 2
29 #define FTC_HASH_MIN_LOAD 1
30 #define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
32 /* this one _must_ be a power of 2! */
33 #define FTC_HASH_INITIAL_SIZE 8
36 /*************************************************************************/
37 /*************************************************************************/
39 /***** CACHE NODE DEFINITIONS *****/
41 /*************************************************************************/
42 /*************************************************************************/
44 /* add a new node to the head of the manager's circular MRU list */
46 ftc_node_mru_link( FTC_Node node
,
49 void *nl
= &manager
->nodes_list
;
52 FTC_MruNode_Prepend( (FTC_MruNode
*)nl
,
58 /* remove a node from the manager's MRU list */
60 ftc_node_mru_unlink( FTC_Node node
,
63 void *nl
= &manager
->nodes_list
;
66 FTC_MruNode_Remove( (FTC_MruNode
*)nl
,
74 /* move a node to the head of the manager's MRU list */
76 ftc_node_mru_up( FTC_Node node
,
79 FTC_MruNode_Up( (FTC_MruNode
*)&manager
->nodes_list
,
83 #endif /* !FTC_INLINE */
86 /* Note that this function cannot fail. If we cannot re-size the
87 * buckets array appropriately, we simply degrade the hash table's
91 ftc_cache_resize( FTC_Cache cache
)
95 FTC_Node node
, *pnode
;
97 FT_UInt mask
= cache
->mask
;
98 FT_UInt count
= mask
+ p
+ 1; /* number of buckets */
101 /* do we need to shrink the buckets array? */
102 if ( cache
->slack
< 0 )
104 FTC_Node new_list
= NULL
;
107 /* try to expand the buckets array _before_ splitting
112 FT_Memory memory
= cache
->memory
;
116 /* if we can't expand the array, leave immediately */
117 if ( FT_RENEW_ARRAY( cache
->buckets
, (mask
+1)*2, (mask
+1)*4 ) )
121 /* split a single bucket */
122 pnode
= cache
->buckets
+ p
;
130 if ( node
->hash
& ( mask
+ 1 ) )
133 node
->link
= new_list
;
140 cache
->buckets
[p
+ mask
+ 1] = new_list
;
142 cache
->slack
+= FTC_HASH_MAX_LOAD
;
146 cache
->mask
= 2 * mask
+ 1;
153 /* do we need to expand the buckets array? */
154 else if ( cache
->slack
> (FT_Long
)count
* FTC_HASH_SUB_LOAD
)
156 FT_UInt old_index
= p
+ mask
;
160 if ( old_index
+ 1 <= FTC_HASH_INITIAL_SIZE
)
165 FT_Memory memory
= cache
->memory
;
169 /* if we can't shrink the array, leave immediately */
170 if ( FT_RENEW_ARRAY( cache
->buckets
,
171 ( mask
+ 1 ) * 2, mask
+ 1 ) )
180 pnode
= cache
->buckets
+ p
;
182 pnode
= &(*pnode
)->link
;
184 pold
= cache
->buckets
+ old_index
;
188 cache
->slack
-= FTC_HASH_MAX_LOAD
;
191 else /* the hash table is balanced */
197 /* remove a node from its cache's hash table */
199 ftc_node_hash_unlink( FTC_Node node0
,
206 idx
= (FT_UInt
)( node0
->hash
& cache
->mask
);
207 if ( idx
< cache
->p
)
208 idx
= (FT_UInt
)( node0
->hash
& ( 2 * cache
->mask
+ 1 ) );
210 pnode
= cache
->buckets
+ idx
;
214 FTC_Node node
= *pnode
;
219 FT_ERROR(( "ftc_node_hash_unlink: unknown node!\n" ));
226 pnode
= &(*pnode
)->link
;
229 *pnode
= node0
->link
;
233 ftc_cache_resize( cache
);
237 /* add a node to the `top' of its cache's hash table */
239 ftc_node_hash_link( FTC_Node node
,
246 idx
= (FT_UInt
)( node
->hash
& cache
->mask
);
247 if ( idx
< cache
->p
)
248 idx
= (FT_UInt
)( node
->hash
& (2 * cache
->mask
+ 1 ) );
250 pnode
= cache
->buckets
+ idx
;
256 ftc_cache_resize( cache
);
260 /* remove a node from the cache manager */
261 #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
266 ftc_node_destroy( FTC_Node node
,
267 FTC_Manager manager
)
272 #ifdef FT_DEBUG_ERROR
273 /* find node's cache */
274 if ( node
->cache_index
>= manager
->num_caches
)
276 FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
281 cache
= manager
->caches
[node
->cache_index
];
283 #ifdef FT_DEBUG_ERROR
286 FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
291 manager
->cur_weight
-= cache
->clazz
.node_weight( node
, cache
);
293 /* remove node from mru list */
294 ftc_node_mru_unlink( node
, manager
);
296 /* remove node from cache's hash table */
297 ftc_node_hash_unlink( node
, cache
);
299 /* now finalize it */
300 cache
->clazz
.node_free( node
, cache
);
303 /* check, just in case of general corruption :-) */
304 if ( manager
->num_nodes
== 0 )
305 FT_ERROR(( "ftc_node_destroy: invalid cache node count! = %d\n",
306 manager
->num_nodes
));
311 /*************************************************************************/
312 /*************************************************************************/
314 /***** ABSTRACT CACHE CLASS *****/
316 /*************************************************************************/
317 /*************************************************************************/
320 FT_LOCAL_DEF( FT_Error
)
321 FTC_Cache_Init( FTC_Cache cache
)
323 return ftc_cache_init( cache
);
327 FT_LOCAL_DEF( FT_Error
)
328 ftc_cache_init( FTC_Cache cache
)
330 FT_Memory memory
= cache
->memory
;
335 cache
->mask
= FTC_HASH_INITIAL_SIZE
- 1;
336 cache
->slack
= FTC_HASH_INITIAL_SIZE
* FTC_HASH_MAX_LOAD
;
338 (void)FT_NEW_ARRAY( cache
->buckets
, FTC_HASH_INITIAL_SIZE
* 2 );
344 FTC_Cache_Clear( FTC_Cache cache
)
348 FTC_Manager manager
= cache
->manager
;
353 count
= cache
->p
+ cache
->mask
+ 1;
355 for ( i
= 0; i
< count
; i
++ )
357 FTC_Node
*pnode
= cache
->buckets
+ i
, next
, node
= *pnode
;
365 /* remove node from mru list */
366 ftc_node_mru_unlink( node
, manager
);
368 /* now finalize it */
369 manager
->cur_weight
-= cache
->clazz
.node_weight( node
, cache
);
371 cache
->clazz
.node_free( node
, cache
);
374 cache
->buckets
[i
] = NULL
;
376 ftc_cache_resize( cache
);
382 ftc_cache_done( FTC_Cache cache
)
386 FT_Memory memory
= cache
->memory
;
389 FTC_Cache_Clear( cache
);
391 FT_FREE( cache
->buckets
);
396 cache
->memory
= NULL
;
402 FTC_Cache_Done( FTC_Cache cache
)
404 ftc_cache_done( cache
);
409 ftc_cache_add( FTC_Cache cache
,
414 node
->cache_index
= (FT_UInt16
) cache
->index
;
417 ftc_node_hash_link( node
, cache
);
418 ftc_node_mru_link( node
, cache
->manager
);
421 FTC_Manager manager
= cache
->manager
;
424 manager
->cur_weight
+= cache
->clazz
.node_weight( node
, cache
);
426 if ( manager
->cur_weight
>= manager
->max_weight
)
429 FTC_Manager_Compress( manager
);
436 FT_LOCAL_DEF( FT_Error
)
437 FTC_Cache_NewNode( FTC_Cache cache
,
447 * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
448 * errors (OOM) correctly, i.e., by flushing the cache progressively
449 * in order to make more room.
452 FTC_CACHE_TRYLOOP( cache
)
454 error
= cache
->clazz
.node_new( &node
, query
, cache
);
456 FTC_CACHE_TRYLOOP_END();
462 /* don't assume that the cache has the same number of buckets, since
463 * our allocation request might have triggered global cache flushing
465 ftc_cache_add( cache
, hash
, node
);
475 FT_LOCAL_DEF( FT_Error
)
476 FTC_Cache_Lookup( FTC_Cache cache
,
487 FTC_Node_CompareFunc compare
= cache
->clazz
.node_compare
;
490 if ( cache
== NULL
|| anode
== NULL
)
491 return FT_Err_Invalid_Argument
;
493 idx
= hash
& cache
->mask
;
494 if ( idx
< cache
->p
)
495 idx
= hash
& ( cache
->mask
* 2 + 1 );
497 bucket
= cache
->buckets
+ idx
;
505 if ( node
->hash
== hash
&& compare( node
, query
, cache
) )
511 if ( node
!= *bucket
)
514 node
->link
= *bucket
;
518 /* move to head of MRU list */
520 FTC_Manager manager
= cache
->manager
;
523 if ( node
!= manager
->nodes_list
)
524 ftc_node_mru_up( node
, manager
);
530 return FTC_Cache_NewNode( cache
, hash
, query
, anode
);
533 #endif /* !FTC_INLINE */
537 FTC_Cache_RemoveFaceID( FTC_Cache cache
,
541 FTC_Manager manager
= cache
->manager
;
542 FTC_Node frees
= NULL
;
545 count
= cache
->p
+ cache
->mask
;
546 for ( i
= 0; i
< count
; i
++ )
548 FTC_Node
* bucket
= cache
->buckets
+ i
;
549 FTC_Node
* pnode
= bucket
;
554 FTC_Node node
= *pnode
;
560 if ( cache
->clazz
.node_remove_faceid( node
, face_id
, cache
) )
571 /* remove all nodes in the free list */
580 manager
->cur_weight
-= cache
->clazz
.node_weight( node
, cache
);
581 ftc_node_mru_unlink( node
, manager
);
583 cache
->clazz
.node_free( node
, cache
);
588 ftc_cache_resize( cache
);