mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-15 20:09:20 +02:00
* Patch from Bram Kuijvenhoven to fix stream, and add MIME mode
git-svn-id: trunk@6987 -
This commit is contained in:
parent
74cbe82e3f
commit
28e7b354cc
@ -12,7 +12,12 @@
|
|||||||
|
|
||||||
**********************************************************************}
|
**********************************************************************}
|
||||||
|
|
||||||
// Encoding and decoding streams for base64 data as described in RFC2045
|
// Encoding and decoding streams for base64 data as described in
|
||||||
|
// RFC2045 (Mode = bdmMIME) and
|
||||||
|
// RFC3548 (Mode = bdmStrict)
|
||||||
|
|
||||||
|
// Addition of TBase64DecodingMode supporting both Strict and MIME mode is
|
||||||
|
// (C) 2007 Hexis BV, by Bram Kuijvenhoven (bkuijvenhoven@hexis.nl)
|
||||||
|
|
||||||
{$MODE objfpc}
|
{$MODE objfpc}
|
||||||
{$H+}
|
{$H+}
|
||||||
@ -21,7 +26,7 @@ unit base64;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
uses classes;
|
uses classes, sysutils;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -40,51 +45,88 @@ type
|
|||||||
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
(* The TBase64DecodingStream supports two modes:
|
||||||
|
* - 'strict mode':
|
||||||
|
* - follows RFC3548
|
||||||
|
* - rejects any characters outside of base64 alphabet,
|
||||||
|
* - only accepts up to two '=' characters at the end and
|
||||||
|
* - requires the input to have a Size being a multiple of 4; otherwise raises an EBase64DecodeException
|
||||||
|
* - 'MIME mode':
|
||||||
|
* - follows RFC2045
|
||||||
|
* - ignores any characters outside of base64 alphabet
|
||||||
|
* - takes any '=' as end of string
|
||||||
|
* - handles apparently truncated input streams gracefully
|
||||||
|
*)
|
||||||
|
TBase64DecodingMode = (bdmStrict, bdmMIME);
|
||||||
|
|
||||||
|
{ TBase64DecodingStream }
|
||||||
|
|
||||||
TBase64DecodingStream = class(TStream)
|
TBase64DecodingStream = class(TStream)
|
||||||
|
private
|
||||||
|
FMode: TBase64DecodingMode;
|
||||||
|
procedure SetMode(const AValue: TBase64DecodingMode);
|
||||||
|
function GetSize: Int64; override;
|
||||||
|
function GetPosition: Int64; override;
|
||||||
protected
|
protected
|
||||||
InputStream: TStream;
|
InputStream: TStream;
|
||||||
CurPos, InputStreamSize: LongInt;
|
CurPos, // 0-based (decoded) position of this stream (nr. of decoded & Read bytes since last reset)
|
||||||
Buf: array[0..2] of Byte;
|
DecodedSize: Int64; // length of decoded stream ((expected) decoded bytes since last Reset until Mode-dependent end of stream)
|
||||||
BufPos: Integer; // Offset of byte which is to be read next
|
ReadBase64ByteCount: Int64; // number of valid base64 bytes read from input stream since last Reset
|
||||||
fEOF: Boolean;
|
Buf: array[0..2] of Byte; // last 3 decoded bytes
|
||||||
|
BufPos: Integer; // offset in Buf of byte which is to be read next; if >2, next block must be read from InputStream & decoded
|
||||||
|
FEOF: Boolean; // if true, all decoded bytes have been read
|
||||||
public
|
public
|
||||||
constructor Create(AInputStream: TStream);
|
constructor Create(AInputStream: TStream);
|
||||||
|
constructor Create(AInputStream: TStream; AMode: TBase64DecodingMode);
|
||||||
procedure Reset;
|
procedure Reset;
|
||||||
|
|
||||||
function Read(var Buffer; Count: Longint): Longint; override;
|
function Read(var Buffer; Count: Longint): Longint; override;
|
||||||
function Write(const Buffer; Count: Longint): Longint; override;
|
function Write(const Buffer; Count: Longint): Longint; override;
|
||||||
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
function Seek(Offset: Longint; Origin: Word): Longint; override;
|
||||||
|
|
||||||
property EOF: Boolean read fEOF;
|
property EOF: Boolean read fEOF;
|
||||||
|
property Mode: TBase64DecodingMode read FMode write SetMode;
|
||||||
|
end;
|
||||||
|
|
||||||
|
EBase64DecodingException = class(Exception)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
Math;
|
||||||
|
|
||||||
const
|
const
|
||||||
|
SStrictNonBase64Char = 'Non-valid Base64 Encoding character in input';
|
||||||
|
SStrictInputTruncated = 'Input stream was truncated at non-4 byte boundary';
|
||||||
|
SStrictMisplacedPadChar = 'Unexpected padding character ''='' before end of input stream';
|
||||||
|
|
||||||
EncodingTable: PChar =
|
EncodingTable: PChar =
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
|
||||||
DecTable: array[Byte] of Byte =
|
const
|
||||||
(99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 0-15
|
NA = 85; // not in base64 alphabet at all; binary: 01010101
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // 16-31
|
PC = 255; // padding character 11111111
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 62, 99, 99, 99, 63, // 32-47
|
|
||||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 99, 99, 99, 00, 99, 99, // 48-63
|
|
||||||
99, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, // 64-79
|
|
||||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 99, 99, 99, 99, 99, // 80-95
|
|
||||||
99, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
|
|
||||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 99, 99, 99, 99, 99, // 112-127
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
||||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99);
|
|
||||||
|
|
||||||
|
DecTable: array[Byte] of Byte =
|
||||||
|
(NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, // 0-15
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, // 16-31
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 62, NA, NA, NA, 63, // 32-47
|
||||||
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NA, NA, NA, PC, NA, NA, // 48-63
|
||||||
|
NA, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, // 64-79
|
||||||
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, NA, NA, NA, NA, NA, // 80-95
|
||||||
|
NA, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111
|
||||||
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NA, NA, NA, NA, NA, // 112-127
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,
|
||||||
|
NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA);
|
||||||
|
|
||||||
|
Alphabet = ['a'..'z','A'..'Z','0'..'9','+','/','=']; // all 65 chars that are in the base64 encoding alphabet
|
||||||
|
|
||||||
constructor TBase64EncodingStream.Create(AOutputStream: TStream);
|
constructor TBase64EncodingStream.Create(AOutputStream: TStream);
|
||||||
begin
|
begin
|
||||||
@ -164,96 +206,211 @@ begin
|
|||||||
raise EStreamError.Create('Invalid stream operation');
|
raise EStreamError.Create('Invalid stream operation');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TBase64DecodingStream.SetMode(const AValue: TBase64DecodingMode);
|
||||||
|
begin
|
||||||
|
if FMode = AValue then exit;
|
||||||
|
FMode := AValue;
|
||||||
|
DecodedSize := -1; // forget any calculations on this
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBase64DecodingStream.GetSize: Int64;
|
||||||
|
var
|
||||||
|
endBytes: array[0..1] of Char;
|
||||||
|
ipos, isize: Int64;
|
||||||
|
scanBuf: array[0..1023] of Char;
|
||||||
|
count: LongInt;
|
||||||
|
i: Integer;
|
||||||
|
c: Char;
|
||||||
|
begin
|
||||||
|
// Note: this method only works on Seekable InputStreams (for bdmStrict we also get the Size property)
|
||||||
|
if DecodedSize<>-1 then Exit(DecodedSize);
|
||||||
|
ipos := InputStream.Position; // save position in input stream
|
||||||
|
case Mode of
|
||||||
|
bdmMIME: begin
|
||||||
|
// read until end of input stream or first occurence of a '='
|
||||||
|
Result := ReadBase64ByteCount; // keep number of valid base64 bytes since last Reset in Result
|
||||||
|
repeat
|
||||||
|
count := InputStream.Read(scanBuf, SizeOf(scanBuf));
|
||||||
|
for i := 0 to count-1 do begin
|
||||||
|
c := scanBuf[i];
|
||||||
|
if c in Alphabet-['='] then // base64 encoding characters except '='
|
||||||
|
Inc(Result)
|
||||||
|
else if c = '=' then // end marker '='
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
until count = 0;
|
||||||
|
writeln(Result);
|
||||||
|
// we are now either at the end of the stream, or encountered our first '=', stored in c
|
||||||
|
if c = '=' then begin // '=' found
|
||||||
|
if Result mod 4 <= 1 then // badly placed '=', disregard last block
|
||||||
|
Result := (Result div 4) * 3
|
||||||
|
else // 4 byte block ended with '=' or '=='
|
||||||
|
Result := (Result div 4) * 3 + Result mod 4 - 1;
|
||||||
|
end else // end of stream
|
||||||
|
Result := (Result div 4) * 3; // number of valid 4 byte blocks times 3
|
||||||
|
end;
|
||||||
|
bdmStrict:begin
|
||||||
|
// seek to end of input stream, read last two bytes and determine size
|
||||||
|
// from InputStream size and the number of leading '=' bytes
|
||||||
|
// NB we don't raise an exception here if the input does not contains an integer multiple of 4 bytes
|
||||||
|
ipos := InputStream.Position;
|
||||||
|
isize := InputStream.Size;
|
||||||
|
Result := ((ReadBase64ByteCount + (isize - ipos) + 3) div 4) * 3;
|
||||||
|
InputStream.Seek(-2, soFromEnd);
|
||||||
|
InputStream.Read(endBytes, 2);
|
||||||
|
if endBytes[1] = '=' then begin // last byte
|
||||||
|
Dec(Result);
|
||||||
|
if endBytes[0] = '=' then // second to last byte
|
||||||
|
Dec(Result);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
InputStream.Position := ipos; // restore position in input stream
|
||||||
|
// store calculated DecodedSize
|
||||||
|
DecodedSize := Result;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TBase64DecodingStream.GetPosition: Int64;
|
||||||
|
begin
|
||||||
|
Result := CurPos;
|
||||||
|
end;
|
||||||
|
|
||||||
constructor TBase64DecodingStream.Create(AInputStream: TStream);
|
constructor TBase64DecodingStream.Create(AInputStream: TStream);
|
||||||
|
begin
|
||||||
|
Create(AInputStream, bdmMIME); // MIME mode is default
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TBase64DecodingStream.Create(AInputStream: TStream; AMode: TBase64DecodingMode);
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
InputStream := AInputStream;
|
InputStream := AInputStream;
|
||||||
|
Mode := AMode;
|
||||||
Reset;
|
Reset;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TBase64DecodingStream.Reset;
|
procedure TBase64DecodingStream.Reset;
|
||||||
begin
|
begin
|
||||||
InputStreamSize := -1;
|
ReadBase64ByteCount := 0; // number of bytes Read form InputStream since last call to Reset
|
||||||
BufPos := 3;
|
CurPos := 0; // position in decoded byte sequence since last Reset
|
||||||
fEOF := False;
|
DecodedSize := -1; // indicates unknown; will be set after first call to GetSize or when reaching end of stream
|
||||||
|
BufPos := 3; // signals we need to read & decode a new block of 4 bytes
|
||||||
|
FEOF := False;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TBase64DecodingStream.Read(var Buffer; Count: Longint): Longint;
|
function TBase64DecodingStream.Read(var Buffer; Count: Longint): Longint;
|
||||||
var
|
var
|
||||||
p: PChar;
|
p: PByte;
|
||||||
b: Char;
|
b: byte;
|
||||||
ReadBuf: array[0..3] of Byte;
|
ReadBuf: array[0..3] of Byte; // buffer to store last read 4 input bytes
|
||||||
ToRead, OrgToRead, HaveRead, ReadOK, i, j: Integer;
|
ToRead, OrgToRead, HaveRead, ReadOK, i: Integer;
|
||||||
|
|
||||||
|
procedure DetectedEnd(ASize:Int64);
|
||||||
|
begin
|
||||||
|
DecodedSize := ASize;
|
||||||
|
// Correct Count if at end of base64 input
|
||||||
|
if CurPos + Count > DecodedSize then
|
||||||
|
Count := DecodedSize - CurPos;
|
||||||
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if Count <= 0 then exit(0);
|
if Count <= 0 then exit(0); // nothing to read, quit
|
||||||
if InputStreamSize <> -1 then begin
|
if DecodedSize <> -1 then begin // try using calculated size info if possible
|
||||||
if CurPos + Count > InputStreamSize then
|
if CurPos + Count > DecodedSize then
|
||||||
Count := InputStreamSize - CurPos;
|
Count := DecodedSize - CurPos;
|
||||||
if Count <= 0 then exit(0);
|
if Count <= 0 then exit(0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := 0;
|
Result := 0;
|
||||||
p := PChar(@Buffer);
|
p := @Buffer;
|
||||||
while (Count > 0) and not fEOF do begin
|
while true do begin
|
||||||
|
// get new 4-byte block if at end of Buf
|
||||||
if BufPos > 2 then begin
|
if BufPos > 2 then begin
|
||||||
BufPos := 0;
|
BufPos := 0;
|
||||||
// Read the next 4 valid bytes
|
// Read the next 4 valid bytes
|
||||||
ToRead := 4;
|
ToRead := 4; // number of base64 bytes left to read into ReadBuf
|
||||||
ReadOK := 0;
|
ReadOK := 0; // number of base64 bytes already read into ReadBuf
|
||||||
while ToRead > 0 do begin
|
while ToRead > 0 do begin
|
||||||
OrgToRead := ToRead;
|
OrgToRead := ToRead;
|
||||||
HaveRead := InputStream.Read(ReadBuf[ReadOK], ToRead);
|
HaveRead := InputStream.Read(ReadBuf[ReadOK], ToRead);
|
||||||
//WriteLn('ToRead = ', ToRead, ', HaveRead = ', HaveRead, ', ReadOK=', ReadOk);
|
//WriteLn('ToRead = ', ToRead, ', HaveRead = ', HaveRead, ', ReadOK=', ReadOk);
|
||||||
if HaveRead > 0 then begin
|
if HaveRead > 0 then begin // if any new bytes; in ReadBuf[ReadOK .. ReadOK + HaveRead-1]
|
||||||
i := ReadOk;
|
for i := ReadOK to ReadOK + HaveRead - 1 do begin
|
||||||
while i<HaveRead do begin
|
b := DecTable[ReadBuf[i]];
|
||||||
ReadBuf[i] := DecTable[ReadBuf[i]];
|
if b <> NA then begin // valid base64 alphabet character ('=' inclusive)
|
||||||
if ReadBuf[i] = 99 then
|
ReadBuf[ReadOK] := b;
|
||||||
for j := i to 3 do
|
|
||||||
ReadBuf[i] := ReadBuf[i + 1]
|
|
||||||
else begin
|
|
||||||
Inc(i);
|
|
||||||
Inc(ReadOK);
|
Inc(ReadOK);
|
||||||
Dec(ToRead);
|
Dec(ToRead);
|
||||||
|
end else if Mode=bdmStrict then begin // non-valid character
|
||||||
|
raise EBase64DecodingException.CreateFmt(SStrictNonBase64Char,[]);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if HaveRead <> OrgToRead then begin
|
|
||||||
//WriteLn('Ende? ReadOK=', ReadOK, ', count=', Count);
|
if HaveRead <> OrgToRead then begin // less than 4 base64 bytes could be read; end of input stream
|
||||||
|
//WriteLn('End: ReadOK=', ReadOK, ', count=', Count);
|
||||||
for i := ReadOK to 3 do
|
for i := ReadOK to 3 do
|
||||||
ReadBuf[i] := Ord('=');
|
ReadBuf[i] := 0; // pad buffer with zeros so decoding of 4-bytes will be correct
|
||||||
fEOF := True;
|
if (Mode = bdmStrict) and (ReadOK > 0) then
|
||||||
if ReadOK < 2 then exit; // Not enough data available in input stream
|
raise EBase64DecodingException.CreateFmt(SStrictInputTruncated,[]);
|
||||||
break;
|
Break;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Check for fill bytes
|
Inc(ReadBase64ByteCount, ReadOK);
|
||||||
if (Count >= 2) and (ReadBuf[3] = Ord('=')) then begin
|
|
||||||
//WriteLn('Endemarkierung!');
|
// Check for pad characters
|
||||||
fEOF := True;
|
case Mode of
|
||||||
if ReadBuf[2] = Ord('=') then
|
bdmStrict:begin
|
||||||
Count := 1
|
if ReadOK = 0 then // end of input stream was reached at 4-byte boundary
|
||||||
else
|
DetectedEnd(CurPos)
|
||||||
Count := 2;
|
else if (ReadBuf[0] = PC) or (ReadBuf[1] = PC) then
|
||||||
|
raise EBase64DecodingException.CreateFmt(SStrictMisplacedPadChar,[]) // =BBB or B=BB
|
||||||
|
else if (ReadBuf[2] = PC) then begin
|
||||||
|
if (ReadBuf[3] <> PC) or (InputStream.Position < InputStream.Size) then
|
||||||
|
raise EBase64DecodingException.CreateFmt(SStrictMisplacedPadChar,[]); // BB=B or BB==, but not at end of input stream
|
||||||
|
DetectedEnd(CurPos + 1) // only one byte left to read; BB==, at end of input stream
|
||||||
|
end else if (ReadBuf[3] = PC) then begin
|
||||||
|
if (InputStream.Position < InputStream.Size) then
|
||||||
|
raise EBase64DecodingException.CreateFmt(SStrictMisplacedPadChar,[]); // BBB=, but not at end of input stream
|
||||||
|
DetectedEnd(CurPos + 2); // only two bytes left to read; BBB=, at end of input stream
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
bdmMIME:begin
|
||||||
|
if ReadOK = 0 then // end of input stream was reached at 4-byte boundary
|
||||||
|
DetectedEnd(CurPos)
|
||||||
|
else if (ReadBuf[0] = PC) or (ReadBuf[1] = PC) then
|
||||||
|
DetectedEnd(CurPos) // =BBB or B=BB: end here
|
||||||
|
else if (ReadBuf[2] = PC) then begin
|
||||||
|
DetectedEnd(CurPos + 1) // only one byte left to read; BB=B or BB==
|
||||||
|
end else if (ReadBuf[3] = PC) then begin
|
||||||
|
DetectedEnd(CurPos + 2); // only two bytes left to read; BBB=
|
||||||
|
end;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Decode the 4 bytes in the buffer to 3 undecoded bytes
|
// Decode the 4 bytes in the buffer to 3 undecoded bytes
|
||||||
Buf[0] := ReadBuf[0] shl 2 or ReadBuf[1] shr 4;
|
Buf[0] := ReadBuf[0] shl 2 or ReadBuf[1] shr 4;
|
||||||
Buf[1] := (ReadBuf[1] and 15) shl 4 or ReadBuf[2] shr 2;
|
Buf[1] := (ReadBuf[1] and 15) shl 4 or ReadBuf[2] shr 2;
|
||||||
Buf[2] := (ReadBuf[2] and 3) shl 6 or ReadBuf[3];
|
Buf[2] := (ReadBuf[2] and 3) shl 6 or ReadBuf[3];
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Count <= 0 then begin
|
||||||
|
Break;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
p[0] := Chr(Buf[BufPos]);
|
// write one byte to Count
|
||||||
|
p^ := Buf[BufPos];
|
||||||
Inc(p);
|
Inc(p);
|
||||||
Inc(BufPos);
|
Inc(BufPos);
|
||||||
Inc(CurPos);
|
Inc(CurPos);
|
||||||
Dec(Count);
|
Dec(Count);
|
||||||
Inc(Result);
|
Inc(Result);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// check for EOF
|
||||||
|
if (DecodedSize <> -1) and (CurPos >= DecodedSize) then begin
|
||||||
|
FEOF := true;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TBase64DecodingStream.Write(const Buffer; Count: Longint): Longint;
|
function TBase64DecodingStream.Write(const Buffer; Count: Longint): Longint;
|
||||||
@ -262,35 +419,9 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TBase64DecodingStream.Seek(Offset: Longint; Origin: Word): Longint;
|
function TBase64DecodingStream.Seek(Offset: Longint; Origin: Word): Longint;
|
||||||
var
|
|
||||||
ipos: LongInt;
|
|
||||||
endbytes: array[0..1] of Char;
|
|
||||||
begin
|
begin
|
||||||
{This will work only if the input stream supports seeking / Size. If not, the
|
// TODO: implement Seeking in TBase64DecodingStream
|
||||||
input stream will raise an exception; we don't handle them here but pass them
|
raise EStreamError.Create('Invalid stream operation');
|
||||||
to the caller.}
|
|
||||||
if InputStreamSize = -1 then begin
|
|
||||||
ipos := InputStream.Position;
|
|
||||||
InputStreamSize := ((InputStream.Size - ipos + 3) div 4) * 3;
|
|
||||||
InputStream.Seek(-2, soFromEnd);
|
|
||||||
InputStream.Read(endbytes, 2);
|
|
||||||
InputStream.Position := ipos;
|
|
||||||
if endbytes[1] = '=' then begin
|
|
||||||
Dec(InputStreamSize);
|
|
||||||
if endbytes[0] = '=' then
|
|
||||||
Dec(InputStreamSize);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// This stream only supports the Seek modes needed for determining its size
|
|
||||||
if (Origin = soFromCurrent) and (Offset = 0) then
|
|
||||||
Result := CurPos
|
|
||||||
else if (Origin = soFromEnd) and (Offset = 0) then
|
|
||||||
Result := InputStreamSize
|
|
||||||
else if (Origin = soFromBeginning) and (Offset = CurPos) then
|
|
||||||
Result := CurPos
|
|
||||||
else
|
|
||||||
raise EStreamError.Create('Invalid stream operation');
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user