Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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