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.

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