"use strict";
/**
* Generate a character map.
* @param {string} alphabet e.g. "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
* @param {object} mappings map overrides from key to value
* @method
*/
var charmap = function (alphabet, mappings) {
mappings || (mappings = {});
alphabet.split("").forEach(function (c, i) {
if (!(c in mappings)) mappings[c] = i;
});
return mappings;
}
/**
* The RFC 4648 base 32 alphabet and character map.
* @see {@link https://tools.ietf.org/html/rfc4648}
*/
var rfc4648 = {
alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
charmap: {
0: 14,
1: 8
}
};
rfc4648.charmap = charmap(rfc4648.alphabet, rfc4648.charmap);
/**
* The Crockford base 32 alphabet and character map.
* @see {@link http://www.crockford.com/wrmg/base32.html}
*/
var crockford = {
alphabet: "0123456789ABCDEFGHJKMNPQRSTVWXYZ",
charmap: {
O: 0,
I: 1,
L: 1
}
};
crockford.charmap = charmap(crockford.alphabet, crockford.charmap);
/**
* base32hex
* @see {@link https://en.wikipedia.org/wiki/Base32#base32hex}
*/
var base32hex = {
alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUV",
charmap: {}
};
base32hex.charmap = charmap(base32hex.alphabet, base32hex.charmap);
/**
* Create a new `Decoder` with the given options.
*
* @param {object} [options]
* @param {string} [type] Supported Base-32 variants are "rfc4648" and
* "crockford".
* @param {object} [charmap] Override the character map used in decoding.
* @constructor
*/
function Decoder (options) {
this.buf = [];
this.shift = 8;
this.carry = 0;
if (options) {
switch (options.type) {
case "rfc4648":
this.charmap = exports.rfc4648.charmap;
break;
case "crockford":
this.charmap = exports.crockford.charmap;
break;
case "base32hex":
this.charmap = exports.base32hex.charmap;
break;
default:
throw new Error("invalid type");
}
if (options.charmap) this.charmap = options.charmap;
}
}
/**
* The default character map coresponds to RFC4648.
*/
Decoder.prototype.charmap = rfc4648.charmap;
/**
* Decode a string, continuing from the previous state.
*
* @param {string} str
* @return {Decoder} this
*/
Decoder.prototype.write = function (str) {
var charmap = this.charmap;
var buf = this.buf;
var shift = this.shift;
var carry = this.carry;
// decode string
str.toUpperCase().split("").forEach(function (char) {
// ignore padding
if (char == "=") return;
// lookup symbol
var symbol = charmap[char] & 0xff;
// 1: 00000 000
// 2: 00 00000 0
// 3: 0000 0000
// 4: 0 00000 00
// 5: 000 00000
// 6: 00000 000
// 7: 00 00000 0
shift -= 5;
if (shift > 0) {
carry |= symbol << shift;
} else if (shift < 0) {
buf.push(carry | (symbol >> -shift));
shift += 8;
carry = (symbol << shift) & 0xff;
} else {
buf.push(carry | symbol);
shift = 8;
carry = 0;
}
});
// save state
this.shift = shift;
this.carry = carry;
// for chaining
return this;
};
/**
* Finish decoding.
*
* @param {string} [str] The final string to decode.
* @return {Array} Decoded byte array.
*/
Decoder.prototype.finalize = function (str) {
if (str) {
this.write(str);
}
if (this.shift !== 8 && this.carry !== 0) {
this.buf.push(this.carry);
this.shift = 8;
this.carry = 0;
}
return this.buf;
};
/**
* Create a new `Encoder` with the given options.
*
* @param {object} [options]
* @param {string} [type] Supported Base-32 variants are "rfc4648" and
* "crockford".
* @param {object} [alphabet] Override the alphabet used in encoding.
* @constructor
*/
function Encoder (options) {
this.buf = "";
this.shift = 3;
this.carry = 0;
if (options) {
switch (options.type) {
case "rfc4648":
this.alphabet = exports.rfc4648.alphabet;
break;
case "crockford":
this.alphabet = exports.crockford.alphabet;
break;
case "base32hex":
this.alphabet = exports.base32hex.alphabet;
break;
default:
throw new Error("invalid type");
}
if (options.alphabet) this.alphabet = options.alphabet;
else if (options.lc) this.alphabet = this.alphabet.toLowerCase();
}
}
/**
* The default alphabet coresponds to RFC4648.
*/
Encoder.prototype.alphabet = rfc4648.alphabet;
/**
* Encode a byte array, continuing from the previous state.
*
* @param {byte[]} buf The byte array to encode.
* @return {Encoder} this
*/
Encoder.prototype.write = function (buf) {
var shift = this.shift;
var carry = this.carry;
var symbol;
var byte;
var i;
// encode each byte in buf
for (i = 0; i < buf.length; i++) {
byte = buf[i];
// 1: 00000 000
// 2: 00 00000 0
// 3: 0000 0000
// 4: 0 00000 00
// 5: 000 00000
// 6: 00000 000
// 7: 00 00000 0
symbol = carry | (byte >> shift);
this.buf += this.alphabet[symbol & 0x1f];
if (shift > 5) {
shift -= 5;
symbol = byte >> shift;
this.buf += this.alphabet[symbol & 0x1f];
}
shift = 5 - shift;
carry = byte << shift;
shift = 8 - shift;
}
// save state
this.shift = shift;
this.carry = carry;
// for chaining
return this;
};
/**
* Finish encoding.
*
* @param {byte[]} [buf] The final byte array to encode.
* @return {string} The encoded byte array.
*/
Encoder.prototype.finalize = function (buf) {
if (buf) {
this.write(buf);
}
if (this.shift !== 3) {
this.buf += this.alphabet[this.carry & 0x1f];
this.shift = 3;
this.carry = 0;
}
return this.buf;
};
/**
* Convenience encoder.
*
* @param {byte[]} buf The byte array to encode.
* @param {object} [options] Options to pass to the encoder.
* @return {string} The encoded string.
*/
exports.encode = function (buf, options) {
return new Encoder(options).finalize(buf);
};
/**
* Convenience decoder.
*
* @param {string} str The string to decode.
* @param {object} [options] Options to pass to the decoder.
* @return {byte[]} The decoded byte array.
*/
exports.decode = function (str, options) {
return new Decoder(options).finalize(str);
};
// Exports.
exports.Decoder = Decoder;
exports.Encoder = Encoder;
exports.charmap = charmap;
exports.crockford = crockford;
exports.rfc4648 = rfc4648;
exports.base32hex = base32hex;