Statistics
| Branch: | Tag: | Revision:

root / UCacheManager.pas

History | View | Annotate | Download (6.5 kB)

1
(*
2
 * CDDL HEADER START
3
 *
4
 * The contents of this file are subject to the terms of the
5
 * Common Development and Distribution License, Version 1.0 only
6
 * (the "License").  You may not use this file except in compliance
7
 * with the License.
8
 *
9
 * You can obtain a copy of the license at
10
 * http://www.opensource.org/licenses/cddl1.php.
11
 * See the License for the specific language governing permissions
12
 * and limitations under the License.
13
 *
14
 * When distributing Covered Code, include this CDDL HEADER in each
15
 * file and include the License file at
16
 * http://www.opensource.org/licenses/cddl1.php.  If applicable,
17
 * add the following below this CDDL HEADER, with the fields enclosed
18
 * by brackets "[]" replaced with your own identifying * information:
19
 *      Portions Copyright [yyyy] [name of copyright owner]
20
 *
21
 * CDDL HEADER END
22
 *
23
 *
24
 *      Portions Copyright 2010 Andreas Schneider
25
 *)
26
unit UCacheManager;
27
28
{$mode objfpc}{$H+}
29
{$interfaces corba}
30
31
interface
32
33
uses
34
  SysUtils, Classes;
35
36
type
37
38
  ICacheable = interface['{0ABAA4DE-8128-47B3-ABFE-5250A74A0428}']
39
    function CanBeRemoved: Boolean;
40
    procedure RemoveFromCache;
41
  end;
42
43
  { TCacheManager }
44
45
  generic TCacheManager<T> = class
46
  public type
47
    { Types }
48
    TRemoveObjectEvent = procedure(AObject: T) of object;
49
50
    PCacheEntry = ^TCacheEntry;
51
    TCacheEntry = record
52
      ID: Integer;
53
      Obj: T;
54
      Next: PCacheEntry;
55
    end;
56
  protected
57
    { Members }
58
    FSize: Integer;
59
    FFirst: PCacheEntry;
60
    FLast: PCacheEntry;
61
    FOnRemoveObject: TRemoveObjectEvent;
62
    procedure DoRemoveObject(var AObject: T; ANotify: Boolean = True);
63
  public
64
    constructor Create(ASize: Integer);
65
    destructor Destroy; override;
66
67
    { Fields }
68
    property OnRemoveObject: TRemoveObjectEvent read FOnRemoveObject
69
      write FOnRemoveObject;
70
71
    { Methods }
72
    function QueryID(const AID: Integer; out AObj: T): Boolean;
73
    procedure StoreID(AID: Integer; AObj: T);
74
    procedure DiscardID(AID: Integer);
75
    procedure DiscardObj(AObj: T);
76
    procedure RemoveID(AID: Integer);
77
    procedure Clear;
78
    function Iterate(var ACacheEntry: PCacheEntry): Boolean;
79
  end;
80
81
implementation
82
83
uses
84
  Logging;
85
86
{ TCacheManager }
87
88
procedure TCacheManager.DoRemoveObject(var AObject: T; ANotify: Boolean = True);
89
var
90
  cacheable: ICacheable;
91
begin
92
  if ANotify and Assigned(FOnRemoveObject) then FOnRemoveObject(AObject);
93
94
  if TObject(AObject).GetInterface(ICacheable, cacheable) then
95
    cacheable.RemoveFromCache
96
  else
97
    TObject(AObject).Free;
98
  TObject(AObject) := nil;
99
end;
100
101
constructor TCacheManager.Create(ASize: Integer);
102
var
103
  i: Integer;
104
  current: PCacheEntry;
105
begin
106
  FOnRemoveObject := nil;
107
  FSize := ASize;
108
  if FSize > 0 then
109
  begin
110
    New(FFirst);
111
    current := FFirst;
112
    current^.ID := LongInt($FFFFFFFF);
113
    current^.Obj := nil;
114
    for i := 2 to FSize do
115
    begin
116
      New(current^.Next);
117
      FLast := current;
118
      current := current^.Next;
119
      current^.ID := LongInt($FFFFFFFF);
120
      current^.Obj := nil;
121
    end;
122
    current^.Next := nil;
123
  end;
124
end;
125
126
destructor TCacheManager.Destroy;
127
var
128
  i: Integer;
129
  current, last: PCacheEntry;
130
begin
131
  current := FFirst;
132
  for i := 1 to FSize do
133
  begin
134
    if Pointer(current^.Obj) <> nil then
135
      DoRemoveObject(current^.Obj);
136
    last := current;
137
    current := current^.Next;
138
    Dispose(last);
139
  end;
140
  inherited;
141
end;
142
143
procedure TCacheManager.DiscardID(AID: Integer);
144
var
145
  current: PCacheEntry;
146
begin
147
  current := FFirst;
148
  while (current <> nil) do
149
  begin
150
    if (current^.ID = AID) then
151
    begin
152
      current^.ID := LongInt($FFFFFFFF);
153
      current^.Obj := nil;
154
      current := nil;
155
    end else
156
      current := current^.Next;
157
  end;
158
end;
159
160
procedure TCacheManager.DiscardObj(AObj: T);
161
var
162
  current: PCacheEntry;
163
begin
164
  current := FFirst;
165
  while (current <> nil) do
166
  begin
167
    if (current^.Obj = AObj) then
168
    begin
169
      current^.ID := LongInt($FFFFFFFF);
170
      current^.Obj := nil;
171
      current := nil;
172
    end else
173
      current := current^.Next;
174
  end;
175
end;
176
177
procedure TCacheManager.RemoveID(AID: Integer);
178
var
179
  current: PCacheEntry;
180
begin
181
  current := FFirst;
182
  FLast := current;
183
  while (current <> nil) do
184
  begin
185
    if (current^.ID = AID) then
186
    begin
187
      current^.ID := LongInt($FFFFFFFF);
188
      if Pointer(current^.Obj) <> nil then
189
        DoRemoveObject(current^.Obj, False);
190
    end;
191
    if (current^.Next <> nil) then
192
      FLast := current;
193
    current := current^.Next;
194
  end;
195
end;
196
197
procedure TCacheManager.Clear;
198
var
199
  current: PCacheEntry;
200
begin
201
  current := FFirst;
202
  while current <> nil do
203
  begin
204
    if Pointer(current^.Obj) <> nil then
205
    begin
206
      current^.ID := LongInt($FFFFFFFF);
207
      DoRemoveObject(current^.Obj);
208
    end;
209
    current := current^.Next;
210
  end;
211
end;
212
213
function TCacheManager.Iterate(var ACacheEntry: PCacheEntry): Boolean;
214
begin
215
  if ACacheEntry = nil then
216
    ACacheEntry := FFirst
217
  else
218
    ACacheEntry := ACacheEntry^.Next;
219
  Result := ACacheEntry <> nil;
220
end;
221
222
function TCacheManager.QueryID(const AID: Integer;
223
  out AObj: T): Boolean;
224
var
225
  current: PCacheEntry;
226
begin
227
  current := FFirst;
228
  FLast := current;
229
  Result := False;
230
  while (current <> nil) and (not Result) do
231
  begin
232
    if (current^.ID = AID) then
233
    begin
234
      Result := True;
235
      AObj := current^.Obj;
236
      if current <> FFirst then
237
      begin
238
        FLast^.Next := current^.Next;
239
        current^.Next := FFirst;
240
        FFirst := current;
241
      end;
242
    end;
243
    if (current^.Next <> nil) then
244
      FLast := current;
245
    current := current^.Next;
246
  end;
247
end;
248
249
procedure TCacheManager.StoreID(AID: Integer; AObj: T);
250
var
251
  current: PCacheEntry;
252
  cacheable: ICacheable;
253
  i: Integer;
254
begin
255
  current := FLast^.Next; //well, FLast is not really the last, but the one before the last ;)
256
  FLast^.Next := nil;
257
  current^.Next := FFirst;
258
  FFirst := current;
259
  if Pointer(FFirst^.Obj) <> nil then //if the last cache entry did contain an object, remove it now or grow
260
  begin
261
    if TObject(FFirst^.Obj).GetInterface(ICacheable, cacheable) and
262
      not cacheable.CanBeRemoved then
263
    begin
264
      Logger.Send([lcInfo], 'Cache growing (%s)', [ClassName]);
265
      New(FLast^.Next);
266
      current := FLast^.Next;
267
      current^.ID := FFirst^.ID;
268
      current^.Obj := FFirst^.Obj;
269
      for i := 2 to FSize do
270
      begin
271
        New(current^.Next);
272
        FLast := current;
273
        current := current^.Next;
274
        current^.ID := LongInt($FFFFFFFF);
275
        current^.Obj := nil;
276
      end;
277
      current^.Next := nil;
278
      FSize := FSize * 2;
279
    end else
280
      DoRemoveObject(current^.Obj);
281
  end;
282
  FFirst^.ID := AID;
283
  FFirst^.Obj := AObj;
284
end;
285
286
end.
287