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

  • Thread starter Aaron D. Gifford
  • Start date
A

Aaron D. Gifford

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],v*16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v*4&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.
 
A

Aaron D. Gifford

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.
 
S

Siep Korteling

Aaron said:
(...)
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],v*16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v*4&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
 
I

Intransition

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? =A0Suggestions?

While lacking in the joy of code golf you may still be interested to
know about <a href=3D"http://rubyworks.github.com/radix">Radix</a>
 
K

Kirk Haines

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);.pack('m');end


Kirk Haines
 
A

Aaron D. Gifford

def e(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.
 
A

apeiros

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],v*16&48]:a[0]==2?[4,a[1]+c[v>>4|a[2],1],v*4&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.size*8%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 :)

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 ;-)
Stefan (aka apeiros)
 
A

Aaron D. Gifford

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

That's 23 characters.
Gorgeous!

Mine:
(s.unpack("B*")[0]+'0'*((6-s.size*8%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.
 
A

Aaron D. Gifford

(s.unpack("B*")[0]+'0'*((6-s.size*8%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.size*8%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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top