diff --git a/README.md b/README.md
index 589273a..904f0fa 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Unibabel
-==========
+========
-Base64, TypedArrays, and UTF-8 / Unicode conversions in Browser (and Node) JavaScript
+Minimalistic Base64, TypedArrays, and UTF-8 / Unicode conversions in Browser (and Node) JavaScript
See
@@ -45,6 +45,13 @@ var uint8Array = Unibabel.base64ToArr(base64)
* hexToBuffer(hexstr) => array
* bufferToHex(array) => hexstr
+**Base32 APIs**
+
+`unibabel.base32.js`
+
+* base32ToBuffer(b32str) => array
+* bufferToBase32(array) => b32str
+
**Helper APIs**
* utf8ToBinaryString(utf8str) => binstr
@@ -80,14 +87,22 @@ alert(sMyOutput);
License
=======
-Mozilla has licensed this code in the Public Domain, which means that I am at liberty to re-license my copy
-under the Apache 2, which is something that, general speaking, your legal department will feel more comfortable with.
+* `unibabel.js` and `unibabel.hex.js` are dual-licensed as Apache 2.0 and MIT.
+* `unibabel.base32.js` is a modified version of [thirty-two](https://github.com/chrisumbel/thirty-two) and is therefore licensed MIT.
+
+Some parts of the code were taken from MDN, which Mozilla has licensed in the Public Domain,
+which means that I am at liberty to re-license my copy under the Apache 2 and MIT licenses.
See
ChangeLog
====
+v2.1.0
+------
+
+Added `unibabel.base32.js`
+
v2.0.0
------
diff --git a/index.html b/index.html
index ea40a01..0a1a8e4 100644
--- a/index.html
+++ b/index.html
@@ -1,8 +1,12 @@
-
-
-
+
+
+
This is for testing. Look in the console.
+
+
+
+
diff --git a/node-test-base32.js b/node-test-base32.js
new file mode 100644
index 0000000..7883cae
--- /dev/null
+++ b/node-test-base32.js
@@ -0,0 +1,8 @@
+var base32 = require('thirty-two');
+var str = "I ½ ♥ 𩶘";
+var buf = new Buffer(str, 'utf8');
+console.log('charLen', 7);
+console.log('byteLen', buf.byteLength, JSON.stringify(buf.toString('utf8')));
+var b32 = base32.encode(buf); // to base32
+console.log('encoded', b32.toString('utf8'));
+console.log('decoded', base32.decode(b32).toString('utf8'));
diff --git a/test.js b/test.js
index acb0317..52c1a2f 100644
--- a/test.js
+++ b/test.js
@@ -5,10 +5,27 @@ var Unibabel = window.Unibabel;
//UTF-8
var pass = true;
-var str = "I ½ ♥ 𩶘";
-var buf = Unibabel.utf8ToBuffer(str);
-var base64 = Unibabel.bufferToBase64(buf);
-var hex = Unibabel.bufferToHex(buf);
+var references = {
+ string: "I ½ ♥ 𩶘"
+, array: [ 73, 32, 194, 189, 32, 226, 153, 165, 32, 240, 169, 182, 152 ]
+, hex: "4920c2bd20e299a520f0a9b698"
+, base64: "SSDCvSDimaUg8Km2mA=="
+, base32: 'JEQMFPJA4KM2KIHQVG3JQ==='
+};
+references.buffer = new Uint8Array(references.array);
+var binrefs = {
+ // Note that the binary string "ÿâó<86>Î<93>k" can't be serialized to text
+ array: [ 255, 226, 26, 243, 134, 206, 147, 107 ]
+, hex: "ffe21af386ce936b"
+, base64: "/+Ia84bOk2s="
+};
+binrefs.buffer = new Uint8Array(binrefs.array);
+
+var str = references.string;
+var buf = Unibabel.utf8ToBuffer(references.string);
+var base64 = Unibabel.bufferToBase64(references.buffer);
+var hex = Unibabel.bufferToHex(references.buffer);
+var b32 = Unibabel.bufferToBase32(references.buffer);
function buffersAreEqual(buf1, buf2) {
if (buf1.length !== buf2.length) {
@@ -21,25 +38,23 @@ function buffersAreEqual(buf1, buf2) {
});
}
-console.log('buffer', buf);
// TODO compare buffer
-var reference = [ 73, 32, 194, 189, 32, 226, 153, 165, 32, 240, 169, 182, 152 ];
-if (!buffersAreEqual(buf, reference)) {
+if (!buffersAreEqual(buf, references.array)) {
pass = false;
- console.log('[FAIL] utf8 -> buffer', buf);
+ console.warn('[FAIL] utf8 -> buffer', buf);
}
-if (base64 !== "SSDCvSDimaUg8Km2mA==") {
+if (base64 !== references.base64) {
pass = false;
- console.log('[FAIL] utf8 -> base64', base64);
+ console.warn('[FAIL] utf8 -> base64', base64);
}
-if (hex !== "4920c2bd20e299a520f0a9b698") {
+if (hex !== references.hex) {
pass = false;
- console.log('[FAIL] utf8 -> hex', hex);
+ console.warn('[FAIL] utf8 -> hex', hex);
}
// binary
-var bytes = [ 255, 226, 26, 243, 134, 206, 147, 107 ];
+var bytes = binrefs.array;
buf = new Uint8Array(bytes);
str = Unibabel.bufferToBinaryString(buf);
base64 = Unibabel.bufferToBase64(buf);
@@ -50,17 +65,31 @@ hex = Unibabel.bufferToHex(buf);
// pass = false;
// console.log('[FAIL] binary -> str', str);
// }
-if ("/+Ia84bOk2s=" !== base64) {
+if (binrefs.base64 !== base64) {
pass = false;
- console.log('[FAIL] binary -> base64', base64);
+ console.warn('[FAIL] binary -> base64', base64);
}
-if (hex !== "ffe21af386ce936b") {
+if (binrefs.hex !== hex) {
pass = false;
- console.log('[FAIL] binary -> hex', hex);
+ console.warn('[FAIL] binary -> hex', hex);
+}
+
+//
+// Base32
+//
+b32 = Unibabel.bufferToBase32(references.buffer);
+if (references.base32 !== b32) {
+ pass = false;
+ console.warn('[FAIL] binary -> base32', references.base32, '!==', b32);
+}
+buf = Unibabel.base32ToBuffer(references.base32);
+if (!buffersAreEqual(buf, references.buffer)) {
+ pass = false;
+ console.warn('[FAIL] base32 -> binary', references.buffer, '!==', buf);
}
if (pass) {
- console.log('[PASS] :-D');
+ console.info('[PASS] :-D');
}
}());
diff --git a/unibabel.base32.js b/unibabel.base32.js
new file mode 100644
index 0000000..9b57d7c
--- /dev/null
+++ b/unibabel.base32.js
@@ -0,0 +1,137 @@
+/*
+Copyright (c) 2011, Chris Umbel
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+(function (exports) {
+'use strict';
+
+var charTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+var byteTable = [
+ 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff
+];
+
+function quintetCount(buff) {
+ var quintets = Math.floor(buff.length / 5);
+ return buff.length % 5 === 0 ? quintets: quintets + 1;
+}
+
+exports.bufferToBase32 = function(plain) {
+ // plain MUST come in either as Array or Uint8Array
+ if('undefined' !== typeof Uint8Array) {
+ if (!(plain instanceof Uint8Array)){
+ plain = new Uint8Array(plain);
+ }
+ }
+ var i = 0;
+ var j = 0;
+ var shiftIndex = 0;
+ var digit = 0;
+ var encoded = new Array(quintetCount(plain) * 8);
+
+ /* byte by byte isn't as pretty as quintet by quintet but tests a bit
+ faster. will have to revisit. */
+ while(i < plain.length) {
+ var current = plain[i];
+
+ if(shiftIndex > 3) {
+ digit = current & (0xff >> shiftIndex);
+ shiftIndex = (shiftIndex + 5) % 8;
+ digit = (digit << shiftIndex) | ((i + 1 < plain.length) ?
+ plain[i + 1] : 0) >> (8 - shiftIndex);
+ i++;
+ } else {
+ digit = (current >> (8 - (shiftIndex + 5))) & 0x1f;
+ shiftIndex = (shiftIndex + 5) % 8;
+ if(shiftIndex === 0) { i++; }
+ }
+
+ encoded[j] = charTable[digit];
+ j++;
+ }
+
+ for(i = j; i < encoded.length; i++) {
+ encoded[i] = '=';
+ }
+
+ return encoded.join('');
+};
+
+exports.base32ToBuffer = function(encoded) {
+ var shiftIndex = 0;
+ var plainDigit = 0;
+ var plainChar;
+ var plainPos = 0;
+ var len = Math.ceil(encoded.length * 5 / 8);
+ var decoded;
+ encoded = encoded.split('').map(function (ch) {
+ return ch.charCodeAt(0);
+ });
+ if('undefined' !== typeof Uint8Array) {
+ encoded = new Uint8Array(encoded);
+ decoded = new Uint8Array(len);
+ } else {
+ decoded = new Array(len);
+ }
+
+ /* byte by byte isn't as pretty as octet by octet but tests a bit
+ faster. will have to revisit. */
+ for(var i = 0; i < encoded.length; i++) {
+ if(encoded[i] === 0x3d){ //'='
+ break;
+ }
+
+ var encodedByte = encoded[i] - 0x30;
+
+ if(encodedByte < byteTable.length) {
+ plainDigit = byteTable[encodedByte];
+
+ if(shiftIndex <= 3) {
+ shiftIndex = (shiftIndex + 5) % 8;
+
+ if(shiftIndex === 0) {
+ plainChar |= plainDigit;
+ decoded[plainPos] = plainChar;
+ plainPos++;
+ plainChar = 0;
+ } else {
+ plainChar |= 0xff & (plainDigit << (8 - shiftIndex));
+ }
+ } else {
+ shiftIndex = (shiftIndex + 5) % 8;
+ plainChar |= 0xff & (plainDigit >>> shiftIndex);
+ decoded[plainPos] = plainChar;
+ plainPos++;
+
+ plainChar = 0xff & (plainDigit << (8 - shiftIndex));
+ }
+ } else {
+ throw new Error('Invalid input - it is not base32 encoded string');
+ }
+ }
+ return decoded.slice(0, plainPos);
+};
+
+}(window.Unibabel || window));