123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765 |
- { ***************************************************************************
- Copyright (c) 2016-2021 Kike Pérez
- Unit : Quick.MemoryCache
- Description : Cache objects with expiration control
- Author : Kike Pérez
- Version : 1.0
- Created : 14/07/2019
- Modified : 17/05/2021
- This file is part of QuickLib: https://github.com/exilon/QuickLib
- ***************************************************************************
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- *************************************************************************** }
- unit Quick.MemoryCache;
- {$i QuickLib.inc}
- interface
- uses
- System.SysUtils,
- System.Generics.Collections,
- System.DateUtils,
- System.TypInfo,
- RTTI,
- Quick.Commons,
- Quick.Value,
- Quick.Threads,
- Quick.Cache.Intf,
- Quick.MemoryCache.Types,
- Quick.MemoryCache.Serializer.Json,
- Quick.MemoryCache.Compressor.GZip;
- type
- TCacheFlushedEvent = reference to procedure(aRemovedEntries : Integer);
- TBeginPurgerJobEvent = reference to procedure;
- TEndPurgerJobEvent = reference to procedure(aPurgedEntries : Integer);
- TPurgerJobErrorEvent = reference to procedure(const aErrorMsg : string);
- IMemoryCache<T> = interface
- ['{57927AD7-C993-4C3C-B552-43A39F99E73A}']
- function GetCompression: Boolean;
- procedure SetCompression(const Value: Boolean);
- function GetCachedObjects: Integer;
- function GetCacheSize: Integer;
- procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
- procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
- procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
- procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
- property Compression : Boolean read GetCompression write SetCompression;
- property CachedObjects : Integer read GetCachedObjects;
- property CacheSize : Integer read GetCacheSize;
- property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
- property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
- property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
- property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
- procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
- function GetValue(const aKey : string) : T;
- function TryGetValue(const aKey : string; out aValue : T) : Boolean;
- procedure RemoveValue(const aKey : string);
- procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
- procedure Flush;
- end;
- IMemoryCache = interface(ICache)
- ['{F109AE78-43D7-4983-B8ED-52A41533EEED}']
- function GetCompression: Boolean;
- procedure SetCompression(const Value: Boolean);
- function GetCachedObjects: Integer;
- function GetCacheSize: Integer;
- procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
- procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
- procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
- procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
- property Compression : Boolean read GetCompression write SetCompression;
- property CachedObjects : Integer read GetCachedObjects;
- property CacheSize : Integer read GetCacheSize;
- property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
- property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
- property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
- property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
- procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
- function GetValue(const aKey : string) : string; overload;
- function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
- function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
- function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
- function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
- procedure RemoveValue(const aKey : string);
- procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
- procedure Flush;
- end;
- TCacheEntry = class(TInterfacedObject,ICacheEntry)
- private
- fCreationDate : TDateTime;
- fExpiration : Cardinal;
- fExpirationDate : TDateTime;
- fCompression : Boolean;
- fCompressor : ICacheCompressor;
- fData : string;
- fIsCompressed : Boolean;
- function GetCreationDate: TDateTime;
- function GetData: string;
- function GetExpiration: Cardinal;
- procedure SetCreationDate(const Value: TDateTime);
- procedure SetData(const Value: string);
- procedure SetExpiration(aMilliseconds : Cardinal);
- function GetExpirationDate: TDateTime;
- procedure SetExpirationDate(const Value: TDateTime);
- public
- constructor Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
- property CreationDate : TDateTime read GetCreationDate write SetCreationDate;
- property Expiration : Cardinal read GetExpiration write SetExpiration;
- property ExpirationDate : TDateTime read GetExpirationDate write SetExpirationDate;
- property Data : string read GetData write SetData;
- function Size : Integer;
- function IsExpired : Boolean;
- end;
- TMemoryCacheBase = class(TInterfacedObject)
- private
- fPurgerInterval : Integer;
- fMaxSize : Integer;
- fCachedObjects : Integer;
- fCacheSize : Integer;
- fCompression : Boolean;
- fLock : TMultiReadExclusiveWriteSynchronizer;
- fCacheJobs : TScheduledTasks;
- fOnCacheFlushed : TCacheFlushedEvent;
- fOnPurgerJobError : TPurgerJobErrorEvent;
- fOnBeginPurgerJob : TBeginPurgerJobEvent;
- fOnEndPurgerJob : TEndPurgerJobEvent;
- procedure CreatePurgerJobs;
- procedure RemoveExpiredCacheEntries; virtual;
- procedure SetPurgerInterval(const Value: Integer);
- protected
- fItems : TDictionary<string,ICacheEntry>;
- fSerializer : ICacheSerializer;
- fCompressor : ICacheCompressor;
- function GetCachedObjects: Integer;
- function GetCacheSize: Integer;
- procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
- procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
- procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
- procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
- function GetCompression: Boolean;
- procedure SetCompression(const Value: Boolean);
- public
- constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); virtual;
- destructor Destroy; override;
- property MaxSize : Integer read fMaxSize write fMaxSize;
- property PurgerInterval : Integer read fPurgerInterval;
- property Compression : Boolean read GetCompression write SetCompression;
- property CachedObjects : Integer read GetCachedObjects;
- property CacheSize : Integer read GetCacheSize;
- property OnCacheFlushed : TCacheFlushedEvent read fOnCacheFlushed write SetOnCacheFlushed;
- property OnBeginPurgerJob : TBeginPurgerJobEvent read fOnBeginPurgerJob write SetOnBeginPurgerJob;
- property OnEndPurgerJob : TEndPurgerJobEvent read fOnEndPurgerJob write SetOnEndPurgerJob;
- property OnPurgeJobError : TPurgerJobErrorEvent read fOnPurgerJobError write SetOnPurgerJobError;
- procedure RemoveValue(const aKey : string); virtual;
- procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
- procedure Flush; virtual;
- end;
- TMemoryCache<T> = class(TMemoryCacheBase,IMemoryCache<T>)
- private
- procedure SetValue(const aKey : string; aValue : T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
- public
- constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); override;
- procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
- function GetValue(const aKey : string) : T;
- function TryGetValue(const aKey : string; out oValue : T) : Boolean;
- procedure RemoveValue(const aKey : string); override;
- end;
- TMemoryCache = class(TMemoryCacheBase,IMemoryCache)
- private
- procedure SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
- public
- procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
- procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
- procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
- function GetValue(const aKey : string) : string; overload;
- function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
- function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
- function TryGetValue<T>(const aKey : string; out oValue : T) : Boolean; overload;
- function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
- function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
- end;
- EMemoryCacheConfigError = class(Exception);
- EMemoryCacheSetError = class(Exception);
- EMemoryCacheGetError = class(Exception);
- EMemoryCacheFlushError = class(Exception);
- implementation
- { TMemoryCacheBase }
- constructor TMemoryCacheBase.Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil);
- begin
- fCompression := True;
- SetPurgerInterval(aPurgerInterval);
- fCachedObjects := 0;
- fCacheSize := 0;
- fLock := TMultiReadExclusiveWriteSynchronizer.Create;
- if aCacheSerializer <> nil then fSerializer := aCacheSerializer
- else fSerializer := TCacheJsonSerializer.Create;
- if aCacheCompressor <> nil then fCompressor := aCacheCompressor
- else fCompressor := TCacheCompressorGZip.Create;
- fItems := TDictionary<string,ICacheEntry>.Create;
- fCacheJobs := TScheduledTasks.Create;
- CreatePurgerJobs;
- fCacheJobs.Start;
- end;
- procedure TMemoryCacheBase.CreatePurgerJobs;
- begin
- fCacheJobs.AddTask('RemoveExpired',procedure (task : ITask)
- begin
- RemoveExpiredCacheEntries;
- end
- ).OnException(procedure(task : ITask; aException : Exception)
- begin
- if Assigned(fOnPurgerJobError) then fOnPurgerJobError(aException.Message);
- end
- ).StartInSeconds(fPurgerInterval).RepeatEvery(fPurgerInterval,TTimeMeasure.tmSeconds);
- end;
- destructor TMemoryCacheBase.Destroy;
- begin
- fItems.Free;
- fLock.Free;
- fCacheJobs.Stop;
- fCacheJobs.Free;
- inherited;
- end;
- procedure TMemoryCacheBase.Flush;
- begin
- fLock.BeginWrite;
- try
- fItems.Clear;
- if Assigned(fOnCacheFlushed) then fOnCacheFlushed(fCachedObjects);
- fCachedObjects := 0;
- fCacheSize := 0;
- finally
- fLock.EndWrite;
- end;
- end;
- procedure TMemoryCacheBase.Refresh(const aKey: string; aExpirationMilliseconds : Integer);
- var
- cacheitem : ICacheEntry;
- begin
- if fItems.TryGetValue(aKey,cacheitem) then
- begin
- cacheitem.CreationDate := Now;
- cacheitem.Expiration := aExpirationMilliseconds;
- end;
- end;
- procedure TMemoryCacheBase.RemoveExpiredCacheEntries;
- var
- pair : TPair<string,ICacheEntry>;
- removedentries : Integer;
- begin
- if Assigned(fOnBeginPurgerJob) then fOnBeginPurgerJob;
- removedentries := 0;
- fLock.BeginRead;
- try
- for pair in fItems do
- begin
- if pair.Value.IsExpired then
- begin
- fLock.BeginWrite;
- try
- //decrease cacheitem size to cachesize
- AtomicDecrement(fCacheSize,pair.Value.Size);
- //remove cacheitem from cache
- fItems.Remove(pair.Key);
- //decrease cachedobjects
- AtomicDecrement(fCachedObjects,1);
- Inc(removedentries);
- finally
- fLock.EndWrite;
- end;
- end;
- end;
- finally
- fLock.EndRead;
- if Assigned(fOnEndPurgerJob) then fOnEndPurgerJob(removedentries);
- end;
- end;
- procedure TMemoryCacheBase.RemoveValue(const aKey: string);
- var
- cacheitem : ICacheEntry;
- begin
- if fItems.TryGetValue(aKey,cacheitem) then
- begin
- //decrease cacheitem size to cachesize
- AtomicDecrement(fCacheSize,cacheitem.Size);
- //remove cacheitem from cache
- fItems.Remove(aKey);
- //decrease cachedobjects
- AtomicDecrement(fCachedObjects,1);
- end;
- end;
- function TMemoryCacheBase.GetCachedObjects: Integer;
- begin
- Result := fCachedObjects;
- end;
- function TMemoryCacheBase.GetCacheSize: Integer;
- begin
- Result := fCacheSize;
- end;
- function TMemoryCacheBase.GetCompression: Boolean;
- begin
- Result := fCompression;
- end;
- procedure TMemoryCacheBase.SetCompression(const Value: Boolean);
- begin
- fCompression := Value;
- end;
- procedure TMemoryCacheBase.SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
- begin
- fOnBeginPurgerJob := Value;
- end;
- procedure TMemoryCacheBase.SetOnCacheFlushed(const Value: TCacheFlushedEvent);
- begin
- fOnCacheFlushed := Value;
- end;
- procedure TMemoryCacheBase.SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
- begin
- fOnEndPurgerJob := Value;
- end;
- procedure TMemoryCacheBase.SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
- begin
- fOnPurgerJobError := Value;
- end;
- procedure TMemoryCacheBase.SetPurgerInterval(const Value: Integer);
- begin
- if Value > 5 then
- begin
- fPurgerInterval := Value;
- end
- else raise EMemoryCacheConfigError.Create('Purger Interval must be greater than 5 seconds');
- end;
- { TCacheItem }
- constructor TCacheEntry.Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
- begin
- fIsCompressed := False;
- fCompression := aCompression;
- fCompressor := aCacheCompressor;
- end;
- function TCacheEntry.GetCreationDate: TDateTime;
- begin
- Result := fCreationDate;
- end;
- function TCacheEntry.GetData: string;
- begin
- if fIsCompressed then Result := fCompressor.Decompress(fData)
- else Result := fData;
- end;
- function TCacheEntry.GetExpiration: Cardinal;
- begin
- Result := fExpiration;
- end;
- function TCacheEntry.GetExpirationDate: TDateTime;
- begin
- Result := fExpirationDate;
- end;
- procedure TCacheEntry.SetCreationDate(const Value: TDateTime);
- begin
- fCreationDate := Value;
- end;
- procedure TCacheEntry.SetExpiration(aMilliseconds: Cardinal);
- begin
- fExpiration := aMilliseconds;
- fExpirationDate := IncMilliSecond(fCreationDate,fExpiration);
- end;
- procedure TCacheEntry.SetExpirationDate(const Value: TDateTime);
- begin
- fExpiration := MilliSecondOf(Value);
- fExpirationDate := Value;
- end;
- function TCacheEntry.IsExpired: Boolean;
- begin
- if fExpiration = 0 then Result := False
- else Result := Now() > fExpirationDate;
- end;
- procedure TCacheEntry.SetData(const Value: string);
- begin
- fIsCompressed := False;
- //var a := value;
- //var b := value.Length;
- if fCompression then
- begin
- if ((Value.Length + 1) * 2) > 1024 then
- begin
- fData := fCompressor.Compress(Value);
- fIsCompressed := True;
- end
- else
- begin
- fData := Value;
- end;
- end
- else fData := Value;
- end;
- function TCacheEntry.Size: Integer;
- begin
- //Result := (fData.Length + 1) * SizeOf(Char);
- Result := (fData.Length + 1) * StringElementSize(fData);
- end;
- { TMemoryCache<T> }
- constructor TMemoryCache<T>.Create(aPurgerInterval: Integer; aCacheSerializer: ICacheSerializer; aCacheCompressor: ICacheCompressor);
- begin
- inherited Create(aPurgerInterval,aCacheSerializer,aCacheCompressor);
- end;
- function TMemoryCache<T>.GetValue(const aKey: string): T;
- begin
- TryGetValue(aKey,Result);
- end;
- procedure TMemoryCache<T>.RemoveValue(const aKey: string);
- begin
- inherited RemoveValue(aKey);
- end;
- procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationDate: TDateTime);
- begin
- SetValue(aKey,aValue,0,aExpirationDate);
- end;
- procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMillisecons: Integer);
- begin
- SetValue(aKey,aValue,aExpirationMillisecons,0.0);
- end;
- procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
- var
- serialized : string;
- cacheitem : TCacheEntry;
- begin
- fLock.BeginWrite;
- try
- cacheitem := TCacheEntry.Create(fCompression,fCompressor);
- cacheitem.CreationDate := Now();
- cacheitem.Expiration := aExpirationMilliseconds;
- if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
- //add object to cache
- case PTypeInfo(TypeInfo(T))^.Kind of
- tkClass, tkPointer :
- begin
- //object type need to be serialized
- cacheitem.Data := fSerializer.Serialize(PObject(@aValue)^);
- end;
- tkString, tkWideString, tkUString, tkChar, tkWideChar : cacheitem.Data := string((@aValue)^);
- {$IFNDEF NEXTGEN}
- tkAnsiString : cacheitem.Data := string(AnsiString((@aValue)^));
- {$ENDIF}
- else
- begin
- raise EMemoryCacheSetError.Create('Type not supported as cache');
- end;
- end;
- RemoveValue(aKey);
- fItems.Add(aKey,cacheitem);
- //add cacheitem size to cachesize
- AtomicIncrement(fCacheSize,cacheitem.Size);
- //increment cacheobjects
- AtomicIncrement(fCachedObjects,1);
- finally
- fLock.EndWrite;
- end;
- end;
- function TMemoryCache<T>.TryGetValue(const aKey: string; out oValue: T): Boolean;
- var
- cacheitem : ICacheEntry;
- flexvalue : TFlexValue;
- obj : TObject;
- begin
- fLock.BeginRead;
- try
- Result := fItems.TryGetValue(aKey,cacheitem);
- //check if cacheitem already expired
- if Result and cacheitem.IsExpired then Exit(False);
- finally
- fLock.EndRead;
- end;
- if Result then
- begin
- flexvalue.AsString := cacheitem.Data;
- case PTypeInfo(TypeInfo(T))^.Kind of
- tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
- tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
- tkFloat :
- begin
- if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
- else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
- end;
- tkString,
- tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
- {$IFDEF MSWINDOWS}
- tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
- tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
- {$ENDIF}
- tkEnumeration :
- begin
- if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
- else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
- end;
- tkClass, tkPointer :
- begin
- obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
- fSerializer.Deserialize(cacheitem.Data,obj);
- oValue := TValue.From(obj).AsType<T>;
- //oValue := T((@obj)^);
- end
- else raise EMemoryCacheGetError.Create('Error casting value from cache');
- end;
- end;
- end;
- { TMemoryCache }
- function TMemoryCache.GetValue(const aKey: string): string;
- begin
- TryGetValue(aKey,Result);
- end;
- procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds: Integer);
- begin
- SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
- end;
- procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationDate: TDateTime);
- begin
- SetValue(aKey,aValue,0,aExpirationDate);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds: Integer);
- begin
- SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationDate: TDateTime);
- begin
- SetValue(aKey,aValue,0,aExpirationDate);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
- begin
- SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,aExpirationDate);
- end;
- procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
- var
- cacheitem : TCacheEntry;
- begin
- fLock.BeginWrite;
- try
- cacheitem := TCacheEntry.Create(fCompression,fCompressor);
- cacheitem.CreationDate := Now();
- cacheitem.Expiration := aExpirationMilliseconds;
- if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
- //add object to cache
- cacheitem.Data := aValue;
- RemoveValue(aKey);
- fItems.Add(aKey,cacheitem);
- //add cacheitem size to cachesize
- AtomicIncrement(fCacheSize,cacheitem.Size);
- //increment cacheobjects
- AtomicIncrement(fCachedObjects,1);
- finally
- fLock.EndWrite;
- end;
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationDate: TDateTime);
- begin
- SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationMilliseconds: Integer);
- begin
- SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationDate: TDateTime);
- begin
- SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
- end;
- procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationMilliseconds: Integer);
- begin
- SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
- end;
- function TMemoryCache.TryGetValue(const aKey: string; aValue : TObject): Boolean;
- var
- cacheitem : ICacheEntry;
- begin
- fLock.BeginRead;
- try
- if aValue = nil then raise EMemoryCacheGetError.Create('Cannot passed a nil object as param');
- Result := fItems.TryGetValue(aKey,cacheitem);
- //check if cacheitem already expired
- if (not Result) or (cacheitem.IsExpired) then Exit(False);
- finally
- fLock.EndRead;
- end;
- fSerializer.Deserialize(cacheitem.Data,aValue);
- end;
- function TMemoryCache.TryGetValue(const aKey: string; out aValue: string): Boolean;
- begin
- Result := TryGetValue<string>(aKey,aValue);
- end;
- function TMemoryCache.TryGetValue<T>(const aKey: string; out oValue: T): Boolean;
- var
- cacheitem : ICacheEntry;
- flexvalue : TFlexValue;
- obj : TObject;
- begin
- fLock.BeginRead;
- try
- Result := fItems.TryGetValue(aKey,cacheitem);
- //check if cacheitem already expired
- if Result and cacheitem.IsExpired then Exit(False);
- finally
- fLock.EndRead;
- end;
- if Result then
- begin
- flexvalue.AsString := cacheitem.Data;
- case PTypeInfo(TypeInfo(T))^.Kind of
- tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
- tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
- tkFloat :
- begin
- if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
- else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
- end;
- tkString,
- tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
- {$IFDEF MSWINDOWS}
- tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
- tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
- {$ENDIF}
- tkEnumeration :
- begin
- if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
- else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
- end;
- tkClass, tkPointer :
- begin
- obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
- fSerializer.Deserialize(flexvalue.AsString,obj);
- oValue := TValue.From(obj).AsType<T>;
- end;
- else raise EMemoryCacheGetError.Create('Error casting value from cache');
- end;
- end;
- end;
- function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<string>): Boolean;
- var
- cacheitem : ICacheEntry;
- begin
- fLock.BeginRead;
- try
- Result := fItems.TryGetValue(aKey,cacheitem);
- //check if cacheitem already expired
- if Result and cacheitem.IsExpired then Exit(False);
- finally
- fLock.EndRead;
- end;
- if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
- end;
- function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<TObject>): Boolean;
- var
- cacheitem : ICacheEntry;
- begin
- fLock.BeginRead;
- try
- Result := fItems.TryGetValue(aKey,cacheitem);
- //check if cacheitem already expired
- if Result and cacheitem.IsExpired then Exit(False);
- finally
- fLock.EndRead;
- end;
- if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
- end;
- end.
|