Source: codecZ85.js

/**
 * @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;
  }
}