1*37da2899SCharles.Forsyth /***************************************************************************/ 2*37da2899SCharles.Forsyth /* */ 3*37da2899SCharles.Forsyth /* ftccache.c */ 4*37da2899SCharles.Forsyth /* */ 5*37da2899SCharles.Forsyth /* The FreeType internal cache interface (body). */ 6*37da2899SCharles.Forsyth /* */ 7*37da2899SCharles.Forsyth /* Copyright 2000-2001, 2002 by */ 8*37da2899SCharles.Forsyth /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 9*37da2899SCharles.Forsyth /* */ 10*37da2899SCharles.Forsyth /* This file is part of the FreeType project, and may only be used, */ 11*37da2899SCharles.Forsyth /* modified, and distributed under the terms of the FreeType project */ 12*37da2899SCharles.Forsyth /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 13*37da2899SCharles.Forsyth /* this file you indicate that you have read the license and */ 14*37da2899SCharles.Forsyth /* understand and accept it fully. */ 15*37da2899SCharles.Forsyth /* */ 16*37da2899SCharles.Forsyth /***************************************************************************/ 17*37da2899SCharles.Forsyth 18*37da2899SCharles.Forsyth 19*37da2899SCharles.Forsyth #include <ft2build.h> 20*37da2899SCharles.Forsyth #include FT_CACHE_MANAGER_H 21*37da2899SCharles.Forsyth #include FT_INTERNAL_OBJECTS_H 22*37da2899SCharles.Forsyth #include FT_INTERNAL_DEBUG_H 23*37da2899SCharles.Forsyth 24*37da2899SCharles.Forsyth #include "ftcerror.h" 25*37da2899SCharles.Forsyth 26*37da2899SCharles.Forsyth 27*37da2899SCharles.Forsyth #define FTC_HASH_MAX_LOAD 2 28*37da2899SCharles.Forsyth #define FTC_HASH_MIN_LOAD 1 29*37da2899SCharles.Forsyth #define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD ) 30*37da2899SCharles.Forsyth 31*37da2899SCharles.Forsyth /* this one _must_ be a power of 2! */ 32*37da2899SCharles.Forsyth #define FTC_HASH_INITIAL_SIZE 8 33*37da2899SCharles.Forsyth 34*37da2899SCharles.Forsyth 35*37da2899SCharles.Forsyth /*************************************************************************/ 36*37da2899SCharles.Forsyth /*************************************************************************/ 37*37da2899SCharles.Forsyth /***** *****/ 38*37da2899SCharles.Forsyth /***** CACHE NODE DEFINITIONS *****/ 39*37da2899SCharles.Forsyth /***** *****/ 40*37da2899SCharles.Forsyth /*************************************************************************/ 41*37da2899SCharles.Forsyth /*************************************************************************/ 42*37da2899SCharles.Forsyth 43*37da2899SCharles.Forsyth FT_EXPORT_DEF( void ) ftc_node_done(FTC_Node node,FTC_Cache cache)44*37da2899SCharles.Forsyth ftc_node_done( FTC_Node node, 45*37da2899SCharles.Forsyth FTC_Cache cache ) 46*37da2899SCharles.Forsyth { 47*37da2899SCharles.Forsyth FTC_Family family; 48*37da2899SCharles.Forsyth FTC_FamilyEntry entry; 49*37da2899SCharles.Forsyth 50*37da2899SCharles.Forsyth 51*37da2899SCharles.Forsyth entry = cache->manager->families.entries + node->fam_index; 52*37da2899SCharles.Forsyth family = entry->family; 53*37da2899SCharles.Forsyth 54*37da2899SCharles.Forsyth /* remove from parent set table - eventually destroy the set */ 55*37da2899SCharles.Forsyth if ( --family->num_nodes == 0 ) 56*37da2899SCharles.Forsyth FT_LruList_Remove( cache->families, (FT_LruNode) family ); 57*37da2899SCharles.Forsyth } 58*37da2899SCharles.Forsyth 59*37da2899SCharles.Forsyth 60*37da2899SCharles.Forsyth /* add a new node to the head of the manager's circular MRU list */ 61*37da2899SCharles.Forsyth static void ftc_node_mru_link(FTC_Node node,FTC_Manager manager)62*37da2899SCharles.Forsyth ftc_node_mru_link( FTC_Node node, 63*37da2899SCharles.Forsyth FTC_Manager manager ) 64*37da2899SCharles.Forsyth { 65*37da2899SCharles.Forsyth FTC_Node first = manager->nodes_list; 66*37da2899SCharles.Forsyth 67*37da2899SCharles.Forsyth 68*37da2899SCharles.Forsyth if ( first ) 69*37da2899SCharles.Forsyth { 70*37da2899SCharles.Forsyth FTC_Node last = first->mru_prev; 71*37da2899SCharles.Forsyth 72*37da2899SCharles.Forsyth 73*37da2899SCharles.Forsyth FT_ASSERT( last->mru_next == first ); 74*37da2899SCharles.Forsyth 75*37da2899SCharles.Forsyth node->mru_prev = last; 76*37da2899SCharles.Forsyth node->mru_next = first; 77*37da2899SCharles.Forsyth 78*37da2899SCharles.Forsyth last->mru_next = node; 79*37da2899SCharles.Forsyth first->mru_prev = node; 80*37da2899SCharles.Forsyth } 81*37da2899SCharles.Forsyth else 82*37da2899SCharles.Forsyth { 83*37da2899SCharles.Forsyth FT_ASSERT( manager->num_nodes == 0 ); 84*37da2899SCharles.Forsyth 85*37da2899SCharles.Forsyth node->mru_next = node; 86*37da2899SCharles.Forsyth node->mru_prev = node; 87*37da2899SCharles.Forsyth } 88*37da2899SCharles.Forsyth 89*37da2899SCharles.Forsyth manager->nodes_list = node; 90*37da2899SCharles.Forsyth manager->num_nodes++; 91*37da2899SCharles.Forsyth } 92*37da2899SCharles.Forsyth 93*37da2899SCharles.Forsyth 94*37da2899SCharles.Forsyth /* remove a node from the manager's MRU list */ 95*37da2899SCharles.Forsyth static void ftc_node_mru_unlink(FTC_Node node,FTC_Manager manager)96*37da2899SCharles.Forsyth ftc_node_mru_unlink( FTC_Node node, 97*37da2899SCharles.Forsyth FTC_Manager manager ) 98*37da2899SCharles.Forsyth { 99*37da2899SCharles.Forsyth FTC_Node first = manager->nodes_list; 100*37da2899SCharles.Forsyth FTC_Node prev = node->mru_prev; 101*37da2899SCharles.Forsyth FTC_Node next = node->mru_next; 102*37da2899SCharles.Forsyth 103*37da2899SCharles.Forsyth 104*37da2899SCharles.Forsyth FT_ASSERT( first != NULL && manager->num_nodes > 0 ); 105*37da2899SCharles.Forsyth FT_ASSERT( next->mru_prev == node ); 106*37da2899SCharles.Forsyth FT_ASSERT( prev->mru_next == node ); 107*37da2899SCharles.Forsyth 108*37da2899SCharles.Forsyth next->mru_prev = prev; 109*37da2899SCharles.Forsyth prev->mru_next = next; 110*37da2899SCharles.Forsyth 111*37da2899SCharles.Forsyth if ( node == first ) 112*37da2899SCharles.Forsyth { 113*37da2899SCharles.Forsyth /* this is the last node in the list; update its head pointer */ 114*37da2899SCharles.Forsyth if ( node == next ) 115*37da2899SCharles.Forsyth manager->nodes_list = NULL; 116*37da2899SCharles.Forsyth else 117*37da2899SCharles.Forsyth manager->nodes_list = next; 118*37da2899SCharles.Forsyth } 119*37da2899SCharles.Forsyth 120*37da2899SCharles.Forsyth node->mru_next = NULL; 121*37da2899SCharles.Forsyth node->mru_prev = NULL; 122*37da2899SCharles.Forsyth manager->num_nodes--; 123*37da2899SCharles.Forsyth } 124*37da2899SCharles.Forsyth 125*37da2899SCharles.Forsyth 126*37da2899SCharles.Forsyth /* move a node to the head of the manager's MRU list */ 127*37da2899SCharles.Forsyth static void ftc_node_mru_up(FTC_Node node,FTC_Manager manager)128*37da2899SCharles.Forsyth ftc_node_mru_up( FTC_Node node, 129*37da2899SCharles.Forsyth FTC_Manager manager ) 130*37da2899SCharles.Forsyth { 131*37da2899SCharles.Forsyth FTC_Node first = manager->nodes_list; 132*37da2899SCharles.Forsyth 133*37da2899SCharles.Forsyth 134*37da2899SCharles.Forsyth if ( node != first ) 135*37da2899SCharles.Forsyth { 136*37da2899SCharles.Forsyth FTC_Node prev = node->mru_prev; 137*37da2899SCharles.Forsyth FTC_Node next = node->mru_next; 138*37da2899SCharles.Forsyth FTC_Node last; 139*37da2899SCharles.Forsyth 140*37da2899SCharles.Forsyth 141*37da2899SCharles.Forsyth prev->mru_next = next; 142*37da2899SCharles.Forsyth next->mru_prev = prev; 143*37da2899SCharles.Forsyth 144*37da2899SCharles.Forsyth last = first->mru_prev; 145*37da2899SCharles.Forsyth node->mru_next = first; 146*37da2899SCharles.Forsyth node->mru_prev = last; 147*37da2899SCharles.Forsyth first->mru_prev = node; 148*37da2899SCharles.Forsyth last->mru_next = node; 149*37da2899SCharles.Forsyth 150*37da2899SCharles.Forsyth manager->nodes_list = node; 151*37da2899SCharles.Forsyth } 152*37da2899SCharles.Forsyth } 153*37da2899SCharles.Forsyth 154*37da2899SCharles.Forsyth 155*37da2899SCharles.Forsyth /* remove a node from its cache's hash table */ 156*37da2899SCharles.Forsyth static FT_Error ftc_node_hash_unlink(FTC_Node node,FTC_Cache cache)157*37da2899SCharles.Forsyth ftc_node_hash_unlink( FTC_Node node, 158*37da2899SCharles.Forsyth FTC_Cache cache ) 159*37da2899SCharles.Forsyth { 160*37da2899SCharles.Forsyth FT_Error error = 0; 161*37da2899SCharles.Forsyth FTC_Node *pnode; 162*37da2899SCharles.Forsyth FT_UInt idx, num_buckets; 163*37da2899SCharles.Forsyth 164*37da2899SCharles.Forsyth 165*37da2899SCharles.Forsyth idx = (FT_UInt)( node->hash & cache->mask ); 166*37da2899SCharles.Forsyth if ( idx < cache->p ) 167*37da2899SCharles.Forsyth idx = (FT_UInt)( node->hash & ( 2 * cache->mask + 1 ) ); 168*37da2899SCharles.Forsyth 169*37da2899SCharles.Forsyth pnode = cache->buckets + idx; 170*37da2899SCharles.Forsyth 171*37da2899SCharles.Forsyth for (;;) 172*37da2899SCharles.Forsyth { 173*37da2899SCharles.Forsyth if ( *pnode == NULL ) 174*37da2899SCharles.Forsyth { 175*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_hash_unlink: unknown node!\n" )); 176*37da2899SCharles.Forsyth return FT_Err_Ok; 177*37da2899SCharles.Forsyth } 178*37da2899SCharles.Forsyth 179*37da2899SCharles.Forsyth if ( *pnode == node ) 180*37da2899SCharles.Forsyth { 181*37da2899SCharles.Forsyth *pnode = node->link; 182*37da2899SCharles.Forsyth node->link = NULL; 183*37da2899SCharles.Forsyth break; 184*37da2899SCharles.Forsyth } 185*37da2899SCharles.Forsyth 186*37da2899SCharles.Forsyth pnode = &(*pnode)->link; 187*37da2899SCharles.Forsyth } 188*37da2899SCharles.Forsyth 189*37da2899SCharles.Forsyth num_buckets = ( cache->p + cache->mask + 1 ); 190*37da2899SCharles.Forsyth 191*37da2899SCharles.Forsyth if ( ++cache->slack > (FT_Long)num_buckets * FTC_HASH_SUB_LOAD ) 192*37da2899SCharles.Forsyth { 193*37da2899SCharles.Forsyth FT_UInt p = cache->p; 194*37da2899SCharles.Forsyth FT_UInt mask = cache->mask; 195*37da2899SCharles.Forsyth FT_UInt old_index = p + mask; 196*37da2899SCharles.Forsyth FTC_Node* pold; 197*37da2899SCharles.Forsyth 198*37da2899SCharles.Forsyth 199*37da2899SCharles.Forsyth FT_ASSERT( old_index >= FTC_HASH_INITIAL_SIZE ); 200*37da2899SCharles.Forsyth 201*37da2899SCharles.Forsyth if ( p == 0 ) 202*37da2899SCharles.Forsyth { 203*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 204*37da2899SCharles.Forsyth 205*37da2899SCharles.Forsyth 206*37da2899SCharles.Forsyth cache->mask >>= 1; 207*37da2899SCharles.Forsyth p = cache->mask; 208*37da2899SCharles.Forsyth 209*37da2899SCharles.Forsyth if ( FT_RENEW_ARRAY( cache->buckets, ( mask + 1 ) * 2, (mask+1) ) ) 210*37da2899SCharles.Forsyth { 211*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_hash_unlink: couldn't shunk buckets!\n" )); 212*37da2899SCharles.Forsyth goto Exit; 213*37da2899SCharles.Forsyth } 214*37da2899SCharles.Forsyth } 215*37da2899SCharles.Forsyth else 216*37da2899SCharles.Forsyth p--; 217*37da2899SCharles.Forsyth 218*37da2899SCharles.Forsyth pnode = cache->buckets + p; 219*37da2899SCharles.Forsyth while ( *pnode ) 220*37da2899SCharles.Forsyth pnode = &(*pnode)->link; 221*37da2899SCharles.Forsyth 222*37da2899SCharles.Forsyth pold = cache->buckets + old_index; 223*37da2899SCharles.Forsyth *pnode = *pold; 224*37da2899SCharles.Forsyth *pold = NULL; 225*37da2899SCharles.Forsyth 226*37da2899SCharles.Forsyth cache->slack -= FTC_HASH_MAX_LOAD; 227*37da2899SCharles.Forsyth cache->p = p; 228*37da2899SCharles.Forsyth } 229*37da2899SCharles.Forsyth 230*37da2899SCharles.Forsyth Exit: 231*37da2899SCharles.Forsyth return error; 232*37da2899SCharles.Forsyth } 233*37da2899SCharles.Forsyth 234*37da2899SCharles.Forsyth 235*37da2899SCharles.Forsyth 236*37da2899SCharles.Forsyth /* add a node to the "top" of its cache's hash table */ 237*37da2899SCharles.Forsyth static FT_Error ftc_node_hash_link(FTC_Node node,FTC_Cache cache)238*37da2899SCharles.Forsyth ftc_node_hash_link( FTC_Node node, 239*37da2899SCharles.Forsyth FTC_Cache cache ) 240*37da2899SCharles.Forsyth { 241*37da2899SCharles.Forsyth FTC_Node *pnode; 242*37da2899SCharles.Forsyth FT_UInt idx; 243*37da2899SCharles.Forsyth FT_Error error = 0; 244*37da2899SCharles.Forsyth 245*37da2899SCharles.Forsyth 246*37da2899SCharles.Forsyth idx = (FT_UInt)( node->hash & cache->mask ); 247*37da2899SCharles.Forsyth if ( idx < cache->p ) 248*37da2899SCharles.Forsyth idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) ); 249*37da2899SCharles.Forsyth 250*37da2899SCharles.Forsyth pnode = cache->buckets + idx; 251*37da2899SCharles.Forsyth 252*37da2899SCharles.Forsyth node->link = *pnode; 253*37da2899SCharles.Forsyth *pnode = node; 254*37da2899SCharles.Forsyth 255*37da2899SCharles.Forsyth if ( --cache->slack < 0 ) 256*37da2899SCharles.Forsyth { 257*37da2899SCharles.Forsyth FT_UInt p = cache->p; 258*37da2899SCharles.Forsyth FT_UInt mask = cache->mask; 259*37da2899SCharles.Forsyth FTC_Node new_list; 260*37da2899SCharles.Forsyth 261*37da2899SCharles.Forsyth 262*37da2899SCharles.Forsyth /* split a single bucket */ 263*37da2899SCharles.Forsyth new_list = NULL; 264*37da2899SCharles.Forsyth pnode = cache->buckets + p; 265*37da2899SCharles.Forsyth 266*37da2899SCharles.Forsyth for (;;) 267*37da2899SCharles.Forsyth { 268*37da2899SCharles.Forsyth node = *pnode; 269*37da2899SCharles.Forsyth if ( node == NULL ) 270*37da2899SCharles.Forsyth break; 271*37da2899SCharles.Forsyth 272*37da2899SCharles.Forsyth if ( node->hash & ( mask + 1 ) ) 273*37da2899SCharles.Forsyth { 274*37da2899SCharles.Forsyth *pnode = node->link; 275*37da2899SCharles.Forsyth node->link = new_list; 276*37da2899SCharles.Forsyth new_list = node; 277*37da2899SCharles.Forsyth } 278*37da2899SCharles.Forsyth else 279*37da2899SCharles.Forsyth pnode = &node->link; 280*37da2899SCharles.Forsyth } 281*37da2899SCharles.Forsyth 282*37da2899SCharles.Forsyth cache->buckets[p + mask + 1] = new_list; 283*37da2899SCharles.Forsyth 284*37da2899SCharles.Forsyth cache->slack += FTC_HASH_MAX_LOAD; 285*37da2899SCharles.Forsyth 286*37da2899SCharles.Forsyth if ( p >= mask ) 287*37da2899SCharles.Forsyth { 288*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 289*37da2899SCharles.Forsyth 290*37da2899SCharles.Forsyth 291*37da2899SCharles.Forsyth if ( FT_RENEW_ARRAY( cache->buckets, 292*37da2899SCharles.Forsyth ( mask + 1 ) * 2, ( mask + 1 ) * 4 ) ) 293*37da2899SCharles.Forsyth { 294*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_hash_link: couldn't expand buckets!\n" )); 295*37da2899SCharles.Forsyth goto Exit; 296*37da2899SCharles.Forsyth } 297*37da2899SCharles.Forsyth 298*37da2899SCharles.Forsyth cache->mask = 2 * mask + 1; 299*37da2899SCharles.Forsyth cache->p = 0; 300*37da2899SCharles.Forsyth } 301*37da2899SCharles.Forsyth else 302*37da2899SCharles.Forsyth cache->p = p + 1; 303*37da2899SCharles.Forsyth } 304*37da2899SCharles.Forsyth 305*37da2899SCharles.Forsyth Exit: 306*37da2899SCharles.Forsyth return error; 307*37da2899SCharles.Forsyth } 308*37da2899SCharles.Forsyth 309*37da2899SCharles.Forsyth 310*37da2899SCharles.Forsyth 311*37da2899SCharles.Forsyth 312*37da2899SCharles.Forsyth /* remove a node from the cache manager */ 313*37da2899SCharles.Forsyth FT_EXPORT_DEF( void ) ftc_node_destroy(FTC_Node node,FTC_Manager manager)314*37da2899SCharles.Forsyth ftc_node_destroy( FTC_Node node, 315*37da2899SCharles.Forsyth FTC_Manager manager ) 316*37da2899SCharles.Forsyth { 317*37da2899SCharles.Forsyth FT_Memory memory = manager->library->memory; 318*37da2899SCharles.Forsyth FTC_Cache cache; 319*37da2899SCharles.Forsyth FTC_FamilyEntry entry; 320*37da2899SCharles.Forsyth FTC_Cache_Class clazz; 321*37da2899SCharles.Forsyth 322*37da2899SCharles.Forsyth 323*37da2899SCharles.Forsyth #ifdef FT_DEBUG_ERROR 324*37da2899SCharles.Forsyth /* find node's cache */ 325*37da2899SCharles.Forsyth if ( node->fam_index >= manager->families.count ) 326*37da2899SCharles.Forsyth { 327*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_destroy: invalid node handle\n" )); 328*37da2899SCharles.Forsyth return; 329*37da2899SCharles.Forsyth } 330*37da2899SCharles.Forsyth #endif 331*37da2899SCharles.Forsyth 332*37da2899SCharles.Forsyth entry = manager->families.entries + node->fam_index; 333*37da2899SCharles.Forsyth cache = entry->cache; 334*37da2899SCharles.Forsyth 335*37da2899SCharles.Forsyth #ifdef FT_DEBUG_ERROR 336*37da2899SCharles.Forsyth if ( cache == NULL ) 337*37da2899SCharles.Forsyth { 338*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_destroy: invalid node handle\n" )); 339*37da2899SCharles.Forsyth return; 340*37da2899SCharles.Forsyth } 341*37da2899SCharles.Forsyth #endif 342*37da2899SCharles.Forsyth 343*37da2899SCharles.Forsyth clazz = cache->clazz; 344*37da2899SCharles.Forsyth 345*37da2899SCharles.Forsyth manager->cur_weight -= clazz->node_weight( node, cache ); 346*37da2899SCharles.Forsyth 347*37da2899SCharles.Forsyth /* remove node from mru list */ 348*37da2899SCharles.Forsyth ftc_node_mru_unlink( node, manager ); 349*37da2899SCharles.Forsyth 350*37da2899SCharles.Forsyth /* remove node from cache's hash table */ 351*37da2899SCharles.Forsyth ftc_node_hash_unlink( node, cache ); 352*37da2899SCharles.Forsyth 353*37da2899SCharles.Forsyth /* now finalize it */ 354*37da2899SCharles.Forsyth if ( clazz->node_done ) 355*37da2899SCharles.Forsyth clazz->node_done( node, cache ); 356*37da2899SCharles.Forsyth 357*37da2899SCharles.Forsyth FT_FREE( node ); 358*37da2899SCharles.Forsyth 359*37da2899SCharles.Forsyth /* check, just in case of general corruption :-) */ 360*37da2899SCharles.Forsyth if ( manager->num_nodes == 0 ) 361*37da2899SCharles.Forsyth FT_ERROR(( "ftc_node_destroy: invalid cache node count! = %d\n", 362*37da2899SCharles.Forsyth manager->num_nodes )); 363*37da2899SCharles.Forsyth } 364*37da2899SCharles.Forsyth 365*37da2899SCharles.Forsyth 366*37da2899SCharles.Forsyth /*************************************************************************/ 367*37da2899SCharles.Forsyth /*************************************************************************/ 368*37da2899SCharles.Forsyth /***** *****/ 369*37da2899SCharles.Forsyth /***** CACHE FAMILY DEFINITIONS *****/ 370*37da2899SCharles.Forsyth /***** *****/ 371*37da2899SCharles.Forsyth /*************************************************************************/ 372*37da2899SCharles.Forsyth /*************************************************************************/ 373*37da2899SCharles.Forsyth 374*37da2899SCharles.Forsyth 375*37da2899SCharles.Forsyth FT_EXPORT_DEF( FT_Error ) ftc_family_init(FTC_Family family,FTC_Query query,FTC_Cache cache)376*37da2899SCharles.Forsyth ftc_family_init( FTC_Family family, 377*37da2899SCharles.Forsyth FTC_Query query, 378*37da2899SCharles.Forsyth FTC_Cache cache ) 379*37da2899SCharles.Forsyth { 380*37da2899SCharles.Forsyth FT_Error error; 381*37da2899SCharles.Forsyth FTC_Manager manager = cache->manager; 382*37da2899SCharles.Forsyth FT_Memory memory = manager->library->memory; 383*37da2899SCharles.Forsyth FTC_FamilyEntry entry; 384*37da2899SCharles.Forsyth 385*37da2899SCharles.Forsyth 386*37da2899SCharles.Forsyth family->cache = cache; 387*37da2899SCharles.Forsyth family->num_nodes = 0; 388*37da2899SCharles.Forsyth 389*37da2899SCharles.Forsyth /* now add to manager's family table */ 390*37da2899SCharles.Forsyth error = ftc_family_table_alloc( &manager->families, memory, &entry ); 391*37da2899SCharles.Forsyth if ( !error ) 392*37da2899SCharles.Forsyth { 393*37da2899SCharles.Forsyth entry->cache = cache; 394*37da2899SCharles.Forsyth entry->family = family; 395*37da2899SCharles.Forsyth family->fam_index = entry->index; 396*37da2899SCharles.Forsyth 397*37da2899SCharles.Forsyth query->family = family; /* save family in query */ 398*37da2899SCharles.Forsyth } 399*37da2899SCharles.Forsyth 400*37da2899SCharles.Forsyth return error; 401*37da2899SCharles.Forsyth } 402*37da2899SCharles.Forsyth 403*37da2899SCharles.Forsyth 404*37da2899SCharles.Forsyth FT_EXPORT_DEF( void ) ftc_family_done(FTC_Family family)405*37da2899SCharles.Forsyth ftc_family_done( FTC_Family family ) 406*37da2899SCharles.Forsyth { 407*37da2899SCharles.Forsyth FTC_Manager manager = family->cache->manager; 408*37da2899SCharles.Forsyth 409*37da2899SCharles.Forsyth 410*37da2899SCharles.Forsyth /* remove from manager's family table */ 411*37da2899SCharles.Forsyth ftc_family_table_free( &manager->families, family->fam_index ); 412*37da2899SCharles.Forsyth } 413*37da2899SCharles.Forsyth 414*37da2899SCharles.Forsyth 415*37da2899SCharles.Forsyth /*************************************************************************/ 416*37da2899SCharles.Forsyth /*************************************************************************/ 417*37da2899SCharles.Forsyth /***** *****/ 418*37da2899SCharles.Forsyth /***** ABSTRACT CACHE CLASS *****/ 419*37da2899SCharles.Forsyth /***** *****/ 420*37da2899SCharles.Forsyth /*************************************************************************/ 421*37da2899SCharles.Forsyth /*************************************************************************/ 422*37da2899SCharles.Forsyth 423*37da2899SCharles.Forsyth 424*37da2899SCharles.Forsyth 425*37da2899SCharles.Forsyth FT_EXPORT_DEF( FT_Error ) ftc_cache_init(FTC_Cache cache)426*37da2899SCharles.Forsyth ftc_cache_init( FTC_Cache cache ) 427*37da2899SCharles.Forsyth { 428*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 429*37da2899SCharles.Forsyth FTC_Cache_Class clazz = cache->clazz; 430*37da2899SCharles.Forsyth FT_Error error; 431*37da2899SCharles.Forsyth 432*37da2899SCharles.Forsyth 433*37da2899SCharles.Forsyth cache->p = 0; 434*37da2899SCharles.Forsyth cache->mask = FTC_HASH_INITIAL_SIZE - 1; 435*37da2899SCharles.Forsyth cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD; 436*37da2899SCharles.Forsyth 437*37da2899SCharles.Forsyth if ( FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 ) ) 438*37da2899SCharles.Forsyth goto Exit; 439*37da2899SCharles.Forsyth 440*37da2899SCharles.Forsyth /* now, initialize the lru list of families for this cache */ 441*37da2899SCharles.Forsyth if ( clazz->family_size > 0 ) 442*37da2899SCharles.Forsyth { 443*37da2899SCharles.Forsyth FT_LruList_ClassRec* lru_class = &cache->family_class; 444*37da2899SCharles.Forsyth 445*37da2899SCharles.Forsyth 446*37da2899SCharles.Forsyth lru_class->list_size = sizeof( FT_LruListRec ); 447*37da2899SCharles.Forsyth lru_class->list_init = NULL; 448*37da2899SCharles.Forsyth lru_class->list_done = NULL; 449*37da2899SCharles.Forsyth 450*37da2899SCharles.Forsyth lru_class->node_size = clazz->family_size; 451*37da2899SCharles.Forsyth lru_class->node_init = (FT_LruNode_InitFunc) clazz->family_init; 452*37da2899SCharles.Forsyth lru_class->node_done = (FT_LruNode_DoneFunc) clazz->family_done; 453*37da2899SCharles.Forsyth lru_class->node_flush = (FT_LruNode_FlushFunc) NULL; 454*37da2899SCharles.Forsyth lru_class->node_compare = (FT_LruNode_CompareFunc)clazz->family_compare; 455*37da2899SCharles.Forsyth 456*37da2899SCharles.Forsyth error = FT_LruList_New( (FT_LruList_Class) lru_class, 457*37da2899SCharles.Forsyth 0, /* max items == 0 => unbounded list */ 458*37da2899SCharles.Forsyth cache, 459*37da2899SCharles.Forsyth memory, 460*37da2899SCharles.Forsyth &cache->families ); 461*37da2899SCharles.Forsyth if ( error ) 462*37da2899SCharles.Forsyth FT_FREE( cache->buckets ); 463*37da2899SCharles.Forsyth } 464*37da2899SCharles.Forsyth 465*37da2899SCharles.Forsyth Exit: 466*37da2899SCharles.Forsyth return error; 467*37da2899SCharles.Forsyth } 468*37da2899SCharles.Forsyth 469*37da2899SCharles.Forsyth 470*37da2899SCharles.Forsyth FT_EXPORT_DEF( void ) ftc_cache_clear(FTC_Cache cache)471*37da2899SCharles.Forsyth ftc_cache_clear( FTC_Cache cache ) 472*37da2899SCharles.Forsyth { 473*37da2899SCharles.Forsyth if ( cache ) 474*37da2899SCharles.Forsyth { 475*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 476*37da2899SCharles.Forsyth FTC_Cache_Class clazz = cache->clazz; 477*37da2899SCharles.Forsyth FTC_Manager manager = cache->manager; 478*37da2899SCharles.Forsyth FT_UFast i; 479*37da2899SCharles.Forsyth FT_UInt count; 480*37da2899SCharles.Forsyth 481*37da2899SCharles.Forsyth count = cache->p + cache->mask + 1; 482*37da2899SCharles.Forsyth 483*37da2899SCharles.Forsyth for ( i = 0; i < count; i++ ) 484*37da2899SCharles.Forsyth { 485*37da2899SCharles.Forsyth FTC_Node *pnode = cache->buckets + i, next, node = *pnode; 486*37da2899SCharles.Forsyth 487*37da2899SCharles.Forsyth 488*37da2899SCharles.Forsyth while ( node ) 489*37da2899SCharles.Forsyth { 490*37da2899SCharles.Forsyth next = node->link; 491*37da2899SCharles.Forsyth node->link = NULL; 492*37da2899SCharles.Forsyth 493*37da2899SCharles.Forsyth /* remove node from mru list */ 494*37da2899SCharles.Forsyth ftc_node_mru_unlink( node, manager ); 495*37da2899SCharles.Forsyth 496*37da2899SCharles.Forsyth /* now finalize it */ 497*37da2899SCharles.Forsyth manager->cur_weight -= clazz->node_weight( node, cache ); 498*37da2899SCharles.Forsyth 499*37da2899SCharles.Forsyth if ( clazz->node_done ) 500*37da2899SCharles.Forsyth clazz->node_done( node, cache ); 501*37da2899SCharles.Forsyth 502*37da2899SCharles.Forsyth FT_FREE( node ); 503*37da2899SCharles.Forsyth node = next; 504*37da2899SCharles.Forsyth } 505*37da2899SCharles.Forsyth cache->buckets[i] = NULL; 506*37da2899SCharles.Forsyth } 507*37da2899SCharles.Forsyth 508*37da2899SCharles.Forsyth cache->p = 0; 509*37da2899SCharles.Forsyth 510*37da2899SCharles.Forsyth /* destroy the families */ 511*37da2899SCharles.Forsyth if ( cache->families ) 512*37da2899SCharles.Forsyth FT_LruList_Reset( cache->families ); 513*37da2899SCharles.Forsyth } 514*37da2899SCharles.Forsyth } 515*37da2899SCharles.Forsyth 516*37da2899SCharles.Forsyth 517*37da2899SCharles.Forsyth FT_EXPORT_DEF( void ) ftc_cache_done(FTC_Cache cache)518*37da2899SCharles.Forsyth ftc_cache_done( FTC_Cache cache ) 519*37da2899SCharles.Forsyth { 520*37da2899SCharles.Forsyth if ( cache ) 521*37da2899SCharles.Forsyth { 522*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 523*37da2899SCharles.Forsyth 524*37da2899SCharles.Forsyth 525*37da2899SCharles.Forsyth ftc_cache_clear( cache ); 526*37da2899SCharles.Forsyth 527*37da2899SCharles.Forsyth FT_FREE( cache->buckets ); 528*37da2899SCharles.Forsyth cache->mask = 0; 529*37da2899SCharles.Forsyth cache->slack = 0; 530*37da2899SCharles.Forsyth 531*37da2899SCharles.Forsyth if ( cache->families ) 532*37da2899SCharles.Forsyth { 533*37da2899SCharles.Forsyth FT_LruList_Destroy( cache->families ); 534*37da2899SCharles.Forsyth cache->families = NULL; 535*37da2899SCharles.Forsyth } 536*37da2899SCharles.Forsyth } 537*37da2899SCharles.Forsyth } 538*37da2899SCharles.Forsyth 539*37da2899SCharles.Forsyth 540*37da2899SCharles.Forsyth /* Look up a node in "top" of its cache's hash table. */ 541*37da2899SCharles.Forsyth /* If not found, create a new node. */ 542*37da2899SCharles.Forsyth /* */ 543*37da2899SCharles.Forsyth FT_EXPORT_DEF( FT_Error ) ftc_cache_lookup(FTC_Cache cache,FTC_Query query,FTC_Node * anode)544*37da2899SCharles.Forsyth ftc_cache_lookup( FTC_Cache cache, 545*37da2899SCharles.Forsyth FTC_Query query, 546*37da2899SCharles.Forsyth FTC_Node *anode ) 547*37da2899SCharles.Forsyth { 548*37da2899SCharles.Forsyth FT_Error error = FT_Err_Ok; 549*37da2899SCharles.Forsyth FT_LruNode lru; 550*37da2899SCharles.Forsyth 551*37da2899SCharles.Forsyth 552*37da2899SCharles.Forsyth if ( !cache || !query || !anode ) 553*37da2899SCharles.Forsyth return FTC_Err_Invalid_Argument; 554*37da2899SCharles.Forsyth 555*37da2899SCharles.Forsyth *anode = NULL; 556*37da2899SCharles.Forsyth 557*37da2899SCharles.Forsyth query->hash = 0; 558*37da2899SCharles.Forsyth query->family = NULL; 559*37da2899SCharles.Forsyth 560*37da2899SCharles.Forsyth /* XXX: we break encapsulation for the sake of speed! */ 561*37da2899SCharles.Forsyth { 562*37da2899SCharles.Forsyth /* first of all, find the relevant family */ 563*37da2899SCharles.Forsyth FT_LruList list = cache->families; 564*37da2899SCharles.Forsyth FT_LruNode fam, *pfam; 565*37da2899SCharles.Forsyth FT_LruNode_CompareFunc compare = list->clazz->node_compare; 566*37da2899SCharles.Forsyth 567*37da2899SCharles.Forsyth pfam = &list->nodes; 568*37da2899SCharles.Forsyth for (;;) 569*37da2899SCharles.Forsyth { 570*37da2899SCharles.Forsyth fam = *pfam; 571*37da2899SCharles.Forsyth if ( fam == NULL ) 572*37da2899SCharles.Forsyth { 573*37da2899SCharles.Forsyth error = FT_LruList_Lookup( list, query, &lru ); 574*37da2899SCharles.Forsyth if ( error ) 575*37da2899SCharles.Forsyth goto Exit; 576*37da2899SCharles.Forsyth 577*37da2899SCharles.Forsyth goto Skip; 578*37da2899SCharles.Forsyth } 579*37da2899SCharles.Forsyth 580*37da2899SCharles.Forsyth if ( compare( fam, query, list->data ) ) 581*37da2899SCharles.Forsyth break; 582*37da2899SCharles.Forsyth 583*37da2899SCharles.Forsyth pfam = &fam->next; 584*37da2899SCharles.Forsyth } 585*37da2899SCharles.Forsyth 586*37da2899SCharles.Forsyth FT_ASSERT( fam != NULL ); 587*37da2899SCharles.Forsyth 588*37da2899SCharles.Forsyth /* move to top of list when needed */ 589*37da2899SCharles.Forsyth if ( fam != list->nodes ) 590*37da2899SCharles.Forsyth { 591*37da2899SCharles.Forsyth *pfam = fam->next; 592*37da2899SCharles.Forsyth fam->next = list->nodes; 593*37da2899SCharles.Forsyth list->nodes = fam; 594*37da2899SCharles.Forsyth } 595*37da2899SCharles.Forsyth 596*37da2899SCharles.Forsyth lru = fam; 597*37da2899SCharles.Forsyth 598*37da2899SCharles.Forsyth Skip: 599*37da2899SCharles.Forsyth ; 600*37da2899SCharles.Forsyth } 601*37da2899SCharles.Forsyth 602*37da2899SCharles.Forsyth { 603*37da2899SCharles.Forsyth FTC_Family family = (FTC_Family) lru; 604*37da2899SCharles.Forsyth FT_UFast hash = query->hash; 605*37da2899SCharles.Forsyth FTC_Node* bucket; 606*37da2899SCharles.Forsyth FT_UInt idx; 607*37da2899SCharles.Forsyth 608*37da2899SCharles.Forsyth 609*37da2899SCharles.Forsyth idx = hash & cache->mask; 610*37da2899SCharles.Forsyth if ( idx < cache->p ) 611*37da2899SCharles.Forsyth idx = hash & ( cache->mask * 2 + 1 ); 612*37da2899SCharles.Forsyth 613*37da2899SCharles.Forsyth bucket = cache->buckets + idx; 614*37da2899SCharles.Forsyth 615*37da2899SCharles.Forsyth 616*37da2899SCharles.Forsyth if ( query->family != family || 617*37da2899SCharles.Forsyth family->fam_index >= cache->manager->families.size ) 618*37da2899SCharles.Forsyth { 619*37da2899SCharles.Forsyth FT_ERROR(( 620*37da2899SCharles.Forsyth "ftc_cache_lookup: invalid query (bad 'family' field)\n" )); 621*37da2899SCharles.Forsyth return FTC_Err_Invalid_Argument; 622*37da2899SCharles.Forsyth } 623*37da2899SCharles.Forsyth 624*37da2899SCharles.Forsyth if ( *bucket ) 625*37da2899SCharles.Forsyth { 626*37da2899SCharles.Forsyth FTC_Node* pnode = bucket; 627*37da2899SCharles.Forsyth FTC_Node_CompareFunc compare = cache->clazz->node_compare; 628*37da2899SCharles.Forsyth 629*37da2899SCharles.Forsyth 630*37da2899SCharles.Forsyth for ( ;; ) 631*37da2899SCharles.Forsyth { 632*37da2899SCharles.Forsyth FTC_Node node; 633*37da2899SCharles.Forsyth 634*37da2899SCharles.Forsyth 635*37da2899SCharles.Forsyth node = *pnode; 636*37da2899SCharles.Forsyth if ( node == NULL ) 637*37da2899SCharles.Forsyth break; 638*37da2899SCharles.Forsyth 639*37da2899SCharles.Forsyth if ( node->hash == hash && 640*37da2899SCharles.Forsyth (FT_UInt)node->fam_index == family->fam_index && 641*37da2899SCharles.Forsyth compare( node, query, cache ) ) 642*37da2899SCharles.Forsyth { 643*37da2899SCharles.Forsyth /* move to head of bucket list */ 644*37da2899SCharles.Forsyth if ( pnode != bucket ) 645*37da2899SCharles.Forsyth { 646*37da2899SCharles.Forsyth *pnode = node->link; 647*37da2899SCharles.Forsyth node->link = *bucket; 648*37da2899SCharles.Forsyth *bucket = node; 649*37da2899SCharles.Forsyth } 650*37da2899SCharles.Forsyth 651*37da2899SCharles.Forsyth /* move to head of MRU list */ 652*37da2899SCharles.Forsyth if ( node != cache->manager->nodes_list ) 653*37da2899SCharles.Forsyth ftc_node_mru_up( node, cache->manager ); 654*37da2899SCharles.Forsyth 655*37da2899SCharles.Forsyth *anode = node; 656*37da2899SCharles.Forsyth goto Exit; 657*37da2899SCharles.Forsyth } 658*37da2899SCharles.Forsyth 659*37da2899SCharles.Forsyth pnode = &node->link; 660*37da2899SCharles.Forsyth } 661*37da2899SCharles.Forsyth } 662*37da2899SCharles.Forsyth 663*37da2899SCharles.Forsyth /* didn't find a node, create a new one */ 664*37da2899SCharles.Forsyth { 665*37da2899SCharles.Forsyth FTC_Cache_Class clazz = cache->clazz; 666*37da2899SCharles.Forsyth FTC_Manager manager = cache->manager; 667*37da2899SCharles.Forsyth FT_Memory memory = cache->memory; 668*37da2899SCharles.Forsyth FTC_Node node; 669*37da2899SCharles.Forsyth 670*37da2899SCharles.Forsyth 671*37da2899SCharles.Forsyth if ( FT_ALLOC( node, clazz->node_size ) ) 672*37da2899SCharles.Forsyth goto Exit; 673*37da2899SCharles.Forsyth 674*37da2899SCharles.Forsyth node->fam_index = (FT_UShort) family->fam_index; 675*37da2899SCharles.Forsyth node->hash = query->hash; 676*37da2899SCharles.Forsyth node->ref_count = 0; 677*37da2899SCharles.Forsyth 678*37da2899SCharles.Forsyth error = clazz->node_init( node, query, cache ); 679*37da2899SCharles.Forsyth if ( error ) 680*37da2899SCharles.Forsyth { 681*37da2899SCharles.Forsyth FT_FREE( node ); 682*37da2899SCharles.Forsyth goto Exit; 683*37da2899SCharles.Forsyth } 684*37da2899SCharles.Forsyth 685*37da2899SCharles.Forsyth error = ftc_node_hash_link( node, cache ); 686*37da2899SCharles.Forsyth if ( error ) 687*37da2899SCharles.Forsyth { 688*37da2899SCharles.Forsyth clazz->node_done( node, cache ); 689*37da2899SCharles.Forsyth FT_FREE( node ); 690*37da2899SCharles.Forsyth goto Exit; 691*37da2899SCharles.Forsyth } 692*37da2899SCharles.Forsyth 693*37da2899SCharles.Forsyth ftc_node_mru_link( node, cache->manager ); 694*37da2899SCharles.Forsyth 695*37da2899SCharles.Forsyth cache->manager->cur_weight += clazz->node_weight( node, cache ); 696*37da2899SCharles.Forsyth 697*37da2899SCharles.Forsyth /* now try to compress the node pool when necessary */ 698*37da2899SCharles.Forsyth if ( manager->cur_weight >= manager->max_weight ) 699*37da2899SCharles.Forsyth { 700*37da2899SCharles.Forsyth node->ref_count++; 701*37da2899SCharles.Forsyth FTC_Manager_Compress( manager ); 702*37da2899SCharles.Forsyth node->ref_count--; 703*37da2899SCharles.Forsyth } 704*37da2899SCharles.Forsyth 705*37da2899SCharles.Forsyth *anode = node; 706*37da2899SCharles.Forsyth } 707*37da2899SCharles.Forsyth } 708*37da2899SCharles.Forsyth 709*37da2899SCharles.Forsyth Exit: 710*37da2899SCharles.Forsyth return error; 711*37da2899SCharles.Forsyth } 712*37da2899SCharles.Forsyth 713*37da2899SCharles.Forsyth 714*37da2899SCharles.Forsyth /* END */ 715