1
/***************************************************************************/
5
/* The FreeType internal cache interface (body). */
7
/* Copyright 2000-2001, 2002, 2003, 2004 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
/***************************************************************************/
20
#include FT_CACHE_INTERNAL_MANAGER_H
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
FTC_MruNode_Prepend( (FTC_MruNode*)&manager->nodes_list,
55
/* remove a node from the manager's MRU list */
57
ftc_node_mru_unlink( FTC_Node node,
60
FTC_MruNode_Remove( (FTC_MruNode*)&manager->nodes_list,
66
/* move a node to the head of the manager's MRU list */
68
ftc_node_mru_up( FTC_Node node,
71
FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
76
/* Note that this function cannot fail. If we cannot re-size the
77
* buckets array appropriately, we simply degrade the hash table's
81
ftc_cache_resize( FTC_Cache cache )
85
FTC_Node node, *pnode;
87
FT_UInt mask = cache->mask;
88
FT_UInt count = mask + p + 1; /* number of buckets */
91
/* do we need to shrink the buckets array? */
92
if ( cache->slack < 0 )
94
FTC_Node new_list = NULL;
97
/* try to expand the buckets array _before_ splitting
102
FT_Memory memory = cache->memory;
105
/* if we can't expand the array, leave immediately */
106
if ( FT_MEM_RENEW_ARRAY( cache->buckets, (mask+1)*2, (mask+1)*4 ) )
110
/* split a single bucket */
111
pnode = cache->buckets + p;
119
if ( node->hash & ( mask + 1 ) )
122
node->link = new_list;
129
cache->buckets[p + mask + 1] = new_list;
131
cache->slack += FTC_HASH_MAX_LOAD;
135
cache->mask = 2 * mask + 1;
142
/* do we need to expand the buckets array? */
143
else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
145
FT_UInt old_index = p + mask;
149
if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
154
FT_Memory memory = cache->memory;
157
/* if we can't shrink the array, leave immediately */
158
if ( FT_MEM_RENEW_ARRAY( cache->buckets,
159
( mask + 1 ) * 2, mask + 1 ) )
168
pnode = cache->buckets + p;
170
pnode = &(*pnode)->link;
172
pold = cache->buckets + old_index;
176
cache->slack -= FTC_HASH_MAX_LOAD;
179
else /* the hash table is balanced */
185
/* remove a node from its cache's hash table */
187
ftc_node_hash_unlink( FTC_Node node0,
194
idx = (FT_UInt)( node0->hash & cache->mask );
195
if ( idx < cache->p )
196
idx = (FT_UInt)( node0->hash & ( 2 * cache->mask + 1 ) );
198
pnode = cache->buckets + idx;
202
FTC_Node node = *pnode;
207
FT_ERROR(( "ftc_node_hash_unlink: unknown node!\n" ));
214
pnode = &(*pnode)->link;
217
*pnode = node0->link;
221
ftc_cache_resize( cache );
225
/* add a node to the `top' of its cache's hash table */
227
ftc_node_hash_link( FTC_Node node,
234
idx = (FT_UInt)( node->hash & cache->mask );
235
if ( idx < cache->p )
236
idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) );
238
pnode = cache->buckets + idx;
244
ftc_cache_resize( cache );
248
/* remove a node from the cache manager */
249
FT_EXPORT_DEF( void )
250
ftc_node_destroy( FTC_Node node,
251
FTC_Manager manager )
256
#ifdef FT_DEBUG_ERROR
257
/* find node's cache */
258
if ( node->cache_index >= manager->num_caches )
260
FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
265
cache = manager->caches[node->cache_index];
267
#ifdef FT_DEBUG_ERROR
270
FT_ERROR(( "ftc_node_destroy: invalid node handle\n" ));
275
manager->cur_weight -= cache->clazz.node_weight( node, cache );
277
/* remove node from mru list */
278
ftc_node_mru_unlink( node, manager );
280
/* remove node from cache's hash table */
281
ftc_node_hash_unlink( node, cache );
283
/* now finalize it */
284
cache->clazz.node_free( node, cache );
287
/* check, just in case of general corruption :-) */
288
if ( manager->num_nodes == 0 )
289
FT_ERROR(( "ftc_node_destroy: invalid cache node count! = %d\n",
290
manager->num_nodes ));
295
/*************************************************************************/
296
/*************************************************************************/
298
/***** ABSTRACT CACHE CLASS *****/
300
/*************************************************************************/
301
/*************************************************************************/
304
FT_EXPORT_DEF( FT_Error )
305
FTC_Cache_Init( FTC_Cache cache )
307
return ftc_cache_init( cache );
311
FT_LOCAL_DEF( FT_Error )
312
ftc_cache_init( FTC_Cache cache )
314
FT_Memory memory = cache->memory;
318
cache->mask = FTC_HASH_INITIAL_SIZE - 1;
319
cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
321
return ( FT_MEM_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 ) );
325
FT_EXPORT_DEF( void )
326
FTC_Cache_Clear( FTC_Cache cache )
330
FTC_Manager manager = cache->manager;
335
count = cache->p + cache->mask + 1;
337
for ( i = 0; i < count; i++ )
339
FTC_Node *pnode = cache->buckets + i, next, node = *pnode;
347
/* remove node from mru list */
348
ftc_node_mru_unlink( node, manager );
350
/* now finalize it */
351
manager->cur_weight -= cache->clazz.node_weight( node, cache );
353
cache->clazz.node_free( node, cache );
356
cache->buckets[i] = NULL;
358
ftc_cache_resize( cache );
364
ftc_cache_done( FTC_Cache cache )
368
FT_Memory memory = cache->memory;
371
FTC_Cache_Clear( cache );
373
FT_FREE( cache->buckets );
378
cache->memory = NULL;
383
FT_EXPORT_DEF( void )
384
FTC_Cache_Done( FTC_Cache cache )
386
ftc_cache_done( cache );
391
ftc_cache_add( FTC_Cache cache,
396
node->cache_index = (FT_UInt16) cache->index;
399
ftc_node_hash_link( node, cache );
400
ftc_node_mru_link( node, cache->manager );
403
FTC_Manager manager = cache->manager;
406
manager->cur_weight += cache->clazz.node_weight( node, cache );
408
if ( manager->cur_weight >= manager->max_weight )
411
FTC_Manager_Compress( manager );
418
FT_EXPORT_DEF( FT_Error )
419
FTC_Cache_NewNode( FTC_Cache cache,
428
* Try to allocate a new cache node. Note that in case of
429
* out-of-memory error (OOM), we'll flush the cache a bit,
432
* On each try, the `tries' variable gives the number
433
* of old nodes we want to flush from the manager's global list
434
* before the next allocation attempt. It barely doubles on
438
error = cache->clazz.node_new( &node, query, cache );
443
/* don't assume that the cache has the same number of buckets, since
444
* our allocation request might have triggered global cache flushing
446
ftc_cache_add( cache, hash, node );
454
if ( error != FT_Err_Out_Of_Memory )
458
FTC_Manager manager = cache->manager;
459
FT_UInt count, tries = 1;
464
error = cache->clazz.node_new( &node, query, cache );
469
if ( error != FT_Err_Out_Of_Memory )
472
count = FTC_Manager_FlushN( manager, tries );
476
if ( count == tries )
479
if ( count < tries || count > manager->num_nodes )
480
count = manager->num_nodes;
489
FT_EXPORT_DEF( FT_Error )
490
FTC_Cache_Lookup( FTC_Cache cache,
501
FTC_Node_CompareFunc compare = cache->clazz.node_compare;
504
if ( cache == NULL || anode == NULL )
505
return FT_Err_Invalid_Argument;
507
idx = hash & cache->mask;
508
if ( idx < cache->p )
509
idx = hash & ( cache->mask * 2 + 1 );
511
bucket = cache->buckets + idx;
519
if ( node->hash == hash && compare( node, query, cache ) )
525
if ( node != *bucket )
528
node->link = *bucket;
532
/* move to head of MRU list */
534
FTC_Manager manager = cache->manager;
537
if ( node != manager->nodes_list )
538
ftc_node_mru_up( node, manager );
544
return FTC_Cache_NewNode( cache, hash, query, anode );
548
FT_EXPORT_DEF( void )
549
FTC_Cache_RemoveFaceID( FTC_Cache cache,
553
FTC_Manager manager = cache->manager;
554
FTC_Node frees = NULL;
557
count = cache->p + cache->mask;
558
for ( i = 0; i < count; i++ )
560
FTC_Node* bucket = cache->buckets + i;
561
FTC_Node* pnode = bucket;
566
FTC_Node node = *pnode;
572
if ( cache->clazz.node_remove_faceid( node, face_id, cache ) )
583
/* remove all nodes in the free list */
592
manager->cur_weight -= cache->clazz.node_weight( node, cache );
593
ftc_node_mru_unlink( node, manager );
595
cache->clazz.node_free( node, cache );
600
ftc_cache_resize( cache );