AutoHotkey Community

It is currently May 27th, 2012, 8:29 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 37 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Base64 coder/decoder
PostPosted: October 18th, 2005, 5:18 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.
Code:
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:
Code:
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:
Code:
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


Last edited by Laszlo on May 22nd, 2010, 6:22 pm, edited 2 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 23rd, 2006, 8:09 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
I needed a Base64 decoder and I was just about to write one myself when I found this with Image Search.
I use a modified version which is basically the two combined with the extra 'inv=0' parameter. It works great anyway, thanks :)

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 23rd, 2006, 8:31 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 12:15 pm 
Offline

Joined: December 27th, 2005, 1:46 pm
Posts: 6837
Location: France (near Paris)
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.

_________________
Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 4:40 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 10:12 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
PhiLho wrote:
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

_________________
GitHubScriptsIronAHK Contact by email not private message.


Last edited by polyethene on April 4th, 2006, 8:23 pm, edited 1 time in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 10:28 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
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...)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 11:10 pm 
Offline

Joined: December 27th, 2005, 1:46 pm
Posts: 6837
Location: France (near Paris)
Titan wrote:
PhiLho wrote:
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.

_________________
Image vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 24th, 2006, 11:40 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
Laszlo wrote:
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.

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: March 26th, 2006, 2:43 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
Here is a fully working Ascii85() (or Base85) function with an example:
Code:
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 . "~>"
}

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 1st, 2006, 10:51 am 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
I got Ascii85() fully working now. *just an update :)

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2006, 7:44 pm 
Offline

Joined: April 4th, 2006, 7:40 pm
Posts: 7
Titan, Laszlo,

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


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2006, 8:22 pm 
Offline
User avatar

Joined: August 11th, 2004, 1:47 am
Posts: 5347
Location: UK
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.

_________________
GitHubScriptsIronAHK Contact by email not private message.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2006, 8:46 pm 
Offline

Joined: April 4th, 2006, 7:40 pm
Posts: 7
No problem. Thank you both for all your contributions!

Titan wrote:
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.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: April 4th, 2006, 10:11 pm 
Offline

Joined: February 14th, 2005, 4:05 pm
Posts: 4710
Location: Boulder, CO
This version is a little more compact and should be slightly faster, because the mod function is called less often.
Code:
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
}


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 37 posts ]  Go to page 1, 2, 3  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: AndyJenk, Aravind, Bing [Bot], hyper_, specter333, Stigg, xXDarknessXx and 10 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group