#1 2017-01-10 12:00:16

macc2010
Member
Registered: 2015-12-28
Posts: 8

Bug in Base64JSONStringToBytes in Delphi mobile compilers

Hello,

I have discovered a bug in the function Base64JSONStringToBytes that is located in the unit SynCrossPlatformJSON compiling with Delphi 10.1 Berlin update 2 and a recently installation of mormot of November 2016 and the project targeting and Android device.

The problem is that in the function Base64JSONStringToBytes the string named JSONString is accesed using 1-based indexing and in mobile compilers in Delphi this strings should be converted to access them using the rule of 0-based indexing ( you can see it in this page : http://docwiki.embarcadero.com/RADStudi … om_Desktop ). The best solution is to use the TStringHelper.Chars property that is 0-based indexing independent of the compiler, so changing the access of JSONString[x] by JSONString.Chars[x-1] the program run well targeting Win32/64 or Android/ios ( mobile devices ).

You can see the corrected function here : 

function Base64JSONStringToBytes(const JSONString: string;
  var Bytes: TByteDynArray; withBase64Magic: boolean): boolean;
var i,bits,value,x,magiclen,len: cardinal;
begin
  result := JSONString='';
  if result then
    exit;
  if withBase64Magic then
    if comparemem(pointer(JSONString),@JSON_BASE64_MAGIC,sizeof(JSON_BASE64_MAGIC)) then
      magiclen := JSON_BASE64_MAGIC_LEN else
      {$ifndef UNICODE}
      //if JSONString[1]='?' then // handle UTF-8 decoding error on ANSI Delphi
      if JSONString.Chars[0]='?' then // <----- NEW CODE
        magiclen := 1 else
      {$endif}
      exit else
    magiclen := 0; // withBase64Magic=false
  x := length(JSONString);
  len := x-magiclen;
  if len and 3<>0 then
    exit;
  if len=0 then
    Bytes := nil else begin
    if BASE64DECODE=nil then begin
      SetLength(BASE64DECODE,128);
      for i := 0 to 127 do
        BASE64DECODE[i] := -1;
      for i := 0 to high(BASE64) do
        BASE64DECODE[ord(BASE64[i])] := i;
    end;
    len := (len shr 2)*3;
    //if Base64One(JSONString[x])<0 then begin
    if Base64One(JSONString.Chars[x-1])<0 then begin // <----- NEW CODE
      dec(len);
      //if Base64One(JSONString[x-1])<0 then
      if Base64One(JSONString.Chars[x-2])<0 then // <----- NEW CODE
        dec(len);
    end;
    SetLength(Bytes,len);
    bits := 0;
    value := 0;
    len := 0;
    for i := magiclen+1 to Length(JSONString) do begin
      //x := ord(JSONString[i]); // inlined Base64One(JSONString[i])
      x := ord(JSONString.Chars[i-1]); // <----- NEW CODE
      if x>127 then
        break;
      x := cardinal(BASE64DECODE[x]);
      if integer(x)<0 then
        break;
      value := value*64+x;
      bits := bits+6;
      if bits>=8 then begin
        bits := bits-8;
        x := value shr bits;
        value := value and ((1 shl bits)-1);
        Bytes[len] := x;
        inc(len);
      end;
    end;
  end;
  result := len=cardinal(length(Bytes));
end;

I suppose that the complementary function BytesToBase64JSONString can have the same problem.

I saw the problem downloading a bitmap content as Base64 from a interface based service server. When I compiled the program targeting Win32 or Win64 the bitmap was decoded using Base64JSONStringToBytes and had a size X. But if I compiled the project targeting Android, the same bitmap had a size of X - 1. Once that I corrected the Base64JSONStringToBytes function to use the Chars property of TStringHelper class the problem solved.

Can anyone confirm me this bug please?. I am in the doubt if I am wrong.

Thank you and best regards.

Offline

Board footer

Powered by FluxBB