ascii85.pp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. {
  2. This file is part of the Free Component Library (FCL)
  3. Copyright (c) 2008 by the Free Pascal development team
  4. See the file COPYING.FPC, included in this distribution,
  5. for details about the copyright.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. **********************************************************************}
  10. // Original header
  11. // I, Danny Milosavljevic, hereby release this code into the public domain.
  12. unit ascii85;
  13. {$M+}
  14. {$MODE OBJFPC}
  15. // Based on C# code from <http://www.codinghorror.com/blog/archives/000410.html> by Jeff Atwood,
  16. // which is based on C code from <http://www.stillhq.com/cgi-bin/cvsweb/ascii85/>.
  17. interface
  18. uses sysutils, classes;
  19. type
  20. TASCII85State = (ascInitial = 0, ascOneEncodedChar = 1, ascTwoEncodedChars = 2, ascThreeEncodedChars = 3, ascFourEncodedChars = 4, ascNoEncodedChar = 5, ascPrefix = 6);
  21. TASCII85RingBuffer = class
  22. private
  23. fBuffer : array[0..1023] of Byte;
  24. fBufferReadPosition : Cardinal;
  25. fBufferWritePosition : Cardinal;
  26. fBufferFillCount : Cardinal;
  27. protected
  28. function GetBufferSize() : Cardinal; inline;
  29. published
  30. property FillCount : Cardinal read fBufferFillCount;
  31. property Size : Cardinal read GetBufferSize;
  32. procedure Write(const aBuffer; aSize : Cardinal); inline;
  33. function Read(var aBuffer; aSize : Cardinal) : Cardinal; inline;
  34. end;
  35. TASCII85DecoderStream = class(TOwnerStream)
  36. private
  37. fBExpectBoundary : Boolean;
  38. fTuple : Cardinal;
  39. fState : TASCII85State;
  40. fBEOF : Boolean;
  41. fBSourceEOF : Boolean;
  42. fBuffer : TASCII85RingBuffer;
  43. fPosition : Int64;
  44. // decoded data:
  45. fEncodedBuffer : array[0..((1024 * 5 + 3) div 4) - 1] of Byte; // 1280. // could also be put on the stack, doesn't need to persist between calls.
  46. private
  47. procedure BufferByte(aValue : Byte); inline;
  48. procedure BufferTuple(aValue : Cardinal; aDecodedCount : Cardinal); inline; // decoding shrinks from 5 byte to 4 byte.
  49. published
  50. constructor Create(aStream : TStream);
  51. procedure Decode(aInput : Byte); inline;
  52. procedure Close();
  53. function ClosedP() : Boolean; inline;
  54. property BExpectBoundary : Boolean read fBExpectBoundary write fBExpectBoundary;
  55. protected
  56. function GetPosition() : Int64; override;
  57. public
  58. destructor Destroy(); override;
  59. function Read(var aBuffer; aCount : longint) : longint; override;
  60. function Seek(aOffset : longint; aOrigin : word) : longint; override;
  61. function Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; override; overload;
  62. end;
  63. // TODO encoder...
  64. TASCII85EncoderStream = class(TOwnerStream)
  65. private
  66. FPos,
  67. FTuple : Cardinal;
  68. FCount,
  69. FWidth : Integer;
  70. FBoundary : Boolean;
  71. protected
  72. Procedure WriteBoundary;
  73. Procedure Flush;
  74. procedure Encode;
  75. public
  76. Constructor Create(ADest: TStream; AWidth : Integer = 72; ABoundary : Boolean = False);
  77. Destructor Destroy; Override;
  78. function Write(Const aBuffer; aCount : longint) : longint; override;
  79. Property Width : Integer Read FWidth;
  80. Property Boundary : Boolean Read FBoundary;
  81. end;
  82. implementation
  83. { TASCII85EncoderStream }
  84. Procedure TASCII85EncoderStream.WriteBoundary;
  85. Const
  86. SBoundary = '<~';
  87. begin
  88. Source.Write(SBoundary[1],2);
  89. FPos:=2;
  90. end;
  91. Procedure TASCII85EncoderStream.Encode;
  92. Var
  93. S : String[7];
  94. I,J : Integer;
  95. Buf : Array[0..4] of Byte;
  96. begin
  97. If (FTuple=0) then
  98. begin
  99. // Write 'z'
  100. S:='z';
  101. Inc(FPos);
  102. If (FPos>FWidth) then
  103. begin
  104. S:=S+sLineBreak;
  105. FPos:=0;
  106. end;
  107. end
  108. else
  109. begin
  110. For I:=0 to 4 do
  111. begin
  112. Buf[i]:=FTuple mod 85;
  113. FTuple:=FTuple div 85;
  114. end;
  115. J:=0;
  116. S:='';
  117. For I:=FCount+1 downto 0 do
  118. begin
  119. Inc(j);
  120. S[J]:=Char(Buf[i]+Ord('!'));
  121. SetLength(S,J);
  122. Inc(FPos);
  123. If (FPos>FWidth) then
  124. begin
  125. FPos:=0;
  126. S:=S+sLinebreak;
  127. J:=Length(S);
  128. end;
  129. end;
  130. end;
  131. Source.Write(S[1],Length(S));
  132. FTuple:=0;
  133. FCount:=-1;
  134. end;
  135. Procedure TASCII85EncoderStream.Flush;
  136. Const
  137. Boundary1 = '~>'+slinebreak;
  138. Boundary2 = slinebreak+Boundary1;
  139. Var
  140. S : String;
  141. begin
  142. If FCount>0 then
  143. Encode;
  144. If FBoundary then
  145. begin
  146. If FPos+2>FWidth then
  147. S:=Boundary2
  148. else
  149. S:=Boundary1;
  150. Source.Write(S[1],Length(S));
  151. FBoundary:=False;
  152. end;
  153. end;
  154. Constructor TASCII85EncoderStream.Create(ADest: TStream; AWidth : Integer = 72; ABoundary : Boolean = False);
  155. begin
  156. Inherited Create(ADest);
  157. FWidth:=AWidth;
  158. FBoundary:=ABoundary;
  159. If FBoundary then
  160. WriteBoundary;
  161. end;
  162. Destructor TASCII85EncoderStream.Destroy;
  163. begin
  164. Flush;
  165. Inherited;
  166. end;
  167. function TASCII85EncoderStream.Write(Const aBuffer; aCount : longint) : longint;
  168. Var
  169. P : PByte;
  170. C : Byte;
  171. begin
  172. P:=@Abuffer;
  173. Result:=ACount;
  174. While ACount>0 do
  175. begin
  176. C:=P^;
  177. Case FCount of
  178. 0 : FTuple:=FTuple or (C shl 24);
  179. 1 : FTuple:=FTuple or (C shl 16);
  180. 2 : FTuple:=FTuple or (C shl 8);
  181. 3 : begin
  182. FTuple:=FTuple or C;
  183. encode;
  184. end;
  185. end;
  186. Inc(FCount);
  187. Inc(P);
  188. Dec(ACount);
  189. end;
  190. end;
  191. { TRingBuffer }
  192. function TASCII85RingBuffer.GetBufferSize() : Cardinal; inline;
  193. begin
  194. Result := Length(fBuffer);
  195. end;
  196. procedure TASCII85RingBuffer.Write(const aBuffer; aSize : Cardinal); inline;
  197. var
  198. vBuffer : PByte;
  199. begin
  200. vBuffer := @aBuffer;
  201. // TODO optimize.
  202. while aSize > 0 do begin
  203. assert(fBufferFillCount < Length(fBuffer));
  204. fBuffer[fBufferWritePosition] := vBuffer^;
  205. Inc(vBuffer);
  206. Inc(fBufferFillCount);
  207. Inc(fBufferWritePosition);
  208. if fBufferWritePosition >= Length(fBuffer) then
  209. fBufferWritePosition := 0;
  210. assert(fBufferWritePosition <> fBufferReadPosition);
  211. Dec(aSize);
  212. end;
  213. end;
  214. function TASCII85RingBuffer.Read(var aBuffer; aSize : Cardinal) : Cardinal; inline;
  215. var
  216. vBuffer : PByte;
  217. vBufferCount : Cardinal;
  218. vBufferCount1 : Cardinal;
  219. vBufferCount2 : Cardinal;
  220. begin
  221. vBuffer := @aBuffer;
  222. Result := 0;
  223. if fBufferFillCount > 0 then begin
  224. vBufferCount := aSize; // try to take as much as requested by the client...
  225. if vBufferCount > fBufferFillCount then // ... if possible.
  226. vBufferCount := fBufferFillCount;
  227. if fBufferReadPosition < fBufferWritePosition then begin { ------RXXXXXXW-------- }
  228. vBufferCount1 := fBufferWritePosition - fBufferReadPosition; // max count for the first Move.
  229. assert(vBufferCount <= vBufferCount1);
  230. Move(fBuffer[fBufferReadPosition], vBuffer^, vBufferCount);
  231. Inc(vBuffer, vBufferCount);
  232. end else begin { XXXW-----RXXXXXXXXXXXX }
  233. vBufferCount1 := Length(fBuffer) - fBufferReadPosition; // count for the first Move.
  234. if vBufferCount < vBufferCount1 then
  235. vBufferCount1 := vBufferCount;
  236. if vBufferCount1 > 0 then begin
  237. Move(fBuffer[fBufferReadPosition], vBuffer^, vBufferCount1);
  238. Inc(vBuffer, vBufferCount1);
  239. end;
  240. vBufferCount2 := vBufferCount - vBufferCount1; // remaining count for the second Move.
  241. if vBufferCount2 > 0 then begin
  242. Move(fBuffer[0], vBuffer^, vBufferCount2);
  243. Inc(vBuffer, vBufferCount2);
  244. end;
  245. end;
  246. Inc(fBufferReadPosition, vBufferCount);
  247. while fBufferReadPosition >= Length(fBuffer) do
  248. Dec(fBufferReadPosition, Length(fBuffer));
  249. assert(fBufferFillCount >= vBufferCount);
  250. Dec(fBufferFillCount, vBufferCount);
  251. Inc(Result, vBufferCount);
  252. end;
  253. end;
  254. { TDecoder }
  255. const
  256. cPow85 : array[0..4] of Cardinal = (85*85*85*85, 85*85*85, 85*85, 85, 1); // uint
  257. function DecodeNonTrivialByte(aInput : Byte) : Cardinal; inline;
  258. begin
  259. if (aInput >= ord('!')) and (aInput <= ord('u')) then
  260. Result := aInput - ord('!')
  261. else
  262. raise EConvertError.Create(Format('could not decode value %d', [aInput]));
  263. // if chr(aInput) in ['!'..'u'] then
  264. end;
  265. constructor TASCII85DecoderStream.Create(aStream : TStream);
  266. begin
  267. inherited Create(aStream);
  268. fBuffer := TASCII85RingBuffer.Create();
  269. end;
  270. procedure TASCII85DecoderStream.BufferByte(aValue : Byte); inline;
  271. begin
  272. fBuffer.Write(aValue, 1);
  273. end;
  274. procedure TASCII85DecoderStream.BufferTuple(aValue : Cardinal; aDecodedCount { DECODED!!!} : Cardinal); inline;
  275. begin
  276. if aDecodedCount >= 1 then begin
  277. BufferByte(aValue shr (24 - (0 * 8)));
  278. if aDecodedCount >= 2 then begin
  279. BufferByte((aValue shr (24 - (1 * 8))) and $ff);
  280. if aDecodedCount >= 3 then begin
  281. BufferByte((aValue shr (24 - (2 * 8))) and $ff);
  282. if aDecodedCount >= 4 then begin
  283. BufferByte((aValue shr (24 - (3 * 8))) and $ff);
  284. if aDecodedCount >= 5 then begin
  285. raise EConvertError.Create('not enough decoded data (internal error).');
  286. end;
  287. end;
  288. end;
  289. end;
  290. end;
  291. end;
  292. procedure TASCII85DecoderStream.Decode(aInput : Byte);
  293. begin
  294. if (aInput in [ 10, 13, 9, {0, 8,} 12, 32]) and (fState <> ascPrefix { chicken}) then // skip whitespace.
  295. Exit;
  296. case fState of
  297. ascInitial, ascNoEncodedChar:
  298. if aInput = ord('z') then begin
  299. BufferTuple(0, 4);
  300. end else begin
  301. if (aInput = ord('<')) and (fState = ascInitial) {and (fBExpectBoundary)} then begin
  302. fState := ascPrefix;
  303. end else begin
  304. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[0];
  305. fState := ascOneEncodedChar;
  306. end;
  307. end;
  308. ascOneEncodedChar:
  309. begin
  310. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[1];
  311. fState := ascTwoEncodedChars;
  312. end;
  313. ascTwoEncodedChars:
  314. begin
  315. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[2];
  316. fState := ascThreeEncodedChars;
  317. end;
  318. ascThreeEncodedChars:
  319. begin
  320. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[3];
  321. fState := ascFourEncodedChars;
  322. end;
  323. ascFourEncodedChars:
  324. begin
  325. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[4];
  326. BufferTuple(fTuple, 4);
  327. fTuple := 0;
  328. fState := ascNoEncodedChar;
  329. end;
  330. ascPrefix:
  331. begin
  332. if aInput = ord('~') then begin
  333. fBExpectBoundary := True;
  334. fState := ascNoEncodedChar
  335. end else begin
  336. // whoops, actually "~" is outside the allowed range, so we CAN find out whether there was supposed to be a boundary string or not on our own...
  337. // we reached this place since we saw a '<', thought it was part of '<~', but it wasn't. '<' is an allowed encoded character.
  338. // catch up on work we should have been doing...
  339. assert(fTuple = 0);
  340. fTuple := fTuple + DecodeNonTrivialByte(ord('<')) * cPow85[0];
  341. //fState := ascOneEncodedChar;
  342. fTuple := fTuple + DecodeNonTrivialByte(aInput) * cPow85[1];
  343. fState := ascTwoEncodedChars;
  344. //raise EConvertError.Create(Format('expected ''<~'', got %d', [aInput]));
  345. end;
  346. end
  347. else
  348. raise EConvertError.Create('internal error');
  349. end;
  350. end;
  351. destructor TASCII85DecoderStream.Destroy();
  352. begin
  353. Self.Close();
  354. FreeAndNil(fBuffer);
  355. inherited Destroy;
  356. end;
  357. function TASCII85DecoderStream.ClosedP() : Boolean; inline;
  358. begin
  359. Result := (fState in [ascInitial, ascNoEncodedChar, ascPrefix]);
  360. end;
  361. procedure TASCII85DecoderStream.Close();
  362. var
  363. vCount : Cardinal;
  364. begin
  365. if fState = ascPrefix then
  366. raise EConvertError.Create('unexpected end of file while trying to find ''<~'' prefix (after the ''<'' was seen).');
  367. if not (fState in [ascInitial, ascNoEncodedChar, ascPrefix]) then begin // we have some bytes left over.
  368. if fState = ascOneEncodedChar then
  369. raise EConvertError.Create('The last block of ASCII85 data cannot be a single byte.');
  370. vCount := Cardinal(fState) - 1; // one less!!
  371. fTuple := fTuple + cPow85[vCount];
  372. BufferTuple(fTuple, vCount);
  373. fState := ascInitial;
  374. end;
  375. end;
  376. function TASCII85DecoderStream.Read(var aBuffer; aCount : longint) : longint;
  377. var
  378. vAvailableCount : Cardinal;
  379. vPermittedReadCount : Cardinal;
  380. vEncodedBufferCount : Cardinal;
  381. vEncodedBufferIndex : Cardinal;
  382. vItem : Byte;
  383. vBuffer : PByte;
  384. vBufferCount : Cardinal;
  385. begin
  386. vBuffer := @aBuffer;
  387. Result := 0;
  388. if fBEOF then begin
  389. Exit;
  390. end;
  391. repeat
  392. // first use up the buffer contents as far as possible.
  393. if aCount <= 0 then
  394. Break;
  395. vBufferCount := fBuffer.Read(vBuffer^, aCount);
  396. assert(vBufferCount <= aCount);
  397. Inc(vBuffer, vBufferCount);
  398. Dec(aCount, vBufferCount);
  399. Inc(Result, vBufferCount);
  400. if fBSourceEOF and (vBufferCount = 0) then begin
  401. fBEOF := True;
  402. Break;
  403. end;
  404. if aCount <= 0 then
  405. Break;
  406. // here, aCount contains the REMAINING request and the buffer is either empty or there wasn't that much needed anyway (in the latter case the Exit above finished the function).
  407. // if then, there's still something needed, fill the buffer only as far as we need to.
  408. assert(fBuffer.FillCount = 0);
  409. vAvailableCount := fBuffer.Size - fBuffer.FillCount;
  410. vPermittedReadCount := vAvailableCount shr 2; // worst-case, decoded data will grow 4x ('z' -> '0000').
  411. {if aCount < vAvailableCount then begin
  412. vAvailableCount := aCount;}
  413. vEncodedBufferCount := 0;
  414. if not fBSourceEOF then
  415. vEncodedBufferCount := Source.Read(fEncodedBuffer[0], vPermittedReadCount);
  416. if (vEncodedBufferCount = 0) then begin // EOF
  417. fBSourceEOF := True;
  418. if not ClosedP() then
  419. Close() // make sure we catch the "virtual characters". This could fill the buffer a little bit.
  420. {else
  421. fBEOF := True};
  422. Continue;
  423. end else // Buffer the output we couldn't pass on so far.
  424. for vEncodedBufferIndex := 0 to vEncodedBufferCount - 1 do begin
  425. vItem := fEncodedBuffer[vEncodedBufferIndex];
  426. if (vItem = ord('~')) and fBExpectBoundary then begin // holy #@! oops...
  427. fBSourceEOF := True;
  428. {if not fBExpectBoundary then -- flag is not yet valid.
  429. raise EConvertError.Create('unexpected ''~>'' (there was no starting ''<~'', so why would there be a final one?).');
  430. }
  431. // note that here, it could be that we ran over the boundary '~>' suffix in the underlying stream and didn't notice. In that case, the 'Decode' call below would break.
  432. if not ClosedP() then
  433. Close(); // make sure we catch the "virtual characters". This could fill the buffer a little bit.
  434. // seek the underlying stream and hope nobody noticed that we completely ignored the boundary :)
  435. try
  436. Source.Seek(vEncodedBufferIndex - vEncodedBufferCount + 1, 1); // from current position.
  437. if Source.ReadByte() <> ord('>') then
  438. raise EConvertError.Create('the final ''~>'' is malformed.');
  439. except
  440. on E : EConvertError do
  441. raise;
  442. {$IFNDEF UNSEEKABLE_STREAMS_ARE_EVIL}
  443. else
  444. ; // too bad... well, we tried.
  445. {$ENDIF}
  446. end;
  447. Break; // for.
  448. end;
  449. Self.Decode(vItem);
  450. end;
  451. until (aCount <= 0) or (fBEOF) or (vPermittedReadCount = 0);
  452. Inc(fPosition, Result);
  453. end;
  454. function TASCII85DecoderStream.Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64;
  455. begin
  456. if (aOrigin = soCurrent) and (aOffset = 0) then begin // get position.
  457. Result := fPosition;
  458. Exit;
  459. end;
  460. raise EReadError.Create('could not seek...');
  461. //assert(fState in [ascInitial, ascNoEncodedChar]);
  462. //Result := inherited Seek(aOffset, aOrigin); // bad idea.
  463. end;
  464. function TASCII85DecoderStream.Seek(aOffset : longint; aOrigin : word) : longint;
  465. begin
  466. Result := Self.Seek(Int64(aOffset), TSeekOrigin(aOrigin));
  467. end;
  468. function TASCII85DecoderStream.GetPosition() : Int64;
  469. begin
  470. Result := fPosition;
  471. end;
  472. initialization
  473. assert(Sizeof(Cardinal) >= 4);
  474. end.