Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenftccache.c
Go to the documentation of this file.
00001 /***************************************************************************/ 00002 /* */ 00003 /* ftccache.c */ 00004 /* */ 00005 /* The FreeType internal cache interface (body). */ 00006 /* */ 00007 /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 by */ 00008 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 00009 /* */ 00010 /* This file is part of the FreeType project, and may only be used, */ 00011 /* modified, and distributed under the terms of the FreeType project */ 00012 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 00013 /* this file you indicate that you have read the license and */ 00014 /* understand and accept it fully. */ 00015 /* */ 00016 /***************************************************************************/ 00017 00018 00019 #include <ft2build.h> 00020 #include "ftcmanag.h" 00021 #include FT_INTERNAL_OBJECTS_H 00022 #include FT_INTERNAL_DEBUG_H 00023 00024 #include "ftccback.h" 00025 #include "ftcerror.h" 00026 00027 #undef FT_COMPONENT 00028 #define FT_COMPONENT trace_cache 00029 00030 00031 #define FTC_HASH_MAX_LOAD 2 00032 #define FTC_HASH_MIN_LOAD 1 00033 #define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD ) 00034 00035 /* this one _must_ be a power of 2! */ 00036 #define FTC_HASH_INITIAL_SIZE 8 00037 00038 00039 /*************************************************************************/ 00040 /*************************************************************************/ 00041 /***** *****/ 00042 /***** CACHE NODE DEFINITIONS *****/ 00043 /***** *****/ 00044 /*************************************************************************/ 00045 /*************************************************************************/ 00046 00047 /* add a new node to the head of the manager's circular MRU list */ 00048 static void 00049 ftc_node_mru_link( FTC_Node node, 00050 FTC_Manager manager ) 00051 { 00052 void *nl = &manager->nodes_list; 00053 00054 00055 FTC_MruNode_Prepend( (FTC_MruNode*)nl, 00056 (FTC_MruNode)node ); 00057 manager->num_nodes++; 00058 } 00059 00060 00061 /* remove a node from the manager's MRU list */ 00062 static void 00063 ftc_node_mru_unlink( FTC_Node node, 00064 FTC_Manager manager ) 00065 { 00066 void *nl = &manager->nodes_list; 00067 00068 00069 FTC_MruNode_Remove( (FTC_MruNode*)nl, 00070 (FTC_MruNode)node ); 00071 manager->num_nodes--; 00072 } 00073 00074 00075 #ifndef FTC_INLINE 00076 00077 /* move a node to the head of the manager's MRU list */ 00078 static void 00079 ftc_node_mru_up( FTC_Node node, 00080 FTC_Manager manager ) 00081 { 00082 FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list, 00083 (FTC_MruNode)node ); 00084 } 00085 00086 #endif /* !FTC_INLINE */ 00087 00088 00089 /* Note that this function cannot fail. If we cannot re-size the 00090 * buckets array appropriately, we simply degrade the hash table's 00091 * performance! 00092 */ 00093 static void 00094 ftc_cache_resize( FTC_Cache cache ) 00095 { 00096 for (;;) 00097 { 00098 FTC_Node node, *pnode; 00099 FT_UFast p = cache->p; 00100 FT_UFast mask = cache->mask; 00101 FT_UFast count = mask + p + 1; /* number of buckets */ 00102 00103 00104 /* do we need to shrink the buckets array? */ 00105 if ( cache->slack < 0 ) 00106 { 00107 FTC_Node new_list = NULL; 00108 00109 00110 /* try to expand the buckets array _before_ splitting 00111 * the bucket lists 00112 */ 00113 if ( p >= mask ) 00114 { 00115 FT_Memory memory = cache->memory; 00116 FT_Error error; 00117 00118 00119 /* if we can't expand the array, leave immediately */ 00120 if ( FT_RENEW_ARRAY( cache->buckets, (mask+1)*2, (mask+1)*4 ) ) 00121 break; 00122 } 00123 00124 /* split a single bucket */ 00125 pnode = cache->buckets + p; 00126 00127 for (;;) 00128 { 00129 node = *pnode; 00130 if ( node == NULL ) 00131 break; 00132 00133 if ( node->hash & ( mask + 1 ) ) 00134 { 00135 *pnode = node->link; 00136 node->link = new_list; 00137 new_list = node; 00138 } 00139 else 00140 pnode = &node->link; 00141 } 00142 00143 cache->buckets[p + mask + 1] = new_list; 00144 00145 cache->slack += FTC_HASH_MAX_LOAD; 00146 00147 if ( p >= mask ) 00148 { 00149 cache->mask = 2 * mask + 1; 00150 cache->p = 0; 00151 } 00152 else 00153 cache->p = p + 1; 00154 } 00155 00156 /* do we need to expand the buckets array? */ 00157 else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD ) 00158 { 00159 FT_UFast old_index = p + mask; 00160 FTC_Node* pold; 00161 00162 00163 if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE ) 00164 break; 00165 00166 if ( p == 0 ) 00167 { 00168 FT_Memory memory = cache->memory; 00169 FT_Error error; 00170 00171 00172 /* if we can't shrink the array, leave immediately */ 00173 if ( FT_RENEW_ARRAY( cache->buckets, 00174 ( mask + 1 ) * 2, mask + 1 ) ) 00175 break; 00176 00177 cache->mask >>= 1; 00178 p = cache->mask; 00179 } 00180 else 00181 p--; 00182 00183 pnode = cache->buckets + p; 00184 while ( *pnode ) 00185 pnode = &(*pnode)->link; 00186 00187 pold = cache->buckets + old_index; 00188 *pnode = *pold; 00189 *pold = NULL; 00190 00191 cache->slack -= FTC_HASH_MAX_LOAD; 00192 cache->p = p; 00193 } 00194 else /* the hash table is balanced */ 00195 break; 00196 } 00197 } 00198 00199 00200 /* remove a node from its cache's hash table */ 00201 static void 00202 ftc_node_hash_unlink( FTC_Node node0, 00203 FTC_Cache cache ) 00204 { 00205 FTC_Node *pnode; 00206 FT_UInt idx; 00207 00208 00209 idx = (FT_UInt)( node0->hash & cache->mask ); 00210 if ( idx < cache->p ) 00211 idx = (FT_UInt)( node0->hash & ( 2 * cache->mask + 1 ) ); 00212 00213 pnode = cache->buckets + idx; 00214 00215 for (;;) 00216 { 00217 FTC_Node node = *pnode; 00218 00219 00220 if ( node == NULL ) 00221 { 00222 FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" )); 00223 return; 00224 } 00225 00226 if ( node == node0 ) 00227 break; 00228 00229 pnode = &(*pnode)->link; 00230 } 00231 00232 *pnode = node0->link; 00233 node0->link = NULL; 00234 00235 cache->slack++; 00236 ftc_cache_resize( cache ); 00237 } 00238 00239 00240 /* add a node to the `top' of its cache's hash table */ 00241 static void 00242 ftc_node_hash_link( FTC_Node node, 00243 FTC_Cache cache ) 00244 { 00245 FTC_Node *pnode; 00246 FT_UInt idx; 00247 00248 00249 idx = (FT_UInt)( node->hash & cache->mask ); 00250 if ( idx < cache->p ) 00251 idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) ); 00252 00253 pnode = cache->buckets + idx; 00254 00255 node->link = *pnode; 00256 *pnode = node; 00257 00258 cache->slack--; 00259 ftc_cache_resize( cache ); 00260 } 00261 00262 00263 /* remove a node from the cache manager */ 00264 #ifdef FT_CONFIG_OPTION_OLD_INTERNALS 00265 FT_BASE_DEF( void ) 00266 #else 00267 FT_LOCAL_DEF( void ) 00268 #endif 00269 ftc_node_destroy( FTC_Node node, 00270 FTC_Manager manager ) 00271 { 00272 FTC_Cache cache; 00273 00274 00275 #ifdef FT_DEBUG_ERROR 00276 /* find node's cache */ 00277 if ( node->cache_index >= manager->num_caches ) 00278 { 00279 FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" )); 00280 return; 00281 } 00282 #endif 00283 00284 cache = manager->caches[node->cache_index]; 00285 00286 #ifdef FT_DEBUG_ERROR 00287 if ( cache == NULL ) 00288 { 00289 FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" )); 00290 return; 00291 } 00292 #endif 00293 00294 manager->cur_weight -= cache->clazz.node_weight( node, cache ); 00295 00296 /* remove node from mru list */ 00297 ftc_node_mru_unlink( node, manager ); 00298 00299 /* remove node from cache's hash table */ 00300 ftc_node_hash_unlink( node, cache ); 00301 00302 /* now finalize it */ 00303 cache->clazz.node_free( node, cache ); 00304 00305 #if 0 00306 /* check, just in case of general corruption :-) */ 00307 if ( manager->num_nodes == 0 ) 00308 FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n", 00309 manager->num_nodes )); 00310 #endif 00311 } 00312 00313 00314 /*************************************************************************/ 00315 /*************************************************************************/ 00316 /***** *****/ 00317 /***** ABSTRACT CACHE CLASS *****/ 00318 /***** *****/ 00319 /*************************************************************************/ 00320 /*************************************************************************/ 00321 00322 00323 FT_LOCAL_DEF( FT_Error ) 00324 FTC_Cache_Init( FTC_Cache cache ) 00325 { 00326 return ftc_cache_init( cache ); 00327 } 00328 00329 00330 FT_LOCAL_DEF( FT_Error ) 00331 ftc_cache_init( FTC_Cache cache ) 00332 { 00333 FT_Memory memory = cache->memory; 00334 FT_Error error; 00335 00336 00337 cache->p = 0; 00338 cache->mask = FTC_HASH_INITIAL_SIZE - 1; 00339 cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD; 00340 00341 (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 ); 00342 return error; 00343 } 00344 00345 00346 static void 00347 FTC_Cache_Clear( FTC_Cache cache ) 00348 { 00349 if ( cache && cache->buckets ) 00350 { 00351 FTC_Manager manager = cache->manager; 00352 FT_UFast i; 00353 FT_UFast count; 00354 00355 00356 count = cache->p + cache->mask + 1; 00357 00358 for ( i = 0; i < count; i++ ) 00359 { 00360 FTC_Node *pnode = cache->buckets + i, next, node = *pnode; 00361 00362 00363 while ( node ) 00364 { 00365 next = node->link; 00366 node->link = NULL; 00367 00368 /* remove node from mru list */ 00369 ftc_node_mru_unlink( node, manager ); 00370 00371 /* now finalize it */ 00372 manager->cur_weight -= cache->clazz.node_weight( node, cache ); 00373 00374 cache->clazz.node_free( node, cache ); 00375 node = next; 00376 } 00377 cache->buckets[i] = NULL; 00378 } 00379 ftc_cache_resize( cache ); 00380 } 00381 } 00382 00383 00384 FT_LOCAL_DEF( void ) 00385 ftc_cache_done( FTC_Cache cache ) 00386 { 00387 if ( cache->memory ) 00388 { 00389 FT_Memory memory = cache->memory; 00390 00391 00392 FTC_Cache_Clear( cache ); 00393 00394 FT_FREE( cache->buckets ); 00395 cache->mask = 0; 00396 cache->p = 0; 00397 cache->slack = 0; 00398 00399 cache->memory = NULL; 00400 } 00401 } 00402 00403 00404 FT_LOCAL_DEF( void ) 00405 FTC_Cache_Done( FTC_Cache cache ) 00406 { 00407 ftc_cache_done( cache ); 00408 } 00409 00410 00411 static void 00412 ftc_cache_add( FTC_Cache cache, 00413 FT_PtrDist hash, 00414 FTC_Node node ) 00415 { 00416 node->hash = hash; 00417 node->cache_index = (FT_UInt16) cache->index; 00418 node->ref_count = 0; 00419 00420 ftc_node_hash_link( node, cache ); 00421 ftc_node_mru_link( node, cache->manager ); 00422 00423 { 00424 FTC_Manager manager = cache->manager; 00425 00426 00427 manager->cur_weight += cache->clazz.node_weight( node, cache ); 00428 00429 if ( manager->cur_weight >= manager->max_weight ) 00430 { 00431 node->ref_count++; 00432 FTC_Manager_Compress( manager ); 00433 node->ref_count--; 00434 } 00435 } 00436 } 00437 00438 00439 FT_LOCAL_DEF( FT_Error ) 00440 FTC_Cache_NewNode( FTC_Cache cache, 00441 FT_PtrDist hash, 00442 FT_Pointer query, 00443 FTC_Node *anode ) 00444 { 00445 FT_Error error; 00446 FTC_Node node; 00447 00448 00449 /* 00450 * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory 00451 * errors (OOM) correctly, i.e., by flushing the cache progressively 00452 * in order to make more room. 00453 */ 00454 00455 FTC_CACHE_TRYLOOP( cache ) 00456 { 00457 error = cache->clazz.node_new( &node, query, cache ); 00458 } 00459 FTC_CACHE_TRYLOOP_END(); 00460 00461 if ( error ) 00462 node = NULL; 00463 else 00464 { 00465 /* don't assume that the cache has the same number of buckets, since 00466 * our allocation request might have triggered global cache flushing 00467 */ 00468 ftc_cache_add( cache, hash, node ); 00469 } 00470 00471 *anode = node; 00472 return error; 00473 } 00474 00475 00476 #ifndef FTC_INLINE 00477 00478 FT_LOCAL_DEF( FT_Error ) 00479 FTC_Cache_Lookup( FTC_Cache cache, 00480 FT_PtrDist hash, 00481 FT_Pointer query, 00482 FTC_Node *anode ) 00483 { 00484 FT_UFast idx; 00485 FTC_Node* bucket; 00486 FTC_Node* pnode; 00487 FTC_Node node; 00488 FT_Error error = FTC_Err_Ok; 00489 00490 FTC_Node_CompareFunc compare = cache->clazz.node_compare; 00491 00492 00493 if ( cache == NULL || anode == NULL ) 00494 return FTC_Err_Invalid_Argument; 00495 00496 idx = hash & cache->mask; 00497 if ( idx < cache->p ) 00498 idx = hash & ( cache->mask * 2 + 1 ); 00499 00500 bucket = cache->buckets + idx; 00501 pnode = bucket; 00502 for (;;) 00503 { 00504 node = *pnode; 00505 if ( node == NULL ) 00506 goto NewNode; 00507 00508 if ( node->hash == hash && compare( node, query, cache ) ) 00509 break; 00510 00511 pnode = &node->link; 00512 } 00513 00514 if ( node != *bucket ) 00515 { 00516 *pnode = node->link; 00517 node->link = *bucket; 00518 *bucket = node; 00519 } 00520 00521 /* move to head of MRU list */ 00522 { 00523 FTC_Manager manager = cache->manager; 00524 00525 00526 if ( node != manager->nodes_list ) 00527 ftc_node_mru_up( node, manager ); 00528 } 00529 *anode = node; 00530 return error; 00531 00532 NewNode: 00533 return FTC_Cache_NewNode( cache, hash, query, anode ); 00534 } 00535 00536 #endif /* !FTC_INLINE */ 00537 00538 00539 FT_LOCAL_DEF( void ) 00540 FTC_Cache_RemoveFaceID( FTC_Cache cache, 00541 FTC_FaceID face_id ) 00542 { 00543 FT_UFast i, count; 00544 FTC_Manager manager = cache->manager; 00545 FTC_Node frees = NULL; 00546 00547 00548 count = cache->p + cache->mask; 00549 for ( i = 0; i < count; i++ ) 00550 { 00551 FTC_Node* bucket = cache->buckets + i; 00552 FTC_Node* pnode = bucket; 00553 00554 00555 for ( ;; ) 00556 { 00557 FTC_Node node = *pnode; 00558 00559 00560 if ( node == NULL ) 00561 break; 00562 00563 if ( cache->clazz.node_remove_faceid( node, face_id, cache ) ) 00564 { 00565 *pnode = node->link; 00566 node->link = frees; 00567 frees = node; 00568 } 00569 else 00570 pnode = &node->link; 00571 } 00572 } 00573 00574 /* remove all nodes in the free list */ 00575 while ( frees ) 00576 { 00577 FTC_Node node; 00578 00579 00580 node = frees; 00581 frees = node->link; 00582 00583 manager->cur_weight -= cache->clazz.node_weight( node, cache ); 00584 ftc_node_mru_unlink( node, manager ); 00585 00586 cache->clazz.node_free( node, cache ); 00587 00588 cache->slack++; 00589 } 00590 00591 ftc_cache_resize( cache ); 00592 } 00593 00594 00595 /* END */ Generated on Sat May 26 2012 04:32:39 for ReactOS by
1.7.6.1
|