Quick.MemoryCache.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 Kike Pérez
  3. Unit : Quick.MemoryCache
  4. Description : Cache objects with expiration control
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 14/07/2019
  8. Modified : 02/11/2019
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.MemoryCache;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. System.SysUtils,
  26. System.Generics.Collections,
  27. System.DateUtils,
  28. System.TypInfo,
  29. RTTI,
  30. Quick.Commons,
  31. Quick.Value,
  32. Quick.Threads,
  33. Quick.Cache.Intf,
  34. Quick.MemoryCache.Types,
  35. Quick.MemoryCache.Serializer.Json,
  36. Quick.MemoryCache.Compressor.GZip;
  37. type
  38. TCacheFlushedEvent = reference to procedure(aRemovedEntries : Integer);
  39. TBeginPurgerJobEvent = reference to procedure;
  40. TEndPurgerJobEvent = reference to procedure(aPurgedEntries : Integer);
  41. TPurgerJobErrorEvent = reference to procedure(const aErrorMsg : string);
  42. IMemoryCache<T> = interface
  43. ['{57927AD7-C993-4C3C-B552-43A39F99E73A}']
  44. function GetCompression: Boolean;
  45. procedure SetCompression(const Value: Boolean);
  46. function GetCachedObjects: Integer;
  47. function GetCacheSize: Integer;
  48. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  49. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  50. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  51. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  52. property Compression : Boolean read GetCompression write SetCompression;
  53. property CachedObjects : Integer read GetCachedObjects;
  54. property CacheSize : Integer read GetCacheSize;
  55. property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
  56. property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
  57. property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
  58. property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
  59. procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
  60. procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
  61. function GetValue(const aKey : string) : T;
  62. function TryGetValue(const aKey : string; out aValue : T) : Boolean;
  63. procedure RemoveValue(const aKey : string);
  64. procedure Flush;
  65. end;
  66. IMemoryCache = interface(ICache)
  67. ['{F109AE78-43D7-4983-B8ED-52A41533EEED}']
  68. function GetCompression: Boolean;
  69. procedure SetCompression(const Value: Boolean);
  70. function GetCachedObjects: Integer;
  71. function GetCacheSize: Integer;
  72. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  73. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  74. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  75. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  76. property Compression : Boolean read GetCompression write SetCompression;
  77. property CachedObjects : Integer read GetCachedObjects;
  78. property CacheSize : Integer read GetCacheSize;
  79. property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
  80. property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
  81. property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
  82. property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
  83. procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
  84. procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
  85. procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
  86. procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
  87. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
  88. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
  89. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
  90. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
  91. function GetValue(const aKey : string) : string; overload;
  92. function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
  93. function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
  94. function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
  95. function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
  96. procedure RemoveValue(const aKey : string);
  97. procedure Flush;
  98. end;
  99. TCacheEntry = class(TInterfacedObject,ICacheEntry)
  100. private
  101. fCreationDate : TDateTime;
  102. fExpiration : Cardinal;
  103. fExpirationDate : TDateTime;
  104. fCompression : Boolean;
  105. fCompressor : ICacheCompressor;
  106. fData : string;
  107. fIsCompressed : Boolean;
  108. function GetCreationDate: TDateTime;
  109. function GetData: string;
  110. function GetExpiration: Cardinal;
  111. procedure SetCreationDate(const Value: TDateTime);
  112. procedure SetData(const Value: string);
  113. procedure SetExpiration(aMilliseconds : Cardinal);
  114. function GetExpirationDate: TDateTime;
  115. procedure SetExpirationDate(const Value: TDateTime);
  116. public
  117. constructor Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
  118. property CreationDate : TDateTime read GetCreationDate write SetCreationDate;
  119. property Expiration : Cardinal read GetExpiration write SetExpiration;
  120. property ExpirationDate : TDateTime read GetExpirationDate write SetExpirationDate;
  121. property Data : string read GetData write SetData;
  122. function Size : Integer;
  123. function IsExpired : Boolean;
  124. end;
  125. TMemoryCacheBase = class(TInterfacedObject)
  126. private
  127. fPurgerInterval : Integer;
  128. fMaxSize : Integer;
  129. fCachedObjects : Integer;
  130. fCacheSize : Integer;
  131. fCompression : Boolean;
  132. fLock : TMultiReadExclusiveWriteSynchronizer;
  133. fCacheJobs : TScheduledTasks;
  134. fOnCacheFlushed : TCacheFlushedEvent;
  135. fOnPurgerJobError : TPurgerJobErrorEvent;
  136. fOnBeginPurgerJob : TBeginPurgerJobEvent;
  137. fOnEndPurgerJob : TEndPurgerJobEvent;
  138. procedure CreatePurgerJobs;
  139. procedure RemoveExpiredCacheEntries; virtual;
  140. procedure SetPurgerInterval(const Value: Integer);
  141. protected
  142. fItems : TDictionary<string,ICacheEntry>;
  143. fSerializer : ICacheSerializer;
  144. fCompressor : ICacheCompressor;
  145. function GetCachedObjects: Integer;
  146. function GetCacheSize: Integer;
  147. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  148. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  149. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  150. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  151. function GetCompression: Boolean;
  152. procedure SetCompression(const Value: Boolean);
  153. public
  154. constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); virtual;
  155. destructor Destroy; override;
  156. property MaxSize : Integer read fMaxSize write fMaxSize;
  157. property PurgerInterval : Integer read fPurgerInterval;
  158. property Compression : Boolean read GetCompression write SetCompression;
  159. property CachedObjects : Integer read GetCachedObjects;
  160. property CacheSize : Integer read GetCacheSize;
  161. property OnCacheFlushed : TCacheFlushedEvent read fOnCacheFlushed write SetOnCacheFlushed;
  162. property OnBeginPurgerJob : TBeginPurgerJobEvent read fOnBeginPurgerJob write SetOnBeginPurgerJob;
  163. property OnEndPurgerJob : TEndPurgerJobEvent read fOnEndPurgerJob write SetOnEndPurgerJob;
  164. property OnPurgeJobError : TPurgerJobErrorEvent read fOnPurgerJobError write SetOnPurgerJobError;
  165. procedure RemoveValue(const aKey : string); virtual;
  166. procedure Flush; virtual;
  167. end;
  168. TMemoryCache<T> = class(TMemoryCacheBase,IMemoryCache<T>)
  169. private
  170. procedure SetValue(const aKey : string; aValue : T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  171. public
  172. constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); override;
  173. procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
  174. procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
  175. function GetValue(const aKey : string) : T;
  176. function TryGetValue(const aKey : string; out oValue : T) : Boolean;
  177. procedure RemoveValue(const aKey : string); override;
  178. end;
  179. TMemoryCache = class(TMemoryCacheBase,IMemoryCache)
  180. private
  181. procedure SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  182. procedure SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  183. public
  184. procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
  185. procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
  186. procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
  187. procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
  188. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
  189. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
  190. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
  191. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
  192. function GetValue(const aKey : string) : string; overload;
  193. function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
  194. function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
  195. function TryGetValue<T>(const aKey : string; out oValue : T) : Boolean; overload;
  196. function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
  197. function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
  198. end;
  199. EMemoryCacheConfigError = class(Exception);
  200. EMemoryCacheSetError = class(Exception);
  201. EMemoryCacheGetError = class(Exception);
  202. EMemoryCacheFlushError = class(Exception);
  203. implementation
  204. { TMemoryCacheBase }
  205. constructor TMemoryCacheBase.Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil);
  206. begin
  207. fCompression := True;
  208. SetPurgerInterval(aPurgerInterval);
  209. fCachedObjects := 0;
  210. fCacheSize := 0;
  211. fLock := TMultiReadExclusiveWriteSynchronizer.Create;
  212. if aCacheSerializer <> nil then fSerializer := aCacheSerializer
  213. else fSerializer := TCacheJsonSerializer.Create;
  214. if aCacheCompressor <> nil then fCompressor := aCacheCompressor
  215. else fCompressor := TCacheCompressorGZip.Create;
  216. fItems := TDictionary<string,ICacheEntry>.Create;
  217. fCacheJobs := TScheduledTasks.Create;
  218. CreatePurgerJobs;
  219. fCacheJobs.Start;
  220. end;
  221. procedure TMemoryCacheBase.CreatePurgerJobs;
  222. begin
  223. fCacheJobs.AddTask('RemoveExpired',procedure (task : ITask)
  224. begin
  225. RemoveExpiredCacheEntries;
  226. end
  227. ).OnException(procedure(task : ITask; aException : Exception)
  228. begin
  229. if Assigned(fOnPurgerJobError) then fOnPurgerJobError(aException.Message);
  230. end
  231. ).StartInSeconds(fPurgerInterval).RepeatEvery(fPurgerInterval,TTimeMeasure.tmSeconds);
  232. end;
  233. destructor TMemoryCacheBase.Destroy;
  234. begin
  235. fItems.Free;
  236. fLock.Free;
  237. fCacheJobs.Stop;
  238. fCacheJobs.Free;
  239. inherited;
  240. end;
  241. procedure TMemoryCacheBase.Flush;
  242. begin
  243. fLock.BeginWrite;
  244. try
  245. fItems.Clear;
  246. if Assigned(fOnCacheFlushed) then fOnCacheFlushed(fCachedObjects);
  247. fCachedObjects := 0;
  248. fCacheSize := 0;
  249. finally
  250. fLock.EndWrite;
  251. end;
  252. end;
  253. procedure TMemoryCacheBase.RemoveExpiredCacheEntries;
  254. var
  255. pair : TPair<string,ICacheEntry>;
  256. removedentries : Integer;
  257. begin
  258. if Assigned(fOnBeginPurgerJob) then fOnBeginPurgerJob;
  259. removedentries := 0;
  260. fLock.BeginRead;
  261. try
  262. for pair in fItems do
  263. begin
  264. if pair.Value.IsExpired then
  265. begin
  266. fLock.BeginWrite;
  267. try
  268. //decrease cacheitem size to cachesize
  269. AtomicDecrement(fCacheSize,pair.Value.Size);
  270. //remove cacheitem from cache
  271. fItems.Remove(pair.Key);
  272. //decrease cachedobjects
  273. AtomicDecrement(fCachedObjects,1);
  274. Inc(removedentries);
  275. finally
  276. fLock.EndWrite;
  277. end;
  278. end;
  279. end;
  280. finally
  281. fLock.EndRead;
  282. if Assigned(fOnEndPurgerJob) then fOnEndPurgerJob(removedentries);
  283. end;
  284. end;
  285. procedure TMemoryCacheBase.RemoveValue(const aKey: string);
  286. var
  287. cacheitem : ICacheEntry;
  288. begin
  289. if fItems.TryGetValue(aKey,cacheitem) then
  290. begin
  291. //decrease cacheitem size to cachesize
  292. AtomicDecrement(fCacheSize,cacheitem.Size);
  293. //remove cacheitem from cache
  294. fItems.Remove(aKey);
  295. //decrease cachedobjects
  296. AtomicDecrement(fCachedObjects,1);
  297. end;
  298. end;
  299. function TMemoryCacheBase.GetCachedObjects: Integer;
  300. begin
  301. Result := fCachedObjects;
  302. end;
  303. function TMemoryCacheBase.GetCacheSize: Integer;
  304. begin
  305. Result := fCacheSize;
  306. end;
  307. function TMemoryCacheBase.GetCompression: Boolean;
  308. begin
  309. Result := fCompression;
  310. end;
  311. procedure TMemoryCacheBase.SetCompression(const Value: Boolean);
  312. begin
  313. fCompression := Value;
  314. end;
  315. procedure TMemoryCacheBase.SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  316. begin
  317. fOnBeginPurgerJob := Value;
  318. end;
  319. procedure TMemoryCacheBase.SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  320. begin
  321. fOnCacheFlushed := Value;
  322. end;
  323. procedure TMemoryCacheBase.SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  324. begin
  325. fOnEndPurgerJob := Value;
  326. end;
  327. procedure TMemoryCacheBase.SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  328. begin
  329. fOnPurgerJobError := Value;
  330. end;
  331. procedure TMemoryCacheBase.SetPurgerInterval(const Value: Integer);
  332. begin
  333. if Value > 5 then
  334. begin
  335. fPurgerInterval := Value;
  336. end
  337. else raise EMemoryCacheConfigError.Create('Purger Interval must be greater than 5 seconds');
  338. end;
  339. { TCacheItem }
  340. constructor TCacheEntry.Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
  341. begin
  342. fIsCompressed := False;
  343. fCompression := aCompression;
  344. fCompressor := aCacheCompressor;
  345. end;
  346. function TCacheEntry.GetCreationDate: TDateTime;
  347. begin
  348. Result := fCreationDate;
  349. end;
  350. function TCacheEntry.GetData: string;
  351. begin
  352. if fIsCompressed then Result := fCompressor.Decompress(fData)
  353. else Result := fData;
  354. end;
  355. function TCacheEntry.GetExpiration: Cardinal;
  356. begin
  357. Result := fExpiration;
  358. end;
  359. function TCacheEntry.GetExpirationDate: TDateTime;
  360. begin
  361. Result := fExpirationDate;
  362. end;
  363. procedure TCacheEntry.SetCreationDate(const Value: TDateTime);
  364. begin
  365. fCreationDate := Value;
  366. end;
  367. procedure TCacheEntry.SetExpiration(aMilliseconds: Cardinal);
  368. begin
  369. fExpiration := aMilliseconds;
  370. fExpirationDate := IncMilliSecond(fCreationDate,fExpiration);
  371. end;
  372. procedure TCacheEntry.SetExpirationDate(const Value: TDateTime);
  373. begin
  374. fExpiration := MilliSecondOf(Value);
  375. fExpirationDate := Value;
  376. end;
  377. function TCacheEntry.IsExpired: Boolean;
  378. begin
  379. if fExpiration = 0 then Result := False
  380. else Result := Now() > fExpirationDate;
  381. end;
  382. procedure TCacheEntry.SetData(const Value: string);
  383. begin
  384. fIsCompressed := False;
  385. //var a := value;
  386. //var b := value.Length;
  387. if fCompression then
  388. begin
  389. if ((Value.Length + 1) * 2) > 1024 then
  390. begin
  391. fData := fCompressor.Compress(Value);
  392. fIsCompressed := True;
  393. end
  394. else
  395. begin
  396. fData := Value;
  397. end;
  398. end
  399. else fData := Value;
  400. end;
  401. function TCacheEntry.Size: Integer;
  402. begin
  403. //Result := (fData.Length + 1) * SizeOf(Char);
  404. Result := (fData.Length + 1) * StringElementSize(fData);
  405. end;
  406. { TMemoryCache<T> }
  407. constructor TMemoryCache<T>.Create(aPurgerInterval: Integer; aCacheSerializer: ICacheSerializer; aCacheCompressor: ICacheCompressor);
  408. begin
  409. inherited Create(aPurgerInterval,aCacheSerializer,aCacheCompressor);
  410. end;
  411. function TMemoryCache<T>.GetValue(const aKey: string): T;
  412. var
  413. cacheitem : ICacheEntry;
  414. begin
  415. fLock.BeginRead;
  416. try
  417. fItems.TryGetValue(aKey,cacheitem);
  418. finally
  419. fLock.EndRead;
  420. end;
  421. end;
  422. procedure TMemoryCache<T>.RemoveValue(const aKey: string);
  423. begin
  424. inherited RemoveValue(aKey);
  425. end;
  426. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationDate: TDateTime);
  427. begin
  428. SetValue(aKey,aValue,0,aExpirationDate);
  429. end;
  430. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMillisecons: Integer);
  431. begin
  432. SetValue(aKey,aValue,aExpirationMillisecons,0.0);
  433. end;
  434. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  435. var
  436. serialized : string;
  437. cacheitem : TCacheEntry;
  438. begin
  439. fLock.BeginWrite;
  440. try
  441. cacheitem := TCacheEntry.Create(fCompression,fCompressor);
  442. cacheitem.CreationDate := Now();
  443. cacheitem.Expiration := aExpirationMilliseconds;
  444. if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
  445. //add object to cache
  446. case PTypeInfo(TypeInfo(T))^.Kind of
  447. tkClass, tkPointer :
  448. begin
  449. //object type need to be serialized
  450. cacheitem.Data := fSerializer.Serialize(PObject(@aValue)^);
  451. end;
  452. tkString, tkWideString, tkUString, tkChar, tkWideChar : cacheitem.Data := string((@aValue)^);
  453. {$IFNDEF NEXTGEN}
  454. tkAnsiString : cacheitem.Data := string(AnsiString((@aValue)^));
  455. {$ENDIF}
  456. else
  457. begin
  458. raise EMemoryCacheSetError.Create('Type not supported as cache');
  459. end;
  460. end;
  461. RemoveValue(aKey);
  462. fItems.Add(aKey,cacheitem);
  463. //add cacheitem size to cachesize
  464. AtomicIncrement(fCacheSize,cacheitem.Size);
  465. //increment cacheobjects
  466. AtomicIncrement(fCachedObjects,1);
  467. finally
  468. fLock.EndWrite;
  469. end;
  470. end;
  471. function TMemoryCache<T>.TryGetValue(const aKey: string; out oValue: T): Boolean;
  472. var
  473. cacheitem : ICacheEntry;
  474. flexvalue : TFlexValue;
  475. obj : TObject;
  476. begin
  477. fLock.BeginRead;
  478. try
  479. Result := fItems.TryGetValue(aKey,cacheitem);
  480. //check if cacheitem already expired
  481. if Result and cacheitem.IsExpired then Exit(False);
  482. finally
  483. fLock.EndRead;
  484. end;
  485. if Result then
  486. begin
  487. flexvalue.AsString := cacheitem.Data;
  488. case PTypeInfo(TypeInfo(T))^.Kind of
  489. tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  490. tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
  491. tkFloat :
  492. begin
  493. if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
  494. else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
  495. end;
  496. tkString,
  497. tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
  498. {$IFDEF MSWINDOWS}
  499. tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
  500. tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
  501. {$ENDIF}
  502. tkEnumeration :
  503. begin
  504. if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
  505. else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  506. end;
  507. tkClass, tkPointer :
  508. begin
  509. obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
  510. fSerializer.Deserialize(cacheitem.Data,obj);
  511. oValue := TValue.From(obj).AsType<T>;
  512. //oValue := T((@obj)^);
  513. end
  514. else raise EMemoryCacheGetError.Create('Error casting value from cache');
  515. end;
  516. end;
  517. end;
  518. { TMemoryCache }
  519. function TMemoryCache.GetValue(const aKey: string): string;
  520. var
  521. cacheitem : ICacheEntry;
  522. begin
  523. if fItems.TryGetValue(aKey,cacheitem) then Result := cacheitem.Data;
  524. end;
  525. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds: Integer);
  526. begin
  527. SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
  528. end;
  529. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationDate: TDateTime);
  530. begin
  531. SetValue(aKey,aValue,0,aExpirationDate);
  532. end;
  533. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds: Integer);
  534. begin
  535. SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
  536. end;
  537. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationDate: TDateTime);
  538. begin
  539. SetValue(aKey,aValue,0,aExpirationDate);
  540. end;
  541. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  542. begin
  543. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,aExpirationDate);
  544. end;
  545. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  546. var
  547. cacheitem : TCacheEntry;
  548. begin
  549. fLock.BeginWrite;
  550. try
  551. cacheitem := TCacheEntry.Create(fCompression,fCompressor);
  552. cacheitem.CreationDate := Now();
  553. cacheitem.Expiration := aExpirationMilliseconds;
  554. if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
  555. //add object to cache
  556. cacheitem.Data := aValue;
  557. RemoveValue(aKey);
  558. fItems.Add(aKey,cacheitem);
  559. //add cacheitem size to cachesize
  560. AtomicIncrement(fCacheSize,cacheitem.Size);
  561. //increment cacheobjects
  562. AtomicIncrement(fCachedObjects,1);
  563. finally
  564. fLock.EndWrite;
  565. end;
  566. end;
  567. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationDate: TDateTime);
  568. begin
  569. SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
  570. end;
  571. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationMilliseconds: Integer);
  572. begin
  573. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
  574. end;
  575. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationDate: TDateTime);
  576. begin
  577. SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
  578. end;
  579. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationMilliseconds: Integer);
  580. begin
  581. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
  582. end;
  583. function TMemoryCache.TryGetValue(const aKey: string; aValue : TObject): Boolean;
  584. var
  585. cacheitem : ICacheEntry;
  586. begin
  587. fLock.BeginRead;
  588. try
  589. if aValue = nil then raise EMemoryCacheGetError.Create('Cannot passed a nil object as param');
  590. Result := fItems.TryGetValue(aKey,cacheitem);
  591. //check if cacheitem already expired
  592. if (not Result) or (cacheitem.IsExpired) then Exit(False);
  593. finally
  594. fLock.EndRead;
  595. end;
  596. fSerializer.Deserialize(cacheitem.Data,aValue);
  597. end;
  598. function TMemoryCache.TryGetValue(const aKey: string; out aValue: string): Boolean;
  599. begin
  600. Result := TryGetValue<string>(aKey,aValue);
  601. end;
  602. function TMemoryCache.TryGetValue<T>(const aKey: string; out oValue: T): Boolean;
  603. var
  604. cacheitem : ICacheEntry;
  605. flexvalue : TFlexValue;
  606. obj : TObject;
  607. begin
  608. fLock.BeginRead;
  609. try
  610. Result := fItems.TryGetValue(aKey,cacheitem);
  611. //check if cacheitem already expired
  612. if Result and cacheitem.IsExpired then Exit(False);
  613. finally
  614. fLock.EndRead;
  615. end;
  616. if Result then
  617. begin
  618. flexvalue.AsString := cacheitem.Data;
  619. case PTypeInfo(TypeInfo(T))^.Kind of
  620. tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  621. tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
  622. tkFloat :
  623. begin
  624. if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
  625. else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
  626. end;
  627. tkString,
  628. tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
  629. {$IFDEF MSWINDOWS}
  630. tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
  631. tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
  632. {$ENDIF}
  633. tkEnumeration :
  634. begin
  635. if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
  636. else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  637. end;
  638. tkClass, tkPointer :
  639. begin
  640. obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
  641. fSerializer.Deserialize(flexvalue.AsString,obj);
  642. oValue := TValue.From(obj).AsType<T>;
  643. end;
  644. else raise EMemoryCacheGetError.Create('Error casting value from cache');
  645. end;
  646. end;
  647. end;
  648. function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<string>): Boolean;
  649. var
  650. cacheitem : ICacheEntry;
  651. flexvalue : TFlexValue;
  652. obj : TObject;
  653. begin
  654. fLock.BeginRead;
  655. try
  656. Result := fItems.TryGetValue(aKey,cacheitem);
  657. //check if cacheitem already expired
  658. if Result and cacheitem.IsExpired then Exit(False);
  659. finally
  660. fLock.EndRead;
  661. end;
  662. if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
  663. end;
  664. function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<TObject>): Boolean;
  665. var
  666. cacheitem : ICacheEntry;
  667. flexvalue : TFlexValue;
  668. obj : TObject;
  669. begin
  670. fLock.BeginRead;
  671. try
  672. Result := fItems.TryGetValue(aKey,cacheitem);
  673. //check if cacheitem already expired
  674. if Result and cacheitem.IsExpired then Exit(False);
  675. finally
  676. fLock.EndRead;
  677. end;
  678. if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
  679. end;
  680. end.