Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Base64 coder/decoder


  • Please log in to reply
42 replies to this topic
Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Wikipedia: The first byte is placed in the most significant eight bits of a 24-bit buffer,
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

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I needed a Base64 decoder and I was just about to write one myself when I found this with Posted Image Search.
I use a modified version which is basically the two combined with the extra 'inv=0' parameter. It works great anyway, thanks :)

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
In the Ask for help section of the Forum somebody requested it half a year ago. He did not even bother to tell if it worked for him. You seem to be the only one, who used it. I am glad it saved some time for you.

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005
It was one of the first script I saved, as an advanced (for me, at the time) example of scripting with AutoHotkey.
I have not yet used it, but I recommanded to Goyyah to check it out because he wanted to create a binary file encoded in a script.
I provided a solution using hexadecimal encoding, but it is a waste of space for large files (> 10KB), so I wrote to use this code to encode the file in a compacter format.
Taking another look, it may need a slight rewrite to handle binary data, but the base is here. (no pun intended...)
Or, for an even smaller result, we could use Base96 (created by Adobe for PDF, IIRC), since we don't have to maintain Mime compatibility or such.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
AHK can handle all of the printable ANSI characters, about 200, so we could probably get an even compacter (but nonstandard) encoding. For speed, character codes 0..31, 127..144, 147..160 and 255 could be remapped to "`c" c denoting a printable character, "``" for "`". This has the advantage, that plain text portions of the data remain unchanged, so looking at program binaries, you could find text messages.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

we could use Base96 (created by Adobe for PDF, IIRC), since we don't have to maintain Mime compatibility or such.

I never heard of this? I couldn't find anything on it either. Talking of MIME, does this insert a linefeed after every 76 characters Laszlo (RFC 2045)?

Here's my version of the script. It's roughly 30% smaller in size but is 9% slower on average (measuring flops is hard so this could be unreliable): *Edit: Removed

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Wow! Titan is going to beat me in my own game (to save a few lines of code by all means). Who can make it even shorter? (I have to finish a paper in the next two weeks, so I cannot compete...)

PhiLho
  • Moderators
  • 6850 posts
  • Last active: Jan 02 2012 10:09 PM
  • Joined: 27 Dec 2005

we could use Base96 (created by Adobe for PDF, IIRC), since we don't have to maintain Mime compatibility or such.

I never heard of this?

Oops, I had a vague doubt, but did checked before, I should have...
That's ASCII85, not Base96... :-P Mix up with Base64 and an unreliable memory.
Posted Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

Wow! Titan is going to beat me in my own game

lol, it was just my 30 sec mod for other fans of short code :p

btw. Ascii85 looks interesting but is it patented? Base64 is quite common so I prefer to stick with that for now.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Here is a fully working Ascii85() (or Base85) function with an example:
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.

)



example := "Ascii85: -----`n" . Ascii85(In)

	. "`n`nInverse: -----`n" . Ascii85(Ascii85(In), 1)

MsgBox, 64, Ascii85, %example%

ExitApp



Ascii85(str, inv=0) { ; by Titan

	xFI := A_FormatInteger

	If inv {

		StringReplace, str, str, <~

		StringReplace, str, str, ~>

		Loop, Parse, str

			If !Mod(A_Index, 5) or StrLen(str) / A_Index = 1 {

				If StrLen(str) / A_Index = 1

					tr := 5 - Mod(A_Index, 5)

				Loop, %tr%

					x += (Asc("0x00") - 33) * (85 ** 5 - (5 - (5 - Mod(A_Index, 5))))

				x += Asc(A_LoopField) - 33

				SetFormat, Integer, Hex

				x += 0

				Loop, 4 {

					StringMid, a, x, (A_Index * 2) + 1, 2

					i := i . Chr("0x" . a)

				} SetFormat, Integer, D

				x = 0

			} Else x += (Asc(A_LoopField) - 33) * (85 ** (5 - Mod(A_Index, 5)))

		StringTrimRight, i, i, %tr%

		Return, i

	} SetFormat, Integer, Hex

	Loop, Parse, str

		If !Mod(A_Index, 4) or (StrLen(str) / A_Index = 1) {

			x := x . Asc(A_LoopField)

			If StrLen(str) / A_Index = 1 and Mod(A_Index, 4)

				tr := 4 - Mod(A_Index, 4)

			Loop, %tr%

				x := x . 0x00

			StringReplace, x, x, 0x, , 1

			x =0x%x%

			SetFormat, Integer, D

			x += 0

			Loop, 5

				i := i . Chr((Floor(Mod(x / (85 ** (5 - A_Index)), 85))) + 33)

			SetFormat, Integer, Hex

			StringTrimLeft, x, x, 4

			x := ""

		} Else x := x . Asc(A_LoopField)

	StringReplace, i, i, !!!!!, z, 1

	StringTrimRight, i, i, %tr%

	SetFormat, Integer, %xFI%

	Return, "<~" . i . "~>"

}

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I got Ascii85() fully working now. *just an update :)

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


rg33
  • Members
  • 7 posts
  • Last active: Jan 15 2007 12:32 PM
  • Joined: 04 Apr 2006
Titan, Laszlo,

Why don't the results from your two different base64 functions match?

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I think I made a mistake somewhere so I'll scrap that post. I use Laszlo's version because it's faster anyway so I didn't quite notice the error, sorry.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


rg33
  • Members
  • 7 posts
  • Last active: Jan 15 2007 12:32 PM
  • Joined: 04 Apr 2006
No problem. Thank you both for all your contributions!

I think I made a mistake somewhere so I'll scrap that post. I use Laszlo's version because it's faster anyway so I didn't quite notice the error, sorry.



Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
This version is a little more compact and should be slightly faster, because the mod function is called less often.
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+/



MsgBox % "Encode = " (Base64Encode(In) == Out)

MsgBox % "Decode = " (Base64Decode(Out) == In)



Base64Encode(string) {

   Loop Parse, string

   {

      m := Mod(A_Index,3)

      IfEqual      m,1, SetEnv buffer, % Asc(A_LoopField) << 16

      Else IfEqual m,2, EnvAdd buffer, % Asc(A_LoopField) << 8

      Else {

         buffer += Asc(A_LoopField)

         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(code) {

   StringReplace code, code, =,,All

   Loop Parse, code

   {

      m := A_Index & 3 ; mod 4

      IfEqual m,0, {

         buffer += DeCode(A_LoopField)

         out := out Chr(buffer>>16) Chr(255 & buffer>>8) Chr(255 & buffer)

      }

      Else IfEqual m,1, SetEnv buffer, % DeCode(A_LoopField) << 18

      Else buffer += DeCode(A_LoopField) << 24-6*m

   }

   IfEqual m,0, Return out

   IfEqual m,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

}