In Base64 the characters A-Z,a-z,0-9,+,/ represent 6 bit values (0-63) (A=0,/=63). The input string (8-bit values) is taken 6-bits at a time and converted to one of the above characters. If the output string of characters is not a multiple of 6, equal signs (=) are used to pad out. Normally used for e-mail messages, the strings are often then sliced into max 78 characters and separated by CR/LFs to make "lines". These "lines" are then quite often prefixed by tabs or spaces to make the "text" look nice.

On converting back, tabs,spaces and CR/LF characters should be ignored. Each character is checked to be A-Z,a-z,0-9,+ or / (or =) and converted into a six-bit value. These values are "appended" to make the original string.

FUNCTION fromBase64(VAR Data : data_rep_type;

VAR Result : data_rep_type) : Boolean;

LABEL 1,2,3,4;

VAR

ch : Char;

i,c : Integer;

l : 0..maxint;

t : Boolean;

Tmp : Byte;

val : Byte;

which : 0..3;

BEGIN

fromBase64:=false;

l:=StringLength(Data);

NewString(Result,(l*3) DIV 4);

i:=1; which:=0; c:=0; t:=false;

WHILE i<=l DO

BEGIN

ch:=IndexString(Data,i);

IF ch IN [%0D%,%0A%,%09%,' '] THEN GOTO 2;

IF ch='=' THEN

BEGIN

c:=c+1;

IF c>2 THEN

BEGIN

error('too many = at end of base64 string');

GOTO 1;

END;

Val:=0;

t:=true;

GOTO 4;

END;

IF t THEN

BEGIN

error('illegal character at end of base64 string');

GOTO 1;

END;

IF ch IN ['A'..'Z'] THEN

val:=Ord(ch)-Ord('A')

ELSE IF ch IN ['a'..'z'] THEN

val:=Ord(ch)-Ord('a')+26

ELSE IF ch IN ['0'..'9'] THEN

val:=Ord(ch)-Ord('0')+52

ELSE IF ch='+' THEN

val:=62

ELSE IF ch='/' THEN

val:=63

ELSE

BEGIN

error('illegal character in base64 string');

GOTO 1;

END;

4: CASE which OF

0 : (* output empty, move in all 6 bits *)

Tmp:=Val*4;

1 : (* Output has 6 bits, needs two more bits *)

BEGIN

CharAppend(Result,Chr(Tmp+(Val DIV 16)));

Tmp:=(Val MOD 16)*16;

END;

2 : (* output has 4 bits, needs 4 more *)

BEGIN

CharAppend(Result,Chr(Tmp+(Val DIV 4)));

Tmp:=(Val MOD 4)*64;

END;

3 : (* Output has 2 bits, needs 6 more *)

CharAppend(Result,Chr(Tmp+Val));

END;

(* step on case *)

which:=(which+1) MOD 4;

2: i:=i+1;

END;

3: IF which<>0 THEN

BEGIN

error('illegal termination of base64 string');

GOTO 1;

END;

(* then remove any padded bytes *)

IF c>0 THEN

BEGIN

l:=StringLength(Result)-c;

SliceString(Result,1,l);

END;

fromBase64:=true;

1:END;

The above is an old Pascal routine. The type data_rep_type is a variant type which supports infinitely long strings. In fact the length is determined by the input length *8 DIV 6.

In base64 each character (except CR and LF and white space) represents six bits of the output string. Since not every input string can be converted to an exact number of output characters (eg: two bytes makes 16 bits = 2*6 bits + 4 bits left) equal signs (=) are used to fill to the end. My routine checks all these cases, and errors appropiately.

On converting back, tabs,spaces and CR/LF characters should be ignored. Each character is checked to be A-Z,a-z,0-9,+ or / (or =) and converted into a six-bit value. These values are "appended" to make the original string.

FUNCTION fromBase64(VAR Data : data_rep_type;

VAR Result : data_rep_type) : Boolean;

LABEL 1,2,3,4;

VAR

ch : Char;

i,c : Integer;

l : 0..maxint;

t : Boolean;

Tmp : Byte;

val : Byte;

which : 0..3;

BEGIN

fromBase64:=false;

l:=StringLength(Data);

NewString(Result,(l*3) DIV 4);

i:=1; which:=0; c:=0; t:=false;

WHILE i<=l DO

BEGIN

ch:=IndexString(Data,i);

IF ch IN [%0D%,%0A%,%09%,' '] THEN GOTO 2;

IF ch='=' THEN

BEGIN

c:=c+1;

IF c>2 THEN

BEGIN

error('too many = at end of base64 string');

GOTO 1;

END;

Val:=0;

t:=true;

GOTO 4;

END;

IF t THEN

BEGIN

error('illegal character at end of base64 string');

GOTO 1;

END;

IF ch IN ['A'..'Z'] THEN

val:=Ord(ch)-Ord('A')

ELSE IF ch IN ['a'..'z'] THEN

val:=Ord(ch)-Ord('a')+26

ELSE IF ch IN ['0'..'9'] THEN

val:=Ord(ch)-Ord('0')+52

ELSE IF ch='+' THEN

val:=62

ELSE IF ch='/' THEN

val:=63

ELSE

BEGIN

error('illegal character in base64 string');

GOTO 1;

END;

4: CASE which OF

0 : (* output empty, move in all 6 bits *)

Tmp:=Val*4;

1 : (* Output has 6 bits, needs two more bits *)

BEGIN

CharAppend(Result,Chr(Tmp+

Tmp:=(Val MOD 16)*16;

END;

2 : (* output has 4 bits, needs 4 more *)

BEGIN

CharAppend(Result,Chr(Tmp+

Tmp:=(Val MOD 4)*64;

END;

3 : (* Output has 2 bits, needs 6 more *)

CharAppend(Result,Chr(Tmp+

END;

(* step on case *)

which:=(which+1) MOD 4;

2: i:=i+1;

END;

3: IF which<>0 THEN

BEGIN

error('illegal termination of base64 string');

GOTO 1;

END;

(* then remove any padded bytes *)

IF c>0 THEN

BEGIN

l:=StringLength(Result)-c;

SliceString(Result,1,l);

END;

fromBase64:=true;

1:END;

The above is an old Pascal routine. The type data_rep_type is a variant type which supports infinitely long strings. In fact the length is determined by the input length *8 DIV 6.

In base64 each character (except CR and LF and white space) represents six bits of the output string. Since not every input string can be converted to an exact number of output characters (eg: two bytes makes 16 bits = 2*6 bits + 4 bits left) equal signs (=) are used to fill to the end. My routine checks all these cases, and errors appropiately.