the next in the middle eight, and the third in the least significant eight bits.
If there are fewer than three bytes to encode, the corresponding buffer bits will be zero.
The buffer is then used, six bits at a time, most significant first, as indices into the string
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" and the indicated character output.
If there were only one or two input bytes, only the first two or three characters of the output
are used and are padded with two or one "=" characters respectively.
The process then repeats on the remaining input data.
In = ( Join Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure. ) Out = ( Join TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0 aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1 c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0 aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdl LCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4= ) StringCaseSense On Chars = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ If Base64(In) = Out MsgBox OK If InvBase64(Out) = In MsgBox OK Base64(string) { Loop Parse, string { If Mod(A_Index,3) = 1 buffer := Asc(A_LoopField) << 16 Else If Mod(A_Index,3) = 2 buffer += Asc(A_LoopField) << 8 Else { buffer += Asc(A_LoopField) out := out . Code(buffer>>18) . Code(buffer>>12) . Code(buffer>>6) . Code(buffer) } } If Mod(StrLen(string),3) = 0 Return out If Mod(StrLen(string),3) = 1 Return out . Code(buffer>>18) . Code(buffer>>12) "==" Return out . Code(buffer>>18) . Code(buffer>>12) . Code(buffer>>6) "=" } InvBase64(code) { StringReplace code, code, =,,All Loop Parse, code { If Mod(A_Index,4) = 1 buffer := DeCode(A_LoopField) << 18 Else If Mod(A_Index,4) = 2 buffer += DeCode(A_LoopField) << 12 Else If Mod(A_Index,4) = 3 buffer += DeCode(A_LoopField) << 6 Else { buffer += DeCode(A_LoopField) out := out . Chr(buffer>>16) . Chr(255 & buffer>>8) . Chr(255 & buffer) } } If Mod(StrLen(code),4) = 0 Return out If Mod(StrLen(code),4) = 2 Return out . Chr(buffer>>16) Return out . Chr(buffer>>16) . Chr(255 & buffer>>8) } Code(i) ; <== Chars[i & 63], 0-base index { Global Chars StringMid i, Chars, (i&63)+1, 1 Return i } DeCode(c) ; c = a char in Chars ==> position [0,63] { Global Chars Return InStr(Chars,c,1) - 1 }
And here is a version, which handles binary buffers, not just AHK strings:
Base64Encode(ByRef bin, n=0) { m := VarSetCapacity(bin) If n not between 1 and %m% n = %m% Loop %n% { A := *(&bin+A_Index-1) m := Mod(A_Index,3) IfEqual m,1, SetEnv buffer,% A << 16 Else IfEqual m,2, EnvAdd buffer,% A << 8 Else { buffer += A out := out Code(buffer>>18) Code(buffer>>12) Code(buffer>>6) Code(buffer) } } IfEqual m,0, Return out IfEqual m,1, Return out Code(buffer>>18) Code(buffer>>12) "==" Return out Code(buffer>>18) Code(buffer>>12) Code(buffer>>6) "=" } Base64Decode(ByRef bin, code) { StringReplace code, code, =,,All VarSetCapacity(bin, 3*StrLen(code)//4, 0) Loop Parse, code { m := A_Index & 3 ; mod 4 IfEqual m,0, { buffer += DeCode(A_LoopField) Append(bin, pos, buffer>>16, 255 & buffer>>8, 255 & buffer) } Else IfEqual m,1, SetEnv buffer, % DeCode(A_LoopField) << 18 Else buffer += DeCode(A_LoopField) << 24-6*m } IfEqual m,0, Return IfEqual m,2 Append(bin, pos, buffer>>16) Else Append(bin, pos, buffer>>16, 255 & buffer>>8) } Append(ByRef bin, ByRef pos, c1, c2="", c3="", c4="") { pos += 0 Loop 4 { IfEqual c%A_Index%,, Break DllCall("RtlFillMemory",UInt,&bin+pos, UInt,1, UChar,c%A_Index%) pos++ } } Code(i) { ; <== Chars[i & 63], 0-base index Global Chars StringMid i, Chars, (i&63)+1, 1 Return i } DeCode(c) { ; c = a char in Chars ==> position [0,63] Global Chars Return InStr(Chars,c,1) - 1 }The same binary en/decoder is below, in compact form, with tests:
A = 123-aB. ; TEST Loop 8 { C := Base64Encode(A,A_Index) Base64Decode(D,C) VarSetCapacity(D,-1) ; use when D is string (instead of taking binary info) MsgBox % SubStr(A,1,A_Index) "`n" C "`n" D } Base64Encode(ByRef bin, n=0) { m := VarSetCapacity(bin) Loop % n<1 || n>m ? m : n A := *(&bin+A_Index-1) ,m := Mod(A_Index,3) ,b := m=1 ? A << 16 : m=2 ? b+(A<<8) : b+A ,out .= m ? "" : Code(b>>18) Code(b>>12) Code(b>>6) Code(b) Return out (m ? Code(b>>18) Code(b>>12) (m=1 ? "==" : Code(b>>6) "=") : "") } Code(i) { ; <== Chars[i & 63], 0-base index Static Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" Return SubStr(Chars,(i&63)+1,1) } Base64Decode(ByRef bin, code) { Static Chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" StringReplace code, code, =,, All VarSetCapacity(bin, 3*StrLen(code)//4, 0) pos = 0 Loop Parse, code m := A_Index&3, d := InStr(Chars,A_LoopField,1) - 1 ,b := m ? (m=1 ? d<<18 : b+(d<<24-6*m)) : b+d ,Append(bin, pos, 3*!m, b>>16, 255 & b>>8, 255 & b) Append(bin, pos, !!m+(m&1), b>>16, 255 & b>>8, 0) } Append(ByRef bin, ByRef pos, k, c1,c2,c3) { Loop %k% DllCall("RtlFillMemory",UInt,&bin+pos++, UInt,1, UChar,c%A_Index%) }
Edit 2006.07.09: added version to handle binary buffers.
Edit 2010.05.22: bugfix in binary version, compact code added