/**
* @fileOverview Z85 codec implementation.
* @summary Z85 encoding is the "string-safe" ZeroMQ variant of Base85
* encoding. The character set avoids the single and double
* quotes and the backslash, making the encoded string
* safe to embed in command-line interpreters.
* Base85 uses 5 characters to encode 4 bytes of data,
* making the encoded size 1/4 larger than the original;
* this also makes it more efficient than uuencode or Base64,
* which uses 4 characters to encode 3 bytes of data, making
* the encoded size 1/3 larger than the original.
*
* @author Manjul Apratim
*/
/**
* Z85 encoding/decoding
* http://rfc.zeromq.org/spec:32/Z85/
* @namespace
*/
sjcl.codec.z85 = {
/** The Z85 alphabet.
* @private
*/
_chars: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#",
/** The decoder map (maps base 85 to base 256).
* @private
*/
_byteMap: [
0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
],
/**
* @summary Method to convert a bitArray to a Z85-encoded string.
* The bits represented by the array MUST be multiples of 4 bytes.
* @param {bitArray} arr - The input bitArray.
* @return {string} The Z85-encoded string.
*/
fromBits: function (arr) {
// Sanity checks
if (!arr) {
return null;
}
// Check we have multiples of 4 bytes (32 bits)
if (0 !== sjcl.bitArray.bitLength(arr) % 32) {
throw new sjcl.exception.invalid("Invalid bitArray length!");
}
var out = "", c = sjcl.codec.z85._chars;
// Convert sequences of 4 bytes (each word) to 5 characters.
for (var i = 0; i < arr.length; ++i) {
// Each element in the bitArray is a 32-bit (4-byte) word.
var word = arr[i];
var value = 0;
for (var j = 0; j < 4; ++j) {
// Extract each successive byte from the word from the left.
var byteChunk = (word >>> 8*(4 - j - 1)) & 0xFF;
// Accumulate in base-256
value = value*256 + byteChunk;
}
var divisor = 85*85*85*85;
while (divisor) {
out += c.charAt(Math.floor(value/divisor) % 85);
divisor = Math.floor(divisor/85);
}
}
// Sanity check - each 4-bytes (1 word) should yield 5 characters.
var encodedSize = arr.length*5;
if (out.length !== encodedSize) {
throw new sjcl.exception.invalid("Bad Z85 conversion!");
}
return out;
},
/**
* @summary Method to convert a Z85-encoded string to a bitArray.
* The length of the string MUST be a multiple of 5
* (else it is not a valid Z85 string).
* @param {string} str - A valid Z85-encoded string.
* @return {bitArray} The decoded data represented as a bitArray.
*/
toBits: function(str) {
// Sanity check
if (!str) {
return [];
}
// Accept only strings bounded to 5 bytes
if (0 !== str.length % 5) {
throw new sjcl.exception.invalid("Invalid Z85 string!");
}
var out = [], value = 0, byteMap = sjcl.codec.z85._byteMap;
var word = 0, wordSize = 0;
for (var i = 0; i < str.length;) {
// Accumulate value in base 85.
value = value * 85 + byteMap[str[i++].charCodeAt(0) - 32];
if (0 === i % 5) {
// Output value in base-256
var divisor = 256*256*256;
while (divisor) {
// The following is equivalent to a left shift by 8 bits
// followed by OR-ing; however, left shift may cause sign problems
// due to 2's complement interpretation,
// and we're operating on unsigned values.
word = (word * Math.pow(2, 8)) + (Math.floor(value/divisor) % 256);
++wordSize;
// If 4 bytes have been acumulated, push the word into the bitArray.
if (4 === wordSize) {
out.push(word);
word = 0, wordSize = 0;
}
divisor = Math.floor(divisor/256);
}
value = 0;
}
}
return out;
}
}