You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

719 rivejä
16 KiB

  1. unit uutlStreamHelper;
  2. { Package: Utils
  3. Prefix: utl - UTiLs
  4. Beschreibung: diese Unit enthält Klassen zum lesen und schreiben von Werten in einen Stream
  5. TutlStreamReader - Wrapper für beliebige Streams, handelt Datentypen
  6. TutlStreamWriter - Wrapper für beliebige Streams, handelt Datentypen }
  7. {$mode objfpc}{$H+}
  8. interface
  9. uses
  10. SysUtils, Classes, Contnrs, syncobjs;
  11. type
  12. TutlFourCC = string[4];
  13. { TutlStreamUtility }
  14. TutlStreamUtility = class
  15. private
  16. FStream: TStream;
  17. FOwnsStream: boolean;
  18. FPositions: TStack;
  19. protected
  20. public
  21. constructor Create(BaseStream: TStream; OwnsStream: Boolean=false);
  22. destructor Destroy; override;
  23. property Stream: TStream read FStream;
  24. procedure Push;
  25. procedure Pop;
  26. procedure Drop;
  27. end;
  28. { TutlStreamReader }
  29. TutlStreamReader = class(TutlStreamUtility)
  30. protected
  31. function ReadBuffer(Var Buffer; Size: int64): int64;
  32. public
  33. function ReadFourCC: TutlFourCC;
  34. function CheckFourCC(Correct: TutlFourCC): boolean;
  35. function ReadByte: Byte;
  36. function ReadWord: Word;
  37. function ReadCardinal: Cardinal;
  38. function ReadInteger: Integer;
  39. function ReadInt64: Int64;
  40. function ReadSingle: Single;
  41. function ReadDouble: Double;
  42. function ReadAnsiString: AnsiString;
  43. function ReadLine: AnsiString;
  44. function IsEOF: boolean;
  45. end;
  46. { TutlStreamWriter }
  47. TutlStreamWriter = class(TutlStreamUtility)
  48. protected
  49. procedure WriteBuffer(var Data; Size: int64);
  50. public
  51. procedure WriteFourCC(FCC: TutlFourCC);
  52. procedure WriteByte(A: Byte);
  53. procedure WriteWord(A: Word);
  54. procedure WriteCardinal(A: Cardinal);
  55. procedure WriteInteger(A: Integer);
  56. procedure WriteInt64(A: Int64);
  57. procedure WriteSingle(A: Single);
  58. procedure WriteDouble(A: Double);
  59. procedure WriteAnsiString(A: AnsiString);
  60. procedure WriteAnsiBytes(A: AnsiString);
  61. procedure WriteLine(A: AnsiString);
  62. end;
  63. { TutlPagedBufferStream }
  64. TutlPagedBufferStream = class(TOwnerStream)
  65. public const
  66. DEFAULT_BUFLEN = 4096*16;
  67. private
  68. FVirtualSize, FVirtualPosition: Int64;
  69. FBuffer: TBytes;
  70. FBufferStart: Int64;
  71. FBufferModified: boolean;
  72. protected
  73. function GetSize: Int64; override;
  74. procedure SetSize(const NewSize: Int64); override;
  75. procedure ReMapBuffer;
  76. procedure FlushBuffer;
  77. public
  78. constructor Create(const BaseStream: TStream; const BufferSize: Cardinal = DEFAULT_BUFLEN; const aOwnsStream: Boolean = false);
  79. destructor Destroy; override;
  80. function Read(var Buffer; Count: Integer): Integer; override;
  81. function Write(const Buffer; Count: Integer): Integer; override;
  82. function Seek(Offset: Integer; Origin: Word): Integer; override;
  83. end;
  84. { TutlFIFOStream }
  85. TutlFIFOStream = class(TStream)
  86. private const MAX_PAGE_SIZE = 4096;
  87. private type
  88. PPage = ^TPage;
  89. TPage = record
  90. Next: PPage;
  91. Data: packed array[0..MAX_PAGE_SIZE-1] of byte;
  92. end;
  93. private
  94. fLockFree: boolean;
  95. fPageFirst, fPageLast: PPage;
  96. fReadPtr, fWritePtr: Cardinal;
  97. fTotalSize: Int64;
  98. fDataLock: TCriticalSection;
  99. protected
  100. function GetSize: Int64; override;
  101. public
  102. constructor Create(const aLockFree: boolean = false);
  103. destructor Destroy; override;
  104. function Read(var Buffer; Count: Longint): Longint; override;
  105. function Reserve(var Buffer; Count: Longint): Longint;
  106. function Discard(Count: Longint): Longint;
  107. function Write(const Buffer; Count: Longint): Longint; override;
  108. function Seek(const {%H-}Offset: Int64; {%H-}Origin: TSeekOrigin): Int64; override; overload;
  109. procedure BeginOperation;
  110. procedure EndOperation;
  111. procedure Clear;
  112. property LockFree: boolean read fLockFree;
  113. end;
  114. TutlBase64Decoder = class(TStringStream)
  115. public const
  116. CODE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  117. PADDING_CHARACTER = '=';
  118. protected
  119. public
  120. function Read(var Buffer; Count: Longint): Longint; override;
  121. function Decode(const aOutput: TStream): boolean;
  122. constructor Create;
  123. end;
  124. implementation
  125. uses RtlConsts, uutlExceptions, Math;
  126. type
  127. TPositionData = class
  128. Position: Int64;
  129. constructor Create(Pos: Int64);
  130. end;
  131. constructor TPositionData.Create(Pos: Int64);
  132. begin
  133. inherited Create;
  134. Position:= Pos;
  135. end;
  136. { TutlStreamUtility }
  137. constructor TutlStreamUtility.Create(BaseStream: TStream; OwnsStream: Boolean);
  138. begin
  139. inherited Create;
  140. FStream:= BaseStream;
  141. FOwnsStream:= OwnsStream;
  142. FPositions:= TStack.Create;
  143. end;
  144. destructor TutlStreamUtility.Destroy;
  145. begin
  146. if FOwnsStream then
  147. FreeAndNil(FStream)
  148. else
  149. FStream:= nil;
  150. while FPositions.AtLeast(1) do
  151. TPositionData(FPositions.Pop).Free;
  152. FreeAndNil(FPositions);
  153. inherited;
  154. end;
  155. procedure TutlStreamUtility.Pop;
  156. var
  157. p: TPositionData;
  158. begin
  159. p:= TPositionData(FPositions.Pop);
  160. FStream.Position:= p.Position;
  161. p.Free;
  162. end;
  163. procedure TutlStreamUtility.Drop;
  164. var
  165. p: TPositionData;
  166. begin
  167. p:= TPositionData(FPositions.Pop);
  168. if Assigned(p) then
  169. p.Free;
  170. end;
  171. procedure TutlStreamUtility.Push;
  172. begin
  173. FPositions.Push(TPositionData.Create(FStream.Position));
  174. end;
  175. { TutlStreamReader }
  176. function TutlStreamReader.ReadBuffer(var Buffer; Size: int64): int64;
  177. begin
  178. if (FStream.Position + Size > FStream.Size) then
  179. raise EInvalidOperation.Create('stream is to small');
  180. Result:= FStream.Read(Buffer, Size);
  181. end;
  182. function TutlStreamReader.ReadFourCC: TutlFourCC;
  183. begin
  184. SetLength(Result, 4);
  185. ReadBuffer(Result[1], 4);
  186. end;
  187. function TutlStreamReader.CheckFourCC(Correct: TutlFourCC): boolean;
  188. begin
  189. Result:= ReadFourCC=Correct;
  190. end;
  191. function TutlStreamReader.ReadByte: Byte;
  192. begin
  193. ReadBuffer(Result{%H-}, Sizeof(Result));
  194. end;
  195. function TutlStreamReader.ReadWord: Word;
  196. begin
  197. ReadBuffer(Result{%H-}, Sizeof(Result));
  198. end;
  199. function TutlStreamReader.ReadCardinal: Cardinal;
  200. begin
  201. ReadBuffer(Result{%H-}, Sizeof(Result));
  202. end;
  203. function TutlStreamReader.ReadInteger: Integer;
  204. begin
  205. ReadBuffer(Result{%H-}, Sizeof(Result));
  206. end;
  207. function TutlStreamReader.ReadInt64: Int64;
  208. begin
  209. ReadBuffer(Result{%H-}, Sizeof(Result));
  210. end;
  211. function TutlStreamReader.ReadSingle: Single;
  212. begin
  213. ReadBuffer(Result{%H-}, Sizeof(Result));
  214. end;
  215. function TutlStreamReader.ReadDouble: Double;
  216. begin
  217. ReadBuffer(Result{%H-}, Sizeof(Result));
  218. end;
  219. function TutlStreamReader.ReadAnsiString: AnsiString;
  220. begin
  221. SetLength(Result, ReadCardinal);
  222. ReadBuffer(Result[1], Length(Result));
  223. end;
  224. function TutlStreamReader.ReadLine: AnsiString;
  225. const
  226. READ_LENGTH = 80;
  227. var
  228. rp, rl: integer;
  229. cp: PAnsiChar;
  230. bpos: Int64;
  231. r: integer;
  232. EOF: Boolean;
  233. procedure ReadSome;
  234. begin
  235. SetLength(Result, rl + READ_LENGTH);
  236. r:= FStream.Read(Result[rl + 1], READ_LENGTH);
  237. inc(rl, r);
  238. EOF:= r <> READ_LENGTH;
  239. cp:= @Result[rp];
  240. end;
  241. begin
  242. Result:= '';
  243. rl:= 0;
  244. bpos:= FStream.Position;
  245. repeat
  246. rp:= rl + 1;
  247. ReadSome;
  248. while rp <= rl do begin
  249. if cp^ in [#10, #13] then begin
  250. inc(bpos, rp);
  251. // never a second char after #10
  252. if cp^ = #13 then begin
  253. if (rp = rl) and not EOF then begin
  254. ReadSome;
  255. end;
  256. if (rp <= rl) then begin
  257. inc(cp);
  258. if cp^ = #10 then
  259. inc(bpos);
  260. end;
  261. end;
  262. FStream.Position:= bpos;
  263. SetLength(Result, rp-1);
  264. Exit;
  265. end;
  266. inc(cp);
  267. inc(rp);
  268. end;
  269. until EOF;
  270. SetLength(Result, rl);
  271. end;
  272. function TutlStreamReader.IsEOF: boolean;
  273. begin
  274. Result:= FStream.Position = FStream.Size;
  275. end;
  276. { TutlStreamWriter }
  277. procedure TutlStreamWriter.WriteBuffer(var Data; Size: int64);
  278. begin
  279. FStream.Write(Data, Size);
  280. end;
  281. procedure TutlStreamWriter.WriteFourCC(FCC: TutlFourCC);
  282. begin
  283. WriteBuffer(FCC[1], 4);
  284. end;
  285. procedure TutlStreamWriter.WriteByte(A: Byte);
  286. begin
  287. WriteBuffer(A, SizeOf(a));
  288. end;
  289. procedure TutlStreamWriter.WriteWord(A: Word);
  290. begin
  291. WriteBuffer(A, SizeOf(a));
  292. end;
  293. procedure TutlStreamWriter.WriteCardinal(A: Cardinal);
  294. begin
  295. WriteBuffer(A, SizeOf(a));
  296. end;
  297. procedure TutlStreamWriter.WriteInteger(A: Integer);
  298. begin
  299. WriteBuffer(A, SizeOf(a));
  300. end;
  301. procedure TutlStreamWriter.WriteInt64(A: Int64);
  302. begin
  303. WriteBuffer(A, SizeOf(a));
  304. end;
  305. procedure TutlStreamWriter.WriteSingle(A: Single);
  306. begin
  307. WriteBuffer(A, SizeOf(a));
  308. end;
  309. procedure TutlStreamWriter.WriteDouble(A: Double);
  310. begin
  311. WriteBuffer(A, SizeOf(a));
  312. end;
  313. procedure TutlStreamWriter.WriteAnsiString(A: AnsiString);
  314. begin
  315. WriteCardinal(Length(A));
  316. WriteBuffer(A[1], Length(a));
  317. end;
  318. procedure TutlStreamWriter.WriteAnsiBytes(A: AnsiString);
  319. begin
  320. WriteBuffer(A[1], Length(A));
  321. end;
  322. procedure TutlStreamWriter.WriteLine(A: AnsiString);
  323. begin
  324. WriteAnsiBytes(A + sLineBreak);
  325. end;
  326. { TutlPagedBufferStream }
  327. constructor TutlPagedBufferStream.Create(const BaseStream: TStream; const BufferSize: Cardinal;
  328. const aOwnsStream: Boolean);
  329. begin
  330. inherited Create(BaseStream);
  331. SourceOwner:= aOwnsStream;
  332. SetLength(FBuffer, BufferSize);
  333. FVirtualPosition:= 0;
  334. FVirtualSize:= Source.Size;
  335. FBufferStart:= -1;
  336. ReMapBuffer;
  337. end;
  338. destructor TutlPagedBufferStream.Destroy;
  339. begin
  340. FlushBuffer;
  341. SetLength(FBuffer, 0);
  342. inherited;
  343. end;
  344. function TutlPagedBufferStream.Seek(Offset: Integer; Origin: Word): Integer;
  345. begin
  346. case Origin of
  347. soFromBeginning: FVirtualPosition := Offset;
  348. soFromCurrent: Inc(FVirtualPosition, Offset);
  349. soFromEnd: FVirtualPosition := Size + Offset;
  350. end;
  351. ReMapBuffer;
  352. Result := FVirtualPosition;
  353. end;
  354. function TutlPagedBufferStream.GetSize: Int64;
  355. begin
  356. Result:= FVirtualSize;
  357. end;
  358. procedure TutlPagedBufferStream.SetSize(const NewSize: Int64);
  359. begin
  360. FVirtualSize:= NewSize;
  361. Source.Size:= NewSize;
  362. if Position > FVirtualSize then
  363. Position:= FVirtualSize;
  364. end;
  365. function TutlPagedBufferStream.Write(const Buffer; Count: Integer): Integer;
  366. var
  367. bw, c: Int64;
  368. bp: Pointer;
  369. begin
  370. bw:= 0;
  371. bp:= @Buffer;
  372. while (bw < Count) do begin
  373. ReMapBuffer;
  374. // Wie viel Daten können wir schreiben?
  375. c:= Min(Count - bw, Length(FBuffer) - (FVirtualPosition-FBufferStart));
  376. // das schreiben und buffer weiterschieben
  377. Move(bp^, FBuffer[FVirtualPosition-FBufferStart], c);
  378. Inc(Bp, c);
  379. Inc(bw, c);
  380. Inc(FVirtualPosition, c);
  381. if FVirtualPosition > FVirtualSize then
  382. FVirtualSize:= FVirtualPosition;
  383. end;
  384. if bw > 0 then
  385. FBufferModified:= true;
  386. Result:= bw;
  387. end;
  388. function TutlPagedBufferStream.Read(var Buffer; Count: Integer): Integer;
  389. var
  390. br, c: Int64;
  391. bp: Pointer;
  392. begin
  393. br:= 0;
  394. bp:= @Buffer;
  395. while (br < Count) and (FVirtualPosition<FVirtualSize) do begin
  396. ReMapBuffer;
  397. // Wie viel Daten daraus brauchen wir bzw. können wir kriegen?
  398. c:= Min(Min(Count - br, Length(FBuffer) - (FVirtualPosition-FBufferStart)), FVirtualSize - FVirtualPosition);
  399. // das rausholen und buffer weiterschieben
  400. Move(FBuffer[FVirtualPosition-FBufferStart], bp^, c);
  401. Inc(Bp, c);
  402. Inc(br, c);
  403. Inc(FVirtualPosition, c);
  404. end;
  405. Result:= br;
  406. end;
  407. procedure TutlPagedBufferStream.ReMapBuffer;
  408. var
  409. newbs: Int64;
  410. inbuf: Int64;
  411. begin
  412. // Welches Buffer-Segment wird gesucht?
  413. newbs:= (FVirtualPosition div Length(FBuffer)) * Length(FBuffer);
  414. // ist das das aktuelle?
  415. if FBufferStart <> newbs then begin
  416. FlushBuffer;
  417. // Segment holen
  418. Source.Position:= newbs;
  419. inbuf:= Min(Length(FBuffer), FVirtualSize - newbs);
  420. Source.ReadBuffer(FBuffer[0], inbuf);
  421. FBufferStart:= newbs;
  422. FBufferModified:= false;
  423. end;
  424. end;
  425. procedure TutlPagedBufferStream.FlushBuffer;
  426. var
  427. towrite: Int64;
  428. begin
  429. if not FBufferModified then
  430. // Nothing to do
  431. Exit;
  432. Source.Position:= FBufferStart;
  433. towrite:= Min(Length(FBuffer), FVirtualSize - FBufferStart);
  434. Source.WriteBuffer(FBuffer[0], towrite);
  435. end;
  436. { TutlFIFOStream }
  437. constructor TutlFIFOStream.Create(const aLockFree: boolean);
  438. begin
  439. inherited Create;
  440. fDataLock:= TCriticalSection.Create;
  441. fTotalSize:= 0;
  442. New(fPageFirst);
  443. fPageFirst^.Next:= nil;
  444. fPageLast:= fPageFirst;
  445. fReadPtr:= 0;
  446. fWritePtr:= 0;
  447. fLockFree:= aLockFree;
  448. end;
  449. destructor TutlFIFOStream.Destroy;
  450. begin
  451. Clear;
  452. Dispose(fPageFirst);
  453. FreeAndNil(fDataLock);
  454. inherited Destroy;
  455. end;
  456. function TutlFIFOStream.GetSize: Int64;
  457. begin
  458. Result:= fTotalSize;
  459. end;
  460. function TutlFIFOStream.Read(var Buffer; Count: Longint): Longint;
  461. begin
  462. BeginOperation;
  463. try
  464. Result:= Reserve(Buffer, Count);
  465. Discard(Result);
  466. finally
  467. EndOperation;
  468. end;
  469. end;
  470. function TutlFIFOStream.Reserve(var Buffer; Count: Longint): Longint;
  471. var
  472. pbuf: PByteArray;
  473. mx: LongInt;
  474. rp: Int64;
  475. p: PPage;
  476. begin
  477. BeginOperation;
  478. try
  479. pbuf:= @Buffer;
  480. Result:= 0;
  481. rp:= fReadPtr;
  482. p:= fPageFirst;
  483. while Count > 0 do begin
  484. mx:= MAX_PAGE_SIZE - rp;
  485. if mx > Count then mx:= Count;
  486. if (p=fPageLast) and (mx > fWritePtr-rp) then mx:= fWritePtr-rp;
  487. if mx=0 then exit;
  488. Move(p^.Data[rp], pbuf^[Result], mx);
  489. inc(rp, mx);
  490. inc(Result, mx);
  491. Dec(Count, mx);
  492. if rp = MAX_PAGE_SIZE then begin
  493. p:= p^.Next;
  494. rp:= 0;
  495. end;
  496. end;
  497. finally
  498. EndOperation;
  499. end;
  500. end;
  501. function TutlFIFOStream.Discard(Count: Longint): Longint;
  502. var
  503. mx: LongInt;
  504. n: PPage;
  505. begin
  506. BeginOperation;
  507. try
  508. Result:= 0;
  509. while Count > 0 do begin
  510. mx:= MAX_PAGE_SIZE - fReadPtr;
  511. if mx > Count then mx:= Count;
  512. if (fPageFirst=fPageLast) and (mx > fWritePtr-fReadPtr) then mx:= fWritePtr-fReadPtr;
  513. if mx=0 then exit;
  514. inc(fReadPtr, mx);
  515. inc(Result, mx);
  516. dec(Count, mx);
  517. dec(fTotalSize, mx);
  518. if fReadPtr=MAX_PAGE_SIZE then begin
  519. n:= fPageFirst^.Next;
  520. if Assigned(n) then begin
  521. Dispose(fPageFirst);
  522. fPageFirst:= n;
  523. fReadPtr:= 0;
  524. end;// else kann nicht passieren, das wird mit (mx > fWritePtr-fReadPtr) und (mx=0) schon bedient
  525. end;
  526. end;
  527. finally
  528. EndOperation;
  529. end;
  530. end;
  531. function TutlFIFOStream.Write(const Buffer; Count: Longint): Longint;
  532. var
  533. mx: LongInt;
  534. pbuf: PByteArray;
  535. begin
  536. BeginOperation;
  537. try
  538. pbuf:= @Buffer;
  539. Result:= 0;
  540. while Count > 0 do begin
  541. mx:= MAX_PAGE_SIZE - fWritePtr;
  542. if mx > Count then mx:= Count;
  543. Move(pbuf^[Result], fPageLast^.Data[fWritePtr], mx);
  544. inc(fWritePtr, mx);
  545. inc(fTotalSize, mx);
  546. dec(Count, mx);
  547. inc(Result, mx);
  548. if fWritePtr = MAX_PAGE_SIZE then begin
  549. New(fPageLast^.Next);
  550. fPageLast:= fPageLast^.Next;
  551. fPageLast^.Next:= nil;
  552. fWritePtr:= 0;
  553. end;
  554. end;
  555. finally
  556. EndOperation;
  557. end;
  558. end;
  559. function TutlFIFOStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
  560. begin
  561. Result:= 0;
  562. raise EStreamError.CreateFmt(SStreamInvalidSeek,[ClassName]);
  563. end;
  564. procedure TutlFIFOStream.BeginOperation;
  565. begin
  566. if not fLockFree then
  567. fDataLock.Acquire;
  568. end;
  569. procedure TutlFIFOStream.EndOperation;
  570. begin
  571. if not fLockFree then
  572. fDataLock.Release;
  573. end;
  574. procedure TutlFIFOStream.Clear;
  575. var
  576. p: PPage;
  577. begin
  578. BeginOperation;
  579. try
  580. while fPageFirst<>fPageLast do begin
  581. p:= fPageFirst;
  582. fPageFirst:= fPageFirst^.Next;
  583. Dispose(p);
  584. end;
  585. fTotalSize:= 0;
  586. fReadPtr:= 0;
  587. fWritePtr:= 0;
  588. finally
  589. EndOperation;
  590. end;
  591. end;
  592. { TutlBase64Decoder }
  593. function TutlBase64Decoder.{%H-}Read(var Buffer; Count: Longint): Longint;
  594. begin
  595. ReadNotImplemented;
  596. end;
  597. function TutlBase64Decoder.Decode(const aOutput: TStream): boolean;
  598. var
  599. a: Integer;
  600. x: Integer;
  601. b: Integer;
  602. c: AnsiChar;
  603. begin
  604. Result:= false;
  605. a := 0;
  606. b := 0;
  607. Position:= 0;
  608. while inherited Read(c{%H-}, sizeof(c)) = sizeof(c) do begin
  609. x := Pos(c, CODE64) - 1;
  610. if (x >= 0) then begin
  611. b := b * 64 + x;
  612. a := a + 6;
  613. if a >= 8 then begin
  614. a := a - 8;
  615. x := b shr a;
  616. b := b mod (1 shl a);
  617. aOutput.WriteByte(x);
  618. end;
  619. end else if c = PADDING_CHARACTER then
  620. break
  621. else
  622. Exit;
  623. end;
  624. Result:= true;
  625. end;
  626. constructor TutlBase64Decoder.Create;
  627. begin
  628. inherited Create('');
  629. end;
  630. end.