THRIFT-2890 binary data may lose bytes with JSON transport under specific circumstances
Client: Delphi
Patch: Jens Geyer

This closes #319

This patch consists of a ported version of the base64 encoding/decoding used in C#. It handles the above case correctly, decodes data more efficiently in-place, and removes the dependency to Indy (IdCoderMIME).
diff --git a/lib/delphi/src/Thrift.Utils.pas b/lib/delphi/src/Thrift.Utils.pas
index 5c3d8a5..05fb964 100644
--- a/lib/delphi/src/Thrift.Utils.pas
+++ b/lib/delphi/src/Thrift.Utils.pas
@@ -49,6 +49,13 @@
   end;
 
 
+  Base64Utils = class sealed
+  public
+    class function Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
+    class function Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer; static;
+  end;
+
+
 implementation
 
 { TOverlappedHelperImpl }
@@ -100,6 +107,82 @@
 end;
 
 
+{ Base64Utils }
+
+class function Base64Utils.Encode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
+const ENCODE_TABLE : PAnsiChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+begin
+  ASSERT( len in [1..3]);
+  dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shr 2) and $3F]);
+  case len of
+    3 : begin
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff + 1] shl 2) and $3C) or ((src[srcOff + 2] shr 6) and $03)]);
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ src[srcOff + 2] and $3F]);
+      result := 4;
+    end;
+
+    2 : begin
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ ((src[srcOff] shl 4) and $30) or ((src[srcOff + 1] shr 4) and $0F)]);
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff + 1] shl 2) and $3C]);
+      result := 3;
+    end;
+
+    1 : begin
+      Inc(dstOff);
+      dst[dstOff] := Byte( ENCODE_TABLE[ (src[srcOff] shl 4) and $30]);
+      result := 2;
+    end;
+
+  else
+    ASSERT( FALSE);
+  end;
+end;
+
+
+class function Base64Utils.Decode( const src : TBytes; srcOff, len : Integer; dst : TBytes; dstOff : Integer) : Integer;
+const DECODE_TABLE : array[0..$FF] of Integer
+                   = ( -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
+                       52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
+                       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+                       15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+                       -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+                       41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
+                       -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1  );
+begin
+  ASSERT( len in [1..4]);
+  result := 1;
+  dst[dstOff] := ((DECODE_TABLE[src[srcOff]     and $0FF] shl 2)
+              or  (DECODE_TABLE[src[srcOff + 1] and $0FF] shr 4));
+
+  if (len > 2) then begin
+    Inc( result);
+    Inc( dstOff);
+    dst[dstOff] := (((DECODE_TABLE[src[srcOff + 1] and $0FF] shl 4) and $F0)
+                or   (DECODE_TABLE[src[srcOff + 2] and $0FF] shr 2));
+
+    if (len > 3) then begin
+      Inc( result);
+      Inc( dstOff);
+      dst[dstOff] := (((DECODE_TABLE[src[srcOff + 2] and $0FF] shl 6) and $C0)
+                  or    DECODE_TABLE[src[srcOff + 3] and $0FF]);
+    end;
+  end;
+end;
 
 
 end.