lazarus-ccr/components/freetypepascal/ttcache.pas

434 lines
12 KiB
ObjectPascal

(*******************************************************************
*
* ttcache.pas 1.0
*
* Generic object cache
*
* Copyright 1996, 1997 by
* David Turner, Robert Wilhelm, and Werner Lemberg.
*
* This file is part of the FreeType project, and may only be used
* modified and distributed under the terms of the FreeType project
* license, LICENSE.TXT. By continuing to use, modify or distribute
* this file you indicate that you have read the license and
* understand and accept it fully.
*
*
* This component defines and implement object caches.
*
* An object class is a structure layout that encapsulate one
* given type of data used by the FreeType engine. Each object
* class is completely described by :
*
* - a 'root' or 'leading' structure containing the first
* important fields of the class. The root structure is
* always of fixed size.
*
* It is implemented as a simple C structure, and may
* contain several pointers to sub-tables that can be
* sized and allocated dynamically.
*
* examples : TFace, TInstance, TGlyph & TExecution_Context
* ( defined in 'ttobjs.h' )
*
* - we make a difference between 'child' pointers and 'peer'
* pointers. A 'child' pointer points to a sub-table that is
* owned by the object, while a 'peer' pointer points to any
* other kind of data the object isn't responsible for.
*
* An object class is thus usually a 'tree' of 'child' tables.
*
* - each object class needs a constructor and a destructor.
*
* A constructor is a function which receives the address of
* freshly allocated and zeroed object root structure and
* 'builds' all the valid child data that must be associated
* to the object before it becomes 'valid'.
*
* A destructor does the inverse job : given the address of
* a valid object, it must discards all its child data and
* zero its main fields (essentially the pointers and array
* sizes found in the root fields).
*
*
*
*
*
*
*
*
*
*
*
*
******************************************************************)
unit TTCache;
interface
uses TTError, TTTypes;
type
(* Simple list node record. A List element is said to be 'unlinked' *)
(* when it doesn't belong to any list *)
(* *)
PList_Element = ^TList_Element;
TList_Element = record
next : PList_Element; (* Pointer to next element of list *)
data : Pointer; (* Pointer to the listed object *)
end;
(* Simple singly-linked list record *)
(* LIFO - style, no tail field *)
TSingle_List = PList_Element;
TConstructor = function( _object : Pointer;
_parent : Pointer ) : TError;
TDestructor = function( _object : Pointer ) : TError;
PCache_Class = ^TCache_Class;
TCache_Class = record
Object_Size : Int;
Idle_Limit : Int;
Init : TConstructor;
Done : TDestructor;
end;
(* A Cache class record holds the data necessary to define *)
(* a cache kind. *)
PCache = ^TCache;
TCache = record
clazz : PCache_Class; (* 'class' reserved in VP & Delphi *)
active : TSingle_List;
idle : TSingle_List;
idle_count : Int;
end;
(* An object cache holds two lists tracking the active and *)
(* idle objects that are currently created and used by the *)
(* engine. It can also be 'protected' by a mutex *)
function Cache_Create( var clazz : TCache_Class;
var cache : TCache ) : TError;
(* Initialize a new cache named 'cache', of class 'clazz', and *)
(* protected by the 'lock' mutex. Note that the mutex is ignored *)
(* as the pascal version isn't thread-safe *)
function Cache_Destroy( var cache : TCache ) : TError;
(* Destroys a cache and all its listed objects *)
function Cache_New( var cache : TCache;
var new_object : Pointer;
parent_data : Pointer ) : TError;
(* Extracts a new object from the cache. *)
function Cache_Done( var cache : TCache; obj : Pointer ) : TError;
(* returns an object to the cache, or discards it depending *)
(* on the cache class' "idle_limit" field *)
(********************************************************)
(* *)
(* Two functions used to manage list elements *)
(* *)
(* Note that they're thread-safe in multi-threaded *)
(* builds. *)
(* *)
function Element_New : PList_Element;
(* Returns a new list element, either fresh or recycled *)
(* Note : the returned element is unlinked *)
procedure Element_Done( element : PList_Element );
(* Recycles or discards an element. *)
(* Note : The element must be unlinked !! *)
function TTCache_Init : TError;
function TTCache_Done : TError;
implementation
uses TTMemory;
const
Null_Single_List = nil;
var
Free_Elements : PList_Element;
(*******************************************************************
*
* Function : Element_New
*
* Description : Gets a new ( either fresh or recycled ) list
* element. The element is unlisted.
*
* Notes : returns nil if out of memory
*
*****************************************************************)
function Element_New : PList_Element;
var
element : PList_Element;
begin
(* LOCK *)
if Free_Elements <> nil then
begin
element := Free_Elements;
Free_Elements := element^.next;
end
else
begin
Alloc( element, sizeof(TList_Element) );
(* by convention, an allocated block is always zeroed *)
(* the fields of element need not be set to NULL then *)
end;
(* UNLOCK *)
Element_New := element;
end;
(*******************************************************************
*
* Function : Element_Done
*
* Description : recycles an unlisted list element
*
* Notes : Doesn't check that the element is unlisted
*
*****************************************************************)
procedure Element_Done( element : PList_Element );
begin
(* LOCK *)
element^.next := Free_Elements;
Free_Elements := element;
(* UNLOCK *)
end;
(*******************************************************************
*
* Function : Cache_Create
*
* Description : Create a new cache object
*
*****************************************************************)
function Cache_Create( var clazz : TCache_Class;
var cache : TCache ) : TError;
begin
cache.clazz := @clazz;
cache.idle_count := 0;
cache.active := Null_Single_List;
cache.idle := Null_Single_List;
Cache_Create := Success;
end;
(*******************************************************************
*
* Function : Cache_Destroy
*
* Description : Destroy a given cache object
*
*****************************************************************)
function Cache_Destroy( var cache : TCache ) : TError;
var
destroy : TDestructor;
current : PList_Element;
next : PList_Element;
begin
(* now destroy all active and idle listed objects *)
destroy := cache.clazz^.done;
(* active list *)
current := cache.active;
while current <> nil do
begin
next := current^.next;
destroy( current^.data );
Free( current^.data );
Element_Done( current );
current := next;
end;
cache.active := Null_SIngle_List;
(* idle list *)
current := cache.idle;
while current <> nil do
begin
next := current^.next;
destroy( current^.data );
Free( current^.data );
Element_Done( current );
current := next;
end;
cache.idle := Null_Single_List;
cache.clazz := nil;
cache.idle_count := 0;
Cache_Destroy := Success;
end;
(*******************************************************************
*
* Function : Cache_New
*
* Description : Extracts one 'new' object from a cache
*
* Notes : The 'parent_data' pointer is passed to the object's
* initialiser when the new object is created from
* scratch. Recycled objects do not use this pointer
*
*****************************************************************)
function Cache_New( var cache : TCache;
var new_object : Pointer;
parent_data : Pointer ) : TError;
var
error : TError;
current : PList_Element;
obj : Pointer;
label
Fail;
begin
(* LOCK *)
current := cache.idle;
if current <> nil then
begin
cache.idle := current^.next;
dec( cache.idle_count )
end;
(* UNLOCK *)
if current = nil then
begin
(* if no object was found in the cache, create a new one *)
if Alloc( obj, cache.clazz^.object_size ) then exit;
current := Element_New;
if current = nil then goto Fail;
current^.data := obj;
error := cache.clazz^.init( obj, parent_data );
if error then goto Fail;
end;
(* LOCK *)
current^.next := cache.active;
cache.active := current;
(* UNLOCK *)
new_object := current^.data;
Cache_New := Success;
exit;
Fail:
Free( obj );
Cache_New := Failure;
end;
(*******************************************************************
*
* Function : Cache_Done
*
* Description : Discards an object intro a cache
*
*****************************************************************)
function Cache_Done( var cache : TCache; obj : Pointer ) : TError;
var
error : TError;
element : PList_Element;
parent : ^PList_Element;
label
Suite;
begin
Cache_Done := failure;
(* find element in list *)
(* LOCK *)
parent := @cache.active;
element := parent^;
while element <> nil do
begin
if element^.data = obj then
begin
parent^ := element^.next;
(* UNLOCK *)
goto Suite;
end;
parent := @element^.next;
element := parent^;
end;
(* UNLOCK *)
(* Element wasn't found !! *)
{$IFDEF DEBUG}
{$ENDIF}
exit;
Suite:
if ( cache.idle_count >= cache.clazz^.idle_limit ) then
begin
(* destroy the object when the cache is full *)
cache.clazz^.done( element^.data );
Free( element^.data );
Element_Done( element );
end
else
begin
(* simply add the object to the idle list *)
(* LOCK *)
element^.next := cache.idle;
cache.idle := element;
inc( cache.idle_count );
(* UNLOCK *)
end;
Cache_Done := Success;
end;
function TTCache_Init : TError;
begin
Free_Elements := nil;
TTCache_Init := Success;
end;
function TTCache_Done : TError;
var
current, next : PList_ELement;
begin
current := free_elements;
while current <> nil do
begin
next := current^.next;
Free( current );
current := next;
end;
TTCache_Done := success;
end;
end.