1 /***************************************************************************/
2 /* */
3 /* ftcchunk.c */
4 /* */
5 /* FreeType chunk cache cache (body). */
6 /* */
7 /* Copyright 2000-2001 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 /***************************************************************************/
19 #include <ft2build.h>
20 #include FT_CACHE_H
21 #include FT_CACHE_INTERNAL_CHUNK_H
22 #include FT_LIST_H
23 #include FT_ERRORS_H
24 #include FT_INTERNAL_OBJECTS_H
26 #include "ftcerror.h"
29 /*************************************************************************/
30 /*************************************************************************/
31 /***** *****/
32 /***** GLYPH NODES *****/
33 /***** *****/
34 /*************************************************************************/
35 /*************************************************************************/
38 /* create a new chunk node, setting its cache index and ref count */
39 FT_EXPORT_DEF( FT_Error )
40 FTC_ChunkNode_Init( FTC_ChunkNode node,
41 FTC_ChunkSet cset,
42 FT_UInt index,
43 FT_Bool alloc )
44 {
45 FTC_Chunk_Cache cache = cset->cache;
46 FTC_CacheNode_Data* data = FTC_CACHENODE_TO_DATA_P( &node->root );
47 FT_Error error = 0;
50 data->cache_index = (FT_UShort)cache->root.cache_index;
51 data->ref_count = (FT_Short) 0;
52 node->cset = cset;
53 node->cset_index = (FT_UShort)index;
54 node->num_elements = (unsigned short)(
55 ( index + 1 < cset->num_chunks )
56 ? cset->element_count
57 : cset->element_max - cset->element_count*index );
58 if ( alloc )
59 {
60 /* allocate elements array */
61 FT_Memory memory;
64 memory = cache->root.memory;
65 error = MEM_Alloc( node->elements,
66 cset->element_size * cset->element_count );
67 }
69 return error;
70 }
73 FT_EXPORT_DEF( void )
74 FTC_ChunkNode_Destroy( FTC_ChunkNode node )
75 {
76 FTC_ChunkSet cset = node->cset;
79 /* remove from parent set table */
80 cset->chunks[node->cset_index] = 0;
82 /* destroy the node */
83 cset->clazz->destroy_node( node );
84 }
87 FT_EXPORT_DEF( FT_ULong )
88 FTC_ChunkNode_Size( FTC_ChunkNode node )
89 {
90 FTC_ChunkSet cset = node->cset;
93 return cset->clazz->size_node( node );
94 }
97 FT_CALLBACK_TABLE_DEF
98 const FTC_CacheNode_Class ftc_chunk_cache_node_class =
99 {
100 (FTC_CacheNode_SizeFunc) FTC_ChunkNode_Size,
101 (FTC_CacheNode_DestroyFunc)FTC_ChunkNode_Destroy
102 };
105 /*************************************************************************/
106 /*************************************************************************/
107 /***** *****/
108 /***** CHUNK SETS *****/
109 /***** *****/
110 /*************************************************************************/
111 /*************************************************************************/
114 FT_EXPORT_DEF( FT_Error )
115 FTC_ChunkSet_New( FTC_Chunk_Cache cache,
116 FT_Pointer type,
117 FTC_ChunkSet *aset )
118 {
119 FT_Error error;
120 FT_Memory memory = cache->root.memory;
121 FTC_Manager manager = cache->root.manager;
122 FTC_ChunkSet cset = 0;
124 FTC_Chunk_Cache_Class* ccache_class;
125 FTC_ChunkSet_Class* clazz;
128 ccache_class = (FTC_Chunk_Cache_Class*)cache->root.clazz;
129 clazz = ccache_class->cset_class;
131 *aset = 0;
133 if ( ALLOC( cset, clazz->cset_byte_size ) )
134 goto Exit;
136 cset->cache = cache;
137 cset->manager = manager;
138 cset->memory = memory;
139 cset->clazz = clazz;
141 /* now compute element_max, element_count and element_size */
142 error = clazz->sizes( cset, type );
143 if ( error )
144 goto Exit;
146 /* compute maximum number of nodes */
147 cset->num_chunks = ( cset->element_max + cset->element_count - 1 ) /
148 cset->element_count;
150 /* allocate chunk pointers table */
151 if ( ALLOC_ARRAY( cset->chunks, cset->num_chunks, FTC_ChunkNode ) )
152 goto Exit;
154 /* initialize set by type if needed */
155 if ( clazz->init )
156 {
157 error = clazz->init( cset, type );
158 if ( error )
159 goto Exit;
160 }
162 *aset = cset;
164 Exit:
165 if ( error && cset )
166 {
167 FREE( cset->chunks );
168 FREE( cset );
169 }
171 return error;
172 }
175 FT_EXPORT_DEF( void )
176 FTC_ChunkSet_Destroy( FTC_ChunkSet cset )
177 {
178 FTC_Chunk_Cache cache = cset->cache;
179 FTC_Manager manager = cache->root.manager;
180 FT_List glyphs_lru = &manager->global_lru;
181 FTC_ChunkNode* bucket = cset->chunks;
182 FTC_ChunkNode* bucket_limit = bucket + cset->num_chunks;
183 FT_Memory memory = cache->root.memory;
185 FTC_ChunkSet_Class* clazz = cset->clazz;
188 /* for each bucket, free the list of glyph nodes */
189 for ( ; bucket < bucket_limit; bucket++ )
190 {
191 FTC_ChunkNode node = bucket[0];
192 FT_ListNode lrunode;
195 if ( node )
196 {
197 lrunode = FTC_CHUNKNODE_TO_LRUNODE( node );
199 manager->num_bytes -= clazz->size_node( node );
200 manager->num_nodes--;
202 FT_List_Remove( glyphs_lru, lrunode );
204 clazz->destroy_node( node );
206 bucket[0] = 0;
207 }
208 }
210 if ( clazz->done )
211 clazz->done( cset );
213 FREE( cset->chunks );
214 FREE( cset );
215 }
218 FT_EXPORT_DEF( FT_Error )
219 FTC_ChunkSet_Lookup_Node( FTC_ChunkSet cset,
220 FT_UInt glyph_index,
221 FTC_ChunkNode *anode,
222 FT_UInt *anindex )
223 {
224 FTC_Chunk_Cache cache = cset->cache;
225 FTC_Manager manager = cache->root.manager;
226 FT_Error error = 0;
228 FTC_ChunkSet_Class* clazz = cset->clazz;
231 *anode = 0;
233 if ( glyph_index >= cset->element_max )
234 error = FTC_Err_Invalid_Argument;
235 else
236 {
237 FT_UInt chunk_size = cset->element_count;
238 FT_UInt chunk_index = glyph_index / chunk_size;
239 FTC_ChunkNode* pnode = cset->chunks + chunk_index;
240 FTC_ChunkNode node = *pnode;
243 if ( !node )
244 {
245 /* we didn't found the glyph image; we will now create a new one */
246 error = clazz->new_node( cset, chunk_index, &node );
247 if ( error )
248 goto Exit;
250 /* store the new chunk in the cset's table */
251 *pnode = node;
253 /* insert the node at the start the global LRU glyph list */
254 FT_List_Insert( &manager->global_lru,
255 FTC_CHUNKNODE_TO_LRUNODE( node ) );
257 manager->num_bytes += clazz->size_node( node );
258 manager->num_nodes++;
260 if ( manager->num_bytes > manager->max_bytes )
261 {
262 FTC_ChunkNode_Ref ( node );
263 FTC_Manager_Compress( manager );
264 FTC_ChunkNode_Unref ( node );
265 }
266 }
268 *anode = node;
269 *anindex = glyph_index - chunk_index * chunk_size;
270 }
272 Exit:
273 return error;
274 }
277 /*************************************************************************/
278 /*************************************************************************/
279 /***** *****/
280 /***** CHUNK SETS LRU CALLBACKS *****/
281 /***** *****/
282 /*************************************************************************/
283 /*************************************************************************/
286 #define FTC_CSET_LRU_GET_CACHE( lru ) \
287 ( (FTC_Chunk_Cache)((lru)->user_data) )
289 #define FTC_CSET_LRU_GET_MANAGER( lru ) \
290 FTC_CSET_LRU_GET_CACHE( lru )->manager
292 #define FTC_LRUNODE_CSET( node ) \
293 ( (FTC_ChunkSet)(node)->root.data )
296 FT_CALLBACK_DEF( FT_Error )
297 ftc_chunk_set_lru_init( FT_Lru lru,
298 FT_LruNode node )
299 {
300 FTC_Chunk_Cache cache = FTC_CSET_LRU_GET_CACHE( lru );
301 FT_Error error;
302 FTC_ChunkSet cset;
305 error = FTC_ChunkSet_New( cache,
306 (FT_Pointer)node->key,
307 &cset );
308 if ( !error )
309 {
310 /* good, now set the set index within the set object */
311 cset->cset_index = (FT_UInt)( node - lru->nodes );
312 node->root.data = cset;
313 }
315 return error;
316 }
319 FT_CALLBACK_DEF( void )
320 ftc_chunk_set_lru_done( FT_Lru lru,
321 FT_LruNode node )
322 {
323 FTC_ChunkSet cset = FTC_LRUNODE_CSET( node );
325 FT_UNUSED( lru );
328 FTC_ChunkSet_Destroy( cset );
329 }
332 FT_CALLBACK_DEF( FT_Bool )
333 ftc_chunk_set_lru_compare( FT_LruNode node,
334 FT_LruKey key )
335 {
336 FTC_ChunkSet cset = FTC_LRUNODE_CSET( node );
339 return cset->clazz->compare( cset, (FT_Pointer)key );
340 }
343 FT_CALLBACK_TABLE_DEF
344 const FT_Lru_Class ftc_chunk_set_lru_class =
345 {
346 sizeof( FT_LruRec ),
347 ftc_chunk_set_lru_init,
348 ftc_chunk_set_lru_done,
349 0, /* no flush */
350 ftc_chunk_set_lru_compare
351 };
354 /*************************************************************************/
355 /*************************************************************************/
356 /***** *****/
357 /***** CHUNK CACHE OBJECTS *****/
358 /***** *****/
359 /*************************************************************************/
360 /*************************************************************************/
363 FT_EXPORT_DEF( FT_Error )
364 FTC_Chunk_Cache_Init( FTC_Chunk_Cache cache )
365 {
366 FT_Memory memory = cache->root.memory;
367 FT_Error error;
369 FTC_Chunk_Cache_Class* ccache_clazz;
372 /* set up root node_class to be used by manager */
373 cache->root.node_clazz =
374 (FTC_CacheNode_Class*)&ftc_chunk_cache_node_class;
376 /* setup `compare' shortcut */
377 ccache_clazz = (FTC_Chunk_Cache_Class*)cache->root.clazz;
378 cache->compare = ccache_clazz->cset_class->compare;
380 error = FT_Lru_New( &ftc_chunk_set_lru_class,
381 FTC_MAX_CHUNK_SETS,
382 cache,
383 memory,
384 1, /* pre_alloc == TRUE */
385 &cache->csets_lru );
386 return error;
387 }
390 FT_EXPORT_DEF( void )
391 FTC_Chunk_Cache_Done( FTC_Chunk_Cache cache )
392 {
393 /* discard glyph sets */
394 FT_Lru_Done( cache->csets_lru );
395 }
398 FT_EXPORT_DEF( FT_Error )
399 FTC_Chunk_Cache_Lookup( FTC_Chunk_Cache cache,
400 FT_Pointer type,
401 FT_UInt gindex,
402 FTC_ChunkNode *anode,
403 FT_UInt *aindex )
404 {
405 FT_Error error;
406 FTC_ChunkSet cset;
407 FTC_ChunkNode node;
408 FT_UInt cindex;
409 FTC_Manager manager;
412 /* check for valid `desc' delayed to FT_Lru_Lookup() */
414 if ( !cache || !anode || !aindex )
415 return FTC_Err_Invalid_Argument;
417 *anode = 0;
418 *aindex = 0;
419 cset = cache->last_cset;
421 if ( !cset || !cache->compare( cset, type ) )
422 {
423 error = FT_Lru_Lookup( cache->csets_lru,
424 (FT_LruKey)type,
425 (FT_Pointer*)&cset );
426 cache->last_cset = cset;
427 if ( error )
428 goto Exit;
429 }
431 error = FTC_ChunkSet_Lookup_Node( cset, gindex, &node, &cindex );
432 if ( error )
433 goto Exit;
435 /* now compress the manager's cache pool if needed */
436 manager = cache->root.manager;
437 if ( manager->num_bytes > manager->max_bytes )
438 {
439 FTC_ChunkNode_Ref ( node );
440 FTC_Manager_Compress( manager );
441 FTC_ChunkNode_Unref ( node );
442 }
444 *anode = node;
445 *aindex = cindex;
447 Exit:
448 return error;
449 }
452 /* END */