add base32 support
This commit is contained in:
parent
f041dcd8bb
commit
94a64a218a
23
README.md
23
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 <https://coolaj86.com/articles/base64-unicode-utf-8-javascript-and-you/>
|
||||
|
||||
|
@ -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 <https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses>
|
||||
|
||||
ChangeLog
|
||||
====
|
||||
|
||||
v2.1.0
|
||||
------
|
||||
|
||||
Added `unibabel.base32.js`
|
||||
|
||||
v2.0.0
|
||||
------
|
||||
|
||||
|
|
10
index.html
10
index.html
|
@ -1,8 +1,12 @@
|
|||
<html>
|
||||
<script src="./index.js"></script>
|
||||
<script src="./unibabel.hex.js"></script>
|
||||
<script src="./test.js"></script>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
This is for testing. Look in the console.
|
||||
<script src="./index.js"></script>
|
||||
<script src="./unibabel.hex.js"></script>
|
||||
<script src="./unibabel.base32.js"></script>
|
||||
<script src="./test.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -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'));
|
65
test.js
65
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');
|
||||
}
|
||||
|
||||
}());
|
||||
|
|
|
@ -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));
|
Loading…
Reference in New Issue