Lazarus

Programming => General => Topic started by: garlar27 on June 19, 2017, 03:31:19 pm

Title: [SOLVED] Exception using ASCII85: EReadError "could not seek..."
Post by: garlar27 on June 19, 2017, 03:31:19 pm
I'm trying to decode an ASCII85 (or Base 85) string using ascii85 unit, but an EReadError exception is raised with the message "could not seek...".
Since ascii85 unit doesn't have an easy Encode/Decode function, I did one based in "base64.DecodeStringBase64();".
The code is:
Code: Pascal  [Select][+][-]
  1. function DecodeStringAscii85(const s:string; const ExpectBoundary: Boolean):string; // BASED ON base64.DecodeStringBase64();
  2. var
  3.   Instream,
  4.   Outstream : TStringStream;
  5.   Decoder   : TASCII85DecoderStream;
  6. begin
  7.   // I think StringReplace() is not needed but I did it anyway...
  8.   Instream:=TStringStream.Create(StringReplace(StringReplace(s, #13, '', [rfReplaceAll]), #10, '', [rfReplaceAll]));
  9.   try
  10.     Outstream:=TStringStream.Create('');
  11.     try
  12.       Decoder:=TASCII85DecoderStream.Create(Instream);
  13.       try
  14.         Decoder.BExpectBoundary := ExpectBoundary;
  15.         Outstream.CopyFrom(Decoder,Decoder.Size);
  16.         Result:=Outstream.DataString;
  17.       finally
  18.         Decoder.Free;
  19.       end;
  20.     finally
  21.       Outstream.Free;
  22.     end;
  23.   finally
  24.     Instream.Free;
  25.   end;
  26. end;
  27.  
Then I took an example from Example for Ascii85 (https://en.wikipedia.org/wiki/Ascii85#Example_for_Ascii85):
Quote
<~9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF<GL>Cj@.4Gp$d7F!,L7@<6@)/0JDEF<G%<+EV:2F!,
O<DJ+*.@<*K0@<6L(Df-\0Ec5e;DffZ(EZee.Bl.9pF"AGXBPCsi+DGm>@3BB/F*&OCAfu2/AKY
i(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF<G:8+EV:.+Cf>-FD5W8ARlolDIa
l(DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G
>uD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>
and passed it to the function like this:
Code: Pascal  [Select][+][-]
  1.   DecodeStringAscii85(Memo1.Text, TRUE);
  2.  
Does any body knows what could be the problem?
Title: Re: Exception using ASCII85: EReadError "could not seek..."
Post by: Xor-el on June 19, 2017, 05:12:40 pm
although I have not tested your code, this library  https://github.com/Xor-el/BaseNcodingPascal can be used to work with ASCII85 (also known as base85)
Title: Re: Exception using ASCII85: EReadError "could not seek..."
Post by: michalis on June 19, 2017, 06:30:36 pm
I'm trying to decode an ASCII85 (or Base 85) string using ascii85 unit, but an EReadError exception is raised with the message "could not seek...".

This is most likely because you call Decoder.Size.

The TASCII85DecoderStream class does not override the Size implementation, so the default implementation of TStream.GetSize is used. And TStream.GetSize uses seeking. But, the TASCII85DecoderStream.Seek does not allow seeking (raising the EReadError you see, in most non-trivial cases).

You can verify this by looking at TASCII85DecoderStream implementation (in packages/fcl-base/src/ascii85.pp in FPC sources) and TStream implementation (in rtl/objpas/classes/streams.inc in FPC sources).

You most probably (but I have not tried it, so don't hold me to it) solve it by passing 0 instead of Decoder.Size to CopyFrom. The CopyFrom may work then OK. In general, it's not guaranteed that the CopyFrom will work, it's even documented (https://www.freepascal.org/docs-html/rtl/classes/tstream.copyfrom.html): "Note that this cannot be used with streams that do not allow seeking or do not allow determining the size of the stream.". But, at least looking at the CopyFrom implementation in current FPC 3.1.1, it may actually work in this simple case.

If this doesn't help, you will need to implement your own version of "CopyFrom" that works on a non-seekable stream like TASCII85DecoderStream. It's not very difficult, you can take a look at what the TStream.CopyFrom is doing in FPC sources (rtl/objpas/classes/streams.inc) :)
Title: Re: Exception using ASCII85: EReadError "could not seek..."
Post by: ASerge on June 19, 2017, 07:37:33 pm
After adding a patch and changing BExpectBoundary to False, your example works:
Code: Pascal  [Select][+][-]
  1. type
  2.   TASCII85DecoderStream = class(ascii85.TASCII85DecoderStream)
  3.     function Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; override;
  4.   end;
  5.  
  6. function TASCII85DecoderStream.Seek(const aOffset: Int64;
  7.   aOrigin: TSeekOrigin): Int64;
  8. begin
  9.   if (aOffset = 0) and (aOrigin = soBeginning) then
  10.     Result := 0
  11.   else
  12.     Result := inherited;
  13. end;
Title: Re: Exception using ASCII85: EReadError "could not seek..."
Post by: michalis on June 19, 2017, 07:48:56 pm
After adding a patch and changing BExpectBoundary to False, your example works:

Ah, indeed, the "CopyFrom" implementation still requires the "Source.Position:=0;" to work, which your patch fixes. Cool!
Title: [SOLVED] Re: Exception using ASCII85: EReadError "could not seek..."
Post by: garlar27 on June 21, 2017, 04:48:16 pm
After adding a patch and changing BExpectBoundary to False, your example works:
Code: Pascal  [Select][+][-]
  1. type
  2.   TASCII85DecoderStream = class(ascii85.TASCII85DecoderStream)
  3.     function Seek(const aOffset: Int64; aOrigin: TSeekOrigin): Int64; override;
  4.   end;
  5.  
  6. function TASCII85DecoderStream.Seek(const aOffset: Int64;
  7.   aOrigin: TSeekOrigin): Int64;
  8. begin
  9.   if (aOffset = 0) and (aOrigin = soBeginning) then
  10.     Result := 0
  11.   else
  12.     Result := inherited;
  13. end;

Thanks a lot!!! It really solved the problem.
ASerge: Did you sent this patch to fpc?

michalis: Thanks for thedata!!
TinyPortal © 2005-2018