classes.inc 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896
  1. {%MainUnit classes.pp}
  2. {
  3. This file is part of the Free Component Library (FCL)
  4. Copyright (c) 1999-2000 by Michael Van Canneyt and Florian Klaempfl
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {**********************************************************************
  12. * Class implementations are in separate files. *
  13. **********************************************************************}
  14. type
  15. {$ifdef CPU16}
  16. TFilerFlagsInt = Byte;
  17. {$else CPU16}
  18. TFilerFlagsInt = LongInt;
  19. {$endif CPU16}
  20. var
  21. ClassList : TThreadlist;
  22. ClassAliasList : TStringList;
  23. {
  24. Include all message strings
  25. Add a language with IFDEF LANG_NAME
  26. just befor the final ELSE. This way English will always be the default.
  27. }
  28. {$IFDEF LANG_GERMAN}
  29. {$i constsg.inc}
  30. {$ELSE}
  31. {$IFDEF LANG_SPANISH}
  32. {$i constss.inc}
  33. {$ENDIF}
  34. {$ENDIF}
  35. { Utility routines }
  36. {$i util.inc}
  37. { TBits implementation }
  38. {$i bits.inc}
  39. { All streams implementations: }
  40. { Tstreams THandleStream TFileStream TResourcseStreams TStringStream }
  41. { TCustomMemoryStream TMemoryStream }
  42. {$i streams.inc}
  43. { TParser implementation}
  44. {$i parser.inc}
  45. { TCollection and TCollectionItem implementations }
  46. {$i collect.inc}
  47. { TList and TThreadList implementations }
  48. {$i lists.inc}
  49. { TStrings and TStringList implementations }
  50. {$i stringl.inc}
  51. { ObservableMemberAttribute, TObservers and TObserverMapping}
  52. {$i observer.inc}
  53. { TThread implementation }
  54. { system independend threading code }
  55. var
  56. { event executed by SychronizeInternal to wake main thread if it sleeps in
  57. CheckSynchronize }
  58. SynchronizeTimeoutEvent: PRtlEvent;
  59. { the head of the queue containing the entries to be Synchronized - Nil if the
  60. queue is empty }
  61. ThreadQueueHead: TThread.PThreadQueueEntry;
  62. { the tail of the queue containing the entries to be Synchronized - Nil if the
  63. queue is empty }
  64. ThreadQueueTail: TThread.PThreadQueueEntry;
  65. { used for serialized access to the queue }
  66. ThreadQueueLock: TRtlCriticalSection;
  67. { usage counter for ThreadQueueLock }
  68. ThreadQueueLockCounter : longint;
  69. { this list holds all instances of external threads that need to be freed at
  70. the end of the program }
  71. ExternalThreads: TThreadList;
  72. { this list signals that the ExternalThreads list is cleared and thus the
  73. thread instances don't need to remove themselves }
  74. ExternalThreadsCleanup: Boolean = False;
  75. { this must be a global var, otherwise unwanted optimizations might happen in
  76. TThread.SpinWait() }
  77. SpinWaitDummy: LongWord;
  78. {$ifdef FPC_HAS_FEATURE_THREADING}
  79. threadvar
  80. {$else}
  81. var
  82. {$endif}
  83. { the instance of the current thread; in case of an external thread this is
  84. Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
  85. that threadvars are initialized with 0!) }
  86. CurrentThreadVar: TThread;
  87. type
  88. { this type is used if a thread is created using
  89. TThread.CreateAnonymousThread }
  90. { TAnonymousThread }
  91. TAnonymousThread = class(TThread)
  92. private
  93. fProc: TProcedure;
  94. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  95. fAnonProc : TThreadProcedure;
  96. {$ENDIF}
  97. FMethod : TThreadMethod;
  98. protected
  99. procedure Execute; override;
  100. public
  101. constructor Create(aProc: TProcedure);
  102. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  103. constructor Create(aProc: TThreadProcedure);
  104. {$ENDIF}
  105. constructor Create(aProc: TThreadMethod);
  106. end;
  107. procedure TAnonymousThread.Execute;
  108. begin
  109. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  110. if assigned(fAnonProc) then
  111. fAnonProc()
  112. else
  113. {$ENDIF}
  114. if assigned(FMethod) then
  115. FMethod()
  116. else
  117. fProc();
  118. end;
  119. constructor TAnonymousThread.Create(aProc: TProcedure);
  120. begin
  121. { an anonymous thread is created suspended and with FreeOnTerminate set }
  122. inherited Create(True);
  123. FreeOnTerminate := True;
  124. fProc := aProc;
  125. end;
  126. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  127. constructor TAnonymousThread.Create(aProc: TThreadProcedure);
  128. begin
  129. inherited Create(True);
  130. FreeOnTerminate := True;
  131. fAnonProc := aProc;
  132. end;
  133. {$ENDIF}
  134. constructor TAnonymousThread.Create(aProc: TThreadMethod);
  135. begin
  136. inherited Create(True);
  137. FreeOnTerminate := True;
  138. FMethod := aProc;
  139. end;
  140. type
  141. { this type is used by TThread.GetCurrentThread if the thread does not yet
  142. have a value in CurrentThreadVar (Note: the main thread is also created as
  143. a TExternalThread) }
  144. TExternalThread = class(TThread)
  145. protected
  146. { dummy method to remove the warning }
  147. procedure Execute; override;
  148. public
  149. constructor Create;
  150. destructor Destroy; override;
  151. end;
  152. procedure TExternalThread.Execute;
  153. begin
  154. { empty }
  155. end;
  156. constructor TExternalThread.Create;
  157. begin
  158. FExternalThread := True;
  159. { the parameter is unimportant if FExternalThread is True }
  160. inherited Create(False);
  161. with ExternalThreads.LockList do
  162. try
  163. Add(Self);
  164. finally
  165. ExternalThreads.UnlockList;
  166. end;
  167. end;
  168. destructor TExternalThread.Destroy;
  169. begin
  170. inherited;
  171. if not ExternalThreadsCleanup then
  172. with ExternalThreads.LockList do
  173. try
  174. Extract(Self);
  175. finally
  176. ExternalThreads.UnlockList;
  177. end;
  178. end;
  179. function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
  180. var
  181. FreeThread: Boolean;
  182. Thread: TThread absolute ThreadObjPtr;
  183. begin
  184. { if Suspend checks FSuspended before doing anything, make sure it }
  185. { knows we're currently not suspended (this flag may have been set }
  186. { to true if CreateSuspended was true) }
  187. // Thread.FSuspended:=false;
  188. // wait until AfterConstruction has been called, so we cannot
  189. // free ourselves before TThread.Create has finished
  190. // (since that one may check our VTM in case of $R+, and
  191. // will call the AfterConstruction method in all cases)
  192. // Thread.Suspend;
  193. try
  194. { The thread may be already terminated at this point, e.g. if it was intially
  195. suspended, or if it wasn't ever scheduled for execution for whatever reason.
  196. So bypass user code if terminated. }
  197. if not Thread.Terminated then begin
  198. CurrentThreadVar := Thread;
  199. Thread.Execute;
  200. end;
  201. except
  202. Thread.FFatalException := TObject(AcquireExceptionObject);
  203. end;
  204. FreeThread := Thread.FFreeOnTerminate;
  205. Result := Thread.FReturnValue;
  206. Thread.FFinished := True;
  207. Thread.DoTerminate;
  208. if FreeThread then
  209. Thread.Free;
  210. {$ifdef FPC_HAS_FEATURE_THREADING}
  211. EndThread(Result);
  212. {$endif}
  213. end;
  214. { system-dependent code }
  215. {$i tthread.inc}
  216. constructor TThread.Create(CreateSuspended: Boolean;
  217. const StackSize: SizeUInt);
  218. begin
  219. inherited Create;
  220. {$ifdef FPC_HAS_FEATURE_THREADING}
  221. InterlockedIncrement(ThreadQueueLockCounter);
  222. {$endif}
  223. if FExternalThread then
  224. {$ifdef FPC_HAS_FEATURE_THREADING}
  225. FThreadID := GetCurrentThreadID
  226. {$else}
  227. FThreadID := 0{GetCurrentThreadID}
  228. {$endif}
  229. else
  230. SysCreate(CreateSuspended, StackSize);
  231. end;
  232. destructor TThread.Destroy;
  233. begin
  234. if not FExternalThread then begin
  235. SysDestroy;
  236. {$ifdef FPC_HAS_FEATURE_THREADING}
  237. if FHandle <> TThreadID(0) then
  238. CloseThread(FHandle);
  239. {$endif}
  240. end;
  241. RemoveQueuedEvents(Self);
  242. DoneSynchronizeEvent;
  243. {$ifdef FPC_HAS_FEATURE_THREADING}
  244. if InterlockedDecrement(ThreadQueueLockCounter)=0 then
  245. DoneCriticalSection(ThreadQueueLock);
  246. {$endif}
  247. { set CurrentThreadVar to Nil? }
  248. inherited Destroy;
  249. end;
  250. procedure TThread.Start;
  251. begin
  252. { suspend/resume are now deprecated in Delphi (they also don't work
  253. on most platforms in FPC), so a different method was required
  254. to start a thread if it's create with fSuspended=true -> that's
  255. what this method is for. }
  256. Resume;
  257. end;
  258. function TThread.GetSuspended: Boolean;
  259. begin
  260. GetSuspended:=FSuspended;
  261. end;
  262. procedure TThread.Terminate;
  263. begin
  264. FTerminated := True;
  265. TerminatedSet;
  266. end;
  267. procedure TThread.TerminatedSet;
  268. begin
  269. // Empty, must be overridden.
  270. end;
  271. procedure TThread.AfterConstruction;
  272. begin
  273. inherited AfterConstruction;
  274. // enable for all platforms once http://bugs.freepascal.org/view.php?id=16884
  275. // is fixed for all platforms (in case the fix for non-unix platforms also
  276. // requires this field at least)
  277. {$if defined(unix) or defined(windows) or defined(os2) or defined(hasamiga)}
  278. if not FExternalThread and not FInitialSuspended then
  279. Resume;
  280. {$endif}
  281. end;
  282. procedure ExecuteThreadQueueEntry(aEntry: TThread.PThreadQueueEntry);
  283. begin
  284. if Assigned(aEntry^.Method) then
  285. aEntry^.Method()
  286. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  287. else
  288. if Assigned(aEntry^.ThreadProc) then
  289. aEntry^.ThreadProc
  290. {$endif}
  291. end;
  292. procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry; aQueueIfMain: Boolean);
  293. var
  294. thd: TThread;
  295. issync: Boolean;
  296. begin
  297. { do we really need a synchronized call? }
  298. {$ifdef FPC_HAS_FEATURE_THREADING}
  299. if (GetCurrentThreadID = MainThreadID) and (not aQueueIfMain or not IsMultiThread) then
  300. {$endif}
  301. begin
  302. try
  303. ExecuteThreadQueueEntry(aEntry);
  304. finally
  305. if not Assigned(aEntry^.SyncEvent) then
  306. Dispose(aEntry);
  307. end;
  308. {$ifdef FPC_HAS_FEATURE_THREADING}
  309. end else begin
  310. { store thread and whether we're dealing with a synchronized event; the
  311. event record itself might already be freed after the ThreadQueueLock is
  312. released (in case of a Queue() call; for a Synchronize() call the record
  313. will stay valid, thus accessing SyncEvent later on (if issync is true) is
  314. okay) }
  315. thd := aEntry^.Thread;
  316. issync := Assigned(aEntry^.SyncEvent);
  317. System.EnterCriticalSection(ThreadQueueLock);
  318. try
  319. { add the entry to the thread queue }
  320. if Assigned(ThreadQueueTail) then begin
  321. ThreadQueueTail^.Next := aEntry;
  322. end else
  323. ThreadQueueHead := aEntry;
  324. ThreadQueueTail := aEntry;
  325. finally
  326. System.LeaveCriticalSection(ThreadQueueLock);
  327. end;
  328. { ensure that the main thread knows that something awaits }
  329. RtlEventSetEvent(SynchronizeTimeoutEvent);
  330. if assigned(WakeMainThread) then
  331. WakeMainThread(thd);
  332. { is this a Synchronize or Queue entry? }
  333. if issync then begin
  334. RtlEventWaitFor(aEntry^.SyncEvent);
  335. if Assigned(aEntry^.Exception) then
  336. raise aEntry^.Exception;
  337. end;
  338. {$endif def FPC_HAS_FEATURE_THREADING}
  339. end;
  340. end;
  341. procedure TThread.InitSynchronizeEvent;
  342. begin
  343. if Assigned(FSynchronizeEntry) then
  344. Exit;
  345. New(FSynchronizeEntry);
  346. FillChar(FSynchronizeEntry^, SizeOf(TThreadQueueEntry), 0);
  347. FSynchronizeEntry^.Thread := Self;
  348. FSynchronizeEntry^.ThreadID := ThreadID;
  349. {$ifdef FPC_HAS_FEATURE_THREADING}
  350. FSynchronizeEntry^.SyncEvent := RtlEventCreate;
  351. {$else}
  352. FSynchronizeEntry^.SyncEvent := nil{RtlEventCreate};
  353. {$endif}
  354. end;
  355. procedure TThread.DoneSynchronizeEvent;
  356. begin
  357. if not Assigned(FSynchronizeEntry) then
  358. Exit;
  359. {$ifdef FPC_HAS_FEATURE_THREADING}
  360. RtlEventDestroy(FSynchronizeEntry^.SyncEvent);
  361. {$endif}
  362. Dispose(FSynchronizeEntry);
  363. FSynchronizeEntry := Nil;
  364. end;
  365. class function TThread.CurrentIsMain : Boolean;
  366. begin
  367. Result:=TThread.Current.ThreadID=MainThreadID;
  368. end;
  369. class procedure TThread.Synchronize(AThread: TThread; AMethod: TThreadMethod);
  370. var
  371. syncentry: PThreadQueueEntry;
  372. thread: TThread;
  373. begin
  374. {$ifdef FPC_HAS_FEATURE_THREADING}
  375. if Assigned(AThread) and (AThread.ThreadID = GetCurrentThreadID) then
  376. {$else}
  377. if Assigned(AThread) then
  378. {$endif}
  379. thread := AThread
  380. else if Assigned(CurrentThreadVar) then
  381. thread := CurrentThreadVar
  382. else begin
  383. thread := Nil;
  384. { use a local synchronize event }
  385. New(syncentry);
  386. FillChar(syncentry^, SizeOf(TThreadQueueEntry), 0);
  387. {$ifdef FPC_HAS_FEATURE_THREADING}
  388. syncentry^.ThreadID := GetCurrentThreadID;
  389. syncentry^.SyncEvent := RtlEventCreate;
  390. {$else}
  391. syncentry^.ThreadID := 0{GetCurrentThreadID};
  392. syncentry^.SyncEvent := nil{RtlEventCreate};
  393. {$endif}
  394. end;
  395. if Assigned(thread) then begin
  396. { the Synchronize event is instantiated on demand }
  397. thread.InitSynchronizeEvent;
  398. syncentry := thread.FSynchronizeEntry;
  399. end;
  400. syncentry^.Exception := Nil;
  401. syncentry^.Method := AMethod;
  402. try
  403. ThreadQueueAppend(syncentry, False);
  404. finally
  405. syncentry^.Method := Nil;
  406. syncentry^.Next := Nil;
  407. if not Assigned(thread) then begin
  408. { clean up again }
  409. {$ifdef FPC_HAS_FEATURE_THREADING}
  410. RtlEventDestroy(syncentry^.SyncEvent);
  411. {$endif}
  412. Dispose(syncentry);
  413. end;
  414. end;
  415. end;
  416. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  417. class procedure TThread.Synchronize(AThread: TThread; AProcedure: TThreadProcedure);
  418. var
  419. syncentry: PThreadQueueEntry;
  420. thread: TThread;
  421. begin
  422. {$ifdef FPC_HAS_FEATURE_THREADING}
  423. if Assigned(AThread) and (AThread.ThreadID = GetCurrentThreadID) then
  424. {$else}
  425. if Assigned(AThread) then
  426. {$endif}
  427. thread := AThread
  428. else if Assigned(CurrentThreadVar) then
  429. thread := CurrentThreadVar
  430. else begin
  431. thread := Nil;
  432. { use a local synchronize event }
  433. New(syncentry);
  434. FillChar(syncentry^, SizeOf(TThreadQueueEntry), 0);
  435. {$ifdef FPC_HAS_FEATURE_THREADING}
  436. syncentry^.ThreadID := GetCurrentThreadID;
  437. syncentry^.SyncEvent := RtlEventCreate;
  438. {$else}
  439. syncentry^.ThreadID := 0{GetCurrentThreadID};
  440. syncentry^.SyncEvent := nil{RtlEventCreate};
  441. {$endif}
  442. end;
  443. if Assigned(thread) then begin
  444. { the Synchronize event is instantiated on demand }
  445. thread.InitSynchronizeEvent;
  446. syncentry := thread.FSynchronizeEntry;
  447. end;
  448. syncentry^.Exception := Nil;
  449. syncentry^.ThreadProc := AProcedure;
  450. try
  451. ThreadQueueAppend(syncentry, False);
  452. finally
  453. syncentry^.ThreadProc := Nil;
  454. syncentry^.Next := Nil;
  455. if not Assigned(thread) then begin
  456. { clean up again }
  457. {$ifdef FPC_HAS_FEATURE_THREADING}
  458. RtlEventDestroy(syncentry^.SyncEvent);
  459. {$endif}
  460. Dispose(syncentry);
  461. end;
  462. end;
  463. end;
  464. {$endif}
  465. procedure TThread.Synchronize(AMethod: TThreadMethod);
  466. begin
  467. TThread.Synchronize(self,AMethod);
  468. end;
  469. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  470. procedure TThread.Synchronize(AProcedure: TThreadProcedure);
  471. begin
  472. TThread.Synchronize(self,AProcedure);
  473. end;
  474. {$endif}
  475. Function PopThreadQueueHead : TThread.PThreadQueueEntry;
  476. begin
  477. Result:=ThreadQueueHead;
  478. if (Result<>Nil) then
  479. begin
  480. {$ifdef FPC_HAS_FEATURE_THREADING}
  481. System.EnterCriticalSection(ThreadQueueLock);
  482. try
  483. {$endif}
  484. Result:=ThreadQueueHead;
  485. if Result<>Nil then
  486. ThreadQueueHead:=ThreadQueueHead^.Next;
  487. if Not Assigned(ThreadQueueHead) then
  488. ThreadQueueTail := Nil;
  489. {$ifdef FPC_HAS_FEATURE_THREADING}
  490. finally
  491. System.LeaveCriticalSection(ThreadQueueLock);
  492. end;
  493. {$endif}
  494. end;
  495. end;
  496. function CheckSynchronize(timeout : longint=0) : boolean;
  497. { assumes being called from GUI thread }
  498. var
  499. ExceptObj: TObject;
  500. tmpentry: TThread.PThreadQueueEntry;
  501. begin
  502. result:=false;
  503. { first sanity check }
  504. if Not IsMultiThread then
  505. Exit
  506. {$ifdef FPC_HAS_FEATURE_THREADING}
  507. { second sanity check }
  508. else if GetCurrentThreadID<>MainThreadID then
  509. raise EThread.CreateFmt(SCheckSynchronizeError,[GetCurrentThreadID]);
  510. if timeout>0 then
  511. RtlEventWaitFor(SynchronizeTimeoutEvent,timeout)
  512. else
  513. RtlEventResetEvent(SynchronizeTimeoutEvent);
  514. tmpentry := PopThreadQueueHead;
  515. while Assigned(tmpentry) do
  516. begin
  517. { at least one method is handled, so return true }
  518. result := true;
  519. { step 2: execute the method }
  520. exceptobj := Nil;
  521. try
  522. ExecuteThreadQueueEntry(tmpentry);
  523. except
  524. exceptobj := TObject(AcquireExceptionObject);
  525. end;
  526. { step 3: error handling and cleanup }
  527. if Assigned(tmpentry^.SyncEvent) then
  528. begin
  529. { for Synchronize entries we pass back the Exception and trigger
  530. the event that Synchronize waits in }
  531. tmpentry^.Exception := exceptobj;
  532. RtlEventSetEvent(tmpentry^.SyncEvent)
  533. end
  534. else
  535. begin
  536. { for Queue entries we dispose the entry and raise the exception }
  537. Dispose(tmpentry);
  538. if Assigned(exceptobj) then
  539. raise exceptobj;
  540. end;
  541. tmpentry := PopThreadQueueHead;
  542. end
  543. {$endif};
  544. end;
  545. class function TThread.GetCurrentThread: TThread;
  546. begin
  547. { if this is the first time GetCurrentThread is called for an external thread
  548. we need to create a corresponding TExternalThread instance }
  549. Result := CurrentThreadVar;
  550. if not Assigned(Result) then begin
  551. Result := TExternalThread.Create;
  552. CurrentThreadVar := Result;
  553. end;
  554. end;
  555. class function TThread.GetIsSingleProcessor: Boolean;
  556. begin
  557. Result := FProcessorCount <= 1;
  558. end;
  559. procedure TThread.Queue(aMethod: TThreadMethod);
  560. begin
  561. Queue(Self, aMethod);
  562. end;
  563. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  564. procedure TThread.Queue(aProcedure: TThreadProcedure);
  565. begin
  566. Queue(Self, aProcedure);
  567. end;
  568. {$endif}
  569. class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
  570. begin
  571. InternalQueue(aThread, aMethod, False);
  572. end;
  573. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  574. class procedure TThread.Queue(aThread: TThread; AProcedure: TThreadProcedure);
  575. begin
  576. InternalQueue(aThread, aProcedure, False);
  577. end;
  578. {$endif}
  579. class procedure TThread.InternalQueue(aThread: TThread; aMethod: TThreadMethod; aQueueIfMain: Boolean); static;
  580. var
  581. queueentry: PThreadQueueEntry;
  582. begin
  583. New(queueentry);
  584. FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
  585. queueentry^.Thread := aThread;
  586. {$ifdef FPC_HAS_FEATURE_THREADING}
  587. queueentry^.ThreadID := GetCurrentThreadID;
  588. {$else}
  589. queueentry^.ThreadID := 0{GetCurrentThreadID};
  590. {$endif}
  591. queueentry^.Method := aMethod;
  592. { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
  593. ThreadQueueAppend(queueentry, aQueueIfMain);
  594. end;
  595. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  596. class procedure TThread.InternalQueue(aThread: TThread; aProcedure: TThreadProcedure; aQueueIfMain: Boolean); static;
  597. var
  598. queueentry: PThreadQueueEntry;
  599. begin
  600. New(queueentry);
  601. FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
  602. queueentry^.Thread := aThread;
  603. {$ifdef FPC_HAS_FEATURE_THREADING}
  604. queueentry^.ThreadID := GetCurrentThreadID;
  605. {$else}
  606. queueentry^.ThreadID := 0{GetCurrentThreadID};
  607. {$endif}
  608. queueentry^.ThreadProc := aProcedure;
  609. { the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
  610. ThreadQueueAppend(queueentry, aQueueIfMain);
  611. end;
  612. {$endif}
  613. procedure TThread.ForceQueue(aMethod: TThreadMethod);
  614. begin
  615. ForceQueue(Self, aMethod);
  616. end;
  617. class procedure TThread.ForceQueue(aThread: TThread; aMethod: TThreadMethod); static;
  618. begin
  619. InternalQueue(aThread, aMethod, True);
  620. end;
  621. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  622. class procedure TThread.ForceQueue(aThread: TThread; aMethod: TThreadProcedure); static;
  623. begin
  624. InternalQueue(aThread, aMethod, True);
  625. end;
  626. {$endif}
  627. class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: TThreadMethod);
  628. var
  629. entry, tmpentry, lastentry: PThreadQueueEntry;
  630. begin
  631. { anything to do at all? }
  632. if not Assigned(aThread) and not Assigned(aMethod) then
  633. Exit;
  634. {$ifdef FPC_HAS_FEATURE_THREADING}
  635. System.EnterCriticalSection(ThreadQueueLock);
  636. try
  637. {$endif}
  638. lastentry := Nil;
  639. entry := ThreadQueueHead;
  640. while Assigned(entry) do begin
  641. if
  642. { only entries not added by Synchronize }
  643. not Assigned(entry^.SyncEvent)
  644. { check for the thread }
  645. and (not Assigned(aThread) or (entry^.Thread = aThread) or (entry^.ThreadID = aThread.ThreadID))
  646. { check for the method }
  647. and (not Assigned(aMethod) or
  648. (
  649. (TMethod(entry^.Method).Code = TMethod(aMethod).Code) and
  650. (TMethod(entry^.Method).Data = TMethod(aMethod).Data)
  651. ))
  652. then begin
  653. { ok, we need to remove this entry }
  654. tmpentry := entry;
  655. if Assigned(lastentry) then
  656. lastentry^.Next := entry^.Next;
  657. entry := entry^.Next;
  658. if ThreadQueueHead = tmpentry then
  659. ThreadQueueHead := entry;
  660. if ThreadQueueTail = tmpentry then
  661. ThreadQueueTail := lastentry;
  662. { only dispose events added by Queue }
  663. if not Assigned(tmpentry^.SyncEvent) then
  664. Dispose(tmpentry);
  665. end else begin
  666. { leave this entry }
  667. lastentry := entry;
  668. entry := entry^.Next;
  669. end;
  670. end;
  671. {$ifdef FPC_HAS_FEATURE_THREADING}
  672. finally
  673. System.LeaveCriticalSection(ThreadQueueLock);
  674. end;
  675. {$endif}
  676. end;
  677. class procedure TThread.RemoveQueuedEvents(aMethod: TThreadMethod);
  678. begin
  679. RemoveQueuedEvents(Nil, aMethod);
  680. end;
  681. class procedure TThread.RemoveQueuedEvents(aThread: TThread);
  682. begin
  683. RemoveQueuedEvents(aThread, Nil);
  684. end;
  685. class function TThread.CheckTerminated: Boolean;
  686. begin
  687. { this method only works with threads created by TThread, so we can make a
  688. shortcut here }
  689. if not Assigned(CurrentThreadVar) then
  690. raise EThreadExternalException.Create(SThreadExternal);
  691. Result := CurrentThreadVar.FTerminated;
  692. end;
  693. class procedure TThread.SetReturnValue(aValue: Integer);
  694. begin
  695. { this method only works with threads created by TThread, so we can make a
  696. shortcut here }
  697. if not Assigned(CurrentThreadVar) then
  698. raise EThreadExternalException.Create(SThreadExternal);
  699. CurrentThreadVar.FReturnValue := aValue;
  700. end;
  701. class function TThread.CreateAnonymousThread(aProc: TProcedure): TThread;
  702. begin
  703. if not Assigned(aProc) then
  704. raise Exception.Create(SNoProcGiven);
  705. Result := TAnonymousThread.Create(aProc);
  706. end;
  707. {$ifdef FPC_HAS_REFERENCE_PROCEDURE}
  708. class function TThread.CreateAnonymousThread(aProc: TThreadProcedure): TThread;
  709. begin
  710. if not Assigned(aProc) then
  711. raise Exception.Create(SNoProcGiven);
  712. Result := TAnonymousThread.Create(aProc);
  713. end;
  714. {$ENDIF}
  715. class function TThread.CreateAnonymousThread(aProc: TThreadMethod): TThread;
  716. begin
  717. if not Assigned(aProc) then
  718. raise Exception.Create(SNoProcGiven);
  719. Result := TAnonymousThread.Create(aProc);
  720. end;
  721. class procedure TThread.NameThreadForDebugging(aThreadName: UnicodeString; aThreadID: TThreadID);
  722. begin
  723. {$ifdef FPC_HAS_FEATURE_THREADING}
  724. SetThreadDebugName(aThreadID, aThreadName);
  725. {$endif}
  726. end;
  727. class procedure TThread.NameThreadForDebugging(aThreadName: AnsiString; aThreadID: TThreadID);
  728. begin
  729. {$ifdef FPC_HAS_FEATURE_THREADING}
  730. SetThreadDebugName(aThreadID, aThreadName);
  731. {$endif}
  732. end;
  733. class procedure TThread.Yield;
  734. begin
  735. {$ifdef FPC_HAS_FEATURE_THREADING}
  736. ThreadSwitch;
  737. {$endif}
  738. end;
  739. class procedure TThread.Sleep(aMilliseconds: Cardinal);
  740. begin
  741. {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.Sleep(aMilliseconds);
  742. end;
  743. class procedure TThread.SpinWait(aIterations: LongWord);
  744. var
  745. i: LongWord;
  746. begin
  747. { yes, it's just a simple busy wait to burn some cpu cycles... and as the job
  748. of this loop is to burn CPU cycles we switch off any optimizations that
  749. could interfere with this (e.g. loop unrolling) }
  750. { Do *NOT* do $PUSH, $OPTIMIZATIONS OFF, <code>, $POP because optimization is
  751. not a local switch, which means $PUSH/POP doesn't affect it, so that turns
  752. off *ALL* optimizations for code below this point. Thanks to this we shipped
  753. large parts of the classes unit with optimizations off between 2012-12-27
  754. and 2014-06-06.
  755. Instead, use a global var for the spinlock, because that is always handled
  756. as volatile, so the access won't be optimized away by the compiler. (KB) }
  757. for i:=1 to aIterations do
  758. begin
  759. Inc(SpinWaitDummy); // SpinWaitDummy *MUST* be global
  760. end;
  761. end;
  762. {$ifndef HAS_TTHREAD_GETSYSTEMTIMES}
  763. class function TThread.GetSystemTimes(out aSystemTimes: TSystemTimes) : Boolean;
  764. begin
  765. { by default we just return a zeroed out record }
  766. FillChar(aSystemTimes, SizeOf(aSystemTimes), 0);
  767. Result:=False;
  768. end;
  769. {$endif}
  770. class function TThread.GetCPUUsage(var Previous: TSystemTimes): Integer;
  771. var
  772. Act : TSystemTimes;
  773. Load,Idle: QWord;
  774. begin
  775. Result:=0;
  776. if not GetSystemTimes(Act) then
  777. exit;
  778. Load:=(Act.UserTime-Previous.UserTime) +
  779. (Act.KernelTime-Previous.KernelTime) +
  780. (Act.NiceTime-Previous.NiceTime);
  781. Idle:=Act.IdleTime-Previous.IdleTime;
  782. Previous:=Act;
  783. if (Load<>0) and (Load>Idle) then
  784. Result:=100*Trunc(1-(Idle/Load));
  785. end;
  786. class function TThread.GetTickCount: LongWord;
  787. begin
  788. Result := {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.GetTickCount;
  789. end;
  790. class function TThread.GetTickCount64: QWord;
  791. begin
  792. Result := {$IFDEF FPC_DOTTEDUNITS}System.{$ENDIF}SysUtils.GetTickCount64;
  793. end;
  794. { TSimpleThread allows objects to create a threading method without defining
  795. a new thread class }
  796. Type
  797. TSimpleThread = class(TThread)
  798. private
  799. FExecuteMethod: TThreadExecuteHandler;
  800. protected
  801. procedure Execute; override;
  802. public
  803. constructor Create(ExecuteMethod: TThreadExecuteHandler; AOnterminate : TNotifyEvent);
  804. end;
  805. TSimpleStatusThread = class(TThread)
  806. private
  807. FExecuteMethod: TThreadExecuteStatusHandler;
  808. FStatus : String;
  809. FOnStatus : TThreadStatusNotifyEvent;
  810. protected
  811. procedure Execute; override;
  812. Procedure DoStatus;
  813. Procedure SetStatus(Const AStatus : String);
  814. public
  815. constructor Create(ExecuteMethod: TThreadExecuteStatusHandler; AOnStatus : TThreadStatusNotifyEvent; AOnterminate : TNotifyEvent);
  816. end;
  817. TSimpleProcThread = class(TThread)
  818. private
  819. FExecuteMethod: TThreadExecuteCallBack;
  820. FCallOnTerminate : TNotifyCallBack;
  821. FData : Pointer;
  822. protected
  823. Procedure TerminateCallBack(Sender : TObject);
  824. procedure Execute; override;
  825. public
  826. constructor Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnterminate : TNotifyCallBack);
  827. end;
  828. TSimpleStatusProcThread = class(TThread)
  829. private
  830. FExecuteMethod: TThreadExecuteStatusCallBack;
  831. FCallOnTerminate : TNotifyCallBack;
  832. FStatus : String;
  833. FOnStatus : TThreadStatusNotifyCallBack;
  834. FData : Pointer;
  835. protected
  836. procedure Execute; override;
  837. Procedure DoStatus;
  838. Procedure SetStatus(Const AStatus : String);
  839. Procedure TerminateCallBack(Sender : TObject);
  840. public
  841. constructor Create(ExecuteMethod: TThreadExecuteStatusCallBack; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnterminate : TNotifyCallBack);
  842. end;
  843. { TSimpleThread }
  844. constructor TSimpleThread.Create(ExecuteMethod: TThreadExecuteHandler; AOnTerminate: TNotifyEvent);
  845. begin
  846. FExecuteMethod := ExecuteMethod;
  847. OnTerminate := AOnTerminate;
  848. inherited Create(False);
  849. end;
  850. procedure TSimpleThread.Execute;
  851. begin
  852. FreeOnTerminate := True;
  853. FExecuteMethod;
  854. end;
  855. { TSimpleStatusThread }
  856. constructor TSimpleStatusThread.Create(ExecuteMethod: TThreadExecuteStatusHandler;AOnStatus : TThreadStatusNotifyEvent; AOnTerminate: TNotifyEvent);
  857. begin
  858. FExecuteMethod := ExecuteMethod;
  859. OnTerminate := AOnTerminate;
  860. FOnStatus:=AOnStatus;
  861. FStatus:='';
  862. inherited Create(False);
  863. end;
  864. procedure TSimpleStatusThread.Execute;
  865. begin
  866. FreeOnTerminate := True;
  867. FExecuteMethod(@SetStatus);
  868. end;
  869. procedure TSimpleStatusThread.SetStatus(Const AStatus : String);
  870. begin
  871. If (AStatus=FStatus) then
  872. exit;
  873. FStatus:=AStatus;
  874. If Assigned(FOnStatus) then
  875. Synchronize(@DoStatus);
  876. end;
  877. procedure TSimpleStatusThread.DoStatus;
  878. begin
  879. FOnStatus(Self,FStatus);
  880. end;
  881. { TSimpleProcThread }
  882. constructor TSimpleProcThread.Create(ExecuteMethod: TThreadExecuteCallBack; AData : Pointer; AOnTerminate: TNotifyCallBack);
  883. begin
  884. FExecuteMethod := ExecuteMethod;
  885. FCallOnTerminate := AOnTerminate;
  886. FData:=AData;
  887. If Assigned(FCallOnTerminate) then
  888. OnTerminate:=@TerminateCallBack;
  889. inherited Create(False);
  890. end;
  891. procedure TSimpleProcThread.Execute;
  892. begin
  893. FreeOnTerminate := True;
  894. FExecuteMethod(FData);
  895. end;
  896. procedure TSimpleProcThread.TerminateCallBack(Sender : TObject);
  897. begin
  898. if Assigned(FCallOnTerminate) then
  899. FCallOnTerminate(Sender,FData);
  900. end;
  901. { TSimpleStatusProcThread }
  902. constructor TSimpleStatusProcThread.Create(ExecuteMethod: TThreadExecuteStatusCallback; AData : Pointer; AOnStatus : TThreadStatusNotifyCallBack; AOnTerminate: TNotifyCallBack);
  903. begin
  904. FExecuteMethod := ExecuteMethod;
  905. FCallOnTerminate := AOnTerminate;
  906. FData:=AData;
  907. If Assigned(FCallOnTerminate) then
  908. OnTerminate:=@TerminateCallBack;
  909. FOnStatus:=AOnStatus;
  910. FStatus:='';
  911. inherited Create(False);
  912. end;
  913. procedure TSimpleStatusProcThread.Execute;
  914. begin
  915. FreeOnTerminate := True;
  916. FExecuteMethod(FData,@SetStatus);
  917. end;
  918. procedure TSimpleStatusProcThread.SetStatus(Const AStatus : String);
  919. begin
  920. If (AStatus=FStatus) then
  921. exit;
  922. FStatus:=AStatus;
  923. If Assigned(FOnStatus) then
  924. Synchronize(@DoStatus);
  925. end;
  926. procedure TSimpleStatusProcThread.DoStatus;
  927. begin
  928. FOnStatus(Self,FData,FStatus);
  929. end;
  930. procedure TSimpleStatusProcThread.TerminateCallBack(Sender : TObject);
  931. begin
  932. if Assigned(FCallOnTerminate) then
  933. FCallOnTerminate(Sender,FData);
  934. end;
  935. class function TThread.ExecuteInThread(AMethod: TThreadExecuteHandler; AOnTerminate: TNotifyEvent): TThread;
  936. begin
  937. Result:=TSimpleThread.Create(AMethod,AOnTerminate);
  938. end;
  939. class function TThread.ExecuteInThread(AMethod: TThreadExecuteCallback; AData: Pointer; AOnTerminate: TNotifyCallBack): TThread;
  940. begin
  941. Result:=TSimpleProcThread.Create(AMethod,AData,AOnTerminate);
  942. end;
  943. class function TThread.ExecuteInThread(AMethod: TThreadExecuteStatusHandler; AOnStatus: TThreadStatusNotifyEvent;
  944. AOnTerminate: TNotifyEvent): TThread;
  945. begin
  946. If Not Assigned(AOnStatus) then
  947. Raise EThread.Create(SErrStatusCallBackRequired);
  948. Result:=TSimpleStatusThread.Create(AMethod,AOnStatus,AOnTerminate);
  949. end;
  950. class function TThread.ExecuteInThread(AMethod: TThreadExecuteStatusCallback; AOnStatus: TThreadStatusNotifyCallback;
  951. AData: Pointer; AOnTerminate: TNotifyCallBack): TThread;
  952. begin
  953. If Not Assigned(AOnStatus) then
  954. Raise EThread.Create(SErrStatusCallBackRequired);
  955. Result:=TSimpleStatusProcThread.Create(AMethod,AData,AOnStatus,AOnTerminate);
  956. end;
  957. { TPersistent implementation }
  958. {$i persist.inc }
  959. {$i sllist.inc}
  960. {$i resref.inc}
  961. { TComponent implementation }
  962. {$i compon.inc}
  963. { TBasicAction implementation }
  964. {$i action.inc}
  965. { TDataModule implementation }
  966. {$i dm.inc}
  967. { Class and component registration routines }
  968. {$I cregist.inc}
  969. { Interface related stuff }
  970. {$I intf.inc}
  971. {**********************************************************************
  972. * Miscellaneous procedures and functions *
  973. **********************************************************************}
  974. function ExtractStrings(Separators, WhiteSpace: TSysCharSet; Content: PAnsiChar; Strings: TStrings; AddEmptyStrings : Boolean = False): Integer;
  975. var
  976. b, c : PAnsiChar;
  977. procedure SkipWhitespace;
  978. begin
  979. while (c^ in Whitespace) do
  980. inc (c);
  981. end;
  982. procedure AddString;
  983. var
  984. l : integer;
  985. s : string;
  986. begin
  987. l := c-b;
  988. if (l > 0) or AddEmptyStrings then
  989. begin
  990. if assigned(Strings) then
  991. begin
  992. setlength(s, l);
  993. if l>0 then
  994. move (b^, s[1],l*SizeOf(AnsiChar));
  995. Strings.Add (s);
  996. end;
  997. inc (result);
  998. end;
  999. end;
  1000. var
  1001. quoted : AnsiChar;
  1002. begin
  1003. result := 0;
  1004. c := Content;
  1005. Quoted := #0;
  1006. Separators := Separators + [#13, #10] - ['''','"'];
  1007. SkipWhitespace;
  1008. b := c;
  1009. while (c^ <> #0) do
  1010. begin
  1011. if (c^ = Quoted) then
  1012. begin
  1013. if ((c+1)^ = Quoted) then
  1014. inc (c)
  1015. else
  1016. Quoted := #0
  1017. end
  1018. else if (Quoted = #0) and (c^ in ['''','"']) then
  1019. Quoted := c^;
  1020. if (Quoted = #0) and (c^ in Separators) then
  1021. begin
  1022. AddString;
  1023. inc (c);
  1024. SkipWhitespace;
  1025. b := c;
  1026. end
  1027. else
  1028. inc (c);
  1029. end;
  1030. if (c <> b) then
  1031. AddString;
  1032. end;
  1033. { Point and rectangle constructors }
  1034. function Point(AX, AY: Integer): TPoint;
  1035. begin
  1036. with Result do
  1037. begin
  1038. X := AX;
  1039. Y := AY;
  1040. end;
  1041. end;
  1042. function SmallPoint(AX, AY: SmallInt): TSmallPoint;
  1043. begin
  1044. with Result do
  1045. begin
  1046. X := AX;
  1047. Y := AY;
  1048. end;
  1049. end;
  1050. function Rect(ALeft, ATop, ARight, ABottom: Integer): TRect;
  1051. begin
  1052. with Result do
  1053. begin
  1054. Left := ALeft;
  1055. Top := ATop;
  1056. Right := ARight;
  1057. Bottom := ABottom;
  1058. end;
  1059. end;
  1060. function Bounds(ALeft, ATop, AWidth, AHeight: Integer): TRect;
  1061. begin
  1062. with Result do
  1063. begin
  1064. Left := ALeft;
  1065. Top := ATop;
  1066. Right := ALeft + AWidth;
  1067. Bottom := ATop + AHeight;
  1068. end;
  1069. end;
  1070. function PointsEqual(const P1, P2: TPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1071. begin
  1072. { lazy, but should work }
  1073. result:=QWord(P1)=QWord(P2);
  1074. end;
  1075. function PointsEqual(const P1, P2: TSmallPoint): Boolean; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1076. begin
  1077. { lazy, but should work }
  1078. result:=DWord(P1)=DWord(P2);
  1079. end;
  1080. function InvalidPoint(X, Y: Integer): Boolean;
  1081. begin
  1082. result:=(X=-1) and (Y=-1);
  1083. end;
  1084. function InvalidPoint(const At: TPoint): Boolean;
  1085. begin
  1086. result:=(At.x=-1) and (At.y=-1);
  1087. end;
  1088. function InvalidPoint(const At: TSmallPoint): Boolean;
  1089. begin
  1090. result:=(At.x=-1) and (At.y=-1);
  1091. end;
  1092. { Object filing routines }
  1093. var
  1094. IntConstList: TThreadList;
  1095. type
  1096. TIntConst = class
  1097. IntegerType: PTypeInfo; // The integer type RTTI pointer
  1098. IdentToIntFn: TIdentToInt; // Identifier to Integer conversion
  1099. IntToIdentFn: TIntToIdent; // Integer to Identifier conversion
  1100. constructor Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  1101. AIntToIdent: TIntToIdent);
  1102. end;
  1103. constructor TIntConst.Create(AIntegerType: PTypeInfo; AIdentToInt: TIdentToInt;
  1104. AIntToIdent: TIntToIdent);
  1105. begin
  1106. IntegerType := AIntegerType;
  1107. IdentToIntFn := AIdentToInt;
  1108. IntToIdentFn := AIntToIdent;
  1109. end;
  1110. procedure RegisterIntegerConsts(IntegerType: Pointer; IdentToIntFn: TIdentToInt;
  1111. IntToIdentFn: TIntToIdent);
  1112. begin
  1113. IntConstList.Add(TIntConst.Create(IntegerType, IdentToIntFn, IntToIdentFn));
  1114. end;
  1115. function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
  1116. var
  1117. i: Integer;
  1118. begin
  1119. with IntConstList.LockList do
  1120. try
  1121. for i := 0 to Count - 1 do
  1122. if TIntConst(Items[i]).IntegerType = AIntegerType then
  1123. exit(TIntConst(Items[i]).IntToIdentFn);
  1124. Result := nil;
  1125. finally
  1126. IntConstList.UnlockList;
  1127. end;
  1128. end;
  1129. function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
  1130. var
  1131. i: Integer;
  1132. begin
  1133. with IntConstList.LockList do
  1134. try
  1135. for i := 0 to Count - 1 do
  1136. with TIntConst(Items[I]) do
  1137. if TIntConst(Items[I]).IntegerType = AIntegerType then
  1138. exit(IdentToIntFn);
  1139. Result := nil;
  1140. finally
  1141. IntConstList.UnlockList;
  1142. end;
  1143. end;
  1144. function IdentToInt(const Ident: String; out Int: LongInt;
  1145. const Map: array of TIdentMapEntry): Boolean;
  1146. var
  1147. i: Integer;
  1148. begin
  1149. for i := Low(Map) to High(Map) do
  1150. if CompareText(Map[i].Name, Ident) = 0 then
  1151. begin
  1152. Int := Map[i].Value;
  1153. exit(True);
  1154. end;
  1155. Result := False;
  1156. end;
  1157. function IntToIdent(Int: LongInt; var Ident: String;
  1158. const Map: array of TIdentMapEntry): Boolean;
  1159. var
  1160. i: Integer;
  1161. begin
  1162. for i := Low(Map) to High(Map) do
  1163. if Map[i].Value = Int then
  1164. begin
  1165. Ident := Map[i].Name;
  1166. exit(True);
  1167. end;
  1168. Result := False;
  1169. end;
  1170. function GlobalIdentToInt(const Ident: String; var Int: LongInt):boolean;
  1171. var
  1172. i : Integer;
  1173. begin
  1174. with IntConstList.LockList do
  1175. try
  1176. for i := 0 to Count - 1 do
  1177. if TIntConst(Items[I]).IdentToIntFn(Ident, Int) then
  1178. Exit(True);
  1179. Result := false;
  1180. finally
  1181. IntConstList.UnlockList;
  1182. end;
  1183. end;
  1184. { TPropFixup }
  1185. // Tainted. TPropFixup is being removed.
  1186. Type
  1187. TInitHandler = Class(TObject)
  1188. AHandler : TInitComponentHandler;
  1189. AClass : TComponentClass;
  1190. end;
  1191. {$ifndef i8086}
  1192. type
  1193. TCodePtrList = TList;
  1194. {$endif i8086}
  1195. Var
  1196. InitHandlerList : TList;
  1197. FindGlobalComponentList : TCodePtrList;
  1198. procedure RegisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  1199. begin
  1200. if not(assigned(FindGlobalComponentList)) then
  1201. FindGlobalComponentList:=TCodePtrList.Create;
  1202. if FindGlobalComponentList.IndexOf(CodePointer(AFindGlobalComponent))<0 then
  1203. FindGlobalComponentList.Add(CodePointer(AFindGlobalComponent));
  1204. end;
  1205. procedure UnregisterFindGlobalComponentProc(AFindGlobalComponent: TFindGlobalComponent);
  1206. begin
  1207. if assigned(FindGlobalComponentList) then
  1208. FindGlobalComponentList.Remove(CodePointer(AFindGlobalComponent));
  1209. end;
  1210. function FindGlobalComponent(const Name: string): TComponent;
  1211. var
  1212. i : sizeint;
  1213. begin
  1214. FindGlobalComponent:=nil;
  1215. if assigned(FindGlobalComponentList) then
  1216. begin
  1217. for i:=FindGlobalComponentList.Count-1 downto 0 do
  1218. begin
  1219. FindGlobalComponent:=TFindGlobalComponent(FindGlobalComponentList[i])(name);
  1220. if assigned(FindGlobalComponent) then
  1221. break;
  1222. end;
  1223. end;
  1224. end;
  1225. function IsUniqueGlobalComponentName(const aName: string): Boolean;
  1226. begin
  1227. if Assigned(IsUniqueGlobalComponentNameProc) then
  1228. Result:=IsUniqueGlobalComponentNameProc(aName)
  1229. else
  1230. Result:=Not Assigned(FindGlobalComponent(aName));
  1231. end;
  1232. procedure RegisterInitComponentHandler(ComponentClass: TComponentClass; Handler: TInitComponentHandler);
  1233. Var
  1234. I : Integer;
  1235. H: TInitHandler;
  1236. begin
  1237. If (InitHandlerList=Nil) then
  1238. InitHandlerList:=TList.Create;
  1239. H:=TInitHandler.Create;
  1240. H.Aclass:=ComponentClass;
  1241. H.AHandler:=Handler;
  1242. try
  1243. With InitHandlerList do
  1244. begin
  1245. I:=0;
  1246. While (I<Count) and not H.AClass.InheritsFrom(TInitHandler(Items[I]).AClass) do
  1247. Inc(I);
  1248. { override? }
  1249. if (I<Count) and (TInitHandler(Items[I]).AClass=H.AClass) then
  1250. begin
  1251. TInitHandler(Items[I]).AHandler:=Handler;
  1252. H.Free;
  1253. end
  1254. else
  1255. InitHandlerList.Insert(I,H);
  1256. end;
  1257. except
  1258. H.Free;
  1259. raise;
  1260. end;
  1261. end;
  1262. { all targets should at least include the sysres.inc dummy in the system unit to compile this }
  1263. function CreateComponentfromRes(const res : string;Inst : THandle;var Component : TComponent) : Boolean;
  1264. var
  1265. ResStream : TResourceStream;
  1266. ResID : TFPResourceHandle;
  1267. begin
  1268. if Inst=0 then
  1269. Inst:=HInstance;
  1270. ResId:=System.FindResource(Inst, Res, RT_RCDATA);
  1271. result:=ResID<>0;
  1272. try
  1273. if Result then
  1274. begin
  1275. ResStream:=TResourceStream.Create(Inst,Res,RT_RCDATA);
  1276. try
  1277. Component:=ResStream.ReadComponent(Component);
  1278. finally
  1279. ResStream.Free;
  1280. end;
  1281. end;
  1282. except
  1283. on EResNotFound do
  1284. result:=false;
  1285. end;
  1286. end;
  1287. function DefaultInitHandler(Instance: TComponent; RootAncestor: TClass): Boolean;
  1288. function doinit(_class : TClass) : boolean;
  1289. begin
  1290. result:=false;
  1291. if (_class.ClassType=TComponent) or (_class.ClassType=RootAncestor) then
  1292. exit;
  1293. result:=doinit(_class.ClassParent);
  1294. result:=CreateComponentfromRes(_class.ClassName,0,Instance) or result;
  1295. end;
  1296. begin
  1297. {$ifdef FPC_HAS_FEATURE_THREADING}
  1298. GlobalNameSpace.BeginWrite;
  1299. try
  1300. {$endif}
  1301. result:=doinit(Instance.ClassType);
  1302. if Result then
  1303. Instance.ReadDeltaState;
  1304. {$ifdef FPC_HAS_FEATURE_THREADING}
  1305. finally
  1306. GlobalNameSpace.EndWrite;
  1307. end;
  1308. {$endif}
  1309. end;
  1310. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  1311. Var
  1312. I : Integer;
  1313. begin
  1314. I:=0;
  1315. if not Assigned(InitHandlerList) then begin
  1316. Result := True;
  1317. Exit;
  1318. end;
  1319. Result:=False;
  1320. With InitHandlerList do
  1321. begin
  1322. I:=0;
  1323. // Instance is the normally the lowest one, so that one should be used when searching.
  1324. While Not result and (I<Count) do
  1325. begin
  1326. If (Instance.InheritsFrom(TInitHandler(Items[i]).AClass)) then
  1327. Result:=TInitHandler(Items[i]).AHandler(Instance,RootAncestor);
  1328. Inc(I);
  1329. end;
  1330. end;
  1331. end;
  1332. function InitComponentRes(const ResName: String; Instance: TComponent): Boolean;
  1333. begin
  1334. Result:=ReadComponentRes(ResName,Instance)=Instance;
  1335. end;
  1336. function SysReadComponentRes(HInstance : THandle; const ResName: String; Instance: TComponent): TComponent;
  1337. Var
  1338. H : TFPResourceHandle;
  1339. begin
  1340. { Windows unit also has a FindResource function, use the one from
  1341. system unit here. }
  1342. H:=system.FindResource(HInstance,ResName,RT_RCDATA);
  1343. if (PtrInt(H)=0) then
  1344. Result:=Nil
  1345. else
  1346. With TResourceStream.Create(HInstance,ResName,RT_RCDATA) do
  1347. try
  1348. Result:=ReadComponent(Instance);
  1349. Finally
  1350. Free;
  1351. end;
  1352. end;
  1353. function ReadComponentRes(const ResName: String; Instance: TComponent): TComponent;
  1354. begin
  1355. Result:=SysReadComponentRes(Hinstance,Resname,Instance);
  1356. end;
  1357. function ReadComponentResEx(HInstance: THandle; const ResName: String): TComponent;
  1358. begin
  1359. Result:=SysReadComponentRes(Hinstance,ResName,Nil);
  1360. end;
  1361. function ReadComponentResFile(const FileName: String; Instance: TComponent): TComponent;
  1362. var
  1363. FileStream: TStream;
  1364. begin
  1365. FileStream := TFileStream.Create(FileName, fmOpenRead {!!!:or fmShareDenyWrite});
  1366. try
  1367. Result := FileStream.ReadComponentRes(Instance);
  1368. finally
  1369. FileStream.Free;
  1370. end;
  1371. end;
  1372. procedure WriteComponentResFile(const FileName: String; Instance: TComponent);
  1373. var
  1374. FileStream: TStream;
  1375. begin
  1376. FileStream := TFileStream.Create(FileName, fmCreate);
  1377. try
  1378. FileStream.WriteComponentRes(Instance.ClassName, Instance);
  1379. finally
  1380. FileStream.Free;
  1381. end;
  1382. end;
  1383. Function FindNestedComponent(Root : TComponent; APath : String; CStyle : Boolean = True) : TComponent;
  1384. Function GetNextName : String; {$ifdef CLASSESINLINE} inline; {$endif CLASSESINLINE}
  1385. Var
  1386. P : Integer;
  1387. CM : Boolean;
  1388. begin
  1389. P:=Pos('.',APath);
  1390. CM:=False;
  1391. If (P=0) then
  1392. begin
  1393. If CStyle then
  1394. begin
  1395. P:=Pos('->',APath);
  1396. CM:=P<>0;
  1397. end;
  1398. If (P=0) Then
  1399. P:=Length(APath)+1;
  1400. end;
  1401. Result:=Copy(APath,1,P-1);
  1402. Delete(APath,1,P+Ord(CM));
  1403. end;
  1404. Var
  1405. C : TComponent;
  1406. S : String;
  1407. begin
  1408. If (APath='') then
  1409. Result:=Nil
  1410. else
  1411. begin
  1412. Result:=Root;
  1413. While (APath<>'') And (Result<>Nil) do
  1414. begin
  1415. C:=Result;
  1416. S:=Uppercase(GetNextName);
  1417. Result:=C.FindComponent(S);
  1418. If (Result=Nil) And (S='OWNER') then
  1419. Result:=C;
  1420. end;
  1421. end;
  1422. end;
  1423. {$ifdef FPC_HAS_FEATURE_THREADING}
  1424. threadvar
  1425. {$else}
  1426. var
  1427. {$endif}
  1428. GlobalLoaded, GlobalLists: TFpList;
  1429. procedure BeginGlobalLoading;
  1430. begin
  1431. if not Assigned(GlobalLists) then
  1432. GlobalLists := TFpList.Create;
  1433. GlobalLists.Add(GlobalLoaded);
  1434. GlobalLoaded := TFpList.Create;
  1435. end;
  1436. { Notify all global components that they have been loaded completely }
  1437. procedure NotifyGlobalLoading;
  1438. var
  1439. i: Integer;
  1440. begin
  1441. for i := 0 to GlobalLoaded.Count - 1 do
  1442. TComponent(GlobalLoaded[i]).Loaded;
  1443. end;
  1444. procedure EndGlobalLoading;
  1445. begin
  1446. { Free the memory occupied by BeginGlobalLoading }
  1447. GlobalLoaded.Free;
  1448. GlobalLoaded := TFpList(GlobalLists.Last);
  1449. GlobalLists.Delete(GlobalLists.Count - 1);
  1450. if GlobalLists.Count = 0 then
  1451. begin
  1452. GlobalLists.Free;
  1453. GlobalLists := nil;
  1454. end;
  1455. end;
  1456. function CollectionsEqual(C1, C2: TCollection): Boolean;
  1457. begin
  1458. // !!!: Implement this
  1459. CollectionsEqual:=false;
  1460. end;
  1461. function CollectionsEqual(C1, C2: TCollection; Owner1, Owner2: TComponent): Boolean;
  1462. procedure stream_collection(s : tstream;c : tcollection;o : tcomponent);
  1463. var
  1464. w : twriter;
  1465. begin
  1466. w:=twriter.create(s,4096);
  1467. try
  1468. w.root:=o;
  1469. w.flookuproot:=o;
  1470. w.writecollection(c);
  1471. finally
  1472. w.free;
  1473. end;
  1474. end;
  1475. var
  1476. s1,s2 : tmemorystream;
  1477. begin
  1478. result:=false;
  1479. if (c1.classtype<>c2.classtype) or
  1480. (c1.count<>c2.count) then
  1481. exit;
  1482. if c1.count = 0 then
  1483. begin
  1484. result:= true;
  1485. exit;
  1486. end;
  1487. s1:=tmemorystream.create;
  1488. try
  1489. s2:=tmemorystream.create;
  1490. try
  1491. stream_collection(s1,c1,owner1);
  1492. stream_collection(s2,c2,owner2);
  1493. result:=(s1.size=s2.size) and (CompareChar(s1.memory^,s2.memory^,s1.size)=0);
  1494. finally
  1495. s2.free;
  1496. end;
  1497. finally
  1498. s1.free;
  1499. end;
  1500. end;
  1501. { Object conversion routines }
  1502. type
  1503. CharToOrdFuncty = Function(var charpo: Pointer): Cardinal;
  1504. function CharToOrd(var P: Pointer): Cardinal;
  1505. begin
  1506. result:= ord(PAnsiChar(P)^);
  1507. inc(PAnsiChar(P));
  1508. end;
  1509. function WideCharToOrd(var P: Pointer): Cardinal;
  1510. begin
  1511. result:= ord(pwidechar(P)^);
  1512. inc(pwidechar(P));
  1513. end;
  1514. function Utf8ToOrd(var P:Pointer): Cardinal;
  1515. begin
  1516. // Should also check for illegal utf8 combinations
  1517. Result := Ord(PAnsiChar(P)^);
  1518. Inc(P);
  1519. if (Result and $80) <> 0 then
  1520. if (Ord(Result) and %11100000) = %11000000 then begin
  1521. Result := ((Result and %00011111) shl 6)
  1522. or (ord(PAnsiChar(P)^) and %00111111);
  1523. Inc(P);
  1524. end else if (Ord(Result) and %11110000) = %11100000 then begin
  1525. Result := ((Result and %00011111) shl 12)
  1526. or ((ord(PAnsiChar(P)^) and %00111111) shl 6)
  1527. or (ord((PAnsiChar(P)+1)^) and %00111111);
  1528. Inc(P,2);
  1529. end else begin
  1530. Result := ((ord(Result) and %00011111) shl 18)
  1531. or ((ord(PAnsiChar(P)^) and %00111111) shl 12)
  1532. or ((ord((PAnsiChar(P)+1)^) and %00111111) shl 6)
  1533. or (ord((PAnsiChar(P)+2)^) and %00111111);
  1534. Inc(P,3);
  1535. end;
  1536. end;
  1537. procedure ObjectBinaryToText(Input, Output: TStream; Encoding: TObjectTextEncoding);
  1538. var
  1539. Version: TBinaryObjectReader.TBOVersion;
  1540. procedure OutStr(s: RawByteString);
  1541. begin
  1542. if Length(s) > 0 then
  1543. Output.Write(s[1], Length(s));
  1544. end;
  1545. procedure OutLn(s: RawByteString);
  1546. begin
  1547. OutStr(s + LineEnding);
  1548. end;
  1549. procedure Outchars(P, LastP : Pointer; CharToOrdFunc: CharToOrdFuncty;
  1550. UseBytes: boolean = false);
  1551. var
  1552. res, NewStr: RawByteString;
  1553. w: Cardinal;
  1554. InString, NewInString: Boolean;
  1555. begin
  1556. if p = nil then begin
  1557. res:= '''''';
  1558. end
  1559. else
  1560. begin
  1561. res := '';
  1562. InString := False;
  1563. while P < LastP do
  1564. begin
  1565. NewInString := InString;
  1566. w := CharToOrdfunc(P);
  1567. if w = ord('''') then
  1568. begin //quote AnsiChar
  1569. if not InString then
  1570. NewInString := True;
  1571. NewStr := '''''';
  1572. end
  1573. else if (Ord(w) >= 32) and ((Ord(w) < 127) or (UseBytes and (Ord(w)<256))) then
  1574. begin //printable ascii or bytes
  1575. if not InString then
  1576. NewInString := True;
  1577. NewStr := AnsiChar(w);
  1578. end
  1579. else
  1580. begin //ascii control chars, non ascii
  1581. if InString then
  1582. NewInString := False;
  1583. NewStr := '#' + IntToStr(w);
  1584. end;
  1585. if NewInString <> InString then
  1586. begin
  1587. NewStr := '''' + NewStr;
  1588. InString := NewInString;
  1589. end;
  1590. res := res + NewStr;
  1591. end;
  1592. if InString then
  1593. res := res + '''';
  1594. end;
  1595. OutStr(res);
  1596. end;
  1597. procedure OutString(s: RawByteString);
  1598. begin
  1599. OutChars(Pointer(S),PAnsiChar(S)+Length(S),@CharToOrd,Encoding=oteLFM);
  1600. end;
  1601. procedure OutWString(W: WideString);
  1602. begin
  1603. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1604. end;
  1605. procedure OutUString(W: UnicodeString);
  1606. begin
  1607. OutChars(Pointer(W),pwidechar(W)+Length(W),@WideCharToOrd);
  1608. end;
  1609. procedure OutUtf8Str(s: RawByteString);
  1610. begin
  1611. if Encoding=oteLFM then
  1612. OutChars(Pointer(S),PAnsiChar(S)+Length(S),@CharToOrd)
  1613. else
  1614. OutChars(Pointer(S),PAnsiChar(S)+Length(S),@Utf8ToOrd);
  1615. end;
  1616. function ReadWord : word; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1617. begin
  1618. Result:=Input.ReadWord;
  1619. Result:=LEtoN(Result);
  1620. end;
  1621. function ReadDWord : longword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1622. begin
  1623. Result:=Input.ReadDWord;
  1624. Result:=LEtoN(Result);
  1625. end;
  1626. function ReadQWord : qword; {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1627. begin
  1628. Input.ReadBuffer(Result,sizeof(Result));
  1629. Result:=LEtoN(Result);
  1630. end;
  1631. {$ifndef FPUNONE}
  1632. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1633. function ExtendedToDouble(e : pointer) : double;
  1634. var mant : qword;
  1635. exp : smallint;
  1636. sign : boolean;
  1637. d : qword;
  1638. begin
  1639. move(pbyte(e)[0],mant,8); //mantissa : bytes 0..7
  1640. move(pbyte(e)[8],exp,2); //exponent and sign: bytes 8..9
  1641. mant:=LEtoN(mant);
  1642. exp:=LetoN(word(exp));
  1643. sign:=(exp and $8000)<>0;
  1644. if sign then exp:=exp and $7FFF;
  1645. case exp of
  1646. 0 : mant:=0; //if denormalized, value is too small for double,
  1647. //so it's always zero
  1648. $7FFF : exp:=2047 //either infinity or NaN
  1649. else
  1650. begin
  1651. dec(exp,16383-1023);
  1652. if (exp>=-51) and (exp<=0) then //can be denormalized
  1653. begin
  1654. mant:=mant shr (-exp);
  1655. exp:=0;
  1656. end
  1657. else
  1658. if (exp<-51) or (exp>2046) then //exponent too large.
  1659. begin
  1660. Result:=0;
  1661. exit;
  1662. end
  1663. else //normalized value
  1664. mant:=mant shl 1; //hide most significant bit
  1665. end;
  1666. end;
  1667. d:=word(exp);
  1668. d:=d shl 52;
  1669. mant:=mant shr 12;
  1670. d:=d or mant;
  1671. if sign then d:=d or $8000000000000000;
  1672. Result:=pdouble(@d)^;
  1673. end;
  1674. {$ENDIF}
  1675. {$endif}
  1676. function ReadInt(ValueType: TValueType): Int64;
  1677. begin
  1678. case ValueType of
  1679. vaInt8: Result := ShortInt(Input.ReadByte);
  1680. vaInt16: Result := SmallInt(ReadWord);
  1681. vaInt32: Result := LongInt(ReadDWord);
  1682. vaInt64: Result := Int64(ReadQWord);
  1683. end;
  1684. end;
  1685. function ReadInt: Int64;
  1686. begin
  1687. Result := ReadInt(TValueType(Input.ReadByte));
  1688. end;
  1689. {$ifndef FPUNONE}
  1690. function ReadExtended : extended;
  1691. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1692. var ext : array[0..9] of byte;
  1693. {$ENDIF}
  1694. begin
  1695. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1696. Input.ReadBuffer(ext[0],10);
  1697. Result:=ExtendedToDouble(@(ext[0]));
  1698. {$ELSE}
  1699. Input.ReadBuffer(Result,sizeof(Result));
  1700. {$ENDIF}
  1701. end;
  1702. {$endif}
  1703. function ReadSStr: RawByteString;
  1704. var
  1705. len: Byte;
  1706. begin
  1707. len := Input.ReadByte;
  1708. SetLength(Result, len);
  1709. if (len > 0) then
  1710. Input.ReadBuffer(Result[1], len);
  1711. end;
  1712. function ReadLStr: RawByteString;
  1713. var
  1714. len: DWord;
  1715. begin
  1716. len := ReadDWord;
  1717. SetLength(Result, len);
  1718. if (len > 0) then
  1719. Input.ReadBuffer(Result[1], len);
  1720. end;
  1721. function ReadWStr: WideString;
  1722. var
  1723. len: DWord;
  1724. {$IFDEF ENDIAN_BIG}
  1725. i : integer;
  1726. {$ENDIF}
  1727. begin
  1728. len := ReadDWord;
  1729. SetLength(Result, len);
  1730. if (len > 0) then
  1731. begin
  1732. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1733. {$IFDEF ENDIAN_BIG}
  1734. for i:=1 to len do
  1735. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1736. {$ENDIF}
  1737. end;
  1738. end;
  1739. function ReadUStr: UnicodeString;
  1740. var
  1741. len: DWord;
  1742. {$IFDEF ENDIAN_BIG}
  1743. i : integer;
  1744. {$ENDIF}
  1745. begin
  1746. len := ReadDWord;
  1747. SetLength(Result, len);
  1748. if (len > 0) then
  1749. begin
  1750. Input.ReadBuffer(Pointer(@Result[1])^, len*2);
  1751. {$IFDEF ENDIAN_BIG}
  1752. for i:=1 to len do
  1753. Result[i]:=widechar(SwapEndian(word(Result[i])));
  1754. {$ENDIF}
  1755. end;
  1756. end;
  1757. procedure ReadPropList(indent: RawByteString);
  1758. procedure ProcessValue(ValueType: TValueType; Indent: RawByteString);
  1759. procedure ProcessBinary;
  1760. var
  1761. ToDo, DoNow, i: LongInt;
  1762. lbuf: array[0..31] of Byte;
  1763. s: RawByteString;
  1764. begin
  1765. ToDo := ReadDWord;
  1766. OutLn('{');
  1767. while ToDo > 0 do begin
  1768. DoNow := ToDo;
  1769. if DoNow > 32 then DoNow := 32;
  1770. Dec(ToDo, DoNow);
  1771. s := Indent + ' ';
  1772. Input.ReadBuffer(lbuf, DoNow);
  1773. for i := 0 to DoNow - 1 do
  1774. s := s + IntToHex(lbuf[i], 2);
  1775. OutLn(s);
  1776. end;
  1777. OutLn(indent + '}');
  1778. end;
  1779. var
  1780. s: RawByteString;
  1781. { len: LongInt; }
  1782. IsFirst: Boolean;
  1783. {$ifndef FPUNONE}
  1784. ext: Extended;
  1785. {$endif}
  1786. begin
  1787. case ValueType of
  1788. vaList: begin
  1789. OutStr('(');
  1790. IsFirst := True;
  1791. while True do begin
  1792. ValueType := TValueType(Input.ReadByte);
  1793. if ValueType = vaNull then break;
  1794. if IsFirst then begin
  1795. OutLn('');
  1796. IsFirst := False;
  1797. end;
  1798. OutStr(Indent + ' ');
  1799. ProcessValue(ValueType, Indent + ' ');
  1800. end;
  1801. OutLn(Indent + ')');
  1802. end;
  1803. vaInt8: OutLn(IntToStr(ShortInt(Input.ReadByte)));
  1804. vaInt16: OutLn( IntToStr(SmallInt(ReadWord)));
  1805. vaInt32: OutLn(IntToStr(LongInt(ReadDWord)));
  1806. vaInt64: OutLn(IntToStr(Int64(ReadQWord)));
  1807. {$ifndef FPUNONE}
  1808. vaExtended: begin
  1809. ext:=ReadExtended;
  1810. Str(ext,S);// Do not use localized strings.
  1811. OutLn(S);
  1812. end;
  1813. {$endif}
  1814. vaString: begin
  1815. OutString(ReadSStr);
  1816. OutLn('');
  1817. end;
  1818. vaIdent: OutLn(ReadSStr);
  1819. vaFalse: OutLn('False');
  1820. vaTrue: OutLn('True');
  1821. vaBinary: ProcessBinary;
  1822. vaSet: begin
  1823. OutStr('[');
  1824. IsFirst := True;
  1825. while True do begin
  1826. s := ReadSStr;
  1827. if Length(s) = 0 then break;
  1828. if not IsFirst then OutStr(', ');
  1829. IsFirst := False;
  1830. OutStr(s);
  1831. end;
  1832. OutLn(']');
  1833. end;
  1834. vaLString:
  1835. begin
  1836. OutString(ReadLStr);
  1837. OutLn('');
  1838. end;
  1839. vaWString:
  1840. begin
  1841. OutWString(ReadWStr);
  1842. OutLn('');
  1843. end;
  1844. vaUString:
  1845. begin
  1846. OutWString(ReadWStr);
  1847. OutLn('');
  1848. end;
  1849. vaNil:
  1850. OutLn('nil');
  1851. vaCollection: begin
  1852. OutStr('<');
  1853. while Input.ReadByte <> 0 do begin
  1854. OutLn(Indent);
  1855. Input.Seek(-1, soFromCurrent);
  1856. OutStr(indent + ' item');
  1857. ValueType := TValueType(Input.ReadByte);
  1858. if ValueType <> vaList then
  1859. OutStr('[' + IntToStr(ReadInt(ValueType)) + ']');
  1860. OutLn('');
  1861. ReadPropList(indent + ' ');
  1862. OutStr(indent + ' end');
  1863. end;
  1864. OutLn('>');
  1865. end;
  1866. {vaSingle: begin OutLn('!!Single!!'); exit end;
  1867. vaCurrency: begin OutLn('!!Currency!!'); exit end;
  1868. vaDate: begin OutLn('!!Date!!'); exit end;}
  1869. vaUTF8String: begin
  1870. OutUtf8Str(ReadLStr);
  1871. OutLn('');
  1872. end;
  1873. else
  1874. Raise EReadError.CreateFmt(SErrInvalidPropertyType,[Ord(ValueType)]);
  1875. end;
  1876. end;
  1877. begin
  1878. while Input.ReadByte <> 0 do begin
  1879. Input.Seek(-1, soFromCurrent);
  1880. OutStr(indent + ReadSStr + ' = ');
  1881. ProcessValue(TValueType(Input.ReadByte), Indent);
  1882. end;
  1883. end;
  1884. procedure ReadObject(indent: RawByteString);
  1885. var
  1886. b: Byte;
  1887. ObjUnitName, ObjClassName, ObjName: RawByteString;
  1888. ChildPos: LongInt;
  1889. ValueType: TValueType;
  1890. p: SizeInt;
  1891. begin
  1892. // Check for FilerFlags
  1893. b := Input.ReadByte;
  1894. if (b and $f0) = $f0 then begin
  1895. if (b and 2) <> 0 then ChildPos := ReadInt;
  1896. end else begin
  1897. b := 0;
  1898. Input.Seek(-1, soFromCurrent);
  1899. end;
  1900. ObjUnitName:='';
  1901. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  1902. begin
  1903. ValueType := TValueType(Input.ReadByte);
  1904. if ValueType=vaString then
  1905. ObjClassName := ReadSStr
  1906. else
  1907. ObjClassName := ReadLStr;
  1908. p:=Pos(TBinaryObjectReader.UnitnameSeparator,ObjClassName);
  1909. if p>0 then
  1910. begin
  1911. ObjUnitName:=copy(ObjClassName,1,p-1);
  1912. System.Delete(ObjClassName,1,p);
  1913. end;
  1914. end else
  1915. ObjClassName := ReadSStr;
  1916. ObjName := ReadSStr;
  1917. OutStr(Indent);
  1918. if (b and 1) <> 0 then
  1919. OutStr('inherited')
  1920. else if (b and 4) <> 0 then
  1921. OutStr('inline')
  1922. else
  1923. OutStr('object');
  1924. OutStr(' ');
  1925. if ObjName <> '' then
  1926. OutStr(ObjName + ': ');
  1927. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  1928. begin
  1929. OutStr(ObjUnitName);
  1930. OutStr('/');
  1931. end;
  1932. OutStr(ObjClassName);
  1933. if (b and 2) <> 0 then
  1934. OutStr('[' + IntToStr(ChildPos) + ']');
  1935. OutLn('');
  1936. ReadPropList(indent + ' ');
  1937. while Input.ReadByte <> 0 do begin
  1938. Input.Seek(-1, soFromCurrent);
  1939. ReadObject(indent + ' ');
  1940. end;
  1941. OutLn(indent + 'end');
  1942. end;
  1943. var
  1944. Signature: DWord;
  1945. begin
  1946. Signature:=Input.ReadDWord;
  1947. if Signature = DWord(unaligned(FilerSignature1)) then
  1948. Version:=TBinaryObjectReader.TBOVersion.boVersion1
  1949. else if Signature = DWord(unaligned(FilerSignature)) then
  1950. Version:=TBinaryObjectReader.TBOVersion.boVersion0
  1951. else
  1952. raise EReadError.Create('Illegal stream image' {###SInvalidImage});
  1953. ReadObject('');
  1954. end;
  1955. procedure ObjectBinaryToText(Input, Output: TStream);
  1956. begin
  1957. ObjectBinaryToText(Input,Output,oteDFM);
  1958. end;
  1959. procedure ObjectTextToBinary(Input, Output: TStream);
  1960. var
  1961. parser: TParser;
  1962. Version: TBinaryObjectReader.TBOVersion;
  1963. StartPos: Int64;
  1964. procedure WriteWord(w : word); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1965. begin
  1966. w:=NtoLE(w);
  1967. Output.WriteWord(w);
  1968. end;
  1969. procedure WriteDWord(lw : longword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1970. begin
  1971. lw:=NtoLE(lw);
  1972. Output.WriteDWord(lw);
  1973. end;
  1974. procedure WriteQWord(qw : qword); {$ifdef CLASSESINLINE}inline;{$endif CLASSESINLINE}
  1975. begin
  1976. qw:=NtoLE(qw);
  1977. Output.WriteBuffer(qw,sizeof(qword));
  1978. end;
  1979. {$ifndef FPUNONE}
  1980. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  1981. procedure DoubleToExtended(d : double; e : pointer);
  1982. var mant : qword;
  1983. exp : smallint;
  1984. sign : boolean;
  1985. begin
  1986. mant:=(qword(d) and $000FFFFFFFFFFFFF) shl 12;
  1987. exp :=(qword(d) shr 52) and $7FF;
  1988. sign:=(qword(d) and $8000000000000000)<>0;
  1989. case exp of
  1990. 0 : begin
  1991. if mant<>0 then //denormalized value: hidden bit is 0. normalize it
  1992. begin
  1993. exp:=16383-1022;
  1994. while (mant and $8000000000000000)=0 do
  1995. begin
  1996. dec(exp);
  1997. mant:=mant shl 1;
  1998. end;
  1999. dec(exp); //don't shift, most significant bit is not hidden in extended
  2000. end;
  2001. end;
  2002. 2047 : exp:=$7FFF //either infinity or NaN
  2003. else
  2004. begin
  2005. inc(exp,16383-1023);
  2006. mant:=(mant shr 1) or $8000000000000000; //unhide hidden bit
  2007. end;
  2008. end;
  2009. if sign then exp:=exp or $8000;
  2010. mant:=NtoLE(mant);
  2011. exp:=NtoLE(word(exp));
  2012. move(mant,pbyte(e)[0],8); //mantissa : bytes 0..7
  2013. move(exp,pbyte(e)[8],2); //exponent and sign: bytes 8..9
  2014. end;
  2015. {$ENDIF}
  2016. procedure WriteExtended(const e : extended);
  2017. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  2018. var ext : array[0..9] of byte;
  2019. {$ENDIF}
  2020. begin
  2021. {$IFNDEF FPC_HAS_TYPE_EXTENDED}
  2022. DoubleToExtended(e,@(ext[0]));
  2023. Output.WriteBuffer(ext[0],10);
  2024. {$ELSE}
  2025. Output.WriteBuffer(e,sizeof(e));
  2026. {$ENDIF}
  2027. end;
  2028. {$endif}
  2029. procedure WriteSString(const s: RawByteString);
  2030. var size : byte;
  2031. begin
  2032. if length(s)>255 then size:=255
  2033. else size:=length(s);
  2034. Output.WriteByte(size);
  2035. if Length(s) > 0 then
  2036. Output.WriteBuffer(s[1], size);
  2037. end;
  2038. procedure WriteLString(Const s: RawByteString);
  2039. begin
  2040. WriteDWord(Length(s));
  2041. if Length(s) > 0 then
  2042. Output.WriteBuffer(s[1], Length(s));
  2043. end;
  2044. procedure WriteSorLString(Const s: String);
  2045. begin
  2046. if length(s)<256 then
  2047. begin
  2048. Output.WriteByte(Ord(vaString));
  2049. WriteSString(s);
  2050. end else begin
  2051. Output.WriteByte(Ord(vaLString));
  2052. WriteSString(s);
  2053. end;
  2054. end;
  2055. procedure WriteWString(Const s: WideString);
  2056. var len : longword;
  2057. {$IFDEF ENDIAN_BIG}
  2058. i : integer;
  2059. ws : widestring;
  2060. {$ENDIF}
  2061. begin
  2062. len:=Length(s);
  2063. WriteDWord(len);
  2064. if len > 0 then
  2065. begin
  2066. {$IFDEF ENDIAN_BIG}
  2067. setlength(ws,len);
  2068. for i:=1 to len do
  2069. ws[i]:=widechar(SwapEndian(word(s[i])));
  2070. Output.WriteBuffer(ws[1], len*sizeof(widechar));
  2071. {$ELSE}
  2072. Output.WriteBuffer(s[1], len*sizeof(widechar));
  2073. {$ENDIF}
  2074. end;
  2075. end;
  2076. procedure WriteInteger(value: Int64);
  2077. begin
  2078. if (value >= -128) and (value <= 127) then begin
  2079. Output.WriteByte(Ord(vaInt8));
  2080. Output.WriteByte(byte(value));
  2081. end else if (value >= -32768) and (value <= 32767) then begin
  2082. Output.WriteByte(Ord(vaInt16));
  2083. WriteWord(word(value));
  2084. end else if (value >= -2147483648) and (value <= 2147483647) then begin
  2085. Output.WriteByte(Ord(vaInt32));
  2086. WriteDWord(longword(value));
  2087. end else begin
  2088. Output.WriteByte(ord(vaInt64));
  2089. WriteQWord(qword(value));
  2090. end;
  2091. end;
  2092. procedure ProcessWideString(const left : widestring);
  2093. var ws : widestring;
  2094. begin
  2095. ws:=left+parser.TokenWideString;
  2096. while parser.NextToken = '+' do
  2097. begin
  2098. parser.NextToken; // Get next string fragment
  2099. if not (parser.Token in [toString,toWString]) then
  2100. parser.CheckToken(toWString);
  2101. ws:=ws+parser.TokenWideString;
  2102. end;
  2103. Output.WriteByte(Ord(vaWstring));
  2104. WriteWString(ws);
  2105. end;
  2106. procedure ProcessProperty; forward;
  2107. procedure ProcessValue;
  2108. var
  2109. {$ifndef FPUNONE}
  2110. flt: Extended;
  2111. {$endif}
  2112. s: RawByteString;
  2113. stream: TMemoryStream;
  2114. begin
  2115. case parser.Token of
  2116. toInteger:
  2117. begin
  2118. WriteInteger(parser.TokenInt);
  2119. parser.NextToken;
  2120. end;
  2121. {$ifndef FPUNONE}
  2122. toFloat:
  2123. begin
  2124. Output.WriteByte(Ord(vaExtended));
  2125. flt := Parser.TokenFloat;
  2126. WriteExtended(flt);
  2127. parser.NextToken;
  2128. end;
  2129. {$endif}
  2130. toString:
  2131. begin
  2132. s := parser.TokenString;
  2133. while parser.NextToken = '+' do
  2134. begin
  2135. parser.NextToken; // Get next string fragment
  2136. case parser.Token of
  2137. toString : s:=s+parser.TokenString;
  2138. toWString : begin
  2139. ProcessWideString(WideString(s));
  2140. exit;
  2141. end
  2142. else parser.CheckToken(toString);
  2143. end;
  2144. end;
  2145. if (length(S)>255) then
  2146. begin
  2147. Output.WriteByte(Ord(vaLString));
  2148. WriteLString(S);
  2149. end
  2150. else
  2151. begin
  2152. Output.WriteByte(Ord(vaString));
  2153. WriteSString(s);
  2154. end;
  2155. end;
  2156. toWString:
  2157. ProcessWideString('');
  2158. toSymbol:
  2159. begin
  2160. if CompareText(parser.TokenString, 'True') = 0 then
  2161. Output.WriteByte(Ord(vaTrue))
  2162. else if CompareText(parser.TokenString, 'False') = 0 then
  2163. Output.WriteByte(Ord(vaFalse))
  2164. else if CompareText(parser.TokenString, 'nil') = 0 then
  2165. Output.WriteByte(Ord(vaNil))
  2166. else
  2167. begin
  2168. Output.WriteByte(Ord(vaIdent));
  2169. WriteSString(parser.TokenComponentIdent);
  2170. end;
  2171. Parser.NextToken;
  2172. end;
  2173. // Set
  2174. '[':
  2175. begin
  2176. parser.NextToken;
  2177. Output.WriteByte(Ord(vaSet));
  2178. if parser.Token <> ']' then
  2179. while True do
  2180. begin
  2181. parser.CheckToken(toSymbol);
  2182. WriteSString(parser.TokenString);
  2183. parser.NextToken;
  2184. if parser.Token = ']' then
  2185. break;
  2186. parser.CheckToken(',');
  2187. parser.NextToken;
  2188. end;
  2189. Output.WriteByte(0);
  2190. parser.NextToken;
  2191. end;
  2192. // List
  2193. '(':
  2194. begin
  2195. parser.NextToken;
  2196. Output.WriteByte(Ord(vaList));
  2197. while parser.Token <> ')' do
  2198. ProcessValue;
  2199. Output.WriteByte(0);
  2200. parser.NextToken;
  2201. end;
  2202. // Collection
  2203. '<':
  2204. begin
  2205. parser.NextToken;
  2206. Output.WriteByte(Ord(vaCollection));
  2207. while parser.Token <> '>' do
  2208. begin
  2209. parser.CheckTokenSymbol('item');
  2210. parser.NextToken;
  2211. // ConvertOrder
  2212. Output.WriteByte(Ord(vaList));
  2213. while not parser.TokenSymbolIs('end') do
  2214. ProcessProperty;
  2215. parser.NextToken; // Skip 'end'
  2216. Output.WriteByte(0);
  2217. end;
  2218. Output.WriteByte(0);
  2219. parser.NextToken;
  2220. end;
  2221. // Binary data
  2222. '{':
  2223. begin
  2224. Output.WriteByte(Ord(vaBinary));
  2225. stream := TMemoryStream.Create;
  2226. try
  2227. parser.HexToBinary(stream);
  2228. WriteDWord(stream.Size);
  2229. Output.WriteBuffer(Stream.Memory^, stream.Size);
  2230. finally
  2231. stream.Free;
  2232. end;
  2233. parser.NextToken;
  2234. end;
  2235. else
  2236. parser.Error(SInvalidProperty);
  2237. end;
  2238. end;
  2239. procedure ProcessProperty;
  2240. var
  2241. name: RawByteString;
  2242. begin
  2243. // Get name of property
  2244. parser.CheckToken(toSymbol);
  2245. name := parser.TokenString;
  2246. while True do begin
  2247. parser.NextToken;
  2248. if parser.Token <> '.' then break;
  2249. parser.NextToken;
  2250. parser.CheckToken(toSymbol);
  2251. name := name + '.' + parser.TokenString;
  2252. end;
  2253. WriteSString(name);
  2254. parser.CheckToken('=');
  2255. parser.NextToken;
  2256. ProcessValue;
  2257. end;
  2258. procedure ProcessObject(Root: boolean);
  2259. var
  2260. Flags: Byte;
  2261. ObjectName, ObjUnitName, ObjClassName: RawByteString;
  2262. ChildPos: Integer;
  2263. begin
  2264. if parser.TokenSymbolIs('OBJECT') then
  2265. Flags :=0 { IsInherited := False }
  2266. else begin
  2267. if parser.TokenSymbolIs('INHERITED') then
  2268. Flags := 1 { IsInherited := True; }
  2269. else begin
  2270. parser.CheckTokenSymbol('INLINE');
  2271. Flags := 4;
  2272. end;
  2273. end;
  2274. parser.NextToken;
  2275. parser.CheckToken(toSymbol);
  2276. ObjectName := '';
  2277. ObjUnitName := '';
  2278. ObjClassName := parser.TokenString;
  2279. parser.NextToken;
  2280. if parser.Token = '/' then begin
  2281. ObjUnitName := ObjClassName;
  2282. parser.NextToken;
  2283. parser.CheckToken(toSymbol);
  2284. ObjClassName := parser.TokenString;
  2285. parser.NextToken;
  2286. end else if parser.Token = ':' then begin
  2287. parser.NextToken;
  2288. parser.CheckToken(toSymbol);
  2289. ObjectName := ObjClassName;
  2290. ObjClassName := parser.TokenString;
  2291. parser.NextToken;
  2292. if parser.Token = '/' then begin
  2293. ObjUnitName := ObjClassName;
  2294. parser.NextToken;
  2295. parser.CheckToken(toSymbol);
  2296. ObjClassName := parser.TokenString;
  2297. parser.NextToken;
  2298. end;
  2299. if parser.Token = '[' then begin
  2300. parser.NextToken;
  2301. ChildPos := parser.TokenInt;
  2302. parser.NextToken;
  2303. parser.CheckToken(']');
  2304. parser.NextToken;
  2305. Flags := Flags or 2;
  2306. end;
  2307. end;
  2308. if Root then
  2309. begin
  2310. if (ObjUnitName<>'') then
  2311. Version:=TBinaryObjectReader.TBOVersion.boVersion1;
  2312. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  2313. Output.WriteBuffer(FilerSignature1[1], length(FilerSignature1))
  2314. else
  2315. Output.WriteBuffer(FilerSignature[1], length(FilerSignature));
  2316. end;
  2317. if Flags <> 0 then begin
  2318. Output.WriteByte($f0 or Flags);
  2319. if (Flags and 2) <> 0 then
  2320. WriteInteger(ChildPos);
  2321. end;
  2322. if Version=TBinaryObjectReader.TBOVersion.boVersion1 then
  2323. WriteSorLString(ObjUnitName+TBinaryObjectReader.UnitnameSeparator+ObjClassName)
  2324. else
  2325. WriteSString(ObjClassName);
  2326. WriteSString(ObjectName);
  2327. // Convert property list
  2328. while not (parser.TokenSymbolIs('END') or
  2329. parser.TokenSymbolIs('OBJECT') or
  2330. parser.TokenSymbolIs('INHERITED') or
  2331. parser.TokenSymbolIs('INLINE')) do
  2332. ProcessProperty;
  2333. Output.WriteByte(0); // Terminate property list
  2334. // Convert child objects
  2335. while not parser.TokenSymbolIs('END') do ProcessObject(false);
  2336. parser.NextToken; // Skip end token
  2337. Output.WriteByte(0); // Terminate property list
  2338. end;
  2339. const
  2340. signature: PAnsiChar = 'TPF0';
  2341. begin
  2342. Version:=TBinaryObjectReader.TBOVersion.boVersion0;
  2343. parser := TParser.Create(Input);
  2344. try
  2345. StartPos:=Output.Position;
  2346. ProcessObject(true);
  2347. finally
  2348. parser.Free;
  2349. end;
  2350. end;
  2351. procedure ObjectResourceToText(Input, Output: TStream);
  2352. begin
  2353. Input.ReadResHeader;
  2354. ObjectBinaryToText(Input, Output);
  2355. end;
  2356. procedure ObjectTextToResource(Input, Output: TStream);
  2357. var
  2358. StartPos, FixupInfo: LongInt;
  2359. parser: TParser;
  2360. name: String;
  2361. begin
  2362. // Get form type name
  2363. StartPos := Input.Position;
  2364. parser := TParser.Create(Input);
  2365. try
  2366. if not parser.TokenSymbolIs('OBJECT') then parser.CheckTokenSymbol('INHERITED');
  2367. parser.NextToken;
  2368. parser.CheckToken(toSymbol);
  2369. parser.NextToken;
  2370. parser.CheckToken(':');
  2371. parser.NextToken;
  2372. parser.CheckToken(toSymbol);
  2373. name := parser.TokenString;
  2374. finally
  2375. parser.Free;
  2376. Input.Position := StartPos;
  2377. end;
  2378. name := UpperCase(name);
  2379. Output.WriteResourceHeader(name,FixupInfo); // Write resource header
  2380. ObjectTextToBinary(Input, Output); // Convert the stuff!
  2381. Output.FixupResourceHeader(FixupInfo); // Insert real resource data size
  2382. end;
  2383. function TestStreamFormat(const Stream: TStream): TStreamOriginalFormat;
  2384. const
  2385. StartChars = [#9, #10, #13, #11, 'o', 'O','i','I',' '];
  2386. var
  2387. aPos: Int64;
  2388. Sig: Packed Array[0..3] of byte;
  2389. IntSig : Longint absolute sig;
  2390. begin
  2391. Result:=sofUnknown;
  2392. aPos:=Stream.Position;
  2393. IntSig:=0;
  2394. Stream.Read(Sig,SizeOf(Sig));
  2395. Stream.Seek(aPos,soBeginning);
  2396. if (Sig[0]=$FF) or (IntSig=LongInt(FilerSignature)) or (IntSig=LongInt(FilerSignature1)) or (IntSig=0) then
  2397. Result:=sofBinary
  2398. else if (AnsiChar(Sig[0]) in StartChars) then
  2399. Result:=sofText
  2400. else if (Sig[0]=$EF) and (Sig[1]=$BB) and (Sig[2]=$BF) then
  2401. Result:=sofUTF8Text;
  2402. end;
  2403. { Utility routines }
  2404. Function IfThen(AValue: Boolean; const ATrue: TStringList; const AFalse: TStringList = nil): TStringList; overload;
  2405. begin
  2406. if avalue then
  2407. result:=atrue
  2408. else
  2409. result:=afalse;
  2410. end;
  2411. function LineStart(Buffer, BufPos: PAnsiChar): PAnsiChar;
  2412. begin
  2413. Result := BufPos;
  2414. while Result > Buffer do begin
  2415. Dec(Result);
  2416. if Result[0] = #10 then break;
  2417. end;
  2418. end;
  2419. procedure CommonInit;
  2420. begin
  2421. {$ifdef FPC_HAS_FEATURE_THREADING}
  2422. SynchronizeTimeoutEvent:=RtlEventCreate;
  2423. InterlockedIncrement(ThreadQueueLockCounter);
  2424. InitCriticalSection(ThreadQueueLock);
  2425. MainThreadID:=GetCurrentThreadID;
  2426. {$else}
  2427. MainThreadID:=0{GetCurrentThreadID};
  2428. {$endif}
  2429. ExternalThreads := TThreadList.Create;
  2430. {$ifdef FPC_HAS_FEATURE_THREADING}
  2431. InitCriticalsection(ResolveSection);
  2432. TThread.FProcessorCount := CPUCount;
  2433. {$else}
  2434. TThread.FProcessorCount := 1{CPUCount};
  2435. {$endif}
  2436. InitHandlerList:=Nil;
  2437. FindGlobalComponentList:=nil;
  2438. IntConstList := TThreadList.Create;
  2439. ClassList := TThreadList.Create;
  2440. ClassAliasList := nil;
  2441. { on unix this maps to a simple rw synchornizer }
  2442. GlobalNameSpace := TMultiReadExclusiveWriteSynchronizer.Create;
  2443. RegisterInitComponentHandler(TComponent,@DefaultInitHandler);
  2444. end;
  2445. procedure CommonCleanup;
  2446. var
  2447. i: Integer;
  2448. tmpentry: TThread.PThreadQueueEntry;
  2449. begin
  2450. {$ifdef FPC_HAS_FEATURE_THREADING}
  2451. GlobalNameSpace.BeginWrite;
  2452. {$endif}
  2453. with IntConstList.LockList do
  2454. try
  2455. for i := 0 to Count - 1 do
  2456. TIntConst(Items[I]).Free;
  2457. finally
  2458. IntConstList.UnlockList;
  2459. end;
  2460. IntConstList.Free;
  2461. ClassList.Free;
  2462. ClassAliasList.Free;
  2463. RemoveFixupReferences(nil, '');
  2464. {$ifdef FPC_HAS_FEATURE_THREADING}
  2465. DoneCriticalsection(ResolveSection);
  2466. {$endif}
  2467. GlobalLists.Free;
  2468. ComponentPages.Free;
  2469. FreeAndNil(NeedResolving);
  2470. {$ifdef FPC_HAS_FEATURE_THREADING}
  2471. GlobalNameSpace.EndWrite;
  2472. {$endif}
  2473. { GlobalNameSpace is an interface so this is enough }
  2474. GlobalNameSpace:=nil;
  2475. if (InitHandlerList<>Nil) then
  2476. for i := 0 to InitHandlerList.Count - 1 do
  2477. TInitHandler(InitHandlerList.Items[I]).Free;
  2478. InitHandlerList.Free;
  2479. InitHandlerList:=Nil;
  2480. FindGlobalComponentList.Free;
  2481. FindGlobalComponentList:=nil;
  2482. ExternalThreadsCleanup:=True;
  2483. with ExternalThreads.LockList do
  2484. try
  2485. for i := 0 to Count - 1 do
  2486. TThread(Items[i]).Free;
  2487. finally
  2488. ExternalThreads.UnlockList;
  2489. end;
  2490. FreeAndNil(ExternalThreads);
  2491. {$ifdef FPC_HAS_FEATURE_THREADING}
  2492. RtlEventDestroy(SynchronizeTimeoutEvent);
  2493. try
  2494. System.EnterCriticalSection(ThreadQueueLock);
  2495. {$endif}
  2496. { clean up the queue, but keep in mind that the entries used for Synchronize
  2497. are owned by the corresponding TThread }
  2498. while Assigned(ThreadQueueHead) do begin
  2499. tmpentry := ThreadQueueHead;
  2500. ThreadQueueHead := tmpentry^.Next;
  2501. if not Assigned(tmpentry^.SyncEvent) then
  2502. Dispose(tmpentry);
  2503. end;
  2504. { We also need to reset ThreadQueueTail }
  2505. ThreadQueueTail := nil;
  2506. {$ifdef FPC_HAS_FEATURE_THREADING}
  2507. finally
  2508. System.LeaveCriticalSection(ThreadQueueLock);
  2509. end;
  2510. if InterlockedDecrement(ThreadQueueLockCounter)=0 then
  2511. DoneCriticalSection(ThreadQueueLock);
  2512. {$endif}
  2513. end;
  2514. { TFiler implementation }
  2515. {$i filer.inc}
  2516. { TReader implementation }
  2517. {$i reader.inc}
  2518. { TWriter implementations }
  2519. {$i writer.inc}
  2520. {$i twriter.inc}
  2521. constructor ComponentPlatformsAttribute.Create(const aPlatforms: TPlatformIds);
  2522. begin
  2523. FPlatForms:=aPlatForms;
  2524. end;