123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217 |
- {
- 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}
- { 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;
- { this list holds all instances of external threads that need to be freed at
- the end of the program }
- ExternalThreads: TThreadList;
- { this must be a global var, otherwise unwanted optimizations might happen in
- TThread.SpinWait() }
- SpinWaitDummy: LongWord;
- threadvar
- { 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 = class(TThread)
- private
- fProc: TProcedure;
- protected
- procedure Execute; override;
- public
- { as in TThread aProc needs to be changed to TProc once closures are
- supported }
- constructor Create(aProc: TProcedure);
- end;
- procedure TAnonymousThread.Execute;
- begin
- 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;
- 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;
- end;
- procedure TExternalThread.Execute;
- begin
- { empty }
- end;
- constructor TExternalThread.Create;
- begin
- FExternalThread := True;
- { the parameter is unimportant if FExternalThread is True }
- inherited Create(False);
- 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;
- EndThread(Result);
- end;
- { system-dependent code }
- {$i tthread.inc}
- constructor TThread.Create(CreateSuspended: Boolean;
- const StackSize: SizeUInt);
- begin
- inherited Create;
- if FExternalThread then
- FThreadID := GetCurrentThreadID
- else
- SysCreate(CreateSuspended, StackSize);
- end;
- destructor TThread.Destroy;
- begin
- if not FExternalThread then begin
- SysDestroy;
- if FHandle <> TThreadID(0) then
- CloseThread(FHandle);
- end;
- RemoveQueuedEvents(Self);
- DoneSynchronizeEvent;
- { 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.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)}
- if not FExternalThread and not FInitialSuspended then
- Resume;
- {$endif}
- end;
- procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
- begin
- if Assigned(aEntry^.Method) then
- aEntry^.Method()
- // enable once closures are supported
- {else
- aEntry^.ThreadProc();}
- end;
- procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry);
- begin
- { do we really need a synchronized call? }
- if GetCurrentThreadID = MainThreadID then begin
- ExecuteThreadQueueEntry(aEntry);
- if not Assigned(aEntry^.SyncEvent) then
- Dispose(aEntry);
- end else begin
- 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(aEntry^.Thread);
- { is this a Synchronize or Queue entry? }
- if Assigned(aEntry^.SyncEvent) then begin
- RtlEventWaitFor(aEntry^.SyncEvent);
- if Assigned(aEntry^.Exception) then
- raise aEntry^.Exception;
- end;
- end;
- end;
- procedure TThread.InitSynchronizeEvent;
- begin
- if Assigned(FSynchronizeEntry) then
- Exit;
- New(FSynchronizeEntry);
- FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
- FSynchronizeEntry^.Thread := Self;
- FSynchronizeEntry^.SyncEvent := RtlEventCreate;
- end;
- procedure TThread.DoneSynchronizeEvent;
- begin
- if not Assigned(FSynchronizeEntry) then
- Exit;
- RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
- Dispose(FSynchronizeEntry);
- FSynchronizeEntry := Nil;
- end;
- class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
- begin
- { ensure that we have a TThread instance }
- if not Assigned(AThread) then
- AThread := CurrentThread;
- { the Synchronize event is instantiated on demand }
- AThread.InitSynchronizeEvent;
- AThread.FSynchronizeEntry^.Exception := Nil;
- AThread.FSynchronizeEntry^.Method := AMethod;
- ThreadQueueAppend(AThread.FSynchronizeEntry);
- AThread.FSynchronizeEntry^.Method := Nil;
- AThread.FSynchronizeEntry^.Next := Nil;
- end;
- procedure TThread.Synchronize(AMethod: TThreadMethod);
- begin
- TThread.Synchronize(self,AMethod);
- end;
- Function PopThreadQueueHead : TThread.PThreadQueueEntry;
- begin
- Result:=ThreadQueueHead;
- if (Result<>Nil) then
- begin
- System.EnterCriticalSection(ThreadQueueLock);
- try
- Result:=ThreadQueueHead;
- if Result<>Nil then
- ThreadQueueHead:=ThreadQueueHead^.Next;
- if Not Assigned(ThreadQueueHead) then
- ThreadQueueTail := Nil;
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- end;
- end;
- function CheckSynchronize(timeout : longint=0) : boolean;
- { assumes being called from GUI thread }
- var
- ExceptObj: Exception;
- tmpentry: TThread.PThreadQueueEntry;
- begin
- result:=false;
- { first sanity check }
- if Not IsMultiThread then
- Exit
- { 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
- { step 2: execute the method }
- exceptobj := Nil;
- try
- ExecuteThreadQueueEntry(tmpentry);
- except
- exceptobj := Exception(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;
- 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;
- class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
- var
- queueentry: PThreadQueueEntry;
- begin
- { ensure that we have a valid TThread instance }
- if not Assigned(aThread) then
- aThread := CurrentThread;
- New(queueentry);
- FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
- queueentry^.Thread := aThread;
- queueentry^.Method := aMethod;
- { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
- ThreadQueueAppend(queueentry);
- end;
- class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
- var
- entry, tmpentry, lastentry: PThreadQueueEntry;
- begin
- { anything to do at all? }
- if not Assigned(aThread) or not Assigned(aMethod) then
- Exit;
- System.EnterCriticalSection(ThreadQueueLock);
- try
- lastentry := Nil;
- entry := ThreadQueueHead;
- while Assigned(entry) do begin
- { first check for the thread }
- if Assigned(aThread) and (entry^.Thread <> aThread) then begin
- lastentry := entry;
- entry := entry^.Next;
- Continue;
- end;
- { then check for the method }
- if entry^.Method <> aMethod then begin
- lastentry := entry;
- entry := entry^.Next;
- Continue;
- end;
- { skip entries added by Synchronize }
- if Assigned(entry^.SyncEvent) then begin
- lastentry := entry;
- entry := entry^.Next;
- Continue;
- end;
- { 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;
- finally
- System.LeaveCriticalSection(ThreadQueueLock);
- end;
- 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 THREADNAME_IS_ANSISTRING}
- { the platform implements the AnsiString variant and the UnicodeString variant
- simply calls the AnsiString variant }
- class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
- begin
- NameThreadForDebugging(AnsiString(aThreadName), aThreadID);
- end;
- {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
- class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
- begin
- { empty }
- end;
- {$endif}
- {$else}
- {$ifndef HAS_TTHREAD_NAMETHREADFORDEBUGGING}
- { the platform implements the UnicodeString variant and the AnsiString variant
- simply calls the UnicodeString variant }
- class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
- begin
- { empty }
- end;
- {$endif}
- class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
- begin
- NameThreadForDebugging(UnicodeString(aThreadName), aThreadID);
- end;
- {$endif}
- class procedure TThread.Yield;
- begin
- ThreadSwitch;
- end;
- class procedure TThread.Sleep(aMilliseconds: Cardinal);
- begin
- 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 procedure TThread.GetSystemTimes(out aSystemTimes: TSystemTimes);
- begin
- { by default we just return a zeroed out record }
- FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
- end;
- {$endif}
- class function TThread.GetTickCount: LongWord;
- begin
- Result := SysUtils.GetTickCount;
- end;
- class function TThread.GetTickCount64: QWord;
- begin
- Result := SysUtils.GetTickCount64;
- 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: PChar; Strings: TStrings; AddEmptyStrings : Boolean = False): Integer;
- var
- b, c : pchar;
- 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(char));
- Strings.Add (s);
- end;
- inc (result);
- end;
- end;
- var
- quoted : char;
- 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; var 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;
- 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;
- begin
- result:=true;
- if Inst=0 then
- Inst:=HInstance;
- try
- ResStream:=TResourceStream.Create(Inst,res,RT_RCDATA);
- try
- Component:=ResStream.ReadComponent(Component);
- finally
- ResStream.Free;
- 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
- GlobalNameSpace.BeginWrite;
- try
- result:=doinit(Instance.ClassType);
- finally
- GlobalNameSpace.EndWrite;
- end;
- 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;
- threadvar
- 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(pchar(P)^);
- inc(pchar(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(PChar(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(PChar(P)^) and %00111111);
- Inc(P);
- end else if (Ord(Result) and %11110000) = %11100000 then begin
- Result := ((Result and %00011111) shl 12)
- or ((ord(PChar(P)^) and %00111111) shl 6)
- or (ord((PChar(P)+1)^) and %00111111);
- Inc(P,2);
- end else begin
- Result := ((ord(Result) and %00011111) shl 18)
- or ((ord(PChar(P)^) and %00111111) shl 12)
- or ((ord((PChar(P)+1)^) and %00111111) shl 6)
- or (ord((PChar(P)+2)^) and %00111111);
- Inc(P,3);
- end;
- end;
- procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
- procedure OutStr(s: String);
- begin
- if Length(s) > 0 then
- Output.Write(s[1], Length(s));
- end;
- procedure OutLn(s: String);
- begin
- OutStr(s + LineEnding);
- end;
- procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
- UseBytes: boolean = false);
- var
- res, NewStr: String;
- 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 char
- 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 := char(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: String);
- begin
- OutChars(Pointer(S),PChar(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: String);
- begin
- if Encoding=oteLFM then
- OutChars(Pointer(S),PChar(S)+Length(S),@CharToOrd)
- else
- OutChars(Pointer(S),PChar(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: String;
- var
- len: Byte;
- begin
- len := Input.ReadByte;
- SetLength(Result, len);
- if (len > 0) then
- Input.ReadBuffer(Result[1], len);
- end;
- function ReadLStr: String;
- 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: String);
- procedure ProcessValue(ValueType: TValueType; Indent: String);
- procedure ProcessBinary;
- var
- ToDo, DoNow, i: LongInt;
- lbuf: array[0..31] of Byte;
- s: String;
- 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: String;
- { 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: String);
- var
- b: Byte;
- ObjClassName, ObjName: String;
- ChildPos: LongInt;
- 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;
- 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 + ': ');
- 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;
- type
- PLongWord = ^LongWord;
- const
- signature: PChar = 'TPF0';
- begin
- if Input.ReadDWord <> PLongWord(Pointer(signature))^ then
- 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;
- 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(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 WriteString(s: String);
- 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: String);
- begin
- WriteDWord(Length(s));
- if Length(s) > 0 then
- Output.WriteBuffer(s[1], Length(s));
- 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: String;
- 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(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));
- WriteString(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));
- WriteString(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);
- WriteString(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: String;
- 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;
- WriteString(name);
- parser.CheckToken('=');
- parser.NextToken;
- ProcessValue;
- end;
- procedure ProcessObject;
- var
- Flags: Byte;
- ObjectName, ObjectType: String;
- 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 := '';
- ObjectType := parser.TokenString;
- parser.NextToken;
- if parser.Token = ':' then begin
- parser.NextToken;
- parser.CheckToken(toSymbol);
- ObjectName := ObjectType;
- ObjectType := parser.TokenString;
- parser.NextToken;
- if parser.Token = '[' then begin
- parser.NextToken;
- ChildPos := parser.TokenInt;
- parser.NextToken;
- parser.CheckToken(']');
- parser.NextToken;
- Flags := Flags or 2;
- end;
- end;
- if Flags <> 0 then begin
- Output.WriteByte($f0 or Flags);
- if (Flags and 2) <> 0 then
- WriteInteger(ChildPos);
- end;
- WriteString(ObjectType);
- WriteString(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;
- parser.NextToken; // Skip end token
- Output.WriteByte(0); // Terminate property list
- end;
- const
- signature: PChar = 'TPF0';
- begin
- parser := TParser.Create(Input);
- try
- Output.WriteBuffer(signature[0], 4);
- ProcessObject;
- 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;
- { Utility routines }
- function LineStart(Buffer, BufPos: PChar): PChar;
- begin
- Result := BufPos;
- while Result > Buffer do begin
- Dec(Result);
- if Result[0] = #10 then break;
- end;
- end;
- procedure CommonInit;
- begin
- SynchronizeTimeoutEvent:=RtlEventCreate;
- InitCriticalSection(ThreadQueueLock);
- MainThreadID:=GetCurrentThreadID;
- ExternalThreads := TThreadList.Create;
- TThread.FProcessorCount := CPUCount;
- InitCriticalsection(ResolveSection);
- InitHandlerList:=Nil;
- FindGlobalComponentList:=nil;
- IntConstList := TThreadList.Create;
- ClassList := TThreadList.Create;
- ClassAliasList := TStringList.Create;
- { 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
- GlobalNameSpace.BeginWrite;
- 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, '');
- DoneCriticalsection(ResolveSection);
- GlobalLists.Free;
- ComponentPages.Free;
- FreeAndNil(NeedResolving);
- { 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;
- with ExternalThreads.LockList do
- try
- for i := 0 to Count - 1 do
- TThread(Items[i]).Free;
- finally
- ExternalThreads.UnlockList;
- end;
- FreeAndNil(ExternalThreads);
- RtlEventDestroy(SynchronizeTimeoutEvent);
- { 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;
- DoneCriticalSection(ThreadQueueLock);
- end;
- { TFiler implementation }
- {$i filer.inc}
- { TReader implementation }
- {$i reader.inc}
- { TWriter implementations }
- {$i writer.inc}
- {$i twriter.inc}
|