12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897 |
- {%MainUnit classes.pp}
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {**********************************************************************
- * Class implementations are in separate files. *
- **********************************************************************}
- type
- {$ifdef CPU16}
- TFilerFlagsInt = Byte;
- {$else CPU16}
- TFilerFlagsInt = LongInt;
- {$endif CPU16}
- var
- ClassList : TThreadlist;
- ClassAliasList : TStringList;
- {
- Include all message strings
- Add a language with IFDEF LANG_NAME
- just befor the final ELSE. This way English will always be the default.
- }
- {$IFDEF LANG_GERMAN}
- {$i constsg.inc}
- {$ELSE}
- {$IFDEF LANG_SPANISH}
- {$i constss.inc}
- {$ENDIF}
- {$ENDIF}
- { Utility routines }
- {$i util.inc}
- { TBits implementation }
- {$i bits.inc}
- { All streams implementations: }
- { Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
- { TCustomMemoryStream TMemoryStream }
- {$i streams.inc}
- { TParser implementation}
- {$i parser.inc}
- { TCollection and TCollectionItem implementations }
- {$i collect.inc}
- { TList and TThreadList implementations }
- {$i lists.inc}
- { TStrings and TStringList implementations }
- {$i stringl.inc}
- { ObservableMemberAttribute, TObservers and TObserverMapping}
- {$i observer.inc}
- { TThread implementation }
- { system independend threading code }
- var
- { event executed by SychronizeInternal to wake main thread if it sleeps in
- CheckSynchronize }
- SynchronizeTimeoutEvent: PRtlEvent;
- { the head of the queue containing the entries to be Synchronized - Nil if the
- queue is empty }
- ThreadQueueHead: TThread.PThreadQueueEntry;
- { the tail of the queue containing the entries to be Synchronized - Nil if the
- queue is empty }
- ThreadQueueTail: TThread.PThreadQueueEntry;
- { used for serialized access to the queue }
- ThreadQueueLock: TRtlCriticalSection;
- { usage counter for ThreadQueueLock }
- ThreadQueueLockCounter : longint;
- { this list holds all instances of external threads that need to be freed at
- the end of the program }
- ExternalThreads: TThreadList;
- { this list signals that the ExternalThreads list is cleared and thus the
- thread instances don't need to remove themselves }
- ExternalThreadsCleanup: Boolean = False;
- { this must be a global var, otherwise unwanted optimizations might happen in
- TThread.SpinWait() }
- SpinWaitDummy: LongWord;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- threadvar
- {$else}
- var
- {$endif}
- { the instance of the current thread; in case of an external thread this is
- Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
- that threadvars are initialized with 0!) }
- CurrentThreadVar: TThread;
- type
- { this type is used if a thread is created using
- TThread.CreateAnonymousThread }
- { TAnonymousThread }
- TAnonymousThread = class(TThread)
- private
- fProc: TProcedure;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- fAnonProc : TThreadProcedure;
- {$ENDIF}
- FMethod : TThreadMethod;
- protected
- procedure Execute; override;
- public
- constructor Create(aProc: TProcedure);
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- constructor Create(aProc: TThreadProcedure);
- {$ENDIF}
- constructor Create(aProc: TThreadMethod);
- end;
- procedure TAnonymousThread.Execute;
- begin
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- if assigned(fAnonProc) then
- fAnonProc()
- else
- {$ENDIF}
- if assigned(FMethod) then
- FMethod()
- else
- fProc();
- end;
- constructor TAnonymousThread.Create(aProc: TProcedure);
- begin
- { an anonymous thread is created suspended and with FreeOnTerminate set }
- inherited Create(True);
- FreeOnTerminate := True;
- fProc := aProc;
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- constructor TAnonymousThread.Create(aProc: TThreadProcedure);
- begin
- inherited Create(True);
- FreeOnTerminate := True;
- fAnonProc := aProc;
- end;
- {$ENDIF}
- constructor TAnonymousThread.Create(aProc: TThreadMethod);
- begin
- inherited Create(True);
- FreeOnTerminate := True;
- FMethod := aProc;
- end;
- type
- { this type is used by TThread.GetCurrentThread if the thread does not yet
- have a value in CurrentThreadVar (Note: the main thread is also created as
- a TExternalThread) }
- TExternalThread = class(TThread)
- protected
- { dummy method to remove the warning }
- procedure Execute; override;
- public
- constructor Create;
- destructor Destroy; override;
- end;
- procedure TExternalThread.Execute;
- begin
- { empty }
- end;
- constructor TExternalThread.Create;
- begin
- FExternalThread := True;
- { the parameter is unimportant if FExternalThread is True }
- inherited Create(False);
- with ExternalThreads.LockList do
- try
- Add(Self);
- finally
- ExternalThreads.UnlockList;
- end;
- end;
- destructor TExternalThread.Destroy;
- begin
- inherited;
- if not ExternalThreadsCleanup then
- with ExternalThreads.LockList do
- try
- Extract(Self);
- finally
- ExternalThreads.UnlockList;
- end;
- end;
- function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
- var
- FreeThread: Boolean;
- Thread: TThread absolute ThreadObjPtr;
- begin
- { if Suspend checks FSuspended before doing anything, make sure it }
- { knows we're currently not suspended (this flag may have been set }
- { to true if CreateSuspended was true) }
- // Thread.FSuspended:=false;
- // wait until AfterConstruction has been called, so we cannot
- // free ourselves before TThread.Create has finished
- // (since that one may check our VTM in case of $R+, and
- // will call the AfterConstruction method in all cases)
- // Thread.Suspend;
- try
- { The thread may be already terminated at this point, e.g. if it was intially
- suspended, or if it wasn't ever scheduled for execution for whatever reason.
- So bypass user code if terminated. }
- if not Thread.Terminated then begin
- CurrentThreadVar := Thread;
- Thread.Execute;
- end;
- except
- Thread.FFatalException := TObject(AcquireExceptionObject);
- end;
- FreeThread := Thread.FFreeOnTerminate;
- Result := Thread.FReturnValue;
- Thread.FFinished := True;
- Thread.DoTerminate;
- if FreeThread then
- Thread.Free;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- EndThread(Result);
- {$endif}
- end;
- { system-dependent code }
- {$i tthread.inc}
- constructor TThread.Create(CreateSuspended: Boolean;
- const StackSize: SizeUInt);
- begin
- inherited Create;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- InterlockedIncrement(ThreadQueueLockCounter);
- FThreadQueueLockCounted := true; { Guard against exception in descendant’s Create. }
- {$endif}
- if FExternalThread then
- {$ifdef FPC_HAS_FEATURE_THREADING}
- FThreadID := GetCurrentThreadID
- {$else}
- FThreadID := 0{GetCurrentThreadID}
- {$endif}
- else
- SysCreate(CreateSuspended, StackSize);
- end;
- destructor TThread.Destroy;
- begin
- if not FExternalThread then begin
- SysDestroy;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- if FHandle <> TThreadID(0) then
- CloseThread(FHandle);
- {$endif}
- end;
- RemoveQueuedEvents(Self);
- DoneSynchronizeEvent;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- if FThreadQueueLockCounted and (InterlockedDecrement(ThreadQueueLockCounter)=0) then
- DoneCriticalSection(ThreadQueueLock);
- {$endif}
- { set CurrentThreadVar to Nil? }
- inherited Destroy;
- end;
- procedure TThread.Start;
- begin
- { suspend/resume are now deprecated in Delphi (they also don't work
- on most platforms in FPC), so a different method was required
- to start a thread if it's create with fSuspended=true -> that's
- what this method is for. }
- Resume;
- end;
- function TThread.GetSuspended: Boolean;
- begin
- GetSuspended:=FSuspended;
- end;
- procedure TThread.Terminate;
- begin
- FTerminated := True;
- TerminatedSet;
- end;
- procedure TThread.TerminatedSet;
- begin
- // Empty, must be overridden.
- end;
- procedure TThread.AfterConstruction;
- begin
- inherited AfterConstruction;
- // enable for all platforms once http://bugs.freepascal.org/view.php?id=16884
- // is fixed for all platforms (in case the fix for non-unix platforms also
- // requires this field at least)
- {$if defined(unix) or defined(windows) or defined(os2) or defined(hasamiga)}
- if not FExternalThread and not FInitialSuspended then
- Resume;
- {$endif}
- end;
- procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
- begin
- if Assigned(aEntry^.Method) then
- aEntry^.Method()
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- else
- if Assigned(aEntry^.ThreadProc) then
- aEntry^.ThreadProc
- {$endif}
- end;
- procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry; aQueueIfMain: Boolean);
- var
- thd: TThread;
- issync: Boolean;
- begin
- { do we really need a synchronized call? }
- {$ifdef FPC_HAS_FEATURE_THREADING}
- if (GetCurrentThreadID = MainThreadID) and (not aQueueIfMain or not IsMultiThread) then
- {$endif}
- begin
- try
- ExecuteThreadQueueEntry(aEntry);
- finally
- if not Assigned(aEntry^.SyncEvent) then
- Dispose(aEntry);
- end;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- end else begin
- { store thread and whether we're dealing with a synchronized event; the
- event record itself might already be freed after the ThreadQueueLock is
- released (in case of a Queue() call; for a Synchronize() call the record
- will stay valid, thus accessing SyncEvent later on (if issync is true) is
- okay) }
- thd := aEntry^.Thread;
- issync := Assigned(aEntry^.SyncEvent);
- System.EnterCriticalSection(ThreadQueueLock);
- try
- { add the entry to the thread queue }
- if Assigned(ThreadQueueTail) then begin
- ThreadQueueTail^.Next := aEntry;
- end else
- ThreadQueueHead := aEntry;
- ThreadQueueTail := aEntry;
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- { ensure that the main thread knows that something awaits }
- RtlEventSetEvent(SynchronizeTimeoutEvent);
- if assigned(WakeMainThread) then
- WakeMainThread(thd);
- { is this a Synchronize or Queue entry? }
- if issync then begin
- RtlEventWaitFor(aEntry^.SyncEvent);
- if Assigned(aEntry^.Exception) then
- raise aEntry^.Exception;
- end;
- {$endif def FPC_HAS_FEATURE_THREADING}
- end;
- end;
- procedure TThread.InitSynchronizeEvent;
- begin
- if Assigned(FSynchronizeEntry) then
- Exit;
- New(FSynchronizeEntry);
- FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
- FSynchronizeEntry^.Thread := Self;
- FSynchronizeEntry^.ThreadID := ThreadID;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- FSynchronizeEntry^.SyncEvent := RtlEventCreate;
- {$else}
- FSynchronizeEntry^.SyncEvent := nil{RtlEventCreate};
- {$endif}
- end;
- procedure TThread.DoneSynchronizeEvent;
- begin
- if not Assigned(FSynchronizeEntry) then
- Exit;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
- {$endif}
- Dispose(FSynchronizeEntry);
- FSynchronizeEntry := Nil;
- end;
- class function TThread.CurrentIsMain : Boolean;
- begin
- Result:=TThread.Current.ThreadID=MainThreadID;
- end;
- class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
- var
- syncentry: PThreadQueueEntry;
- thread: TThread;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- if Assigned(AThread) and (AThread.ThreadID = GetCurrentThreadID) then
- {$else}
- if Assigned(AThread) then
- {$endif}
- thread := AThread
- else if Assigned(CurrentThreadVar) then
- thread := CurrentThreadVar
- else begin
- thread := Nil;
- { use a local synchronize event }
- New(syncentry);
- FillChar(syncentry^, SizeOf(TThreadQueueEntry), 0);
- {$ifdef FPC_HAS_FEATURE_THREADING}
- syncentry^.ThreadID := GetCurrentThreadID;
- syncentry^.SyncEvent := RtlEventCreate;
- {$else}
- syncentry^.ThreadID := 0{GetCurrentThreadID};
- syncentry^.SyncEvent := nil{RtlEventCreate};
- {$endif}
- end;
- if Assigned(thread) then begin
- { the Synchronize event is instantiated on demand }
- thread.InitSynchronizeEvent;
- syncentry := thread.FSynchronizeEntry;
- end;
- syncentry^.Exception := Nil;
- syncentry^.Method := AMethod;
- try
- ThreadQueueAppend(syncentry, False);
- finally
- syncentry^.Method := Nil;
- syncentry^.Next := Nil;
- if not Assigned(thread) then begin
- { clean up again }
- {$ifdef FPC_HAS_FEATURE_THREADING}
- RtlEventDestroy(syncentry^.SyncEvent);
- {$endif}
- Dispose(syncentry);
- end;
- end;
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- class procedure TThread.Synchronize(AThread: TThread; AProcedure: TThreadProcedure);
- var
- syncentry: PThreadQueueEntry;
- thread: TThread;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- if Assigned(AThread) and (AThread.ThreadID = GetCurrentThreadID) then
- {$else}
- if Assigned(AThread) then
- {$endif}
- thread := AThread
- else if Assigned(CurrentThreadVar) then
- thread := CurrentThreadVar
- else begin
- thread := Nil;
- { use a local synchronize event }
- New(syncentry);
- FillChar(syncentry^, SizeOf(TThreadQueueEntry), 0);
- {$ifdef FPC_HAS_FEATURE_THREADING}
- syncentry^.ThreadID := GetCurrentThreadID;
- syncentry^.SyncEvent := RtlEventCreate;
- {$else}
- syncentry^.ThreadID := 0{GetCurrentThreadID};
- syncentry^.SyncEvent := nil{RtlEventCreate};
- {$endif}
- end;
- if Assigned(thread) then begin
- { the Synchronize event is instantiated on demand }
- thread.InitSynchronizeEvent;
- syncentry := thread.FSynchronizeEntry;
- end;
- syncentry^.Exception := Nil;
- syncentry^.ThreadProc := AProcedure;
- try
- ThreadQueueAppend(syncentry, False);
- finally
- syncentry^.ThreadProc := Nil;
- syncentry^.Next := Nil;
- if not Assigned(thread) then begin
- { clean up again }
- {$ifdef FPC_HAS_FEATURE_THREADING}
- RtlEventDestroy(syncentry^.SyncEvent);
- {$endif}
- Dispose(syncentry);
- end;
- end;
- end;
- {$endif}
- procedure TThread.Synchronize(AMethod: TThreadMethod);
- begin
- TThread.Synchronize(self,AMethod);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- procedure TThread.Synchronize(AProcedure: TThreadProcedure);
- begin
- TThread.Synchronize(self,AProcedure);
- end;
- {$endif}
- Function PopThreadQueueHead : TThread.PThreadQueueEntry;
- begin
- Result:=ThreadQueueHead;
- if (Result<>Nil) then
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- System.EnterCriticalSection(ThreadQueueLock);
- try
- {$endif}
- Result:=ThreadQueueHead;
- if Result<>Nil then
- ThreadQueueHead:=ThreadQueueHead^.Next;
- if Not Assigned(ThreadQueueHead) then
- ThreadQueueTail := Nil;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- {$endif}
- end;
- end;
- function CheckSynchronize(timeout : longint=0) : boolean;
- { assumes being called from GUI thread }
- var
- ExceptObj: TObject;
- tmpentry: TThread.PThreadQueueEntry;
- begin
- result:=false;
- { first sanity check }
- if Not IsMultiThread then
- Exit
- {$ifdef FPC_HAS_FEATURE_THREADING}
- { second sanity check }
- else if GetCurrentThreadID<>MainThreadID then
- raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
- if timeout>0 then
- RtlEventWaitFor(SynchronizeTimeoutEvent,timeout)
- else
- RtlEventResetEvent(SynchronizeTimeoutEvent);
- tmpentry := PopThreadQueueHead;
- while Assigned(tmpentry) do
- begin
- { at least one method is handled, so return true }
- result := true;
- { step 2: execute the method }
- exceptobj := Nil;
- try
- ExecuteThreadQueueEntry(tmpentry);
- except
- exceptobj := TObject(AcquireExceptionObject);
- end;
- { step 3: error handling and cleanup }
- if Assigned(tmpentry^.SyncEvent) then
- begin
- { for Synchronize entries we pass back the Exception and trigger
- the event that Synchronize waits in }
- tmpentry^.Exception := exceptobj;
- RtlEventSetEvent(tmpentry^.SyncEvent)
- end
- else
- begin
- { for Queue entries we dispose the entry and raise the exception }
- Dispose(tmpentry);
- if Assigned(exceptobj) then
- raise exceptobj;
- end;
- tmpentry := PopThreadQueueHead;
- end
- {$endif};
- end;
- class function TThread.GetCurrentThread: TThread;
- begin
- { if this is the first time GetCurrentThread is called for an external thread
- we need to create a corresponding TExternalThread instance }
- Result := CurrentThreadVar;
- if not Assigned(Result) then begin
- Result := TExternalThread.Create;
- CurrentThreadVar := Result;
- end;
- end;
- class function TThread.GetIsSingleProcessor: Boolean;
- begin
- Result := FProcessorCount <= 1;
- end;
- procedure TThread.Queue(aMethod: TThreadMethod);
- begin
- Queue(Self, aMethod);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- procedure TThread.Queue(aProcedure: TThreadProcedure);
- begin
- Queue(Self, aProcedure);
- end;
- {$endif}
- class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
- begin
- InternalQueue(aThread, aMethod, False);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- class procedure TThread.Queue(aThread: TThread; AProcedure: TThreadProcedure);
- begin
- InternalQueue(aThread, aProcedure, False);
- end;
- {$endif}
- class procedure TThread.InternalQueue(aThread: TThread; aMethod: TThreadMethod; aQueueIfMain: Boolean); static;
- var
- queueentry: PThreadQueueEntry;
- begin
- New(queueentry);
- FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
- queueentry^.Thread := aThread;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- queueentry^.ThreadID := GetCurrentThreadID;
- {$else}
- queueentry^.ThreadID := 0{GetCurrentThreadID};
- {$endif}
- queueentry^.Method := aMethod;
- { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
- ThreadQueueAppend(queueentry, aQueueIfMain);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- class procedure TThread.InternalQueue(aThread: TThread; aProcedure: TThreadProcedure; aQueueIfMain: Boolean); static;
- var
- queueentry: PThreadQueueEntry;
- begin
- New(queueentry);
- FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
- queueentry^.Thread := aThread;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- queueentry^.ThreadID := GetCurrentThreadID;
- {$else}
- queueentry^.ThreadID := 0{GetCurrentThreadID};
- {$endif}
- queueentry^.ThreadProc := aProcedure;
- { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
- ThreadQueueAppend(queueentry, aQueueIfMain);
- end;
- {$endif}
- procedure TThread.ForceQueue(aMethod: TThreadMethod);
- begin
- ForceQueue(Self, aMethod);
- end;
- class procedure TThread.ForceQueue(aThread: TThread; aMethod: TThreadMethod); static;
- begin
- InternalQueue(aThread, aMethod, True);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- class procedure TThread.ForceQueue(aThread: TThread; aMethod: TThreadProcedure); static;
- begin
- InternalQueue(aThread, aMethod, True);
- end;
- {$endif}
- class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
- var
- entry, tmpentry, lastentry: PThreadQueueEntry;
- begin
- { anything to do at all? }
- if not Assigned(aThread) and not Assigned(aMethod) then
- Exit;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- System.EnterCriticalSection(ThreadQueueLock);
- try
- {$endif}
- lastentry := Nil;
- entry := ThreadQueueHead;
- while Assigned(entry) do begin
- if
- { only entries not added by Synchronize }
- not Assigned(entry^.SyncEvent)
- { check for the thread }
- and (not Assigned(aThread) or (entry^.Thread = aThread) or (entry^.ThreadID = aThread.ThreadID))
- { check for the method }
- and (not Assigned(aMethod) or
- (
- (TMethod(entry^.Method).Code = TMethod(aMethod).Code) and
- (TMethod(entry^.Method).Data = TMethod(aMethod).Data)
- ))
- then begin
- { ok, we need to remove this entry }
- tmpentry := entry;
- if Assigned(lastentry) then
- lastentry^.Next := entry^.Next;
- entry := entry^.Next;
- if ThreadQueueHead = tmpentry then
- ThreadQueueHead := entry;
- if ThreadQueueTail = tmpentry then
- ThreadQueueTail := lastentry;
- { only dispose events added by Queue }
- if not Assigned(tmpentry^.SyncEvent) then
- Dispose(tmpentry);
- end else begin
- { leave this entry }
- lastentry := entry;
- entry := entry^.Next;
- end;
- end;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- {$endif}
- end;
- class procedure TThread.RemoveQueuedEvents(aMethod: TThreadMethod);
- begin
- RemoveQueuedEvents(Nil, aMethod);
- end;
- class procedure TThread.RemoveQueuedEvents(aThread: TThread);
- begin
- RemoveQueuedEvents(aThread, Nil);
- end;
- class function TThread.CheckTerminated: Boolean;
- begin
- { this method only works with threads created by TThread, so we can make a
- shortcut here }
- if not Assigned(CurrentThreadVar) then
- raise EThreadExternalException.Create(SThreadExternal);
- Result := CurrentThreadVar.FTerminated;
- end;
- class procedure TThread.SetReturnValue(aValue: Integer);
- begin
- { this method only works with threads created by TThread, so we can make a
- shortcut here }
- if not Assigned(CurrentThreadVar) then
- raise EThreadExternalException.Create(SThreadExternal);
- CurrentThreadVar.FReturnValue := aValue;
- end;
- class function TThread.CreateAnonymousThread(aProc: TProcedure): TThread;
- begin
- if not Assigned(aProc) then
- raise Exception.Create(SNoProcGiven);
- Result := TAnonymousThread.Create(aProc);
- end;
- {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
- class function TThread.CreateAnonymousThread(aProc: TThreadProcedure): TThread;
- begin
- if not Assigned(aProc) then
- raise Exception.Create(SNoProcGiven);
- Result := TAnonymousThread.Create(aProc);
- end;
- {$ENDIF}
- class function TThread.CreateAnonymousThread(aProc: TThreadMethod): TThread;
- begin
- if not Assigned(aProc) then
- raise Exception.Create(SNoProcGiven);
- Result := TAnonymousThread.Create(aProc);
- end;
- class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- SetThreadDebugName(aThreadID, aThreadName);
- {$endif}
- end;
- class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- SetThreadDebugName(aThreadID, aThreadName);
- {$endif}
- end;
- class procedure TThread.Yield;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- ThreadSwitch;
- {$endif}
- end;
- class procedure TThread.Sleep(aMilliseconds: Cardinal);
- begin
- {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.Sleep(aMilliseconds);
- end;
- class procedure TThread.SpinWait(aIterations: LongWord);
- var
- i: LongWord;
- begin
- { yes, it's just a simple busy wait to burn some cpu cycles... and as the job
- of this loop is to burn CPU cycles we switch off any optimizations that
- could interfere with this (e.g. loop unrolling) }
- { Do *NOT* do $PUSH, $OPTIMIZATIONS OFF, <code>, $POP because optimization is
- not a local switch, which means $PUSH/POP doesn't affect it, so that turns
- off *ALL* optimizations for code below this point. Thanks to this we shipped
- large parts of the classes unit with optimizations off between 2012-12-27
- and 2014-06-06.
- Instead, use a global var for the spinlock, because that is always handled
- as volatile, so the access won't be optimized away by the compiler. (KB) }
- for i:=1 to aIterations do
- begin
- Inc(SpinWaitDummy); // SpinWaitDummy *MUST* be global
- end;
- end;
- {$ifndef HAS_TTHREAD_GETSYSTEMTIMES}
- class function TThread.GetSystemTimes(out aSystemTimes: TSystemTimes) : Boolean;
- begin
- { by default we just return a zeroed out record }
- FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
- Result:=False;
- end;
- {$endif}
- class function TThread.GetCPUUsage(var Previous: TSystemTimes): Integer;
- var
- Act : TSystemTimes;
- Load,Idle: QWord;
-
- begin
- Result:=0;
- if not GetSystemTimes(Act) then
- exit;
- Load:=(Act.UserTime-Previous.UserTime) +
- (Act.KernelTime-Previous.KernelTime) +
- (Act.NiceTime-Previous.NiceTime);
- Idle:=Act.IdleTime-Previous.IdleTime;
- Previous:=Act;
- if (Load<>0) and (Load>Idle) then
- Result:=100*Trunc(1-(Idle/Load));
- end;
- class function TThread.GetTickCount: LongWord;
- begin
- Result := {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.GetTickCount;
- end;
- class function TThread.GetTickCount64: QWord;
- begin
- Result := {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.GetTickCount64;
- end;
- { TSimpleThread allows objects to create a threading method without defining
- a new thread class }
- Type
- TSimpleThread = class(TThread)
- private
- FExecuteMethod: TThreadExecuteHandler;
- protected
- procedure Execute; override;
- public
- constructor Create(ExecuteMethod: TThreadExecuteHandler; AOnterminate : TNotifyEvent);
- end;
- TSimpleStatusThread = class(TThread)
- private
- FExecuteMethod: TThreadExecuteStatusHandler;
- FStatus : String;
- FOnStatus : TThreadStatusNotifyEvent;
- protected
- procedure Execute; override;
- Procedure DoStatus;
- Procedure SetStatus(Const AStatus : String);
- public
- constructor Create(ExecuteMethod: TThreadExecuteStatusHandler; AOnStatus : TThreadStatusNotifyEvent; AOnterminate : TNotifyEvent);
- end;
- TSimpleProcThread = class(TThread)
- private
- FExecuteMethod: TThreadExecuteCallBack;
- FCallOnTerminate : TNotifyCallBack;
- FData : Pointer;
- protected
- Procedure TerminateCallBack(Sender : TObject);
- procedure Execute; override;
- public
- constructor Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnterminate : TNotifyCallBack);
- end;
- TSimpleStatusProcThread = class(TThread)
- private
- FExecuteMethod: TThreadExecuteStatusCallBack;
- FCallOnTerminate : TNotifyCallBack;
- FStatus : String;
- FOnStatus : TThreadStatusNotifyCallBack;
- FData : Pointer;
- protected
- procedure Execute; override;
- Procedure DoStatus;
- Procedure SetStatus(Const AStatus : String);
- Procedure TerminateCallBack(Sender : TObject);
- public
- constructor Create(ExecuteMethod: TThreadExecuteStatusCallBack; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnterminate : TNotifyCallBack);
- end;
- { TSimpleThread }
- constructor TSimpleThread.Create(ExecuteMethod: TThreadExecuteHandler; AOnTerminate: TNotifyEvent);
- begin
- FExecuteMethod := ExecuteMethod;
- OnTerminate := AOnTerminate;
- inherited Create(False);
- end;
- procedure TSimpleThread.Execute;
- begin
- FreeOnTerminate := True;
- FExecuteMethod;
- end;
- { TSimpleStatusThread }
- constructor TSimpleStatusThread.Create(ExecuteMethod: TThreadExecuteStatusHandler;AOnStatus : TThreadStatusNotifyEvent; AOnTerminate: TNotifyEvent);
- begin
- FExecuteMethod := ExecuteMethod;
- OnTerminate := AOnTerminate;
- FOnStatus:=AOnStatus;
- FStatus:='';
- inherited Create(False);
- end;
- procedure TSimpleStatusThread.Execute;
- begin
- FreeOnTerminate := True;
- FExecuteMethod(@SetStatus);
- end;
- procedure TSimpleStatusThread.SetStatus(Const AStatus : String);
- begin
- If (AStatus=FStatus) then
- exit;
- FStatus:=AStatus;
- If Assigned(FOnStatus) then
- Synchronize(@DoStatus);
- end;
- procedure TSimpleStatusThread.DoStatus;
- begin
- FOnStatus(Self,FStatus);
- end;
- { TSimpleProcThread }
- constructor TSimpleProcThread.Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnTerminate: TNotifyCallBack);
- begin
- FExecuteMethod := ExecuteMethod;
- FCallOnTerminate := AOnTerminate;
- FData:=AData;
- If Assigned(FCallOnTerminate) then
- OnTerminate:=@TerminateCallBack;
- inherited Create(False);
- end;
- procedure TSimpleProcThread.Execute;
- begin
- FreeOnTerminate := True;
- FExecuteMethod(FData);
- end;
- procedure TSimpleProcThread.TerminateCallBack(Sender : TObject);
- begin
- if Assigned(FCallOnTerminate) then
- FCallOnTerminate(Sender,FData);
- end;
- { TSimpleStatusProcThread }
- constructor TSimpleStatusProcThread.Create(ExecuteMethod: TThreadExecuteStatusCallback; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnTerminate: TNotifyCallBack);
- begin
- FExecuteMethod := ExecuteMethod;
- FCallOnTerminate := AOnTerminate;
- FData:=AData;
- If Assigned(FCallOnTerminate) then
- OnTerminate:=@TerminateCallBack;
- FOnStatus:=AOnStatus;
- FStatus:='';
- inherited Create(False);
- end;
- procedure TSimpleStatusProcThread.Execute;
- begin
- FreeOnTerminate := True;
- FExecuteMethod(FData,@SetStatus);
- end;
- procedure TSimpleStatusProcThread.SetStatus(Const AStatus : String);
- begin
- If (AStatus=FStatus) then
- exit;
- FStatus:=AStatus;
- If Assigned(FOnStatus) then
- Synchronize(@DoStatus);
- end;
- procedure TSimpleStatusProcThread.DoStatus;
- begin
- FOnStatus(Self,FData,FStatus);
- end;
- procedure TSimpleStatusProcThread.TerminateCallBack(Sender : TObject);
- begin
- if Assigned(FCallOnTerminate) then
- FCallOnTerminate(Sender,FData);
- end;
- class function TThread.ExecuteInThread(AMethod: TThreadExecuteHandler; AOnTerminate: TNotifyEvent): TThread;
- begin
- Result:=TSimpleThread.Create(AMethod,AOnTerminate);
- end;
- class function TThread.ExecuteInThread(AMethod: TThreadExecuteCallback; AData: Pointer; AOnTerminate: TNotifyCallBack): TThread;
- begin
- Result:=TSimpleProcThread.Create(AMethod,AData,AOnTerminate);
- end;
- class function TThread.ExecuteInThread(AMethod: TThreadExecuteStatusHandler; AOnStatus: TThreadStatusNotifyEvent;
- AOnTerminate: TNotifyEvent): TThread;
- begin
- If Not Assigned(AOnStatus) then
- Raise EThread.Create(SErrStatusCallBackRequired);
- Result:=TSimpleStatusThread.Create(AMethod,AOnStatus,AOnTerminate);
- end;
- class function TThread.ExecuteInThread(AMethod: TThreadExecuteStatusCallback; AOnStatus: TThreadStatusNotifyCallback;
- AData: Pointer; AOnTerminate: TNotifyCallBack): TThread;
- begin
- If Not Assigned(AOnStatus) then
- Raise EThread.Create(SErrStatusCallBackRequired);
- Result:=TSimpleStatusProcThread.Create(AMethod,AData,AOnStatus,AOnTerminate);
- end;
- { TPersistent implementation }
- {$i persist.inc }
- {$i sllist.inc}
- {$i resref.inc}
- { TComponent implementation }
- {$i compon.inc}
- { TBasicAction implementation }
- {$i action.inc}
- { TDataModule implementation }
- {$i dm.inc}
- { Class and component registration routines }
- {$I cregist.inc}
- { Interface related stuff }
- {$I intf.inc}
- {**********************************************************************
- * Miscellaneous procedures and functions *
- **********************************************************************}
- function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PAnsiChar; Strings: TStrings; AddEmptyStrings : Boolean = False): Integer;
- var
- b, c : PAnsiChar;
- procedure SkipWhitespace;
- begin
- while (c^ in Whitespace) do
- inc (c);
- end;
- procedure AddString;
- var
- l : integer;
- s : string;
- begin
- l := c-b;
- if (l > 0) or AddEmptyStrings then
- begin
- if assigned(Strings) then
- begin
- setlength(s, l);
- if l>0 then
- move (b^, s[1],l*SizeOf(AnsiChar));
- Strings.Add (s);
- end;
- inc (result);
- end;
- end;
- var
- quoted : AnsiChar;
- begin
- result := 0;
- c := Content;
- Quoted := #0;
- Separators := Separators + [#13, #10] - ['''','"'];
- SkipWhitespace;
- b := c;
- while (c^ <> #0) do
- begin
- if (c^ = Quoted) then
- begin
- if ((c+1)^ = Quoted) then
- inc (c)
- else
- Quoted := #0
- end
- else if (Quoted = #0) and (c^ in ['''','"']) then
- Quoted := c^;
- if (Quoted = #0) and (c^ in Separators) then
- begin
- AddString;
- inc (c);
- SkipWhitespace;
- b := c;
- end
- else
- inc (c);
- end;
- if (c <> b) then
- AddString;
- end;
- { Point and rectangle constructors }
- function Point(AX, AY: Integer): TPoint;
- begin
- with Result do
- begin
- X := AX;
- Y := AY;
- end;
- end;
- function SmallPoint(AX, AY: SmallInt): TSmallPoint;
- begin
- with Result do
- begin
- X := AX;
- Y := AY;
- end;
- end;
- function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
- begin
- with Result do
- begin
- Left := ALeft;
- Top := ATop;
- Right := ARight;
- Bottom := ABottom;
- end;
- end;
- function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
- begin
- with Result do
- begin
- Left := ALeft;
- Top := ATop;
- Right := ALeft + AWidth;
- Bottom := ATop + AHeight;
- end;
- end;
- function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- { lazy, but should work }
- result:=QWord(P1)=QWord(P2);
- end;
- function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- { lazy, but should work }
- result:=DWord(P1)=DWord(P2);
- end;
- function InvalidPoint(X, Y: Integer): Boolean;
- begin
- result:=(X=-1) and (Y=-1);
- end;
- function InvalidPoint(const At: TPoint): Boolean;
- begin
- result:=(At.x=-1) and (At.y=-1);
- end;
- function InvalidPoint(const At: TSmallPoint): Boolean;
- begin
- result:=(At.x=-1) and (At.y=-1);
- end;
- { Object filing routines }
- var
- IntConstList: TThreadList;
- type
- TIntConst = class
- IntegerType: PTypeInfo; // The integer type RTTI pointer
- IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
- IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
- constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
- AIntToIdent: TIntToIdent);
- end;
- constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
- AIntToIdent: TIntToIdent);
- begin
- IntegerType := AIntegerType;
- IdentToIntFn := AIdentToInt;
- IntToIdentFn := AIntToIdent;
- end;
- procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
- IntToIdentFn: TIntToIdent);
- begin
- IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
- end;
- function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
- var
- i: Integer;
- begin
- with IntConstList.LockList do
- try
- for i := 0 to Count - 1 do
- if TIntConst(Items[i]).IntegerType = AIntegerType then
- exit(TIntConst(Items[i]).IntToIdentFn);
- Result := nil;
- finally
- IntConstList.UnlockList;
- end;
- end;
- function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
- var
- i: Integer;
- begin
- with IntConstList.LockList do
- try
- for i := 0 to Count - 1 do
- with TIntConst(Items[I]) do
- if TIntConst(Items[I]).IntegerType = AIntegerType then
- exit(IdentToIntFn);
- Result := nil;
- finally
- IntConstList.UnlockList;
- end;
- end;
- function IdentToInt(const Ident: String; out Int: LongInt;
- const Map: array of TIdentMapEntry): Boolean;
- var
- i: Integer;
- begin
- for i := Low(Map) to High(Map) do
- if CompareText(Map[i].Name, Ident) = 0 then
- begin
- Int := Map[i].Value;
- exit(True);
- end;
- Result := False;
- end;
- function IntToIdent(Int: LongInt; var Ident: String;
- const Map: array of TIdentMapEntry): Boolean;
- var
- i: Integer;
- begin
- for i := Low(Map) to High(Map) do
- if Map[i].Value = Int then
- begin
- Ident := Map[i].Name;
- exit(True);
- end;
- Result := False;
- end;
- function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
- var
- i : Integer;
- begin
- with IntConstList.LockList do
- try
- for i := 0 to Count - 1 do
- if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
- Exit(True);
- Result := false;
- finally
- IntConstList.UnlockList;
- end;
- end;
- { TPropFixup }
- // Tainted. TPropFixup is being removed.
- Type
- TInitHandler = Class(TObject)
- AHandler : TInitComponentHandler;
- AClass : TComponentClass;
- end;
- {$ifndef i8086}
- type
- TCodePtrList = TList;
- {$endif i8086}
- Var
- InitHandlerList : TList;
- FindGlobalComponentList : TCodePtrList;
- procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
- begin
- if not(assigned(FindGlobalComponentList)) then
- FindGlobalComponentList:=TCodePtrList.Create;
- if FindGlobalComponentList.IndexOf(CodePointer(AFindGlobalComponent))<0 then
- FindGlobalComponentList.Add(CodePointer(AFindGlobalComponent));
- end;
- procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
- begin
- if assigned(FindGlobalComponentList) then
- FindGlobalComponentList.Remove(CodePointer(AFindGlobalComponent));
- end;
- function FindGlobalComponent(const Name: string): TComponent;
- var
- i : sizeint;
- begin
- FindGlobalComponent:=nil;
- if assigned(FindGlobalComponentList) then
- begin
- for i:=FindGlobalComponentList.Count-1 downto 0 do
- begin
- FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
- if assigned(FindGlobalComponent) then
- break;
- end;
- end;
- end;
- function IsUniqueGlobalComponentName(const aName: string): Boolean;
- begin
- if Assigned(IsUniqueGlobalComponentNameProc) then
- Result:=IsUniqueGlobalComponentNameProc(aName)
- else
- Result:=Not Assigned(FindGlobalComponent(aName));
- end;
- procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
- Var
- I : Integer;
- H: TInitHandler;
- begin
- If (InitHandlerList=Nil) then
- InitHandlerList:=TList.Create;
- H:=TInitHandler.Create;
- H.Aclass:=ComponentClass;
- H.AHandler:=Handler;
- try
- With InitHandlerList do
- begin
- I:=0;
- While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
- Inc(I);
- { override? }
- if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
- begin
- TInitHandler(Items[I]).AHandler:=Handler;
- H.Free;
- end
- else
- InitHandlerList.Insert(I,H);
- end;
- except
- H.Free;
- raise;
- end;
- end;
- { all targets should at least include the sysres.inc dummy in the system unit to compile this }
- function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
- var
- ResStream : TResourceStream;
- ResID : TFPResourceHandle;
-
- begin
- if Inst=0 then
- Inst:=HInstance;
- ResId:=System.FindResource(Inst, Res, RT_RCDATA);
- result:=ResID<>0;
- try
- if Result then
- begin
- ResStream:=TResourceStream.Create(Inst,Res,RT_RCDATA);
- try
- Component:=ResStream.ReadComponent(Component);
- finally
- ResStream.Free;
- end;
- end;
- except
- on EResNotFound do
- result:=false;
- end;
- end;
- function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
- function doinit(_class : TClass) : boolean;
- begin
- result:=false;
- if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
- exit;
- result:=doinit(_class.ClassParent);
- result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
- end;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- GlobalNameSpace.BeginWrite;
- try
- {$endif}
- result:=doinit(Instance.ClassType);
- if Result then
- Instance.ReadDeltaState;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- finally
- GlobalNameSpace.EndWrite;
- end;
- {$endif}
- end;
- function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
- Var
- I : Integer;
- begin
- I:=0;
- if not Assigned(InitHandlerList) then begin
- Result := True;
- Exit;
- end;
- Result:=False;
- With InitHandlerList do
- begin
- I:=0;
- // Instance is the normally the lowest one, so that one should be used when searching.
- While Not result and (I<Count) do
- begin
- If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
- Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
- Inc(I);
- end;
- end;
- end;
- function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
- begin
- Result:=ReadComponentRes(ResName,Instance)=Instance;
- end;
- function SysReadComponentRes(HInstance : THandle; const ResName: String; Instance: TComponent): TComponent;
- Var
- H : TFPResourceHandle;
- begin
- { Windows unit also has a FindResource function, use the one from
- system unit here. }
- H:=system.FindResource(HInstance,ResName,RT_RCDATA);
- if (PtrInt(H)=0) then
- Result:=Nil
- else
- With TResourceStream.Create(HInstance,ResName,RT_RCDATA) do
- try
- Result:=ReadComponent(Instance);
- Finally
- Free;
- end;
- end;
- function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
- begin
- Result:=SysReadComponentRes(Hinstance,Resname,Instance);
- end;
- function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
- begin
- Result:=SysReadComponentRes(Hinstance,ResName,Nil);
- end;
- function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
- var
- FileStream: TStream;
- begin
- FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
- try
- Result := FileStream.ReadComponentRes(Instance);
- finally
- FileStream.Free;
- end;
- end;
- procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
- var
- FileStream: TStream;
- begin
- FileStream := TFileStream.Create(FileName, fmCreate);
- try
- FileStream.WriteComponentRes(Instance.ClassName, Instance);
- finally
- FileStream.Free;
- end;
- end;
- Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
- Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
- Var
- P : Integer;
- CM : Boolean;
- begin
- P:=Pos('.',APath);
- CM:=False;
- If (P=0) then
- begin
- If CStyle then
- begin
- P:=Pos('->',APath);
- CM:=P<>0;
- end;
- If (P=0) Then
- P:=Length(APath)+1;
- end;
- Result:=Copy(APath,1,P-1);
- Delete(APath,1,P+Ord(CM));
- end;
- Var
- C : TComponent;
- S : String;
- begin
- If (APath='') then
- Result:=Nil
- else
- begin
- Result:=Root;
- While (APath<>'') And (Result<>Nil) do
- begin
- C:=Result;
- S:=Uppercase(GetNextName);
- Result:=C.FindComponent(S);
- If (Result=Nil) And (S='OWNER') then
- Result:=C;
- end;
- end;
- end;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- threadvar
- {$else}
- var
- {$endif}
- GlobalLoaded, GlobalLists: TFpList;
- procedure BeginGlobalLoading;
- begin
- if not Assigned(GlobalLists) then
- GlobalLists := TFpList.Create;
- GlobalLists.Add(GlobalLoaded);
- GlobalLoaded := TFpList.Create;
- end;
- { Notify all global components that they have been loaded completely }
- procedure NotifyGlobalLoading;
- var
- i: Integer;
- begin
- for i := 0 to GlobalLoaded.Count - 1 do
- TComponent(GlobalLoaded[i]).Loaded;
- end;
- procedure EndGlobalLoading;
- begin
- { Free the memory occupied by BeginGlobalLoading }
- GlobalLoaded.Free;
- GlobalLoaded := TFpList(GlobalLists.Last);
- GlobalLists.Delete(GlobalLists.Count - 1);
- if GlobalLists.Count = 0 then
- begin
- GlobalLists.Free;
- GlobalLists := nil;
- end;
- end;
- function CollectionsEqual(C1, C2: TCollection): Boolean;
- begin
- // !!!: Implement this
- CollectionsEqual:=false;
- end;
- function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
- procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
- var
- w : twriter;
- begin
- w:=twriter.create(s,4096);
- try
- w.root:=o;
- w.flookuproot:=o;
- w.writecollection(c);
- finally
- w.free;
- end;
- end;
- var
- s1,s2 : tmemorystream;
- begin
- result:=false;
- if (c1.classtype<>c2.classtype) or
- (c1.count<>c2.count) then
- exit;
- if c1.count = 0 then
- begin
- result:= true;
- exit;
- end;
- s1:=tmemorystream.create;
- try
- s2:=tmemorystream.create;
- try
- stream_collection(s1,c1,owner1);
- stream_collection(s2,c2,owner2);
- result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
- finally
- s2.free;
- end;
- finally
- s1.free;
- end;
- end;
- { Object conversion routines }
- type
- CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
- function CharToOrd(var P: Pointer): Cardinal;
- begin
- result:= ord(PAnsiChar(P)^);
- inc(PAnsiChar(P));
- end;
- function WideCharToOrd(var P: Pointer): Cardinal;
- begin
- result:= ord(pwidechar(P)^);
- inc(pwidechar(P));
- end;
- function Utf8ToOrd(var P:Pointer): Cardinal;
- begin
- // Should also check for illegal utf8 combinations
- Result := Ord(PAnsiChar(P)^);
- Inc(P);
- if (Result and $80) <> 0 then
- if (Ord(Result) and %11100000) = %11000000 then begin
- Result := ((Result and %00011111) shl 6)
- or (ord(PAnsiChar(P)^) and %00111111);
- Inc(P);
- end else if (Ord(Result) and %11110000) = %11100000 then begin
- Result := ((Result and %00011111) shl 12)
- or ((ord(PAnsiChar(P)^) and %00111111) shl 6)
- or (ord((PAnsiChar(P)+1)^) and %00111111);
- Inc(P,2);
- end else begin
- Result := ((ord(Result) and %00011111) shl 18)
- or ((ord(PAnsiChar(P)^) and %00111111) shl 12)
- or ((ord((PAnsiChar(P)+1)^) and %00111111) shl 6)
- or (ord((PAnsiChar(P)+2)^) and %00111111);
- Inc(P,3);
- end;
- end;
- procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
- var
- Version: TBinaryObjectReader.TBOVersion;
- procedure OutStr(s: RawByteString);
- begin
- if Length(s) > 0 then
- Output.Write(s[1], Length(s));
- end;
- procedure OutLn(s: RawByteString);
- begin
- OutStr(s + LineEnding);
- end;
- procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
- UseBytes: boolean = false);
- var
- res, NewStr: RawByteString;
- w: Cardinal;
- InString, NewInString: Boolean;
- begin
- if p = nil then begin
- res:= '''''';
- end
- else
- begin
- res := '';
- InString := False;
- while P < LastP do
- begin
- NewInString := InString;
- w := CharToOrdfunc(P);
- if w = ord('''') then
- begin //quote AnsiChar
- if not InString then
- NewInString := True;
- NewStr := '''''';
- end
- else if (Ord(w) >= 32) and ((Ord(w) < 127) or (UseBytes and (Ord(w)<256))) then
- begin //printable ascii or bytes
- if not InString then
- NewInString := True;
- NewStr := AnsiChar(w);
- end
- else
- begin //ascii control chars, non ascii
- if InString then
- NewInString := False;
- NewStr := '#' + IntToStr(w);
- end;
- if NewInString <> InString then
- begin
- NewStr := '''' + NewStr;
- InString := NewInString;
- end;
- res := res + NewStr;
- end;
- if InString then
- res := res + '''';
- end;
- OutStr(res);
- end;
- procedure OutString(s: RawByteString);
- begin
- OutChars(Pointer(S),PAnsiChar(S)+Length(S),@CharToOrd,Encoding=oteLFM);
- end;
- procedure OutWString(W: WideString);
- begin
- OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
- end;
- procedure OutUString(W: UnicodeString);
- begin
- OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
- end;
- procedure OutUtf8Str(s: RawByteString);
- begin
- if Encoding=oteLFM then
- OutChars(Pointer(S),PAnsiChar(S)+Length(S),@CharToOrd)
- else
- OutChars(Pointer(S),PAnsiChar(S)+Length(S),@Utf8ToOrd);
- end;
- function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- Result:=Input.ReadWord;
- Result:=LEtoN(Result);
- end;
- function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- Result:=Input.ReadDWord;
- Result:=LEtoN(Result);
- end;
- function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- Input.ReadBuffer(Result,sizeof(Result));
- Result:=LEtoN(Result);
- end;
- {$ifndef FPUNONE}
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- function ExtendedToDouble(e : pointer) : double;
- var mant : qword;
- exp : smallint;
- sign : boolean;
- d : qword;
- begin
- move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
- move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
- mant:=LEtoN(mant);
- exp:=LetoN(word(exp));
- sign:=(exp and $8000)<>0;
- if sign then exp:=exp and $7FFF;
- case exp of
- 0 : mant:=0; //if denormalized, value is too small for double,
- //so it's always zero
- $7FFF : exp:=2047 //either infinity or NaN
- else
- begin
- dec(exp,16383-1023);
- if (exp>=-51) and (exp<=0) then //can be denormalized
- begin
- mant:=mant shr (-exp);
- exp:=0;
- end
- else
- if (exp<-51) or (exp>2046) then //exponent too large.
- begin
- Result:=0;
- exit;
- end
- else //normalized value
- mant:=mant shl 1; //hide most significant bit
- end;
- end;
- d:=word(exp);
- d:=d shl 52;
- mant:=mant shr 12;
- d:=d or mant;
- if sign then d:=d or $8000000000000000;
- Result:=pdouble(@d)^;
- end;
- {$ENDIF}
- {$endif}
- function ReadInt(ValueType: TValueType): Int64;
- begin
- case ValueType of
- vaInt8: Result := ShortInt(Input.ReadByte);
- vaInt16: Result := SmallInt(ReadWord);
- vaInt32: Result := LongInt(ReadDWord);
- vaInt64: Result := Int64(ReadQWord);
- end;
- end;
- function ReadInt: Int64;
- begin
- Result := ReadInt(TValueType(Input.ReadByte));
- end;
- {$ifndef FPUNONE}
- function ReadExtended : extended;
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- var ext : array[0..9] of byte;
- {$ENDIF}
- begin
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- Input.ReadBuffer(ext[0],10);
- Result:=ExtendedToDouble(@(ext[0]));
- {$ELSE}
- Input.ReadBuffer(Result,sizeof(Result));
- {$ENDIF}
- end;
- {$endif}
- function ReadSStr: RawByteString;
- var
- len: Byte;
- begin
- len := Input.ReadByte;
- SetLength(Result, len);
- if (len > 0) then
- Input.ReadBuffer(Result[1], len);
- end;
- function ReadLStr: RawByteString;
- var
- len: DWord;
- begin
- len := ReadDWord;
- SetLength(Result, len);
- if (len > 0) then
- Input.ReadBuffer(Result[1], len);
- end;
- function ReadWStr: WideString;
- var
- len: DWord;
- {$IFDEF ENDIAN_BIG}
- i : integer;
- {$ENDIF}
- begin
- len := ReadDWord;
- SetLength(Result, len);
- if (len > 0) then
- begin
- Input.ReadBuffer(Pointer(@Result[1])^, len*2);
- {$IFDEF ENDIAN_BIG}
- for i:=1 to len do
- Result[i]:=widechar(SwapEndian(word(Result[i])));
- {$ENDIF}
- end;
- end;
- function ReadUStr: UnicodeString;
- var
- len: DWord;
- {$IFDEF ENDIAN_BIG}
- i : integer;
- {$ENDIF}
- begin
- len := ReadDWord;
- SetLength(Result, len);
- if (len > 0) then
- begin
- Input.ReadBuffer(Pointer(@Result[1])^, len*2);
- {$IFDEF ENDIAN_BIG}
- for i:=1 to len do
- Result[i]:=widechar(SwapEndian(word(Result[i])));
- {$ENDIF}
- end;
- end;
- procedure ReadPropList(indent: RawByteString);
- procedure ProcessValue(ValueType: TValueType; Indent: RawByteString);
- procedure ProcessBinary;
- var
- ToDo, DoNow, i: LongInt;
- lbuf: array[0..31] of Byte;
- s: RawByteString;
- begin
- ToDo := ReadDWord;
- OutLn('{');
- while ToDo > 0 do begin
- DoNow := ToDo;
- if DoNow > 32 then DoNow := 32;
- Dec(ToDo, DoNow);
- s := Indent + ' ';
- Input.ReadBuffer(lbuf, DoNow);
- for i := 0 to DoNow - 1 do
- s := s + IntToHex(lbuf[i], 2);
- OutLn(s);
- end;
- OutLn(indent + '}');
- end;
- var
- s: RawByteString;
- { len: LongInt; }
- IsFirst: Boolean;
- {$ifndef FPUNONE}
- ext: Extended;
- {$endif}
- begin
- case ValueType of
- vaList: begin
- OutStr('(');
- IsFirst := True;
- while True do begin
- ValueType := TValueType(Input.ReadByte);
- if ValueType = vaNull then break;
- if IsFirst then begin
- OutLn('');
- IsFirst := False;
- end;
- OutStr(Indent + ' ');
- ProcessValue(ValueType, Indent + ' ');
- end;
- OutLn(Indent + ')');
- end;
- vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
- vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
- vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
- vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
- {$ifndef FPUNONE}
- vaExtended: begin
- ext:=ReadExtended;
- Str(ext,S);// Do not use localized strings.
- OutLn(S);
- end;
- {$endif}
- vaString: begin
- OutString(ReadSStr);
- OutLn('');
- end;
- vaIdent: OutLn(ReadSStr);
- vaFalse: OutLn('False');
- vaTrue: OutLn('True');
- vaBinary: ProcessBinary;
- vaSet: begin
- OutStr('[');
- IsFirst := True;
- while True do begin
- s := ReadSStr;
- if Length(s) = 0 then break;
- if not IsFirst then OutStr(', ');
- IsFirst := False;
- OutStr(s);
- end;
- OutLn(']');
- end;
- vaLString:
- begin
- OutString(ReadLStr);
- OutLn('');
- end;
- vaWString:
- begin
- OutWString(ReadWStr);
- OutLn('');
- end;
- vaUString:
- begin
- OutWString(ReadWStr);
- OutLn('');
- end;
- vaNil:
- OutLn('nil');
- vaCollection: begin
- OutStr('<');
- while Input.ReadByte <> 0 do begin
- OutLn(Indent);
- Input.Seek(-1, soFromCurrent);
- OutStr(indent + ' item');
- ValueType := TValueType(Input.ReadByte);
- if ValueType <> vaList then
- OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
- OutLn('');
- ReadPropList(indent + ' ');
- OutStr(indent + ' end');
- end;
- OutLn('>');
- end;
- {vaSingle: begin OutLn('!!Single!!'); exit end;
- vaCurrency: begin OutLn('!!Currency!!'); exit end;
- vaDate: begin OutLn('!!Date!!'); exit end;}
- vaUTF8String: begin
- OutUtf8Str(ReadLStr);
- OutLn('');
- end;
- else
- Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
- end;
- end;
- begin
- while Input.ReadByte <> 0 do begin
- Input.Seek(-1, soFromCurrent);
- OutStr(indent + ReadSStr + ' = ');
- ProcessValue(TValueType(Input.ReadByte), Indent);
- end;
- end;
- procedure ReadObject(indent: RawByteString);
- var
- b: Byte;
- ObjUnitName, ObjClassName, ObjName: RawByteString;
- ChildPos: LongInt;
- ValueType: TValueType;
- p: SizeInt;
- begin
- // Check for FilerFlags
- b := Input.ReadByte;
- if (b and $f0) = $f0 then begin
- if (b and 2) <> 0 then ChildPos := ReadInt;
- end else begin
- b := 0;
- Input.Seek(-1, soFromCurrent);
- end;
- ObjUnitName:='';
- if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
- begin
- ValueType := TValueType(Input.ReadByte);
- if ValueType=vaString then
- ObjClassName := ReadSStr
- else
- ObjClassName := ReadLStr;
- p:=Pos(TBinaryObjectReader.UnitnameSeparator,ObjClassName);
- if p>0 then
- begin
- ObjUnitName:=copy(ObjClassName,1,p-1);
- System.Delete(ObjClassName,1,p);
- end;
- end else
- ObjClassName := ReadSStr;
- ObjName := ReadSStr;
- OutStr(Indent);
- if (b and 1) <> 0 then
- OutStr('inherited')
- else if (b and 4) <> 0 then
- OutStr('inline')
- else
- OutStr('object');
- OutStr(' ');
- if ObjName <> '' then
- OutStr(ObjName + ': ');
- if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
- begin
- OutStr(ObjUnitName);
- OutStr('/');
- end;
- OutStr(ObjClassName);
- if (b and 2) <> 0 then
- OutStr('[' + IntToStr(ChildPos) + ']');
- OutLn('');
- ReadPropList(indent + ' ');
- while Input.ReadByte <> 0 do begin
- Input.Seek(-1, soFromCurrent);
- ReadObject(indent + ' ');
- end;
- OutLn(indent + 'end');
- end;
- var
- Signature: DWord;
- begin
- Signature:=Input.ReadDWord;
- if Signature = DWord(unaligned(FilerSignature1)) then
- Version:=TBinaryObjectReader.TBOVersion.boVersion1
- else if Signature = DWord(unaligned(FilerSignature)) then
- Version:=TBinaryObjectReader.TBOVersion.boVersion0
- else
- raise EReadError.Create('Illegal stream image' {###SInvalidImage});
- ReadObject('');
- end;
- procedure ObjectBinaryToText(Input, Output: TStream);
- begin
- ObjectBinaryToText(Input,Output,oteDFM);
- end;
- procedure ObjectTextToBinary(Input, Output: TStream);
- var
- parser: TParser;
- Version: TBinaryObjectReader.TBOVersion;
- StartPos: Int64;
- procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- w:=NtoLE(w);
- Output.WriteWord(w);
- end;
- procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- lw:=NtoLE(lw);
- Output.WriteDWord(lw);
- end;
- procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
- begin
- qw:=NtoLE(qw);
- Output.WriteBuffer(qw,sizeof(qword));
- end;
- {$ifndef FPUNONE}
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- procedure DoubleToExtended(d : double; e : pointer);
- var mant : qword;
- exp : smallint;
- sign : boolean;
- begin
- mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
- exp :=(qword(d) shr 52) and $7FF;
- sign:=(qword(d) and $8000000000000000)<>0;
- case exp of
- 0 : begin
- if mant<>0 then //denormalized value: hidden bit is 0. normalize it
- begin
- exp:=16383-1022;
- while (mant and $8000000000000000)=0 do
- begin
- dec(exp);
- mant:=mant shl 1;
- end;
- dec(exp); //don't shift, most significant bit is not hidden in extended
- end;
- end;
- 2047 : exp:=$7FFF //either infinity or NaN
- else
- begin
- inc(exp,16383-1023);
- mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
- end;
- end;
- if sign then exp:=exp or $8000;
- mant:=NtoLE(mant);
- exp:=NtoLE(word(exp));
- move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
- move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
- end;
- {$ENDIF}
- procedure WriteExtended(const e : extended);
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- var ext : array[0..9] of byte;
- {$ENDIF}
- begin
- {$IFNDEF FPC_HAS_TYPE_EXTENDED}
- DoubleToExtended(e,@(ext[0]));
- Output.WriteBuffer(ext[0],10);
- {$ELSE}
- Output.WriteBuffer(e,sizeof(e));
- {$ENDIF}
- end;
- {$endif}
- procedure WriteSString(const s: RawByteString);
- var size : byte;
- begin
- if length(s)>255 then size:=255
- else size:=length(s);
- Output.WriteByte(size);
- if Length(s) > 0 then
- Output.WriteBuffer(s[1], size);
- end;
- procedure WriteLString(Const s: RawByteString);
- begin
- WriteDWord(Length(s));
- if Length(s) > 0 then
- Output.WriteBuffer(s[1], Length(s));
- end;
- procedure WriteSorLString(Const s: String);
- begin
- if length(s)<256 then
- begin
- Output.WriteByte(Ord(vaString));
- WriteSString(s);
- end else begin
- Output.WriteByte(Ord(vaLString));
- WriteSString(s);
- end;
- end;
- procedure WriteWString(Const s: WideString);
- var len : longword;
- {$IFDEF ENDIAN_BIG}
- i : integer;
- ws : widestring;
- {$ENDIF}
- begin
- len:=Length(s);
- WriteDWord(len);
- if len > 0 then
- begin
- {$IFDEF ENDIAN_BIG}
- setlength(ws,len);
- for i:=1 to len do
- ws[i]:=widechar(SwapEndian(word(s[i])));
- Output.WriteBuffer(ws[1], len*sizeof(widechar));
- {$ELSE}
- Output.WriteBuffer(s[1], len*sizeof(widechar));
- {$ENDIF}
- end;
- end;
- procedure WriteInteger(value: Int64);
- begin
- if (value >= -128) and (value <= 127) then begin
- Output.WriteByte(Ord(vaInt8));
- Output.WriteByte(byte(value));
- end else if (value >= -32768) and (value <= 32767) then begin
- Output.WriteByte(Ord(vaInt16));
- WriteWord(word(value));
- end else if (value >= -2147483648) and (value <= 2147483647) then begin
- Output.WriteByte(Ord(vaInt32));
- WriteDWord(longword(value));
- end else begin
- Output.WriteByte(ord(vaInt64));
- WriteQWord(qword(value));
- end;
- end;
- procedure ProcessWideString(const left : widestring);
- var ws : widestring;
- begin
- ws:=left+parser.TokenWideString;
- while parser.NextToken = '+' do
- begin
- parser.NextToken; // Get next string fragment
- if not (parser.Token in [toString,toWString]) then
- parser.CheckToken(toWString);
- ws:=ws+parser.TokenWideString;
- end;
- Output.WriteByte(Ord(vaWstring));
- WriteWString(ws);
- end;
- procedure ProcessProperty; forward;
- procedure ProcessValue;
- var
- {$ifndef FPUNONE}
- flt: Extended;
- {$endif}
- s: RawByteString;
- stream: TMemoryStream;
- begin
- case parser.Token of
- toInteger:
- begin
- WriteInteger(parser.TokenInt);
- parser.NextToken;
- end;
- {$ifndef FPUNONE}
- toFloat:
- begin
- Output.WriteByte(Ord(vaExtended));
- flt := Parser.TokenFloat;
- WriteExtended(flt);
- parser.NextToken;
- end;
- {$endif}
- toString:
- begin
- s := parser.TokenString;
- while parser.NextToken = '+' do
- begin
- parser.NextToken; // Get next string fragment
- case parser.Token of
- toString : s:=s+parser.TokenString;
- toWString : begin
- ProcessWideString(WideString(s));
- exit;
- end
- else parser.CheckToken(toString);
- end;
- end;
- if (length(S)>255) then
- begin
- Output.WriteByte(Ord(vaLString));
- WriteLString(S);
- end
- else
- begin
- Output.WriteByte(Ord(vaString));
- WriteSString(s);
- end;
- end;
- toWString:
- ProcessWideString('');
- toSymbol:
- begin
- if CompareText(parser.TokenString, 'True') = 0 then
- Output.WriteByte(Ord(vaTrue))
- else if CompareText(parser.TokenString, 'False') = 0 then
- Output.WriteByte(Ord(vaFalse))
- else if CompareText(parser.TokenString, 'nil') = 0 then
- Output.WriteByte(Ord(vaNil))
- else
- begin
- Output.WriteByte(Ord(vaIdent));
- WriteSString(parser.TokenComponentIdent);
- end;
- Parser.NextToken;
- end;
- // Set
- '[':
- begin
- parser.NextToken;
- Output.WriteByte(Ord(vaSet));
- if parser.Token <> ']' then
- while True do
- begin
- parser.CheckToken(toSymbol);
- WriteSString(parser.TokenString);
- parser.NextToken;
- if parser.Token = ']' then
- break;
- parser.CheckToken(',');
- parser.NextToken;
- end;
- Output.WriteByte(0);
- parser.NextToken;
- end;
- // List
- '(':
- begin
- parser.NextToken;
- Output.WriteByte(Ord(vaList));
- while parser.Token <> ')' do
- ProcessValue;
- Output.WriteByte(0);
- parser.NextToken;
- end;
- // Collection
- '<':
- begin
- parser.NextToken;
- Output.WriteByte(Ord(vaCollection));
- while parser.Token <> '>' do
- begin
- parser.CheckTokenSymbol('item');
- parser.NextToken;
- // ConvertOrder
- Output.WriteByte(Ord(vaList));
- while not parser.TokenSymbolIs('end') do
- ProcessProperty;
- parser.NextToken; // Skip 'end'
- Output.WriteByte(0);
- end;
- Output.WriteByte(0);
- parser.NextToken;
- end;
- // Binary data
- '{':
- begin
- Output.WriteByte(Ord(vaBinary));
- stream := TMemoryStream.Create;
- try
- parser.HexToBinary(stream);
- WriteDWord(stream.Size);
- Output.WriteBuffer(Stream.Memory^, stream.Size);
- finally
- stream.Free;
- end;
- parser.NextToken;
- end;
- else
- parser.Error(SInvalidProperty);
- end;
- end;
- procedure ProcessProperty;
- var
- name: RawByteString;
- begin
- // Get name of property
- parser.CheckToken(toSymbol);
- name := parser.TokenString;
- while True do begin
- parser.NextToken;
- if parser.Token <> '.' then break;
- parser.NextToken;
- parser.CheckToken(toSymbol);
- name := name + '.' + parser.TokenString;
- end;
- WriteSString(name);
- parser.CheckToken('=');
- parser.NextToken;
- ProcessValue;
- end;
- procedure ProcessObject(Root: boolean);
- var
- Flags: Byte;
- ObjectName, ObjUnitName, ObjClassName: RawByteString;
- ChildPos: Integer;
- begin
- if parser.TokenSymbolIs('OBJECT') then
- Flags :=0 { IsInherited := False }
- else begin
- if parser.TokenSymbolIs('INHERITED') then
- Flags := 1 { IsInherited := True; }
- else begin
- parser.CheckTokenSymbol('INLINE');
- Flags := 4;
- end;
- end;
- parser.NextToken;
- parser.CheckToken(toSymbol);
- ObjectName := '';
- ObjUnitName := '';
- ObjClassName := parser.TokenString;
- parser.NextToken;
- if parser.Token = '/' then begin
- ObjUnitName := ObjClassName;
- parser.NextToken;
- parser.CheckToken(toSymbol);
- ObjClassName := parser.TokenString;
- parser.NextToken;
- end else if parser.Token = ':' then begin
- parser.NextToken;
- parser.CheckToken(toSymbol);
- ObjectName := ObjClassName;
- ObjClassName := parser.TokenString;
- parser.NextToken;
- if parser.Token = '/' then begin
- ObjUnitName := ObjClassName;
- parser.NextToken;
- parser.CheckToken(toSymbol);
- ObjClassName := parser.TokenString;
- parser.NextToken;
- end;
- if parser.Token = '[' then begin
- parser.NextToken;
- ChildPos := parser.TokenInt;
- parser.NextToken;
- parser.CheckToken(']');
- parser.NextToken;
- Flags := Flags or 2;
- end;
- end;
- if Root then
- begin
- if (ObjUnitName<>'') then
- Version:=TBinaryObjectReader.TBOVersion.boVersion1;
- if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
- Output.WriteBuffer(FilerSignature1[1], length(FilerSignature1))
- else
- Output.WriteBuffer(FilerSignature[1], length(FilerSignature));
- end;
- if Flags <> 0 then begin
- Output.WriteByte($f0 or Flags);
- if (Flags and 2) <> 0 then
- WriteInteger(ChildPos);
- end;
- if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
- WriteSorLString(ObjUnitName+TBinaryObjectReader.UnitnameSeparator+ObjClassName)
- else
- WriteSString(ObjClassName);
- WriteSString(ObjectName);
- // Convert property list
- while not (parser.TokenSymbolIs('END') or
- parser.TokenSymbolIs('OBJECT') or
- parser.TokenSymbolIs('INHERITED') or
- parser.TokenSymbolIs('INLINE')) do
- ProcessProperty;
- Output.WriteByte(0); // Terminate property list
- // Convert child objects
- while not parser.TokenSymbolIs('END') do ProcessObject(false);
- parser.NextToken; // Skip end token
- Output.WriteByte(0); // Terminate property list
- end;
- const
- signature: PAnsiChar = 'TPF0';
- begin
- Version:=TBinaryObjectReader.TBOVersion.boVersion0;
- parser := TParser.Create(Input);
- try
- StartPos:=Output.Position;
- ProcessObject(true);
- finally
- parser.Free;
- end;
- end;
- procedure ObjectResourceToText(Input, Output: TStream);
- begin
- Input.ReadResHeader;
- ObjectBinaryToText(Input, Output);
- end;
- procedure ObjectTextToResource(Input, Output: TStream);
- var
- StartPos, FixupInfo: LongInt;
- parser: TParser;
- name: String;
- begin
- // Get form type name
- StartPos := Input.Position;
- parser := TParser.Create(Input);
- try
- if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
- parser.NextToken;
- parser.CheckToken(toSymbol);
- parser.NextToken;
- parser.CheckToken(':');
- parser.NextToken;
- parser.CheckToken(toSymbol);
- name := parser.TokenString;
- finally
- parser.Free;
- Input.Position := StartPos;
- end;
- name := UpperCase(name);
- Output.WriteResourceHeader(name,FixupInfo); // Write resource header
- ObjectTextToBinary(Input, Output); // Convert the stuff!
- Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
- end;
- function TestStreamFormat(const Stream: TStream): TStreamOriginalFormat;
- const
- StartChars = [#9, #10, #13, #11, 'o', 'O','i','I',' '];
- var
- aPos: Int64;
- Sig: Packed Array[0..3] of byte;
- IntSig : Longint absolute sig;
-
- begin
- Result:=sofUnknown;
- aPos:=Stream.Position;
- IntSig:=0;
- Stream.Read(Sig,SizeOf(Sig));
- Stream.Seek(aPos,soBeginning);
- if (Sig[0]=$FF) or (IntSig=LongInt(FilerSignature)) or (IntSig=LongInt(FilerSignature1)) or (IntSig=0) then
- Result:=sofBinary
- else if (AnsiChar(Sig[0]) in StartChars) then
- Result:=sofText
- else if (Sig[0]=$EF) and (Sig[1]=$BB) and (Sig[2]=$BF) then
- Result:=sofUTF8Text;
- end;
- { Utility routines }
- Function IfThen(AValue: Boolean; const ATrue: TStringList; const AFalse: TStringList = nil): TStringList; overload;
- begin
- if avalue then
- result:=atrue
- else
- result:=afalse;
- end;
- function LineStart(Buffer, BufPos: PAnsiChar): PAnsiChar;
- begin
- Result := BufPos;
- while Result > Buffer do begin
- Dec(Result);
- if Result[0] = #10 then break;
- end;
- end;
- procedure CommonInit;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- SynchronizeTimeoutEvent:=RtlEventCreate;
- InterlockedIncrement(ThreadQueueLockCounter);
- InitCriticalSection(ThreadQueueLock);
- MainThreadID:=GetCurrentThreadID;
- {$else}
- MainThreadID:=0{GetCurrentThreadID};
- {$endif}
- ExternalThreads := TThreadList.Create;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- InitCriticalsection(ResolveSection);
- TThread.FProcessorCount := CPUCount;
- {$else}
- TThread.FProcessorCount := 1{CPUCount};
- {$endif}
- InitHandlerList:=Nil;
- FindGlobalComponentList:=nil;
- IntConstList := TThreadList.Create;
- ClassList := TThreadList.Create;
- ClassAliasList := nil;
- { on unix this maps to a simple rw synchornizer }
- GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
- RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
- end;
- procedure CommonCleanup;
- var
- i: Integer;
- tmpentry: TThread.PThreadQueueEntry;
- begin
- {$ifdef FPC_HAS_FEATURE_THREADING}
- GlobalNameSpace.BeginWrite;
- {$endif}
- with IntConstList.LockList do
- try
- for i := 0 to Count - 1 do
- TIntConst(Items[I]).Free;
- finally
- IntConstList.UnlockList;
- end;
- IntConstList.Free;
- ClassList.Free;
- ClassAliasList.Free;
- RemoveFixupReferences(nil, '');
- {$ifdef FPC_HAS_FEATURE_THREADING}
- DoneCriticalsection(ResolveSection);
- {$endif}
- GlobalLists.Free;
- ComponentPages.Free;
- FreeAndNil(NeedResolving);
- {$ifdef FPC_HAS_FEATURE_THREADING}
- GlobalNameSpace.EndWrite;
- {$endif}
- { GlobalNameSpace is an interface so this is enough }
- GlobalNameSpace:=nil;
- if (InitHandlerList<>Nil) then
- for i := 0 to InitHandlerList.Count - 1 do
- TInitHandler(InitHandlerList.Items[I]).Free;
- InitHandlerList.Free;
- InitHandlerList:=Nil;
- FindGlobalComponentList.Free;
- FindGlobalComponentList:=nil;
- ExternalThreadsCleanup:=True;
- with ExternalThreads.LockList do
- try
- for i := 0 to Count - 1 do
- TThread(Items[i]).Free;
- finally
- ExternalThreads.UnlockList;
- end;
- FreeAndNil(ExternalThreads);
- {$ifdef FPC_HAS_FEATURE_THREADING}
- RtlEventDestroy(SynchronizeTimeoutEvent);
- try
- System.EnterCriticalSection(ThreadQueueLock);
- {$endif}
- { clean up the queue, but keep in mind that the entries used for Synchronize
- are owned by the corresponding TThread }
- while Assigned(ThreadQueueHead) do begin
- tmpentry := ThreadQueueHead;
- ThreadQueueHead := tmpentry^.Next;
- if not Assigned(tmpentry^.SyncEvent) then
- Dispose(tmpentry);
- end;
- { We also need to reset ThreadQueueTail }
- ThreadQueueTail := nil;
- {$ifdef FPC_HAS_FEATURE_THREADING}
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- if InterlockedDecrement(ThreadQueueLockCounter)=0 then
- DoneCriticalSection(ThreadQueueLock);
- {$endif}
- end;
- { TFiler implementation }
- {$i filer.inc}
- { TReader implementation }
- {$i reader.inc}
- { TWriter implementations }
- {$i writer.inc}
- {$i twriter.inc}
- constructor ComponentPlatformsAttribute.Create(const aPlatforms: TPlatformIds);
- begin
- FPlatForms:=aPlatForms;
- end;
|