Statistics
| Branch: | Tag: | Revision:

root / UCacheManager.pas

History | View | Annotate | Download (6.5 kB)

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