GXS.PersistentClasses.pas 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819
  1. //
  2. // The graphics engine GXScene https://github.com/glscene
  3. //
  4. unit GXS.PersistentClasses;
  5. (*
  6. Base persistence classes.
  7. These classes are used in GXScene, but are designed for generic purpose.
  8. They implement a slightly different persistence mechanism than that of the FMX,
  9. allowing for object-level versioning (100% backward compatibility) and full
  10. polymorphic persistence.
  11. Internal Note: stripped down versions of XClasses & XLists.
  12. *)
  13. interface
  14. {$I GLScene.Defines.inc}
  15. uses
  16. System.Classes,
  17. System.SysUtils,
  18. GLScene.Strings;
  19. type
  20. PObject = ^TObject;
  21. // Virtual layer similar to VCL's TReader (but reusable) }
  22. TgxVirtualReader = class
  23. private
  24. FStream: TStream;
  25. public
  26. constructor Create(Stream: TStream); virtual;
  27. property Stream: TStream read FStream;
  28. procedure ReadTypeError;
  29. procedure Read(var Buf; Count: Longint); virtual; abstract;
  30. function NextValue: TValueType; virtual; abstract;
  31. function ReadInteger: Integer; virtual; abstract;
  32. function ReadBoolean: Boolean; virtual; abstract;
  33. function ReadString: string; virtual; abstract;
  34. function ReadFloat: Extended; virtual; abstract;
  35. procedure ReadListBegin; virtual; abstract;
  36. procedure ReadListEnd; virtual; abstract;
  37. function EndOfList: Boolean; virtual; abstract;
  38. procedure ReadTStrings(aStrings: TStrings);
  39. end;
  40. // Virtual layer similar to VCL's TWriter (but reusable)
  41. TgxVirtualWriter = class
  42. private
  43. FStream: TStream;
  44. public
  45. constructor Create(Stream: TStream); virtual;
  46. property Stream: TStream read FStream;
  47. procedure Write(const Buf; Count: Longint); virtual; abstract;
  48. procedure WriteInteger(anInteger: Integer); virtual; abstract;
  49. procedure WriteBoolean(aBoolean: Boolean); virtual; abstract;
  50. procedure WriteString(const aString: string); virtual; abstract;
  51. procedure WriteFloat(const aFloat: Extended); virtual; abstract;
  52. procedure WriteListBegin; virtual; abstract;
  53. procedure WriteListEnd; virtual; abstract;
  54. procedure WriteTStrings(const aStrings: TStrings; storeObjects: Boolean = True);
  55. end;
  56. TgxVirtualReaderClass = class of TgxVirtualReader;
  57. TgxVirtualWriterClass = class of TgxVirtualWriter;
  58. (* Interface for persistent objects.
  59. This interface does not really allow polymorphic persistence,
  60. but is rather intended as a way to unify persistence calls for iterators. *)
  61. IgxPersistentObject = interface(IInterface)
  62. ['{A9A0198A-F11B-4325-A92C-2F24DB41652B}']
  63. procedure WriteToFiler(writer: TgxVirtualWriter);
  64. procedure ReadFromFiler(reader: TgxVirtualReader);
  65. end;
  66. (* Base class for persistent objects.
  67. The base requirement is implementation of ReadFromFiler & WriteToFiler
  68. in sub-classes, the immediate benefits are support of streaming (to stream,
  69. file or string), assignment and cloning.
  70. The other requirement being the use of a virtual constructor, which allows
  71. polymorphic construction (don't forget to register your subclasses).
  72. Note that TgxPersistentObject implements IUnknown, but does *not* implement
  73. reference counting. *)
  74. TgxPersistentObject = class(TPersistent, IgxPersistentObject)
  75. protected
  76. procedure RaiseFilerException(const archiveVersion: Integer);
  77. function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  78. function _AddRef: Integer; stdcall;
  79. function _Release: Integer; stdcall;
  80. public
  81. constructor Create; virtual;
  82. constructor CreateFromFiler(reader: TgxVirtualReader);
  83. destructor Destroy; override;
  84. procedure Assign(source: TPersistent); override;
  85. function CreateClone: TgxPersistentObject; virtual;
  86. class function FileSignature: string; virtual;
  87. class function FileVirtualWriter: TgxVirtualWriterClass; virtual;
  88. class function FileVirtualReader: TgxVirtualReaderClass; virtual;
  89. procedure WriteToFiler(writer: TgxVirtualWriter); virtual;
  90. procedure ReadFromFiler(reader: TgxVirtualReader); virtual;
  91. procedure SaveToStream(stream: TStream; writerClass: TgxVirtualWriterClass = nil); virtual;
  92. procedure LoadFromStream(stream: TStream; readerClass: TgxVirtualReaderClass = nil); virtual;
  93. procedure SaveToFile(const fileName: string; writerClass: TgxVirtualWriterClass = nil); virtual;
  94. procedure LoadFromFile(const fileName: string; readerClass: TgxVirtualReaderClass = nil); virtual;
  95. function SaveToString(writerClass: TgxVirtualWriterClass = nil): string; virtual;
  96. procedure LoadFromString(const data: string; readerClass: TgxVirtualReaderClass = nil); virtual;
  97. end;
  98. TGLPersistentObjectClass = class of TgxPersistentObject;
  99. TgxPointerObjectList = array[0..MaxInt div (2*SizeOf(Pointer))] of TObject;
  100. PgxPointerObjectList = ^TgxPointerObjectList;
  101. TObjectListSortCompare = function(item1, item2: TObject): Integer;
  102. (* A persistent Object list.
  103. Similar to TList but works on TObject items and has facilities for
  104. persistence of contained data. Unlike the VCL's TObjectList, this one
  105. does NOT free its objects upon destruction or Clear, use Clean and CleanFree
  106. for that, and as such can be used for object referral lists too.
  107. But only TgxPersistentObject items will be streamed appropriately.
  108. The list can be used in a stack-like fashion with Push & Pop, and can
  109. perform basic boolean set operations.
  110. Note: the IndexOf implementation is up to 3 times faster than that of TList *)
  111. TgxPersistentObjectList = class(TgxPersistentObject)
  112. private
  113. FList: PgxPointerObjectList;
  114. FCount: Integer;
  115. FCapacity: Integer;
  116. FGrowthDelta: Integer;
  117. protected
  118. procedure Error; virtual;
  119. function Get(Index: Integer): TObject;
  120. procedure Put(Index: Integer; Item: TObject);
  121. procedure SetCapacity(newCapacity: Integer);
  122. procedure SetCount(NewCount: Integer);
  123. function GetFirst: TObject;
  124. procedure SetFirst(item: TObject);
  125. function GetLast: TObject;
  126. procedure SetLast(item: TObject);
  127. // Default event for ReadFromFiler
  128. procedure AfterObjectCreatedByReader(Sender: TObject); virtual;
  129. procedure DoClean;
  130. public
  131. constructor Create; override;
  132. destructor Destroy; override;
  133. procedure WriteToFiler(writer: TgxVirtualWriter); override;
  134. procedure ReadFromFiler(reader: TgxVirtualReader); override;
  135. procedure ReadFromFilerWithEvent(reader: TgxVirtualReader;
  136. afterSenderObjectCreated: TNotifyEvent);
  137. function Add(const item: TObject): Integer;
  138. procedure AddNils(nbVals: Cardinal);
  139. procedure Delete(index: Integer);
  140. procedure DeleteItems(index: Integer; nbVals: Cardinal);
  141. procedure Exchange(Index1, Index2: Integer);
  142. procedure Insert(Index: Integer; Item: TObject);
  143. procedure InsertNils(index: Integer; nbVals: Cardinal);
  144. procedure Move(CurIndex, NewIndex: Integer);
  145. function Remove(Item: TObject): Integer;
  146. procedure DeleteAndFree(index: Integer);
  147. procedure DeleteAndFreeItems(index: Integer; nbVals: Cardinal);
  148. function RemoveAndFree(item: TObject): Integer;
  149. property GrowthDelta: integer read FGrowthDelta write FGrowthDelta;
  150. function Expand: TgxPersistentObjectList;
  151. property Items[Index: Integer]: TObject read Get write Put; default;
  152. property Count: Integer read FCount write SetCount;
  153. property List: PgxPointerObjectList read FList;
  154. property Capacity: Integer read FCapacity write SetCapacity;
  155. // Makes sure capacity is at least aCapacity.
  156. procedure RequiredCapacity(aCapacity: Integer);
  157. (* Removes all "nil" from the list.
  158. Note: Capacity is unchanged, no memory us freed, the list is just
  159. made shorter. This functions is orders of magnitude faster than
  160. its TList eponymous. *)
  161. procedure Pack;
  162. // Empty the list without freeing the objects.
  163. procedure Clear; virtual;
  164. // Empty the list and free the objects.
  165. procedure Clean; virtual;
  166. // Empty the list, free the objects and Free self.
  167. procedure CleanFree;
  168. function IndexOf(Item: TObject): Integer;
  169. property First: TObject read GetFirst write SetFirst;
  170. property Last: TObject read GetLast write SetLast;
  171. procedure Push(item: TObject);
  172. function Pop: TObject;
  173. procedure PopAndFree;
  174. function AddObjects(const objectList: TgxPersistentObjectList): Integer;
  175. procedure RemoveObjects(const objectList: TgxPersistentObjectList);
  176. procedure Sort(compareFunc: TObjectListSortCompare);
  177. end;
  178. // Wraps a TReader-compatible reader.
  179. TgxBinaryReader = class(TgxVirtualReader)
  180. protected
  181. function ReadValue: TValueType;
  182. function ReadWideString(vType: TValueType): WideString;
  183. public
  184. procedure Read(var Buf; Count: Longint); override;
  185. function NextValue: TValueType; override;
  186. function ReadInteger: Integer; override;
  187. function ReadBoolean: Boolean; override;
  188. function ReadString: string; override;
  189. function ReadFloat: Extended; override;
  190. procedure ReadListBegin; override;
  191. procedure ReadListEnd; override;
  192. function EndOfList: Boolean; override;
  193. end;
  194. // Wraps a TWriter-compatible writer.
  195. TgxBinaryWriter = class(TgxVirtualWriter)
  196. protected
  197. procedure WriteAnsiString(const aString: AnsiString); virtual;
  198. procedure WriteWideString(const aString: WideString); virtual;
  199. public
  200. procedure Write(const Buf; Count: Longint); override;
  201. procedure WriteInteger(anInteger: Integer); override;
  202. procedure WriteBoolean(aBoolean: Boolean); override;
  203. procedure WriteString(const aString: string); override;
  204. procedure WriteFloat(const aFloat: Extended); override;
  205. procedure WriteListBegin; override;
  206. procedure WriteListEnd; override;
  207. end;
  208. // Reads object persistence in Text format.
  209. TgxTextReader = class(TgxVirtualReader)
  210. private
  211. FValueType: string;
  212. FData: string;
  213. protected
  214. procedure ReadLine(const requestedType: string = '');
  215. public
  216. procedure Read(var Buf; Count: Longint); override;
  217. function NextValue: TValueType; override;
  218. function ReadInteger: Integer; override;
  219. function ReadBoolean: Boolean; override;
  220. function ReadString: string; override;
  221. function ReadFloat: Extended; override;
  222. procedure ReadListBegin; override;
  223. procedure ReadListEnd; override;
  224. function EndOfList: Boolean; override;
  225. end;
  226. // Writes object persistence in Text format.
  227. TgxTextWriter = class(TgxVirtualWriter)
  228. private
  229. FIndentLevel: Integer;
  230. protected
  231. procedure WriteLine(const valueType, data: string);
  232. public
  233. constructor Create(aStream: TStream); override;
  234. destructor Destroy; override;
  235. procedure Write(const Buf; Count: Longint); override;
  236. procedure WriteInteger(anInteger: Integer); override;
  237. procedure WriteBoolean(aBoolean: Boolean); override;
  238. procedure WriteString(const aString: string); override;
  239. procedure WriteFloat(const aFloat: Extended); override;
  240. procedure WriteListBegin; override;
  241. procedure WriteListEnd; override;
  242. end;
  243. // TPersistent which has knowledge of its owner.
  244. TgxOwnedPersistent = class(TPersistent)
  245. private
  246. FOwner: TPersistent;
  247. protected
  248. function GetOwner: TPersistent; override;
  249. public
  250. constructor Create(AOwner: TPersistent); virtual;
  251. end;
  252. // TPersistent thet inplements IInterface.
  253. TgxInterfacedPersistent = class(TPersistent, IInterface)
  254. protected
  255. // Implementing IInterface.
  256. function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  257. function _AddRef: Integer; stdcall;
  258. function _Release: Integer; stdcall;
  259. end;
  260. // TCollectionItem thet inplements IInterface.
  261. TgxInterfacedCollectionItem = class(TCollectionItem, IInterface)
  262. protected
  263. // Implementing IInterface.
  264. function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
  265. function _AddRef: Integer; virtual; stdcall;
  266. function _Release: Integer; virtual; stdcall;
  267. end;
  268. // Triggered when file signature does not match.
  269. EInvalidFileSignature = class(Exception)
  270. end;
  271. // Usually triggered when a filing error is detected.
  272. EFilerException = class(Exception)
  273. end;
  274. procedure RaiseFilerException(aClass: TClass; archiveVersion: Integer);
  275. function UTF8ToWideString(const s: AnsiString): WideString;
  276. // ------------------------------------------------------------------
  277. implementation
  278. // ------------------------------------------------------------------
  279. const
  280. cDefaultListGrowthDelta = 16;
  281. const
  282. cVTInteger = 'Int';
  283. cVTFloat = 'Float';
  284. cVTString = 'Str';
  285. cVTBoolean = 'Bool';
  286. cVTRaw = 'Raw';
  287. cVTListBegin = '{';
  288. cVTListEnd = '}';
  289. cTrue = 'True';
  290. cFalse = 'False';
  291. procedure RaiseFilerException(aClass: TClass; archiveVersion: Integer);
  292. begin
  293. raise EFilerException.Create(aClass.ClassName +
  294. strUnknownArchiveVersion + IntToStr(archiveVersion));
  295. end;
  296. function UTF8ToWideString(const s: AnsiString): WideString;
  297. const
  298. bytesFromUTF8: packed array[0..255] of Byte = (
  299. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  300. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  301. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  302. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  303. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  304. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  305. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  306. 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5);
  307. offsetsFromUTF8: array[0..5] of Cardinal = (
  308. $00000000, $00003080, $000E2080, $03C82080, $FA082080, $82082080);
  309. MaximumUCS2: Cardinal = $0000FFFF;
  310. MaximumUCS4: Cardinal = $7FFFFFFF;
  311. ReplacementCharacter: Cardinal = $0000FFFD;
  312. halfShift: Integer = 10;
  313. halfBase: Cardinal = $0010000;
  314. halfMask: Cardinal = $3FF;
  315. SurrogateHighStart: Cardinal = $D800;
  316. SurrogateLowStart: Cardinal = $DC00;
  317. var
  318. sLength, L, J, T: Cardinal;
  319. ch: Cardinal;
  320. extraBytesToWrite: Word;
  321. begin
  322. sLength := Length(s);
  323. if sLength = 0 then
  324. begin
  325. Result := '';
  326. Exit;
  327. end;
  328. SetLength(Result, sLength); // create enough room
  329. L := 1;
  330. T := 1;
  331. while L <= Cardinal(sLength) do
  332. begin
  333. ch := 0;
  334. extraBytesToWrite := bytesFromUTF8[Ord(S[L])];
  335. for J := extraBytesToWrite downto 1 do
  336. begin
  337. ch := ch + Ord(S[L]);
  338. Inc(L);
  339. ch := ch shl 6;
  340. end;
  341. ch := ch + Ord(S[L]);
  342. Inc(L);
  343. ch := ch - offsetsFromUTF8[extraBytesToWrite];
  344. if ch <= MaximumUCS2 then
  345. begin
  346. Result[T] := WideChar(ch);
  347. Inc(T);
  348. end
  349. else if ch > MaximumUCS4 then
  350. begin
  351. Result[T] := WideChar(ReplacementCharacter);
  352. Inc(T);
  353. end
  354. else
  355. begin
  356. ch := ch - halfBase;
  357. Result[T] := WideChar((ch shr halfShift) + SurrogateHighStart);
  358. Inc(T);
  359. Result[T] := WideChar((ch and halfMask) + SurrogateLowStart);
  360. Inc(T);
  361. end;
  362. end;
  363. SetLength(Result, T - 1); // now fix up length
  364. end;
  365. // ------------------
  366. // ------------------ TgxVirtualReader ------------------
  367. // ------------------
  368. constructor TgxVirtualReader.Create(Stream: TStream);
  369. begin
  370. FStream := Stream;
  371. end;
  372. procedure TgxVirtualReader.ReadTypeError;
  373. begin
  374. raise EReadError.CreateFmt('%s, read type error', [ClassName]);
  375. end;
  376. procedure TgxVirtualReader.ReadTStrings(aStrings: TStrings);
  377. var
  378. i: Integer;
  379. objectsStored: Boolean;
  380. begin
  381. aStrings.BeginUpdate;
  382. aStrings.Clear;
  383. objectsStored := ReadBoolean;
  384. i := ReadInteger;
  385. if objectsStored then
  386. while i > 0 do
  387. begin
  388. aStrings.AddObject(ReadString, TObject(Cardinal(ReadInteger)));
  389. Dec(i);
  390. end
  391. else
  392. while i > 0 do
  393. begin
  394. aStrings.Add(ReadString);
  395. Dec(i);
  396. end;
  397. aStrings.EndUpdate;
  398. end;
  399. // ------------------
  400. // ------------------ TgxVirtualWriter ------------------
  401. // ------------------
  402. constructor TgxVirtualWriter.Create(Stream: TStream);
  403. begin
  404. FStream := Stream;
  405. end;
  406. procedure TgxVirtualWriter.WriteTStrings(const aStrings: TStrings;
  407. storeObjects: Boolean = True);
  408. var
  409. i: Integer;
  410. begin
  411. writeBoolean(storeObjects);
  412. if Assigned(aStrings) then
  413. begin
  414. WriteInteger(aStrings.Count);
  415. if storeObjects then
  416. for i := 0 to aStrings.Count - 1 do
  417. begin
  418. WriteString(aStrings[i]);
  419. WriteInteger(Integer(aStrings.Objects[i]));
  420. end
  421. else
  422. for i := 0 to aStrings.Count - 1 do
  423. WriteString(aStrings[i]);
  424. end
  425. else
  426. WriteInteger(0);
  427. end;
  428. // ------------------
  429. // ------------------ TgxPersistentObject ------------------
  430. // ------------------
  431. constructor TgxPersistentObject.Create;
  432. begin
  433. inherited Create;
  434. end;
  435. constructor TgxPersistentObject.CreateFromFiler(reader: TgxVirtualReader);
  436. begin
  437. Create;
  438. ReadFromFiler(reader);
  439. end;
  440. destructor TgxPersistentObject.Destroy;
  441. begin
  442. inherited Destroy;
  443. end;
  444. procedure TgxPersistentObject.Assign(source: TPersistent);
  445. var
  446. ms: TStringStream; // faster than a TMemoryStream...
  447. begin
  448. if source.ClassType = Self.ClassType then
  449. begin
  450. ms := TStringStream.Create('');
  451. try
  452. TgxPersistentObject(source).SaveToStream(ms);
  453. ms.Position := 0;
  454. LoadFromStream(ms);
  455. finally
  456. ms.Free;
  457. end;
  458. end
  459. else
  460. inherited;
  461. end;
  462. function TgxPersistentObject.CreateClone: TgxPersistentObject;
  463. begin
  464. Result := TGLPersistentObjectClass(Self.ClassType).Create;
  465. Result.Assign(Self);
  466. end;
  467. class function TgxPersistentObject.FileSignature: string;
  468. begin
  469. Result := '';
  470. end;
  471. class function TgxPersistentObject.FileVirtualWriter: TgxVirtualWriterClass;
  472. begin
  473. Result := TgxBinaryWriter;
  474. end;
  475. class function TgxPersistentObject.FileVirtualReader: TgxVirtualReaderClass;
  476. begin
  477. Result := TgxBinaryReader;
  478. end;
  479. procedure TgxPersistentObject.WriteToFiler(writer: TgxVirtualWriter);
  480. begin
  481. // nothing
  482. Assert(Assigned(writer));
  483. end;
  484. procedure TgxPersistentObject.ReadFromFiler(reader: TgxVirtualReader);
  485. begin
  486. // nothing
  487. Assert(Assigned(reader));
  488. end;
  489. procedure TgxPersistentObject.RaiseFilerException(const archiveVersion: Integer);
  490. begin
  491. raise EFilerException.Create(ClassName + strUnknownArchiveVersion + IntToStr(archiveVersion)); //IGNORE
  492. end;
  493. function TgxPersistentObject.QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
  494. begin
  495. if GetInterface(IID, Obj) then
  496. Result := S_OK
  497. else
  498. Result := E_NOINTERFACE;
  499. end;
  500. function TgxPersistentObject._AddRef: Integer; stdcall;
  501. begin
  502. // ignore
  503. Result := 1;
  504. end;
  505. function TgxPersistentObject._Release: Integer; stdcall;
  506. begin
  507. // ignore
  508. Result := 0;
  509. end;
  510. procedure TgxPersistentObject.SaveToStream(stream: TStream; writerClass: TgxVirtualWriterClass = nil);
  511. var
  512. wr: TgxVirtualWriter;
  513. fileSig: AnsiString;
  514. begin
  515. if writerClass = nil then
  516. writerClass := TgxBinaryWriter;
  517. wr := writerClass.Create(stream);
  518. try
  519. if FileSignature <> '' then
  520. begin
  521. fileSig := AnsiString(FileSignature);
  522. wr.Write(fileSig[1], Length(fileSig));
  523. end;
  524. WriteToFiler(wr);
  525. finally
  526. wr.Free;
  527. end;
  528. end;
  529. procedure TgxPersistentObject.LoadFromStream(stream: TStream; readerClass: TgxVirtualReaderClass = nil);
  530. var
  531. rd: TgxVirtualReader;
  532. sig: AnsiString;
  533. begin
  534. if readerClass = nil then
  535. readerClass := TgxBinaryReader;
  536. rd := readerClass.Create(stream);
  537. try
  538. if FileSignature <> '' then
  539. begin
  540. SetLength(sig, Length(FileSignature));
  541. rd.Read(sig[1], Length(FileSignature));
  542. if sig <> AnsiString(FileSignature) then
  543. raise EInvalidFileSignature.Create(strInvalidFileSignature);
  544. end;
  545. ReadFromFiler(rd);
  546. finally
  547. rd.Free;
  548. end;
  549. end;
  550. procedure TgxPersistentObject.SaveToFile(const fileName: string; writerClass: TgxVirtualWriterClass = nil);
  551. var
  552. fs: TStream;
  553. begin
  554. if writerClass = nil then
  555. writerClass := FileVirtualWriter;
  556. fs := TFileStream.Create(fileName, fmCreate);
  557. try
  558. SaveToStream(fs, writerClass);
  559. finally
  560. fs.Free;
  561. end;
  562. end;
  563. procedure TgxPersistentObject.LoadFromFile(const fileName: string; readerClass: TgxVirtualReaderClass = nil);
  564. var
  565. fs: TStream;
  566. begin
  567. if readerClass = nil then
  568. readerClass := FileVirtualReader;
  569. fs := TFileStream.Create(fileName, fmOpenRead + fmShareDenyWrite);
  570. try
  571. LoadFromStream(fs, readerClass);
  572. finally
  573. fs.Free;
  574. end;
  575. end;
  576. function TgxPersistentObject.SaveToString(writerClass: TgxVirtualWriterClass = nil): string;
  577. var
  578. ss: TStringStream;
  579. begin
  580. ss := TStringStream.Create('');
  581. try
  582. SaveToStream(ss, writerClass);
  583. Result := ss.DataString;
  584. finally
  585. ss.Free;
  586. end;
  587. end;
  588. procedure TgxPersistentObject.LoadFromString(const data: string; readerClass: TgxVirtualReaderClass = nil);
  589. var
  590. ss: TStringStream;
  591. begin
  592. ss := TStringStream.Create(data);
  593. try
  594. LoadFromStream(ss, readerClass);
  595. finally
  596. ss.Free;
  597. end;
  598. end;
  599. // ------------------
  600. // ------------------ TgxPersistentObjectList ------------------
  601. // ------------------
  602. constructor TgxPersistentObjectList.Create;
  603. begin
  604. inherited Create;
  605. FGrowthDelta := cDefaultListGrowthDelta;
  606. end;
  607. destructor TgxPersistentObjectList.Destroy;
  608. begin
  609. Clear;
  610. inherited Destroy;
  611. end;
  612. function TgxPersistentObjectList.Add(const item: TObject): Integer;
  613. begin
  614. Result := FCount;
  615. if Result = FCapacity then
  616. SetCapacity(FCapacity + FGrowthDelta);
  617. FList^[Result] := Item;
  618. Inc(FCount);
  619. end;
  620. procedure TgxPersistentObjectList.AddNils(nbVals: Cardinal);
  621. begin
  622. if Integer(nbVals) + Count > Capacity then
  623. SetCapacity(Integer(nbVals) + Count);
  624. FillChar(FList[FCount], Integer(nbVals) * SizeOf(TObject), 0);
  625. FCount := FCount + Integer(nbVals);
  626. end;
  627. function TgxPersistentObjectList.AddObjects(const objectList: TgxPersistentObjectList): Integer;
  628. begin
  629. if Assigned(objectList) then
  630. begin
  631. Result := FCount;
  632. SetCount(Result + objectList.Count);
  633. System.Move(objectList.FList^[0], FList^[Result],
  634. objectList.FCount * SizeOf(TObject));
  635. end
  636. else
  637. Result := 0;
  638. end;
  639. procedure TgxPersistentObjectList.RemoveObjects(const objectList: TgxPersistentObjectList);
  640. var
  641. i: Integer;
  642. begin
  643. for i := 0 to objectList.Count - 1 do
  644. Remove(objectList[i]);
  645. end;
  646. procedure TgxPersistentObjectList.Clear;
  647. begin
  648. if Assigned(Self) and Assigned(FList) then
  649. begin
  650. SetCount(0);
  651. SetCapacity(0);
  652. end;
  653. end;
  654. procedure TgxPersistentObjectList.Delete(index: Integer);
  655. begin
  656. {$IFOPT R+}
  657. if Cardinal(Index) >= Cardinal(FCount) then
  658. Error;
  659. {$ENDIF}
  660. Dec(FCount);
  661. if index < FCount then
  662. System.Move(FList[index + 1], FList[index], (FCount - index) * SizeOf(TObject));
  663. end;
  664. procedure TgxPersistentObjectList.DeleteItems(index: Integer; nbVals: Cardinal);
  665. begin
  666. {$IFOPT R+}
  667. Assert(Cardinal(index) < Cardinal(FCount));
  668. {$ENDIF}
  669. if nbVals > 0 then
  670. begin
  671. if index + Integer(nbVals) < FCount then
  672. begin
  673. System.Move(FList[index + Integer(nbVals)],
  674. FList[index],
  675. (FCount - index - Integer(nbVals)) * SizeOf(TObject));
  676. end;
  677. Dec(FCount, nbVals);
  678. end;
  679. end;
  680. procedure TgxPersistentObjectList.Exchange(index1, index2: Integer);
  681. var
  682. item: TObject;
  683. locList: PgxPointerObjectList;
  684. begin
  685. {$IFOPT R+}
  686. if (Cardinal(Index1) >= Cardinal(FCount)) or
  687. (Cardinal(Index2) >= Cardinal(FCount)) then
  688. Error;
  689. {$ENDIF}
  690. locList := FList;
  691. item := locList^[index1];
  692. locList^[index1] := locList^[index2];
  693. locList^[index2] := item;
  694. end;
  695. function TgxPersistentObjectList.Expand: TgxPersistentObjectList;
  696. begin
  697. if FCount = FCapacity then
  698. SetCapacity(FCapacity + FGrowthDelta);
  699. Result := Self;
  700. end;
  701. function TgxPersistentObjectList.GetFirst: TObject;
  702. begin
  703. {$IFOPT R+}
  704. if Cardinal(FCount) = 0 then
  705. Error;
  706. {$ENDIF}
  707. Result := FList^[0];
  708. end;
  709. procedure TgxPersistentObjectList.SetFirst(item: TObject);
  710. begin
  711. {$IFOPT R+}
  712. if Cardinal(FCount) = 0 then
  713. Error;
  714. {$ENDIF}
  715. FList^[0] := item;
  716. end;
  717. procedure TgxPersistentObjectList.Error;
  718. begin
  719. raise EListError.Create(strListIndexError);
  720. end;
  721. function TgxPersistentObjectList.Get(Index: Integer): TObject;
  722. begin
  723. {$IFOPT R+}
  724. if Cardinal(Index) >= Cardinal(FCount) then
  725. Error;
  726. {$ENDIF}
  727. Result := FList^[Index];
  728. end;
  729. function TgxPersistentObjectList.IndexOf(Item: TObject): Integer;
  730. var
  731. I: Integer;
  732. begin
  733. if FCount <= 0 then
  734. Result := -1
  735. else
  736. begin
  737. Result := -1;
  738. for I := 0 to FCount - 1 do
  739. if FList^[I] = Item then
  740. begin
  741. Result := I;
  742. Exit;
  743. end;
  744. end;
  745. end;
  746. procedure TgxPersistentObjectList.Insert(index: Integer; item: TObject);
  747. begin
  748. {$IFOPT R+}
  749. if Cardinal(index) > Cardinal(FCount) then
  750. Error;
  751. {$ENDIF}
  752. if FCount = FCapacity then
  753. SetCapacity(FCapacity + FGrowthDelta);
  754. if Index < FCount then
  755. System.Move(FList[index], FList[index + 1],
  756. (FCount - index) * SizeOf(TObject));
  757. FList^[index] := item;
  758. Inc(FCount);
  759. end;
  760. procedure TgxPersistentObjectList.InsertNils(index: Integer; nbVals: Cardinal);
  761. var
  762. nc: Integer;
  763. begin
  764. {$IFOPT R+}
  765. Assert(Cardinal(Index) <= Cardinal(FCount));
  766. {$ENDIF}
  767. if nbVals > 0 then
  768. begin
  769. nc := FCount + Integer(nbVals);
  770. if nc > FCapacity then
  771. SetCapacity(nc);
  772. if Index < FCount then
  773. System.Move(FList[Index], FList[Index + Integer(nbVals)],
  774. (FCount - Index) * SizeOf(TObject));
  775. FillChar(FList[Index], Integer(nbVals) * SizeOf(TObject), 0);
  776. FCount := nc;
  777. end;
  778. end;
  779. function TgxPersistentObjectList.GetLast: TObject;
  780. begin
  781. {$IFOPT R+}
  782. if Cardinal(FCount) = 0 then
  783. Error;
  784. {$ENDIF}
  785. Result := FList^[FCount - 1];
  786. end;
  787. procedure TgxPersistentObjectList.SetLast(item: TObject);
  788. begin
  789. {$IFOPT R+}
  790. if Cardinal(FCount) = 0 then
  791. Error;
  792. {$ENDIF}
  793. FList^[FCount - 1] := item;
  794. end;
  795. procedure TgxPersistentObjectList.Move(CurIndex, NewIndex: Integer);
  796. var
  797. item: Pointer;
  798. begin
  799. if curIndex <> newIndex then
  800. begin
  801. {$IFOPT R+}
  802. if Cardinal(newIndex) >= Cardinal(Count) then
  803. Error;
  804. if Cardinal(curIndex) >= Cardinal(Count) then
  805. Error;
  806. {$ENDIF}
  807. item := FList^[curIndex];
  808. if curIndex < newIndex then
  809. begin
  810. // curIndex+1 necessarily exists since curIndex<newIndex and newIndex<Count
  811. System.Move(List[curIndex + 1], List[curIndex], (NewIndex - CurIndex) * SizeOf(TObject));
  812. end
  813. else
  814. begin
  815. // newIndex+1 necessarily exists since newIndex<curIndex and curIndex<Count
  816. System.Move(List[newIndex], List[newIndex + 1], (CurIndex - NewIndex) * SizeOf(TObject));
  817. end;
  818. FList^[newIndex] := TObject(item);
  819. end;
  820. end;
  821. procedure TgxPersistentObjectList.Put(Index: Integer; Item: TObject);
  822. begin
  823. {$IFOPT R+}
  824. if Cardinal(Index) >= Cardinal(FCount) then
  825. Error;
  826. {$ENDIF}
  827. FList^[Index] := Item;
  828. end;
  829. function TgxPersistentObjectList.Remove(item: TObject): Integer;
  830. begin
  831. Result := IndexOf(item);
  832. if Result >= 0 then
  833. Delete(Result);
  834. end;
  835. procedure TgxPersistentObjectList.Pack;
  836. var
  837. i, j, n: Integer;
  838. p: PgxPointerObjectList;
  839. pk: PObject;
  840. begin
  841. p := List;
  842. n := Count - 1;
  843. while (n >= 0) and (p^[n] = nil) do
  844. Dec(n);
  845. for i := 0 to n do
  846. begin
  847. if p^[i] = nil then
  848. begin
  849. pk := @(p^[i]);
  850. for j := i + 1 to n do
  851. begin
  852. if p^[j] <> nil then
  853. begin
  854. pk^ := p^[j];
  855. Inc(pk);
  856. end;
  857. end;
  858. SetCount((Cardinal(pk) - Cardinal(p)) div SizeOf(TObject));
  859. Exit;
  860. end;
  861. end;
  862. SetCount(n + 1);
  863. end;
  864. procedure TgxPersistentObjectList.SetCapacity(newCapacity: Integer);
  865. begin
  866. if newCapacity <> FCapacity then
  867. begin
  868. if newCapacity < FCount then
  869. FCount := newCapacity;
  870. ReallocMem(FList, newCapacity * SizeOf(TObject));
  871. FCapacity := newCapacity;
  872. end;
  873. end;
  874. procedure TgxPersistentObjectList.RequiredCapacity(aCapacity: Integer);
  875. begin
  876. if FCapacity < aCapacity then
  877. SetCapacity(aCapacity);
  878. end;
  879. procedure TgxPersistentObjectList.SetCount(newCount: Integer);
  880. begin
  881. if newCount > FCapacity then
  882. SetCapacity(newCount);
  883. if newCount > FCount then
  884. FillChar(FList[FCount], (newCount - FCount) * SizeOf(TObject), 0);
  885. FCount := NewCount;
  886. end;
  887. procedure TgxPersistentObjectList.DeleteAndFree(index: Integer);
  888. var
  889. obj: TObject;
  890. begin
  891. obj := Get(index);
  892. Delete(index);
  893. obj.Free;
  894. end;
  895. procedure TgxPersistentObjectList.DeleteAndFreeItems(index: Integer; nbVals: Cardinal);
  896. var
  897. i, n: Integer;
  898. begin
  899. {$IFOPT R+}
  900. Assert(Cardinal(index) < Cardinal(FCount));
  901. {$ENDIF}
  902. n := index + Integer(nbVals);
  903. if n >= FCount then
  904. n := FCount - 1;
  905. for i := index to n do
  906. FList^[i].Free;
  907. DeleteItems(index, nbVals);
  908. end;
  909. function TgxPersistentObjectList.RemoveAndFree(item: TObject): Integer;
  910. begin
  911. Result := IndexOf(item);
  912. if Result >= 0 then
  913. begin
  914. Delete(Result);
  915. item.Free;
  916. end;
  917. end;
  918. procedure TgxPersistentObjectList.DoClean;
  919. var
  920. i: Integer;
  921. begin
  922. // a 'for' loop could crash if freeing an item removes other items form the list
  923. i := FCount - 1;
  924. while i >= 0 do
  925. begin
  926. if i < FCount then
  927. FList^[i].Free;
  928. Dec(i);
  929. end;
  930. end;
  931. procedure TgxPersistentObjectList.Clean;
  932. begin
  933. DoClean;
  934. Clear;
  935. end;
  936. procedure TgxPersistentObjectList.CleanFree;
  937. begin
  938. if Self <> nil then
  939. begin
  940. Clean;
  941. Destroy;
  942. end;
  943. end;
  944. procedure TgxPersistentObjectList.WriteToFiler(writer: TgxVirtualWriter);
  945. (*
  946. Object List Filer Format :
  947. Integer (Version)
  948. ListBegin
  949. ...[Object]...[Object]...
  950. ListEnd
  951. with [Object] being either (read vertically)
  952. Boolean (unused) String (ClassName) Integer (reference)
  953. Integer Object Data Object Data
  954. *)
  955. var
  956. i, objId: integer;
  957. objTypes: TList;
  958. aType: TClass;
  959. begin
  960. objTypes := TList.Create;
  961. try
  962. with writer do
  963. begin
  964. WriteInteger(0); // Archive Version 0 (uh... not exactly... but...)
  965. WriteListBegin;
  966. for i := 0 to FCount - 1 do
  967. begin
  968. if FList^[i] = nil then
  969. begin
  970. // store nil as... nil
  971. WriteBoolean(False);
  972. WriteInteger(0);
  973. end
  974. else if (FList^[i] is TgxPersistentObject) then
  975. begin
  976. // yeah, a TgxPersistentObject
  977. aType := FList^[i].ClassType;
  978. objId := objTypes.IndexOf(aType);
  979. if objId < 0 then
  980. begin
  981. // class is unknown
  982. objTypes.Add(aType);
  983. WriteString(aType.ClassName);
  984. end
  985. else
  986. begin
  987. // class already registered
  988. WriteInteger(objId);
  989. end;
  990. TgxPersistentObject(FList^[i]).WriteToFiler(writer);
  991. end
  992. else
  993. begin
  994. // Dunno that stuff here, store as is
  995. WriteBoolean(False);
  996. WriteInteger(Integer(FList^[i]));
  997. end;
  998. end;
  999. WriteListEnd;
  1000. end;
  1001. finally
  1002. objTypes.Free;
  1003. end;
  1004. end;
  1005. procedure TgxPersistentObjectList.ReadFromFilerWithEvent(reader: TgxVirtualReader; afterSenderObjectCreated: TNotifyEvent);
  1006. var
  1007. obj: TgxPersistentObject;
  1008. m: TGLPersistentObjectClass;
  1009. version: integer;
  1010. objTypes: TList;
  1011. begin
  1012. objTypes := TList.Create;
  1013. try
  1014. Clean;
  1015. with reader do
  1016. begin
  1017. version := ReadInteger;
  1018. if version = 0 then
  1019. begin
  1020. ReadListBegin;
  1021. while not EndOfList do
  1022. case Cardinal(NextValue) of
  1023. Cardinal(vaFalse), Cardinal(vaTrue):
  1024. begin
  1025. // stored 'as was' value
  1026. ReadBoolean; // ignored
  1027. Add(TObject(Cardinal(ReadInteger)));
  1028. end;
  1029. Cardinal(vaString), Cardinal(vaLString), Cardinal(vaWString),
  1030. Cardinal(vaInt64) + 1 { vaUTF8String }:
  1031. begin
  1032. // Unknown class, to be registered
  1033. m := TGLPersistentObjectClass(FindClass(ReadString));
  1034. objTypes.Add(m);
  1035. obj := m.Create;
  1036. if Assigned(afterSenderObjectCreated) then
  1037. afterSenderObjectCreated(obj);
  1038. obj.ReadFromFiler(reader);
  1039. Add(obj);
  1040. end;
  1041. Cardinal(vaInt8), Cardinal(vaInt16), Cardinal(vaInt32):
  1042. begin
  1043. // known class, direct retrieve
  1044. m := TGLPersistentObjectClass(objTypes[ReadInteger]);
  1045. obj := m.Create;
  1046. if Assigned(afterSenderObjectCreated) then
  1047. afterSenderObjectCreated(obj);
  1048. obj.ReadFromFiler(reader);
  1049. Add(obj);
  1050. end;
  1051. else
  1052. raise Exception.Create(strBrokenObjectListArchive);
  1053. end;
  1054. ReadListEnd;
  1055. end
  1056. else
  1057. RaiseFilerException(version);
  1058. end;
  1059. finally
  1060. objTypes.Free;
  1061. end;
  1062. end;
  1063. procedure TgxPersistentObjectList.ReadFromFiler(reader: TgxVirtualReader);
  1064. begin
  1065. ReadFromFilerWithEvent(reader, AfterObjectCreatedByReader);
  1066. end;
  1067. procedure TgxPersistentObjectList.AfterObjectCreatedByReader(Sender: TObject);
  1068. begin
  1069. // nothing
  1070. end;
  1071. procedure TgxPersistentObjectList.Push(item: TObject);
  1072. begin
  1073. Add(item);
  1074. end;
  1075. function TgxPersistentObjectList.Pop: TObject;
  1076. begin
  1077. if FCount > 0 then
  1078. begin
  1079. Result := FList^[FCount - 1];
  1080. Dec(FCount);
  1081. end
  1082. else
  1083. Result := nil;
  1084. end;
  1085. procedure TgxPersistentObjectList.PopAndFree;
  1086. begin
  1087. Pop.Free;
  1088. end;
  1089. procedure POListQuickSort(SortList: PgxPointerObjectList; L, R: Integer;
  1090. compareFunc: TObjectListSortCompare);
  1091. var
  1092. I, J: Integer;
  1093. P, T: TObject;
  1094. begin
  1095. repeat
  1096. I := L;
  1097. J := R;
  1098. P := SortList^[(L + R) shr 1];
  1099. repeat
  1100. while compareFunc(SortList^[I], P) < 0 do
  1101. Inc(I);
  1102. while compareFunc(SortList^[J], P) > 0 do
  1103. Dec(J);
  1104. if I <= J then
  1105. begin
  1106. T := SortList^[I];
  1107. SortList^[I] := SortList^[J];
  1108. SortList^[J] := T;
  1109. Inc(I);
  1110. Dec(J);
  1111. end;
  1112. until I > J;
  1113. if L < J then
  1114. POListQuickSort(SortList, L, J, compareFunc);
  1115. L := I;
  1116. until I >= R;
  1117. end;
  1118. procedure TgxPersistentObjectList.Sort(compareFunc: TObjectListSortCompare);
  1119. begin
  1120. if Count > 1 then
  1121. POListQuickSort(FList, 0, Count - 1, compareFunc);
  1122. end;
  1123. // ------------------
  1124. // ------------------ TgxBinaryReader ------------------
  1125. // ------------------
  1126. procedure TgxBinaryReader.Read(var Buf; Count: Longint);
  1127. begin
  1128. FStream.Read(Buf, Count);
  1129. end;
  1130. function TgxBinaryReader.ReadValue: TValueType;
  1131. var
  1132. b: byte;
  1133. begin
  1134. Read(b, 1);
  1135. Result := TValueType(b);
  1136. end;
  1137. function TgxBinaryReader.NextValue: TValueType;
  1138. var
  1139. pos: Int64;
  1140. begin
  1141. pos := FStream.Position;
  1142. Result := ReadValue;
  1143. FStream.Position := pos;
  1144. end;
  1145. function TgxBinaryReader.ReadInteger: Integer;
  1146. var
  1147. tempShort: ShortInt;
  1148. tempSmallInt: SmallInt;
  1149. begin
  1150. case ReadValue of
  1151. vaInt8:
  1152. begin
  1153. Read(tempShort, 1);
  1154. Result := tempShort;
  1155. end;
  1156. vaInt16:
  1157. begin
  1158. Read(tempSmallInt, 2);
  1159. Result := tempSmallInt;
  1160. end;
  1161. vaInt32: Read(Result, 4);
  1162. else
  1163. Result := 0;
  1164. ReadTypeError;
  1165. end;
  1166. end;
  1167. function TgxBinaryReader.ReadBoolean: Boolean;
  1168. begin
  1169. case ReadValue of
  1170. vaTrue: Result := True;
  1171. vaFalse: Result := False;
  1172. else
  1173. ReadTypeError;
  1174. Result := False;
  1175. end;
  1176. end;
  1177. function TgxBinaryReader.ReadString: string;
  1178. var
  1179. n: Cardinal;
  1180. vType: TValueType;
  1181. tempString: AnsiString;
  1182. begin
  1183. n := 0;
  1184. vType := ReadValue;
  1185. case Cardinal(vType) of
  1186. Cardinal(vaWString),
  1187. Cardinal(vaInt64) + 1:
  1188. begin // vaUTF8String
  1189. Result := ReadWideString(vType);
  1190. Exit;
  1191. end;
  1192. Cardinal(vaString): Read(n, 1);
  1193. Cardinal(vaLString): Read(n, 4);
  1194. else
  1195. ReadTypeError;
  1196. end;
  1197. SetLength(tempString, n);
  1198. if n > 0 then
  1199. Read(tempString[1], n);
  1200. Result := string(tempString);
  1201. end;
  1202. function TgxBinaryReader.ReadWideString(vType: TValueType): WideString;
  1203. var
  1204. n: Cardinal;
  1205. utf8buf: AnsiString;
  1206. begin
  1207. Read(n, 4);
  1208. case Cardinal(vType) of
  1209. Cardinal(vaWString):
  1210. begin
  1211. SetLength(Result, n);
  1212. if n > 0 then
  1213. Read(Result[1], n * 2);
  1214. end;
  1215. Cardinal(vaInt64) + 1:
  1216. begin // vaUTF8String
  1217. SetLength(utf8buf, n);
  1218. if n > 0 then
  1219. begin
  1220. Read(utf8buf[1], n);
  1221. Result := UTF8ToWideString(utf8buf);
  1222. end;
  1223. end;
  1224. else
  1225. ReadTypeError;
  1226. end;
  1227. end;
  1228. function TgxBinaryReader.ReadFloat: Extended;
  1229. {$IFDEF WIN64}
  1230. var
  1231. C :TExtended80Rec; // Temporary variable to store 10 bytes floating point number in a Win64 application
  1232. {$ENDIF}
  1233. begin
  1234. {$IFDEF WIN64}
  1235. if ReadValue = vaExtended then
  1236. begin
  1237. Read(C, SizeOf(C)); // Load value into the temp variable
  1238. Result := Extended(C); // Typecast into an Extended: in a win64 application is a Double
  1239. end
  1240. else
  1241. ReadTypeError;
  1242. {$ELSE}
  1243. if ReadValue = vaExtended then
  1244. Read(Result, SizeOf(Result))
  1245. else
  1246. ReadTypeError;
  1247. {$ENDIF}
  1248. end;
  1249. procedure TgxBinaryReader.ReadListBegin;
  1250. begin
  1251. if ReadValue <> vaList then
  1252. ReadTypeError;
  1253. end;
  1254. procedure TgxBinaryReader.ReadListEnd;
  1255. begin
  1256. if ReadValue <> vaNull then
  1257. ReadTypeError;
  1258. end;
  1259. function TgxBinaryReader.EndOfList: Boolean;
  1260. begin
  1261. Result := (NextValue = vaNull);
  1262. end;
  1263. // ------------------
  1264. // ------------------ TgxBinaryWriter ------------------
  1265. // ------------------
  1266. procedure TgxBinaryWriter.Write(const Buf; Count: Longint);
  1267. begin
  1268. FStream.Write(Buf, Count);
  1269. end;
  1270. procedure TgxBinaryWriter.WriteInteger(anInteger: Integer);
  1271. type
  1272. TIntStruct = packed record
  1273. typ: byte;
  1274. val: Integer;
  1275. end;
  1276. var
  1277. ins: TIntStruct;
  1278. begin
  1279. ins.val := anInteger;
  1280. if (anInteger >= Low(ShortInt)) and (anInteger <= High(ShortInt)) then
  1281. begin
  1282. ins.typ := byte(vaInt8);
  1283. Write(ins, 2);
  1284. end
  1285. else if (anInteger >= Low(SmallInt)) and (anInteger <= High(SmallInt)) then
  1286. begin
  1287. ins.typ := byte(vaInt16);
  1288. Write(ins, 3);
  1289. end
  1290. else
  1291. begin
  1292. ins.typ := byte(vaInt32);
  1293. Write(ins, 5);
  1294. end;
  1295. end;
  1296. procedure TgxBinaryWriter.WriteBoolean(aBoolean: Boolean);
  1297. const
  1298. cBoolToType: array[False..True] of byte = (byte(vaFalse), byte(vaTrue));
  1299. begin
  1300. Write(cBoolToType[aBoolean], 1);
  1301. end;
  1302. procedure TgxBinaryWriter.WriteAnsiString(const aString: AnsiString);
  1303. type
  1304. TStringHeader = packed record
  1305. typ: Byte;
  1306. length: Integer;
  1307. end;
  1308. var
  1309. sh: TStringHeader;
  1310. begin
  1311. sh.Length := Length(aString);
  1312. if sh.Length <= 255 then
  1313. begin
  1314. sh.typ := byte(vaString);
  1315. Write(sh, 2);
  1316. if sh.Length > 0 then
  1317. Write(aString[1], sh.Length);
  1318. end
  1319. else
  1320. begin
  1321. sh.typ := byte(vaLString);
  1322. Write(sh, 5);
  1323. Write(aString[1], sh.Length);
  1324. end;
  1325. end;
  1326. procedure TgxBinaryWriter.WriteWideString(const aString: WideString);
  1327. type
  1328. TStringHeader = packed record
  1329. typ: Byte;
  1330. length: Integer;
  1331. end;
  1332. var
  1333. sh: TStringHeader;
  1334. begin
  1335. sh.Length := Length(aString);
  1336. sh.typ := byte(vaWString);
  1337. Write(sh, 5);
  1338. if sh.Length > 0 then
  1339. Write(aString[1], sh.length * SizeOf(WideChar));
  1340. end;
  1341. procedure TgxBinaryWriter.WriteString(const aString: string);
  1342. begin
  1343. {$IFDEF UNICODE}
  1344. // TODO: should really check if the string can be simplified to: vaString / vaLString / vaUTF8String
  1345. WriteWideString(aString);
  1346. {$ELSE}
  1347. WriteAnsiString(aString);
  1348. {$ENDIF}
  1349. end;
  1350. procedure TgxBinaryWriter.WriteFloat(const aFloat: Extended);
  1351. type
  1352. TExtendedStruct = packed record
  1353. typ: Byte;
  1354. {$IFDEF WIN64}
  1355. val :TExtended80Rec; // Structure to handle a 10 bytes floating point value
  1356. {$ELSE}
  1357. val :Extended;
  1358. {$ENDIF}
  1359. end;
  1360. var
  1361. str: TExtendedStruct;
  1362. begin
  1363. {$IFDEF WIN64}
  1364. str.typ := byte(vaExtended);
  1365. str.val := TExtended80Rec(aFloat); // Typecast the float value (in a Win64 app the type is a Double) into the 10 bytes struct
  1366. Write(str, SizeOf(str));
  1367. {$ELSE}
  1368. str.typ := byte(vaExtended);
  1369. str.val := aFloat;
  1370. Write(str, SizeOf(str));
  1371. {$ENDIF}
  1372. end;
  1373. procedure TgxBinaryWriter.WriteListBegin;
  1374. const
  1375. buf: byte = byte(vaList);
  1376. begin
  1377. Write(buf, 1);
  1378. end;
  1379. procedure TgxBinaryWriter.WriteListEnd;
  1380. const
  1381. buf: byte = byte(vaNull);
  1382. begin
  1383. Write(buf, 1);
  1384. end;
  1385. // ------------------
  1386. // ------------------ TgxTextReader ------------------
  1387. // ------------------
  1388. procedure TgxTextReader.ReadLine(const requestedType: string = '');
  1389. var
  1390. line: string;
  1391. c: Byte;
  1392. p: Integer;
  1393. begin
  1394. // will need speed upgrade, someday...
  1395. line := '';
  1396. repeat
  1397. Stream.Read(c, 1);
  1398. if c >= 32 then
  1399. line := line + chr(c);
  1400. until c = 10;
  1401. line := Trim(line);
  1402. p := Pos(' ', line);
  1403. if p > 0 then
  1404. begin
  1405. FValueType := Copy(line, 1, p - 1);
  1406. FData := Trim(Copy(line, p + 1, MaxInt));
  1407. end
  1408. else
  1409. begin
  1410. FValueType := line;
  1411. FData := '';
  1412. end;
  1413. if requestedType <> '' then
  1414. if requestedType <> FValueType then
  1415. raise EFilerException.Create('Invalid type, expected "'
  1416. + requestedType + '", found "FValueType".');
  1417. end;
  1418. procedure TgxTextReader.Read(var Buf; Count: Longint);
  1419. function HexCharToInt(const c: Char): Integer;
  1420. begin
  1421. if c <= '9' then
  1422. Result := Integer(c) - Integer('0')
  1423. else if c < 'a' then
  1424. Result := Integer(c) - Integer('A') + 10
  1425. else
  1426. Result := Integer(c) - Integer('a') + 10;
  1427. end;
  1428. var
  1429. i, j: Integer;
  1430. begin
  1431. ReadLine(cVTRaw);
  1432. j := 1;
  1433. for i := 0 to Count - 1 do
  1434. begin
  1435. PAnsiChar(@Buf)[i] := AnsiChar((HexCharToInt(FData[j]) shl 4)
  1436. + HexCharToInt(FData[j + 1]));
  1437. Inc(j, 2);
  1438. end;
  1439. end;
  1440. function TgxTextReader.NextValue: TValueType;
  1441. var
  1442. p: Int64;
  1443. begin
  1444. p := Stream.Position;
  1445. ReadLine;
  1446. if FValueType = cVTInteger then
  1447. Result := vaInt32
  1448. else if FValueType = cVTFloat then
  1449. Result := vaExtended
  1450. else if FValueType = cVTString then
  1451. Result := vaString
  1452. else if FValueType = cVTBoolean then
  1453. if FData = cTrue then
  1454. Result := vaTrue
  1455. else
  1456. Result := vaFalse
  1457. else if FValueType = cVTRaw then
  1458. Result := vaBinary
  1459. else if FValueType = cVTListBegin then
  1460. Result := vaList
  1461. else
  1462. Result := vaNULL;
  1463. Stream.Position := p;
  1464. end;
  1465. function TgxTextReader.ReadInteger: Integer;
  1466. begin
  1467. ReadLine(cVTInteger);
  1468. Result := StrToInt(FData);
  1469. end;
  1470. function TgxTextReader.ReadBoolean: Boolean;
  1471. begin
  1472. ReadLine(cVTBoolean);
  1473. Result := (FData = cTrue);
  1474. end;
  1475. function TgxTextReader.ReadString: string;
  1476. var
  1477. i: Integer;
  1478. begin
  1479. ReadLine(cVTString);
  1480. Result := '';
  1481. i := 1;
  1482. while i < Length(FData) do
  1483. begin
  1484. if FData[i] = '#' then
  1485. begin
  1486. Result := Result + Char(StrToInt(Copy(FData, i + 1, 3)));
  1487. Inc(i, 3);
  1488. end
  1489. else
  1490. Result := Result + FData[i];
  1491. Inc(i);
  1492. end;
  1493. Assert(FData[i] = '.', 'Invalid stored string.');
  1494. end;
  1495. function TgxTextReader.ReadFloat: Extended;
  1496. var
  1497. oldDc: Char;
  1498. begin
  1499. ReadLine(cVTInteger);
  1500. oldDc := FormatSettings.DecimalSeparator;
  1501. FormatSettings.DecimalSeparator := '.';
  1502. Result := StrToFloat(FData);
  1503. FormatSettings.DecimalSeparator := oldDc;
  1504. end;
  1505. procedure TgxTextReader.ReadListBegin;
  1506. begin
  1507. ReadLine(cVTListBegin);
  1508. end;
  1509. procedure TgxTextReader.ReadListEnd;
  1510. begin
  1511. ReadLine(cVTListEnd);
  1512. end;
  1513. function TgxTextReader.EndOfList: Boolean;
  1514. var
  1515. p: Int64;
  1516. begin
  1517. p := Stream.Position;
  1518. ReadLine;
  1519. Result := (FValueType = cVTListEnd);
  1520. Stream.Position := p;
  1521. end;
  1522. // ------------------
  1523. // ------------------ TgxTextWriter ------------------
  1524. // ------------------
  1525. constructor TgxTextWriter.Create(aStream: TStream);
  1526. begin
  1527. inherited;
  1528. end;
  1529. destructor TgxTextWriter.Destroy;
  1530. begin
  1531. inherited;
  1532. end;
  1533. procedure TgxTextWriter.WriteLine(const valueType, data: string);
  1534. var
  1535. buf: AnsiString;
  1536. begin
  1537. buf := StringOfChar(AnsiChar(#32), FIndentLevel);
  1538. buf := buf + AnsiString(valueType + ' ' + data) + #13#10;
  1539. Stream.Write(buf[1], Length(buf));
  1540. end;
  1541. procedure TgxTextWriter.Write(const Buf; Count: Longint);
  1542. const
  1543. cNibbleToHex: PChar = '0123456789ABCDEF';
  1544. var
  1545. i, j, b: Integer;
  1546. data: string;
  1547. begin
  1548. SetLength(data, Count * 2);
  1549. j := 1;
  1550. for i := 0 to Count - 1 do
  1551. begin
  1552. b := Integer(PAnsiChar(@buf)[i]);
  1553. data[j] := cNibbleToHex[b shr 4];
  1554. data[j + 1] := cNibbleToHex[b and 15];
  1555. Inc(j, 2);
  1556. end;
  1557. WriteLine(cVTRaw, data);
  1558. end;
  1559. procedure TgxTextWriter.WriteInteger(anInteger: Integer);
  1560. begin
  1561. WriteLine(cVTInteger, IntToStr(anInteger));
  1562. end;
  1563. procedure TgxTextWriter.WriteBoolean(aBoolean: Boolean);
  1564. begin
  1565. if aBoolean then
  1566. WriteLine(cVTBoolean, cTrue)
  1567. else
  1568. WriteLine(cVTBoolean, cFalse);
  1569. end;
  1570. procedure TgxTextWriter.WriteString(const aString: string);
  1571. var
  1572. i: Integer;
  1573. s: string;
  1574. begin
  1575. s := '';
  1576. for i := 1 to Length(aString) do
  1577. if aString[i] >= #32 then
  1578. s := s + aString[i]
  1579. else
  1580. s := s + Format('#%.3d', [Integer(aString[i])]);
  1581. WriteLine(cVTString, s + '.');
  1582. end;
  1583. procedure TgxTextWriter.WriteFloat(const aFloat: Extended);
  1584. begin
  1585. WriteLine(cVTInteger, FloatToStr(aFloat));
  1586. end;
  1587. procedure TgxTextWriter.WriteListBegin;
  1588. begin
  1589. WriteLine(cVTListBegin, '');
  1590. Inc(FIndentLevel, 3);
  1591. end;
  1592. procedure TgxTextWriter.WriteListEnd;
  1593. begin
  1594. Dec(FIndentLevel, 3);
  1595. WriteLine(cVTListEnd, '');
  1596. end;
  1597. // ------------------
  1598. // ------------------ TgxOwnedPersistent ------------------
  1599. // ------------------
  1600. constructor TgxOwnedPersistent.Create(AOwner: TPersistent);
  1601. begin
  1602. FOwner := AOwner;
  1603. end;
  1604. function TgxOwnedPersistent.GetOwner: TPersistent;
  1605. begin
  1606. Result := FOwner;
  1607. end;
  1608. // ------------------
  1609. // ------------------ TgxInterfacedPersistent ------------------
  1610. // ------------------
  1611. function TgxInterfacedPersistent._AddRef: Integer; stdcall;
  1612. begin
  1613. Result := -1; //ignore
  1614. end;
  1615. function TgxInterfacedPersistent._Release: Integer; stdcall;
  1616. begin
  1617. Result := -1; //ignore
  1618. end;
  1619. function TgxInterfacedPersistent.QueryInterface(const IID: TGUID;
  1620. out Obj): HResult; stdcall;
  1621. begin
  1622. if GetInterface(IID, Obj) then
  1623. Result := S_OK
  1624. else
  1625. Result := E_NOINTERFACE;
  1626. end;
  1627. // ------------------
  1628. // ------------------ TgxInterfacedCollectionItem ------------------
  1629. // ------------------
  1630. function TgxInterfacedCollectionItem._AddRef: Integer; stdcall;
  1631. begin
  1632. Result := -1; //ignore
  1633. end;
  1634. function TgxInterfacedCollectionItem._Release: Integer; stdcall;
  1635. begin
  1636. Result := -1; //ignore
  1637. end;
  1638. function TgxInterfacedCollectionItem.QueryInterface(const IID: TGUID;
  1639. out Obj): HResult; stdcall;
  1640. begin
  1641. if GetInterface(IID, Obj) then
  1642. Result := S_OK
  1643. else
  1644. Result := E_NOINTERFACE;
  1645. end;
  1646. // ------------------------------------------------------------------
  1647. initialization
  1648. // ------------------------------------------------------------------
  1649. RegisterClass(TgxPersistentObjectList);
  1650. end.