Skip to content

Updated the optimized Base64 library

January 12, 2012

More than two years ago, I made a blog post about how to optimized the existing Base64 libraries.

This library was highly linked and used in multiple project as it’s 100% free (MIT license)

A few days ago I decided to take another look at it just for fun. To see if I could get it to be a bit faster.

Here is some change I made to make it even faster:

On of the biggest change was to go from bytearray.writeInt() to direct byte access bytearray[]

Optimizing the old version


//BEFORE

c = data[i++] << 16 | data[i++] << 8 | data[i++];

c = (_encodeChars[c >>> 18] << 24) | (_encodeChars[c >>> 12 & 0x3f] << 16) | (_encodeChars[c >>> 6 & 0x3f] << 8 ) | _encodeChars[c & 0x3f];

 out.writeInt(c);

//AFTER

c = data[int(i++)] << 16 | data[int(i++)] << 8 | data[int(i++)];

 out[int(outPos++)] = _encodeChars[int(c >>> 18)];
 out[int(outPos++)] = _encodeChars[int(c >>> 12 & 0x3f)];
 out[int(outPos++)] = _encodeChars[int(c >>> 6 & 0x3f)];
 out[int(outPos++)] = _encodeChars[int(c & 0x3f)];

You can also see the explicit int cast for all bytearray access. It does not always make a big difference, but depending on debug/release runtime and code, it can help a bit.

Another thing I change is the handling of “invalid” base 64 data.

When decoding a base 64 string, one of the high-cost operation was validation.

By removing validation of invalid data, it made the code 30% faster.

While I understand that validation is important in many case, having a ultra-fast library is also important for many developper.

Why not using Azoth, haXe or Alchemy SWCs

Well I could.

The problem is that right now they all use the fast-memory opcodes that won’t be supported for FlashPlayer 11.2 and upper.

This mean that any application that was build with libraries likes theses won’t be able to run in the future “as-is”

Hence, having a native-as3 library with sources that you can edit is very important.

 

The Result:

Grab the full source, please go and take it on this site:

http://www.sociodox.com/base64.html

and here:

/*
 * Copyright (C) 2012 Jean-Philippe Auclair
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 * Base64 library for ActionScript 3.0.
 * By: Jean-Philippe Auclair : http://jpauclair.net
 * Based on article: https://jpauclair.net/2010/01/09/base64-optimized-as3-lib/
 * Benchmark:
 * This version: encode: 260ms decode: 255ms
 * Blog version: encode: 322ms decode: 694ms
 * as3Crypto encode: 6728ms decode: 4098ms
 *
 * Encode: com.sociodox.utils.Base64 is 25.8x faster than as3Crypto Base64
 * Decode: com.sociodox.utils.Base64 is 16x faster than as3Crypto Base64
 *
 * Optimize & Profile any Flash content with TheMiner ( http://www.sociodox.com/theminer )
 */
package com.sociodox.utils
{
 import flash.utils.ByteArray;
 public class Base64
 {
 private static const _encodeChars:Vector.<int> = InitEncoreChar();
 private static const _decodeChars:Vector.<int> = InitDecodeChar();

 public static function encode(data:ByteArray):String
 {
 var out:ByteArray = new ByteArray();
 //Presetting the length keep the memory smaller and optimize speed since there is no "grow" needed
 out.length = (2 + data.length - ((data.length + 2) % 3)) * 4 / 3; //Preset length //1.6 to 1.5 ms
 var i:int = 0;
 var r:int = data.length % 3;
 var len:int = data.length - r;
 var c:uint; //read (3) character AND write (4) characters
 var outPos:int=0;
 while (i < len)
 {
 //Read 3 Characters (8bit * 3 = 24 bits)
 c = data[int(i++)] << 16 | data[int(i++)] << 8 | data[int(i++)];

 out[int(outPos++)] = _encodeChars[int(c >>> 18)];
 out[int(outPos++)] = _encodeChars[int(c >>> 12 & 0x3f)];
 out[int(outPos++)] = _encodeChars[int(c >>> 6 & 0x3f)];
 out[int(outPos++)] = _encodeChars[int(c & 0x3f)];
 }

 if (r == 1) //Need two "=" padding
 {
 //Read one char, write two chars, write padding
 c = data[int(i)];

out[int(outPos++)] = _encodeChars[int(c >>> 2)];
 out[int(outPos++)] = _encodeChars[int((c & 0x03) << 4)];
 out[int(outPos++)] = 61;
 out[int(outPos++)] = 61;
 }
 else if (r == 2) //Need one "=" padding
 {
 c = data[int(i++)] << 8 | data[int(i)];

out[int(outPos++)] = _encodeChars[int(c >>> 10)];
 out[int(outPos++)] = _encodeChars[int(c >>> 4 & 0x3f)];
 out[int(outPos++)] = _encodeChars[int((c & 0x0f) << 2)];
 out[int(outPos++)] = 61;
 }

return out.readUTFBytes(out.length);
 }


 public static function decode(str:String):ByteArray
 {
 var c1:int;
 var c2:int;
 var c3:int;
 var c4:int;
 var i:int = 0;
 var len:int = str.length;

var byteString:ByteArray = new ByteArray();
 byteString.writeUTFBytes(str);
 var outPos:int = 0;
 while (i < len)
 {
 //c1
 c1 = _decodeChars[int(byteString[i++])];
 if (c1 == -1) break;

 //c2
 c2 = _decodeChars[int(byteString[i++])];
 if (c2 == -1) break;

 byteString[int(outPos++)] = (c1 << 2) | ((c2 & 0x30) >> 4);

 //c3
 c3 = byteString[int(i++)];
 if (c3 == 61)
 {
 byteString.length = outPos
 return byteString;
 }

 c3 = _decodeChars[int(c3)];
 if (c3 == -1) break;

 byteString[int(outPos++)] = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);

 //c4
 c4 = byteString[int(i++)];
 if (c4 == 61)
 {
 byteString.length = outPos
 return byteString;
 }

 c4 = _decodeChars[int(c4)];
 if (c4 == -1) break;

 byteString[int(outPos++)] = ((c3 & 0x03) << 6) | c4;
 }
 byteString.length = outPos
 return byteString;
 }

 public static function InitEncoreChar() : Vector.<int>
 {
 var encodeChars:Vector.<int> = new Vector.<int>(64,true);

 // We could push the number directly, but i think it's nice to see the characters (with no overhead on encode/decode)
 var chars:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 for (var i:int = 0; i < 64; i++)
 {
 encodeChars[i] = chars.charCodeAt(i);
 }

return encodeChars;
 }

public static function InitDecodeChar():Vector.<int>
{

var decodeChars:Vector.<int> = new <int>[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1];

return decodeChars;
}

 }
}

If you have any trick to make this faster, please post a comment!

11 Comments
  1. Cool update!

    It doesn’t make decoding any faster, but InitDecodeChar could use a faster way of initializing the Vector:

    var decodeChars:Vector. = new [ /* all of those ints */ ];

    For more on why, check out my article:

    http://jacksondunstan.com/articles/702

    Also, your title has a typo: “Bas64” instead of “Base64”.

    Thanks again for the fast encode/decode functions!

    • It stripped out some of my syntax. Let me try that line of code again:

      var decodeChars:Vector.<int> = new <int>[ /* all of those ints */ ];

    • Typo in the Title…. awesome! lol
      Thanks jackson. I hoped you would read the article and give me feedback ;P
      I’ll update the code in a few minutes
      Thanks!

    • It’s updated!
      I didn’t even know we could do that!
      Awesome trick.
      Thanks Jackson

  2. Eliad permalink

    Is there a reason why all the ‘\’ appearances in the base 64 code under http://www.sociodox.com/base64.html
    are ‘\’ ???

  3. Eliad permalink

    Jackson, how did you manage to add the Angle brackets ?!?! :S

    anyway there should be the word “int” within Angle brackets there (instead of the first ‘\’)

    and then “nt” within Angle brackets (instead of the second ‘\’)

    • Wow… epic paste fail.
      Good Catch. Thanks for reporting Eliad!

      • Eliad permalink

        You’re welcomed !
        and thank YOU for the great job!

  4. Greg permalink

    Hi, I have a problem using your library. I’m totally newbie in Flash so please help.
    I’ve connected your library with “import com.sociodox.utils.Base64;”
    After that I try to compile my project but get the following error:
    Base64.as, Line 146: 1100: Syntax error: XML does not have matching begin and end tags.
    Base64.as, Line 146: 1084: Syntax error: expecting rightbrace before end of program.
    Here is a screenshot: clip2net.com/s/5Jbzlt

    Please advise!

Trackbacks & Pingbacks

  1. Base64 encoder library download assitance | PHP Developer Resource

Comments are closed.

%d bloggers like this: