1
(*******************************************************************
7
* Copyright 1996, 1997 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.
17
* This component defines and implement object caches.
19
* An object class is a structure layout that encapsulate one
20
* given type of data used by the FreeType engine. Each object
21
* class is completely described by :
23
* - a 'root' or 'leading' structure containing the first
24
* important fields of the class. The root structure is
25
* always of fixed size.
27
* It is implemented as a simple C structure, and may
28
* contain several pointers to sub-tables that can be
29
* sized and allocated dynamically.
31
* examples : TFace, TInstance, TGlyph & TExecution_Context
32
* ( defined in 'ttobjs.h' )
34
* - we make a difference between 'child' pointers and 'peer'
35
* pointers. A 'child' pointer points to a sub-table that is
36
* owned by the object, while a 'peer' pointer points to any
37
* other kind of data the object isn't responsible for.
39
* An object class is thus usually a 'tree' of 'child' tables.
41
* - each object class needs a constructor and a destructor.
43
* A constructor is a function which receives the address of
44
* freshly allocated and zeroed object root structure and
45
* 'builds' all the valid child data that must be associated
46
* to the object before it becomes 'valid'.
48
* A destructor does the inverse job : given the address of
49
* a valid object, it must discards all its child data and
50
* zero its main fields (essentially the pointers and array
51
* sizes found in the root fields).
64
******************************************************************)
74
(* Simple list node record. A List element is said to be 'unlinked' *)
75
(* when it doesn't belong to any list *)
77
PList_Element = ^TList_Element;
78
TList_Element = record
80
next : PList_Element; (* Pointer to next element of list *)
81
data : Pointer; (* Pointer to the listed object *)
85
(* Simple singly-linked list record *)
86
(* LIFO - style, no tail field *)
87
TSingle_List = PList_Element;
90
TConstructor = function( _object : Pointer;
91
_parent : Pointer ) : TError;
93
TDestructor = function( _object : Pointer ) : TError;
95
PCache_Class = ^TCache_Class;
102
(* A Cache class record holds the data necessary to define *)
107
clazz : PCache_Class; (* 'class' reserved in VP & Delphi *)
108
active : TSingle_List;
113
(* An object cache holds two lists tracking the active and *)
114
(* idle objects that are currently created and used by the *)
115
(* engine. It can also be 'protected' by a mutex *)
117
function Cache_Create( var clazz : TCache_Class;
118
var cache : TCache ) : TError;
119
(* Initialize a new cache named 'cache', of class 'clazz', and *)
120
(* protected by the 'lock' mutex. Note that the mutex is ignored *)
121
(* as the pascal version isn't thread-safe *)
123
function Cache_Destroy( var cache : TCache ) : TError;
124
(* Destroys a cache and all its listed objects *)
126
function Cache_New( var cache : TCache;
127
var new_object : Pointer;
128
parent_data : Pointer ) : TError;
129
(* Extracts a new object from the cache. *)
131
function Cache_Done( var cache : TCache; obj : Pointer ) : TError;
132
(* returns an object to the cache, or discards it depending *)
133
(* on the cache class' "idle_limit" field *)
135
(********************************************************)
137
(* Two functions used to manage list elements *)
139
(* Note that they're thread-safe in multi-threaded *)
143
function Element_New : PList_Element;
144
(* Returns a new list element, either fresh or recycled *)
145
(* Note : the returned element is unlinked *)
147
procedure Element_Done( element : PList_Element );
148
(* Recycles or discards an element. *)
149
(* Note : The element must be unlinked !! *)
154
function TTCache_Init : TError;
156
function TTCache_Done : TError;
164
Null_Single_List = nil;
167
Free_Elements : PList_Element;
169
(*******************************************************************
171
* Function : Element_New
173
* Description : Gets a new ( either fresh or recycled ) list
174
* element. The element is unlisted.
176
* Notes : returns nil if out of memory
178
*****************************************************************)
180
function Element_New : PList_Element;
182
element : PList_Element;
186
if Free_Elements <> nil then
188
element := Free_Elements;
189
Free_Elements := element^.next;
193
Alloc( element, sizeof(TList_Element) );
194
(* by convention, an allocated block is always zeroed *)
195
(* the fields of element need not be set to NULL then *)
200
Element_New := element;
203
(*******************************************************************
205
* Function : Element_Done
207
* Description : recycles an unlisted list element
209
* Notes : Doesn't check that the element is unlisted
211
*****************************************************************)
213
procedure Element_Done( element : PList_Element );
217
element^.next := Free_Elements;
218
Free_Elements := element;
224
(*******************************************************************
226
* Function : Cache_Create
228
* Description : Create a new cache object
230
*****************************************************************)
231
function Cache_Create( var clazz : TCache_Class;
232
var cache : TCache ) : TError;
234
cache.clazz := @clazz;
235
cache.idle_count := 0;
236
cache.active := Null_Single_List;
237
cache.idle := Null_Single_List;
239
Cache_Create := Success;
243
(*******************************************************************
245
* Function : Cache_Destroy
247
* Description : Destroy a given cache object
249
*****************************************************************)
250
function Cache_Destroy( var cache : TCache ) : TError;
252
destroy : TDestructor;
253
current : PList_Element;
254
next : PList_Element;
256
(* now destroy all active and idle listed objects *)
258
destroy := cache.clazz^.done;
261
current := cache.active;
262
while current <> nil do
264
next := current^.next;
265
destroy( current^.data );
266
Free( current^.data );
267
Element_Done( current );
270
cache.active := Null_SIngle_List;
273
current := cache.idle;
274
while current <> nil do
276
next := current^.next;
277
destroy( current^.data );
278
Free( current^.data );
279
Element_Done( current );
282
cache.idle := Null_Single_List;
285
cache.idle_count := 0;
287
Cache_Destroy := Success;
291
(*******************************************************************
293
* Function : Cache_New
295
* Description : Extracts one 'new' object from a cache
297
* Notes : The 'parent_data' pointer is passed to the object's
298
* initialiser when the new object is created from
299
* scratch. Recycled objects do not use this pointer
301
*****************************************************************)
302
function Cache_New( var cache : TCache;
303
var new_object : Pointer;
304
parent_data : Pointer ) : TError;
307
current : PList_Element;
313
current := cache.idle;
314
if current <> nil then
316
cache.idle := current^.next;
317
dec( cache.idle_count )
321
if current = nil then
323
(* if no object was found in the cache, create a new one *)
325
if Alloc( obj, cache.clazz^.object_size ) then exit;
327
current := Element_New;
328
if current = nil then goto Fail;
330
current^.data := obj;
332
error := cache.clazz^.init( obj, parent_data );
333
if error then goto Fail;
337
current^.next := cache.active;
338
cache.active := current;
341
new_object := current^.data;
343
Cache_New := Success;
348
Cache_New := Failure;
351
(*******************************************************************
353
* Function : Cache_Done
355
* Description : Discards an object intro a cache
357
*****************************************************************)
359
function Cache_Done( var cache : TCache; obj : Pointer ) : TError;
361
element : PList_Element;
362
parent : ^PList_Element;
366
Cache_Done := failure;
368
(* find element in list *)
370
parent := @cache.active;
372
while element <> nil do
374
if element^.data = obj then
376
parent^ := element^.next;
380
parent := @element^.next;
385
(* Element wasn't found !! *)
386
{$IFDEF FREETYPE_DEBUG}
391
if ( cache.idle_count >= cache.clazz^.idle_limit ) then
393
(* destroy the object when the cache is full *)
394
cache.clazz^.done( element^.data );
395
Free( element^.data );
396
Element_Done( element );
400
(* simply add the object to the idle list *)
402
element^.next := cache.idle;
403
cache.idle := element;
404
inc( cache.idle_count );
408
Cache_Done := Success;
412
function TTCache_Init : TError;
414
Free_Elements := nil;
415
TTCache_Init := Success;
419
function TTCache_Done : TError;
421
current, next : PList_ELement;
423
current := free_elements;
424
while current <> nil do
426
next := current^.next;
430
TTCache_Done := success;