2009-08-02 13:45:26 +02:00
|
|
|
(*
|
|
|
|
* CDDL HEADER START
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the terms of the
|
|
|
|
* Common Development and Distribution License, Version 1.0 only
|
|
|
|
* (the "License"). You may not use this file except in compliance
|
|
|
|
* with the License.
|
|
|
|
*
|
|
|
|
* You can obtain a copy of the license at
|
|
|
|
* http://www.opensource.org/licenses/cddl1.php.
|
|
|
|
* See the License for the specific language governing permissions
|
|
|
|
* and limitations under the License.
|
|
|
|
*
|
|
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
|
|
* file and include the License file at
|
|
|
|
* http://www.opensource.org/licenses/cddl1.php. If applicable,
|
|
|
|
* add the following below this CDDL HEADER, with the fields enclosed
|
|
|
|
* by brackets "[]" replaced with your own identifying * information:
|
|
|
|
* Portions Copyright [yyyy] [name of copyright owner]
|
|
|
|
*
|
|
|
|
* CDDL HEADER END
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Portions Copyright 2009 Andreas Schneider
|
|
|
|
*)
|
|
|
|
unit UCacheManager;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
2009-12-10 21:40:14 +01:00
|
|
|
{$interfaces corba}
|
2009-08-02 13:45:26 +02:00
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
SysUtils, Classes;
|
|
|
|
|
|
|
|
type
|
|
|
|
|
2009-12-10 21:40:14 +01:00
|
|
|
ICacheable = interface['{0ABAA4DE-8128-47B3-ABFE-5250A74A0428}']
|
|
|
|
function CanBeRemoved: Boolean;
|
|
|
|
procedure RemoveFromCache;
|
|
|
|
end;
|
|
|
|
|
2009-08-02 13:45:26 +02:00
|
|
|
{ TCacheManager }
|
|
|
|
|
2009-12-08 15:12:09 +01:00
|
|
|
generic TCacheManager<T> = class
|
2015-05-01 12:48:35 +02:00
|
|
|
public type
|
2009-12-08 15:12:09 +01:00
|
|
|
{ Types }
|
|
|
|
TRemoveObjectEvent = procedure(AObject: T) of object;
|
|
|
|
|
|
|
|
PCacheEntry = ^TCacheEntry;
|
|
|
|
TCacheEntry = record
|
|
|
|
ID: Integer;
|
|
|
|
Obj: T;
|
|
|
|
Next: PCacheEntry;
|
|
|
|
end;
|
2015-05-01 12:48:35 +02:00
|
|
|
protected
|
2009-08-02 13:45:26 +02:00
|
|
|
{ Members }
|
|
|
|
FSize: Integer;
|
|
|
|
FFirst: PCacheEntry;
|
|
|
|
FLast: PCacheEntry;
|
|
|
|
FOnRemoveObject: TRemoveObjectEvent;
|
2009-12-10 21:40:14 +01:00
|
|
|
procedure DoRemoveObject(var AObject: T; ANotify: Boolean = True);
|
2009-08-02 13:45:26 +02:00
|
|
|
public
|
2009-12-08 15:12:09 +01:00
|
|
|
constructor Create(ASize: Integer);
|
|
|
|
destructor Destroy; override;
|
|
|
|
|
2009-08-02 13:45:26 +02:00
|
|
|
{ Fields }
|
2009-12-08 15:12:09 +01:00
|
|
|
property OnRemoveObject: TRemoveObjectEvent read FOnRemoveObject
|
|
|
|
write FOnRemoveObject;
|
|
|
|
|
2009-08-02 13:45:26 +02:00
|
|
|
{ Methods }
|
2009-12-08 15:12:09 +01:00
|
|
|
function QueryID(const AID: Integer; out AObj: T): Boolean;
|
|
|
|
procedure StoreID(AID: Integer; AObj: T);
|
2009-08-02 13:45:26 +02:00
|
|
|
procedure DiscardID(AID: Integer);
|
2009-12-08 15:12:09 +01:00
|
|
|
procedure DiscardObj(AObj: T);
|
2009-08-02 13:45:26 +02:00
|
|
|
procedure RemoveID(AID: Integer);
|
|
|
|
procedure Clear;
|
|
|
|
function Iterate(var ACacheEntry: PCacheEntry): Boolean;
|
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2009-12-10 21:40:14 +01:00
|
|
|
uses
|
|
|
|
Logging;
|
|
|
|
|
2009-08-02 13:45:26 +02:00
|
|
|
{ TCacheManager }
|
|
|
|
|
2009-12-10 21:40:14 +01:00
|
|
|
procedure TCacheManager.DoRemoveObject(var AObject: T; ANotify: Boolean = True);
|
|
|
|
var
|
|
|
|
cacheable: ICacheable;
|
|
|
|
begin
|
|
|
|
if ANotify and Assigned(FOnRemoveObject) then FOnRemoveObject(AObject);
|
|
|
|
|
|
|
|
if TObject(AObject).GetInterface(ICacheable, cacheable) then
|
|
|
|
cacheable.RemoveFromCache
|
|
|
|
else
|
|
|
|
TObject(AObject).Free;
|
|
|
|
TObject(AObject) := nil;
|
|
|
|
end;
|
|
|
|
|
2009-08-02 13:45:26 +02:00
|
|
|
constructor TCacheManager.Create(ASize: Integer);
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
FOnRemoveObject := nil;
|
|
|
|
FSize := ASize;
|
|
|
|
if FSize > 0 then
|
|
|
|
begin
|
|
|
|
New(FFirst);
|
|
|
|
current := FFirst;
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
|
|
|
current^.Obj := nil;
|
|
|
|
for i := 2 to FSize do
|
|
|
|
begin
|
|
|
|
New(current^.Next);
|
|
|
|
FLast := current;
|
|
|
|
current := current^.Next;
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
|
|
|
current^.Obj := nil;
|
|
|
|
end;
|
|
|
|
current^.Next := nil;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TCacheManager.Destroy;
|
|
|
|
var
|
|
|
|
i: Integer;
|
|
|
|
current, last: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
for i := 1 to FSize do
|
|
|
|
begin
|
2009-12-08 15:12:09 +01:00
|
|
|
if Pointer(current^.Obj) <> nil then
|
2009-12-10 21:40:14 +01:00
|
|
|
DoRemoveObject(current^.Obj);
|
2009-08-02 13:45:26 +02:00
|
|
|
last := current;
|
|
|
|
current := current^.Next;
|
|
|
|
Dispose(last);
|
|
|
|
end;
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TCacheManager.DiscardID(AID: Integer);
|
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
while (current <> nil) do
|
|
|
|
begin
|
|
|
|
if (current^.ID = AID) then
|
|
|
|
begin
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
|
|
|
current^.Obj := nil;
|
|
|
|
current := nil;
|
|
|
|
end else
|
|
|
|
current := current^.Next;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2009-12-08 15:12:09 +01:00
|
|
|
procedure TCacheManager.DiscardObj(AObj: T);
|
2009-08-02 13:45:26 +02:00
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
while (current <> nil) do
|
|
|
|
begin
|
|
|
|
if (current^.Obj = AObj) then
|
|
|
|
begin
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
|
|
|
current^.Obj := nil;
|
|
|
|
current := nil;
|
|
|
|
end else
|
|
|
|
current := current^.Next;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TCacheManager.RemoveID(AID: Integer);
|
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
FLast := current;
|
|
|
|
while (current <> nil) do
|
|
|
|
begin
|
|
|
|
if (current^.ID = AID) then
|
|
|
|
begin
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
2009-12-08 15:12:09 +01:00
|
|
|
if Pointer(current^.Obj) <> nil then
|
2009-12-10 21:40:14 +01:00
|
|
|
DoRemoveObject(current^.Obj, False);
|
2009-08-02 13:45:26 +02:00
|
|
|
end;
|
|
|
|
if (current^.Next <> nil) then
|
|
|
|
FLast := current;
|
|
|
|
current := current^.Next;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TCacheManager.Clear;
|
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
while current <> nil do
|
|
|
|
begin
|
2009-12-08 15:12:09 +01:00
|
|
|
if Pointer(current^.Obj) <> nil then
|
2009-08-02 13:45:26 +02:00
|
|
|
begin
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
2009-12-10 21:40:14 +01:00
|
|
|
DoRemoveObject(current^.Obj);
|
2009-08-02 13:45:26 +02:00
|
|
|
end;
|
|
|
|
current := current^.Next;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TCacheManager.Iterate(var ACacheEntry: PCacheEntry): Boolean;
|
|
|
|
begin
|
|
|
|
if ACacheEntry = nil then
|
|
|
|
ACacheEntry := FFirst
|
|
|
|
else
|
|
|
|
ACacheEntry := ACacheEntry^.Next;
|
|
|
|
Result := ACacheEntry <> nil;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TCacheManager.QueryID(const AID: Integer;
|
2009-12-08 15:12:09 +01:00
|
|
|
out AObj: T): Boolean;
|
2009-08-02 13:45:26 +02:00
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
|
|
|
begin
|
|
|
|
current := FFirst;
|
|
|
|
FLast := current;
|
|
|
|
Result := False;
|
|
|
|
while (current <> nil) and (not Result) do
|
|
|
|
begin
|
|
|
|
if (current^.ID = AID) then
|
|
|
|
begin
|
|
|
|
Result := True;
|
|
|
|
AObj := current^.Obj;
|
|
|
|
if current <> FFirst then
|
|
|
|
begin
|
|
|
|
FLast^.Next := current^.Next;
|
|
|
|
current^.Next := FFirst;
|
|
|
|
FFirst := current;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if (current^.Next <> nil) then
|
|
|
|
FLast := current;
|
|
|
|
current := current^.Next;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2009-12-08 15:12:09 +01:00
|
|
|
procedure TCacheManager.StoreID(AID: Integer; AObj: T);
|
2009-08-02 13:45:26 +02:00
|
|
|
var
|
|
|
|
current: PCacheEntry;
|
2009-12-10 21:40:14 +01:00
|
|
|
cacheable: ICacheable;
|
|
|
|
i: Integer;
|
2009-08-02 13:45:26 +02:00
|
|
|
begin
|
|
|
|
current := FLast^.Next; //well, FLast is not really the last, but the one before the last ;)
|
|
|
|
FLast^.Next := nil;
|
|
|
|
current^.Next := FFirst;
|
|
|
|
FFirst := current;
|
2009-12-10 21:40:14 +01:00
|
|
|
if Pointer(FFirst^.Obj) <> nil then //if the last cache entry did contain an object, remove it now or grow
|
2009-08-02 13:45:26 +02:00
|
|
|
begin
|
2009-12-10 21:40:14 +01:00
|
|
|
if TObject(FFirst^.Obj).GetInterface(ICacheable, cacheable) and
|
|
|
|
not cacheable.CanBeRemoved then
|
|
|
|
begin
|
|
|
|
Logger.Send([lcInfo], 'Cache growing (%s)', [ClassName]);
|
|
|
|
New(FLast^.Next);
|
|
|
|
current := FLast^.Next;
|
|
|
|
current^.ID := FFirst^.ID;
|
|
|
|
current^.Obj := FFirst^.Obj;
|
|
|
|
for i := 2 to FSize do
|
|
|
|
begin
|
|
|
|
New(current^.Next);
|
|
|
|
FLast := current;
|
|
|
|
current := current^.Next;
|
|
|
|
current^.ID := LongInt($FFFFFFFF);
|
|
|
|
current^.Obj := nil;
|
|
|
|
end;
|
|
|
|
current^.Next := nil;
|
|
|
|
FSize := FSize * 2;
|
|
|
|
end else
|
|
|
|
DoRemoveObject(current^.Obj);
|
2009-08-02 13:45:26 +02:00
|
|
|
end;
|
2009-12-10 21:40:14 +01:00
|
|
|
FFirst^.ID := AID;
|
2009-08-02 13:45:26 +02:00
|
|
|
FFirst^.Obj := AObj;
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|
|
|