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.
 
 

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