/modules/freetype2/src/cache/ftccache.c

http://github.com/zpao/v8monkey · C · 595 lines · 379 code · 149 blank · 67 comment · 57 complexity · a30b4d69a8f767bac4851789e85ddc1e MD5 · raw file

  1. /***************************************************************************/
  2. /* */
  3. /* ftccache.c */
  4. /* */
  5. /* The FreeType internal cache interface (body). */
  6. /* */
  7. /* Copyright 2000-2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 by */
  8. /* David Turner, Robert Wilhelm, and Werner Lemberg. */
  9. /* */
  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. */
  15. /* */
  16. /***************************************************************************/
  17. #include <ft2build.h>
  18. #include "ftcmanag.h"
  19. #include FT_INTERNAL_OBJECTS_H
  20. #include FT_INTERNAL_DEBUG_H
  21. #include "ftccback.h"
  22. #include "ftcerror.h"
  23. #undef FT_COMPONENT
  24. #define FT_COMPONENT trace_cache
  25. #define FTC_HASH_MAX_LOAD 2
  26. #define FTC_HASH_MIN_LOAD 1
  27. #define FTC_HASH_SUB_LOAD ( FTC_HASH_MAX_LOAD - FTC_HASH_MIN_LOAD )
  28. /* this one _must_ be a power of 2! */
  29. #define FTC_HASH_INITIAL_SIZE 8
  30. /*************************************************************************/
  31. /*************************************************************************/
  32. /***** *****/
  33. /***** CACHE NODE DEFINITIONS *****/
  34. /***** *****/
  35. /*************************************************************************/
  36. /*************************************************************************/
  37. /* add a new node to the head of the manager's circular MRU list */
  38. static void
  39. ftc_node_mru_link( FTC_Node node,
  40. FTC_Manager manager )
  41. {
  42. void *nl = &manager->nodes_list;
  43. FTC_MruNode_Prepend( (FTC_MruNode*)nl,
  44. (FTC_MruNode)node );
  45. manager->num_nodes++;
  46. }
  47. /* remove a node from the manager's MRU list */
  48. static void
  49. ftc_node_mru_unlink( FTC_Node node,
  50. FTC_Manager manager )
  51. {
  52. void *nl = &manager->nodes_list;
  53. FTC_MruNode_Remove( (FTC_MruNode*)nl,
  54. (FTC_MruNode)node );
  55. manager->num_nodes--;
  56. }
  57. #ifndef FTC_INLINE
  58. /* move a node to the head of the manager's MRU list */
  59. static void
  60. ftc_node_mru_up( FTC_Node node,
  61. FTC_Manager manager )
  62. {
  63. FTC_MruNode_Up( (FTC_MruNode*)&manager->nodes_list,
  64. (FTC_MruNode)node );
  65. }
  66. #endif /* !FTC_INLINE */
  67. /* Note that this function cannot fail. If we cannot re-size the
  68. * buckets array appropriately, we simply degrade the hash table's
  69. * performance!
  70. */
  71. static void
  72. ftc_cache_resize( FTC_Cache cache )
  73. {
  74. for (;;)
  75. {
  76. FTC_Node node, *pnode;
  77. FT_UFast p = cache->p;
  78. FT_UFast mask = cache->mask;
  79. FT_UFast count = mask + p + 1; /* number of buckets */
  80. /* do we need to shrink the buckets array? */
  81. if ( cache->slack < 0 )
  82. {
  83. FTC_Node new_list = NULL;
  84. /* try to expand the buckets array _before_ splitting
  85. * the bucket lists
  86. */
  87. if ( p >= mask )
  88. {
  89. FT_Memory memory = cache->memory;
  90. FT_Error error;
  91. /* if we can't expand the array, leave immediately */
  92. if ( FT_RENEW_ARRAY( cache->buckets, (mask+1)*2, (mask+1)*4 ) )
  93. break;
  94. }
  95. /* split a single bucket */
  96. pnode = cache->buckets + p;
  97. for (;;)
  98. {
  99. node = *pnode;
  100. if ( node == NULL )
  101. break;
  102. if ( node->hash & ( mask + 1 ) )
  103. {
  104. *pnode = node->link;
  105. node->link = new_list;
  106. new_list = node;
  107. }
  108. else
  109. pnode = &node->link;
  110. }
  111. cache->buckets[p + mask + 1] = new_list;
  112. cache->slack += FTC_HASH_MAX_LOAD;
  113. if ( p >= mask )
  114. {
  115. cache->mask = 2 * mask + 1;
  116. cache->p = 0;
  117. }
  118. else
  119. cache->p = p + 1;
  120. }
  121. /* do we need to expand the buckets array? */
  122. else if ( cache->slack > (FT_Long)count * FTC_HASH_SUB_LOAD )
  123. {
  124. FT_UFast old_index = p + mask;
  125. FTC_Node* pold;
  126. if ( old_index + 1 <= FTC_HASH_INITIAL_SIZE )
  127. break;
  128. if ( p == 0 )
  129. {
  130. FT_Memory memory = cache->memory;
  131. FT_Error error;
  132. /* if we can't shrink the array, leave immediately */
  133. if ( FT_RENEW_ARRAY( cache->buckets,
  134. ( mask + 1 ) * 2, mask + 1 ) )
  135. break;
  136. cache->mask >>= 1;
  137. p = cache->mask;
  138. }
  139. else
  140. p--;
  141. pnode = cache->buckets + p;
  142. while ( *pnode )
  143. pnode = &(*pnode)->link;
  144. pold = cache->buckets + old_index;
  145. *pnode = *pold;
  146. *pold = NULL;
  147. cache->slack -= FTC_HASH_MAX_LOAD;
  148. cache->p = p;
  149. }
  150. else /* the hash table is balanced */
  151. break;
  152. }
  153. }
  154. /* remove a node from its cache's hash table */
  155. static void
  156. ftc_node_hash_unlink( FTC_Node node0,
  157. FTC_Cache cache )
  158. {
  159. FTC_Node *pnode;
  160. FT_UInt idx;
  161. idx = (FT_UInt)( node0->hash & cache->mask );
  162. if ( idx < cache->p )
  163. idx = (FT_UInt)( node0->hash & ( 2 * cache->mask + 1 ) );
  164. pnode = cache->buckets + idx;
  165. for (;;)
  166. {
  167. FTC_Node node = *pnode;
  168. if ( node == NULL )
  169. {
  170. FT_TRACE0(( "ftc_node_hash_unlink: unknown node\n" ));
  171. return;
  172. }
  173. if ( node == node0 )
  174. break;
  175. pnode = &(*pnode)->link;
  176. }
  177. *pnode = node0->link;
  178. node0->link = NULL;
  179. cache->slack++;
  180. ftc_cache_resize( cache );
  181. }
  182. /* add a node to the `top' of its cache's hash table */
  183. static void
  184. ftc_node_hash_link( FTC_Node node,
  185. FTC_Cache cache )
  186. {
  187. FTC_Node *pnode;
  188. FT_UInt idx;
  189. idx = (FT_UInt)( node->hash & cache->mask );
  190. if ( idx < cache->p )
  191. idx = (FT_UInt)( node->hash & (2 * cache->mask + 1 ) );
  192. pnode = cache->buckets + idx;
  193. node->link = *pnode;
  194. *pnode = node;
  195. cache->slack--;
  196. ftc_cache_resize( cache );
  197. }
  198. /* remove a node from the cache manager */
  199. #ifdef FT_CONFIG_OPTION_OLD_INTERNALS
  200. FT_BASE_DEF( void )
  201. #else
  202. FT_LOCAL_DEF( void )
  203. #endif
  204. ftc_node_destroy( FTC_Node node,
  205. FTC_Manager manager )
  206. {
  207. FTC_Cache cache;
  208. #ifdef FT_DEBUG_ERROR
  209. /* find node's cache */
  210. if ( node->cache_index >= manager->num_caches )
  211. {
  212. FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
  213. return;
  214. }
  215. #endif
  216. cache = manager->caches[node->cache_index];
  217. #ifdef FT_DEBUG_ERROR
  218. if ( cache == NULL )
  219. {
  220. FT_TRACE0(( "ftc_node_destroy: invalid node handle\n" ));
  221. return;
  222. }
  223. #endif
  224. manager->cur_weight -= cache->clazz.node_weight( node, cache );
  225. /* remove node from mru list */
  226. ftc_node_mru_unlink( node, manager );
  227. /* remove node from cache's hash table */
  228. ftc_node_hash_unlink( node, cache );
  229. /* now finalize it */
  230. cache->clazz.node_free( node, cache );
  231. #if 0
  232. /* check, just in case of general corruption :-) */
  233. if ( manager->num_nodes == 0 )
  234. FT_TRACE0(( "ftc_node_destroy: invalid cache node count (%d)\n",
  235. manager->num_nodes ));
  236. #endif
  237. }
  238. /*************************************************************************/
  239. /*************************************************************************/
  240. /***** *****/
  241. /***** ABSTRACT CACHE CLASS *****/
  242. /***** *****/
  243. /*************************************************************************/
  244. /*************************************************************************/
  245. FT_LOCAL_DEF( FT_Error )
  246. FTC_Cache_Init( FTC_Cache cache )
  247. {
  248. return ftc_cache_init( cache );
  249. }
  250. FT_LOCAL_DEF( FT_Error )
  251. ftc_cache_init( FTC_Cache cache )
  252. {
  253. FT_Memory memory = cache->memory;
  254. FT_Error error;
  255. cache->p = 0;
  256. cache->mask = FTC_HASH_INITIAL_SIZE - 1;
  257. cache->slack = FTC_HASH_INITIAL_SIZE * FTC_HASH_MAX_LOAD;
  258. (void)FT_NEW_ARRAY( cache->buckets, FTC_HASH_INITIAL_SIZE * 2 );
  259. return error;
  260. }
  261. static void
  262. FTC_Cache_Clear( FTC_Cache cache )
  263. {
  264. if ( cache && cache->buckets )
  265. {
  266. FTC_Manager manager = cache->manager;
  267. FT_UFast i;
  268. FT_UFast count;
  269. count = cache->p + cache->mask + 1;
  270. for ( i = 0; i < count; i++ )
  271. {
  272. FTC_Node *pnode = cache->buckets + i, next, node = *pnode;
  273. while ( node )
  274. {
  275. next = node->link;
  276. node->link = NULL;
  277. /* remove node from mru list */
  278. ftc_node_mru_unlink( node, manager );
  279. /* now finalize it */
  280. manager->cur_weight -= cache->clazz.node_weight( node, cache );
  281. cache->clazz.node_free( node, cache );
  282. node = next;
  283. }
  284. cache->buckets[i] = NULL;
  285. }
  286. ftc_cache_resize( cache );
  287. }
  288. }
  289. FT_LOCAL_DEF( void )
  290. ftc_cache_done( FTC_Cache cache )
  291. {
  292. if ( cache->memory )
  293. {
  294. FT_Memory memory = cache->memory;
  295. FTC_Cache_Clear( cache );
  296. FT_FREE( cache->buckets );
  297. cache->mask = 0;
  298. cache->p = 0;
  299. cache->slack = 0;
  300. cache->memory = NULL;
  301. }
  302. }
  303. FT_LOCAL_DEF( void )
  304. FTC_Cache_Done( FTC_Cache cache )
  305. {
  306. ftc_cache_done( cache );
  307. }
  308. static void
  309. ftc_cache_add( FTC_Cache cache,
  310. FT_UInt32 hash,
  311. FTC_Node node )
  312. {
  313. node->hash = hash;
  314. node->cache_index = (FT_UInt16) cache->index;
  315. node->ref_count = 0;
  316. ftc_node_hash_link( node, cache );
  317. ftc_node_mru_link( node, cache->manager );
  318. {
  319. FTC_Manager manager = cache->manager;
  320. manager->cur_weight += cache->clazz.node_weight( node, cache );
  321. if ( manager->cur_weight >= manager->max_weight )
  322. {
  323. node->ref_count++;
  324. FTC_Manager_Compress( manager );
  325. node->ref_count--;
  326. }
  327. }
  328. }
  329. FT_LOCAL_DEF( FT_Error )
  330. FTC_Cache_NewNode( FTC_Cache cache,
  331. FT_UInt32 hash,
  332. FT_Pointer query,
  333. FTC_Node *anode )
  334. {
  335. FT_Error error;
  336. FTC_Node node;
  337. /*
  338. * We use the FTC_CACHE_TRYLOOP macros to support out-of-memory
  339. * errors (OOM) correctly, i.e., by flushing the cache progressively
  340. * in order to make more room.
  341. */
  342. FTC_CACHE_TRYLOOP( cache )
  343. {
  344. error = cache->clazz.node_new( &node, query, cache );
  345. }
  346. FTC_CACHE_TRYLOOP_END();
  347. if ( error )
  348. node = NULL;
  349. else
  350. {
  351. /* don't assume that the cache has the same number of buckets, since
  352. * our allocation request might have triggered global cache flushing
  353. */
  354. ftc_cache_add( cache, hash, node );
  355. }
  356. *anode = node;
  357. return error;
  358. }
  359. #ifndef FTC_INLINE
  360. FT_LOCAL_DEF( FT_Error )
  361. FTC_Cache_Lookup( FTC_Cache cache,
  362. FT_UInt32 hash,
  363. FT_Pointer query,
  364. FTC_Node *anode )
  365. {
  366. FT_UFast idx;
  367. FTC_Node* bucket;
  368. FTC_Node* pnode;
  369. FTC_Node node;
  370. FT_Error error = FTC_Err_Ok;
  371. FTC_Node_CompareFunc compare = cache->clazz.node_compare;
  372. if ( cache == NULL || anode == NULL )
  373. return FTC_Err_Invalid_Argument;
  374. idx = hash & cache->mask;
  375. if ( idx < cache->p )
  376. idx = hash & ( cache->mask * 2 + 1 );
  377. bucket = cache->buckets + idx;
  378. pnode = bucket;
  379. for (;;)
  380. {
  381. node = *pnode;
  382. if ( node == NULL )
  383. goto NewNode;
  384. if ( node->hash == hash && compare( node, query, cache ) )
  385. break;
  386. pnode = &node->link;
  387. }
  388. if ( node != *bucket )
  389. {
  390. *pnode = node->link;
  391. node->link = *bucket;
  392. *bucket = node;
  393. }
  394. /* move to head of MRU list */
  395. {
  396. FTC_Manager manager = cache->manager;
  397. if ( node != manager->nodes_list )
  398. ftc_node_mru_up( node, manager );
  399. }
  400. *anode = node;
  401. return error;
  402. NewNode:
  403. return FTC_Cache_NewNode( cache, hash, query, anode );
  404. }
  405. #endif /* !FTC_INLINE */
  406. FT_LOCAL_DEF( void )
  407. FTC_Cache_RemoveFaceID( FTC_Cache cache,
  408. FTC_FaceID face_id )
  409. {
  410. FT_UFast i, count;
  411. FTC_Manager manager = cache->manager;
  412. FTC_Node frees = NULL;
  413. count = cache->p + cache->mask;
  414. for ( i = 0; i < count; i++ )
  415. {
  416. FTC_Node* bucket = cache->buckets + i;
  417. FTC_Node* pnode = bucket;
  418. for ( ;; )
  419. {
  420. FTC_Node node = *pnode;
  421. if ( node == NULL )
  422. break;
  423. if ( cache->clazz.node_remove_faceid( node, face_id, cache ) )
  424. {
  425. *pnode = node->link;
  426. node->link = frees;
  427. frees = node;
  428. }
  429. else
  430. pnode = &node->link;
  431. }
  432. }
  433. /* remove all nodes in the free list */
  434. while ( frees )
  435. {
  436. FTC_Node node;
  437. node = frees;
  438. frees = node->link;
  439. manager->cur_weight -= cache->clazz.node_weight( node, cache );
  440. ftc_node_mru_unlink( node, manager );
  441. cache->clazz.node_free( node, cache );
  442. cache->slack++;
  443. }
  444. ftc_cache_resize( cache );
  445. }
  446. /* END */