Updated the optimized Base64 library
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!
Trackbacks & Pingbacks
Comments are closed.
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
Is there a reason why all the ‘\’ appearances in the base 64 code under http://www.sociodox.com/base64.html
are ‘\’ ???
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!
You’re welcomed !
and thank YOU for the great job!
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!