1
/***************************************************************************/
5
/* The FreeType internal cache interface (body). */
7
/* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, */
9
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
11
/* This file is part of the FreeType project, and may only be used, */
12
/* modified, and distributed under the terms of the FreeType project */
13
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
14
/* this file you indicate that you have read the license and */
15
/* understand and accept it fully. */
17
/***************************************************************************/
22
#include FT_INTERNAL_OBJECTS_H
23
#include FT_INTERNAL_DEBUG_H
29
#define FT_COMPONENT trace_cache
32
#define FTC_HASH_MAX_LOAD 2
33
#define FTC_HASH_MIN_LOAD 1
34
#define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
36
/* this one _must_ be a power of 2! */
37
#define FTC_HASH_INITIAL_SIZE 8
40
/*************************************************************************/
41
/*************************************************************************/
43
/***** CACHE NODE DEFINITIONS *****/
45
/*************************************************************************/
46
/*************************************************************************/
48
/* add a new node to the head of the manager's circular MRU list */
50
ftc_node_mru_link( FTC_Node node,
53
void *nl = &manager->nodes_list;
56
FTC_MruNode_Prepend( (FTC_MruNode*)nl,
62
/* remove a node from the manager's MRU list */
64
ftc_node_mru_unlink( FTC_Node node,
67
void *nl = &manager->nodes_list;
70
FTC_MruNode_Remove( (FTC_MruNode*)nl,
78
/* move a node to the head of the manager's MRU list */
80
ftc_node_mru_up( FTC_Node node,
83
FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
88
/* get a top bucket for specified hash from cache,
89
* body for FTC_NODE__TOP_FOR_HASH( cache, hash )
91
FT_LOCAL_DEF( FTC_Node* )
92
ftc_get_top_node_for_hash( FTC_Cache cache,
99
idx = (FT_UInt)( hash & cache->mask );
100
if ( idx < cache->p )
101
idx = (FT_UInt)( hash & ( 2 * cache->mask + 1 ) );
102
pnode = cache->buckets + idx;
106
#endif /* !FTC_INLINE */
109
/* Note that this function cannot fail. If we cannot re-size the
110
* buckets array appropriately, we simply degrade the hash table's
114
ftc_cache_resize( FTC_Cache cache )
118
FTC_Node node, *pnode;
119
FT_UFast p = cache->p;
120
FT_UFast mask = cache->mask;
121
FT_UFast count = mask + p + 1; /* number of buckets */
124
/* do we need to shrink the buckets array? */
125
if ( cache->slack < 0 )
127
FTC_Node new_list = NULL;
130
/* try to expand the buckets array _before_ splitting
135
FT_Memory memory = cache->memory;
139
/* if we can't expand the array, leave immediately */
140
if ( FT_RENEW_ARRAY( cache->buckets,
141
( mask + 1 ) * 2, ( mask + 1 ) * 4 ) )
145
/* split a single bucket */
146
pnode = cache->buckets + p;
154
if ( node->hash & ( mask + 1 ) )
157
node->link = new_list;
164
cache->buckets[p + mask + 1] = new_list;
166
cache->slack += FTC_HASH_MAX_LOAD;
170
cache->mask = 2 * mask + 1;
177
/* do we need to expand the buckets array? */
178
else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
180
FT_UFast old_index = p + mask;
184
if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
189
FT_Memory memory = cache->memory;
193
/* if we can't shrink the array, leave immediately */
194
if ( FT_RENEW_ARRAY( cache->buckets,
195
( mask + 1 ) * 2, mask + 1 ) )
204
pnode = cache->buckets + p;
206
pnode = &(*pnode)->link;
208
pold = cache->buckets + old_index;
212
cache->slack -= FTC_HASH_MAX_LOAD;
216
/* otherwise, the hash table is balanced */
223
/* remove a node from its cache's hash table */
225
ftc_node_hash_unlink( FTC_Node node0,
228
FTC_Node *pnode = FTC_NODE__TOP_FOR_HASH( cache, node0->hash );
233
FTC_Node node = *pnode;
238
FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" ));
245
pnode = &(*pnode)->link;
248
*pnode = node0->link;
252
ftc_cache_resize( cache );
256
/* add a node to the `top' of its cache's hash table */
258
ftc_node_hash_link( FTC_Node node,
261
FTC_Node *pnode = FTC_NODE__TOP_FOR_HASH( cache, node->hash );
268
ftc_cache_resize( cache );
272
/* remove a node from the cache manager */
273
#ifdef FT_CONFIG_OPTION_OLD_INTERNALS
278
ftc_node_destroy( FTC_Node node,
279
FTC_Manager manager )
284
#ifdef FT_DEBUG_ERROR
285
/* find node's cache */
286
if ( node->cache_index >= manager->num_caches )
288
FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
293
cache = manager->caches[node->cache_index];
295
#ifdef FT_DEBUG_ERROR
298
FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
303
manager->cur_weight -= cache->clazz.node_weight( node, cache );
305
/* remove node from mru list */
306
ftc_node_mru_unlink( node, manager );
308
/* remove node from cache's hash table */
309
ftc_node_hash_unlink( node, cache );
311
/* now finalize it */
312
cache->clazz.node_free( node, cache );
315
/* check, just in case of general corruption :-) */
316
if ( manager->num_nodes == 0 )
317
FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n",
318
manager->num_nodes ));
323
/*************************************************************************/
324
/*************************************************************************/
326
/***** ABSTRACT CACHE CLASS *****/
328
/*************************************************************************/
329
/*************************************************************************/
332
FT_LOCAL_DEF( FT_Error )
333
FTC_Cache_Init( FTC_Cache cache )
335
return ftc_cache_init( cache );
339
FT_LOCAL_DEF( FT_Error )
340
ftc_cache_init( FTC_Cache cache )
342
FT_Memory memory = cache->memory;
347
cache->mask = FTC_HASH_INITIAL_SIZE - 1;
348
cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
350
(void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
356
FTC_Cache_Clear( FTC_Cache cache )
358
if ( cache && cache->buckets )
360
FTC_Manager manager = cache->manager;
365
count = cache->p + cache->mask + 1;
367
for ( i = 0; i < count; i++ )
369
FTC_Node *pnode = cache->buckets + i, next, node = *pnode;
377
/* remove node from mru list */
378
ftc_node_mru_unlink( node, manager );
380
/* now finalize it */
381
manager->cur_weight -= cache->clazz.node_weight( node, cache );
383
cache->clazz.node_free( node, cache );
386
cache->buckets[i] = NULL;
388
ftc_cache_resize( cache );
394
ftc_cache_done( FTC_Cache cache )
398
FT_Memory memory = cache->memory;
401
FTC_Cache_Clear( cache );
403
FT_FREE( cache->buckets );
408
cache->memory = NULL;
414
FTC_Cache_Done( FTC_Cache cache )
416
ftc_cache_done( cache );
421
ftc_cache_add( FTC_Cache cache,
426
node->cache_index = (FT_UInt16)cache->index;
429
ftc_node_hash_link( node, cache );
430
ftc_node_mru_link( node, cache->manager );
433
FTC_Manager manager = cache->manager;
436
manager->cur_weight += cache->clazz.node_weight( node, cache );
438
if ( manager->cur_weight >= manager->max_weight )
441
FTC_Manager_Compress( manager );
448
FT_LOCAL_DEF( FT_Error )
449
FTC_Cache_NewNode( FTC_Cache cache,
459
* We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
460
* errors (OOM) correctly, i.e., by flushing the cache progressively
461
* in order to make more room.
464
FTC_CACHE_TRYLOOP( cache )
466
error = cache->clazz.node_new( &node, query, cache );
468
FTC_CACHE_TRYLOOP_END( NULL );
474
/* don't assume that the cache has the same number of buckets, since
475
* our allocation request might have triggered global cache flushing
477
ftc_cache_add( cache, hash, node );
487
FT_LOCAL_DEF( FT_Error )
488
FTC_Cache_Lookup( FTC_Cache cache,
496
FT_Error error = FTC_Err_Ok;
497
FT_Bool list_changed = FALSE;
499
FTC_Node_CompareFunc compare = cache->clazz.node_compare;
502
if ( cache == NULL || anode == NULL )
503
return FTC_Err_Invalid_Argument;
505
/* Go to the `top' node of the list sharing same masked hash */
506
bucket = pnode = FTC_NODE__TOP_FOR_HASH( cache, hash );
508
/* Lookup a node with exactly same hash and queried properties. */
509
/* NOTE: _nodcomp() may change the linked list to reduce memory. */
516
if ( node->hash == hash &&
517
compare( node, query, cache, &list_changed ) )
525
/* Update bucket by modified linked list */
526
bucket = pnode = FTC_NODE__TOP_FOR_HASH( cache, hash );
528
/* Update pnode by modified linked list */
529
while ( *pnode != node )
531
if ( *pnode == NULL )
533
FT_ERROR(( "FTC_Cache_Lookup: oops!!! node missing\n" ));
537
pnode = &((*pnode)->link);
541
/* Reorder the list to move the found node to the `top' */
542
if ( node != *bucket )
545
node->link = *bucket;
549
/* move to head of MRU list */
551
FTC_Manager manager = cache->manager;
554
if ( node != manager->nodes_list )
555
ftc_node_mru_up( node, manager );
562
return FTC_Cache_NewNode( cache, hash, query, anode );
565
#endif /* !FTC_INLINE */
569
FTC_Cache_RemoveFaceID( FTC_Cache cache,
573
FTC_Manager manager = cache->manager;
574
FTC_Node frees = NULL;
577
count = cache->p + cache->mask + 1;
578
for ( i = 0; i < count; i++ )
580
FTC_Node* bucket = cache->buckets + i;
581
FTC_Node* pnode = bucket;
586
FTC_Node node = *pnode;
587
FT_Bool list_changed = FALSE;
593
if ( cache->clazz.node_remove_faceid( node, face_id,
594
cache, &list_changed ) )
605
/* remove all nodes in the free list */
614
manager->cur_weight -= cache->clazz.node_weight( node, cache );
615
ftc_node_mru_unlink( node, manager );
617
cache->clazz.node_free( node, cache );
622
ftc_cache_resize( cache );