Base-64 encoding--Just for the fun of it!

Yes, there’s always:

require ‘base64’
Base64::encoe64(data)

But now and then it’s fun to write one’s own base64 encoder just for
the fun of it.

What I want to ask is this: How short can one get a function
definition that takes a string as input and returns a base-64
(MIME64-style, except for NO newlines/linefeeds–output is all one
giant line of encoded text in the character set ‘A’ to ‘Z’, ‘a’ to
‘z’, ‘0’ to ‘9’, ‘+’, and ‘/’)?

The shortest I’ve got it down to is 276 characters. WARNING: My email
client is gonna line-break this and mess it up. Just remove the
whitespace.)

def
e(s);c=(‘A’…‘Z’).to_a.join+(‘a’…‘z’).to_a.join+‘0123456789+/’;(t=s.unpack(‘C*’).inject([0,‘’,0]){|a,v|a[0]==0?[2,a[1]+c[v>>2,1],v16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v4&60]:[0,a[1]+c[v>>6|a[2],1]+c[v&63,
1],0]})[1]+(t[0]==0?‘’:t[0]==2?c[t[2],1]+‘==’:c[t[2],1]+‘=’);end

If the above is too corrupted, I stuck it on my personal web site:
http://www.aarongifford.com/base64.rb.txt

The 276-character encoder works in Ruby 1.9.1 and Ruby 1.8.7 and
super-simplistic tests comparing results against the Base64 encoder
appear to work (as long as you strip out line breaks Base64 includes).

I’d love to hear suggestions to squeeze a few more characters out of it.

Ideas? Suggestions?

Having a bit too much fun fiddling with Ruby,
Aaron out.

This morning I realized I could shave off 3 characters by eliminating
one .join by adding () and converting + to | in the c= initialization.

Also, for Ruby 1.9 only, one can save 8 more characters since string
slices return 1-character strings in 1.9 instead of integers.

I updated http://www.aarongifford.com/base64.rb.txt so it now has the
original and the two improvements. And it adds a random data test on
Unix-based hosts supporting reading random data from /dev/urandom

Oh, I should add that performance is abysmal, but that obviously
wasn’t the goal. chuckle

Aaron out.

Aaron D. Gifford wrote:

(…)
The shortest I’ve got it down to is 276 characters. WARNING: My email
client is gonna line-break this and mess it up. Just remove the
whitespace.)

def
e(s);c=(‘A’…‘Z’).to_a.join+(‘a’…‘z’).to_a.join+‘0123456789+/’;(t=s.unpack(‘C*’).inject([0,’’,0]){|a,v|a[0]==0?[2,a[1]+c[v>>2,1],v16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v4&60]:[0,a[1]+c[v>>6|a[2],1]+c[v&63,
1],0]})[1]+(t[0]==0?’’:t[0]==2?c[t[2],1]+’==’:c[t[2],1]+’=’);end
(…)
I’d love to hear suggestions to squeeze a few more characters out of it.

Ideas? Suggestions?

Having a bit too much fun fiddling with Ruby,
Aaron out.

Heh, in ruby 1.9 you can do:

c=(65…122).to_a.join(&:chr)+‘0123456789+/’

instead of
c=((‘A’…‘Z’).to_a|(‘a’…‘z’).to_a).join+‘0123456789+/’

hth,

Siep

On Mar 5, 1:11 am, “Aaron D. Gifford” [email protected] wrote:

The 276-character encoder works in Ruby 1.9.1 and Ruby 1.8.7 and
super-simplistic tests comparing results against the Base64 encoder
appear to work (as long as you strip out line breaks Base64 includes).

I’d love to hear suggestions to squeeze a few more characters out of it.

Ideas? Suggestions?

While lacking in the joy of code golf you may still be interested to
know about Radix

Thomas S. wrote:

On Mar 5, 1:11�am, “Aaron D. Gifford” [email protected] wrote:

The 276-character encoder works in Ruby 1.9.1 and Ruby 1.8.7 and
super-simplistic tests comparing results against the Base64 encoder
appear to work (as long as you strip out line breaks Base64 includes).

I’d love to hear suggestions to squeeze a few more characters out of it.

Ideas? �Suggestions?

While lacking in the joy of code golf you may still be interested to
know about Radix

Yes i am. And please ignore my previous post in this thread, it’s
rubbish.

Siep

On Thu, Mar 4, 2010 at 11:11 PM, Aaron D. Gifford [email protected]
wrote:

The 276-character encoder works in Ruby 1.9.1 and Ruby 1.8.7 and
super-simplistic tests comparing results against the Base64 encoder
appear to work (as long as you strip out line breaks Base64 includes).

def e(s);[s].pack(‘m’);end

Kirk H.

Yes, there’s always:

require ‘base64’
Base64::encoe64(data)

Actually that’s deprecated in 1.9 afaik. Somebody else already pointed
out that you can use Array#pack.

I’d love to hear suggestions to squeeze a few more characters out of it.

Yours:
c=(‘A’…‘Z’).to_a.join+(‘a’…‘z’).to_a.join+‘0123456789+/’

Mine (1.9 only):
c=[?A…?Z,?a…?z,?0…?9]’’+’+/’

That’s 23 characters.

Yours:
(t=s.unpack(‘C*’).inject([0,’’,0]){|a,v|a[0]==0?[2,a[1]+c[v>>2,1],v16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v4&60]:[0,a[1]+c[v>>6|a[2],1]+c[v&63,1],0]})[1]+(t[0]==0?’’:t[0]==2?c[t[2],1]+’==’:c[t[2],1]+’=’)

Mine:
(s.unpack(“B*”)[0]+‘0’((6-s.size8%6)%6)).scan(/.{6}/).map{|e|c[e.to_i(2)]}’’+’=’((3-s.size%3)%3)

That’s another 104 characters off. So 127 characters less in total :slight_smile:

I also did only some very superficial tests, and mine doesn’t insert
newlines either. Though some clever gsub could do that easily.

Regards & thanks for the challenge :wink:
Stefan (aka apeiros)

On Fri, Mar 5, 2010 at 6:55 PM, Kirk H. [email protected] wrote:

def e(s);[s].pack(‘m’);end

Thanks for contributing something useful to me. I’ve always used the
‘base64’ module in the past, having overlooked that handy little
tidbit.

Aaron out.

On Sat, Mar 6, 2010 at 4:31 PM, [email protected] wrote:

Mine (1.9 only):
c=[?A…?Z,?a…?z,?0…?9]‘’+‘+/’

That’s 23 characters.

Gorgeous!

Mine:
(s.unpack(“B*”)[0]+‘0’((6-s.size8%6)%6)).scan(/.{6}/).map{|e|c[e.to_i(2)]}‘’+‘=’((3-s.size%3)%3)

Beautiful!

I love learning new things every day, and you’ve provided plenty for
me. Thanks for “golf” lessons. smile

Aaron out.

On Sat, Mar 6, 2010 at 4:31 PM, [email protected] wrote:

(s.unpack(“B*”)[0]+‘0’((6-s.size8%6)%6)).scan(/.{6}/).map{|e|c[e.to_i(2)]}‘’+‘=’((3-s.size%3)%3)

I notice you could shave off 2 more characters by using the same ?
character literal for the ‘0’ and the ‘=’ characters:
(s.unpack(“B*”)[0]+?0*((6-s.size8%6)%6)).scan(/.{6}/).map{|e|c[e.to_i(2)]}‘’+?=*((3-s.size%3)%3)

I hope you don’t mind, but I added your version to my file at:
http://www.aarongifford.com/base64.rb.txt

And I stole your character literal and range expansion (splat) ideas
to improve my longer algorithm a bit too.

Thanks again for teaching me about Range expansion inside method calls.

Aaron out.