Source: codecZ85.js

  1. /**
  2. * @fileOverview Z85 codec implementation.
  3. * @summary Z85 encoding is the "string-safe" ZeroMQ variant of Base85
  4. * encoding. The character set avoids the single and double
  5. * quotes and the backslash, making the encoded string
  6. * safe to embed in command-line interpreters.
  7. * Base85 uses 5 characters to encode 4 bytes of data,
  8. * making the encoded size 1/4 larger than the original;
  9. * this also makes it more efficient than uuencode or Base64,
  10. * which uses 4 characters to encode 3 bytes of data, making
  11. * the encoded size 1/3 larger than the original.
  12. *
  13. * @author Manjul Apratim
  14. */
  15. /**
  16. * Z85 encoding/decoding
  17. * http://rfc.zeromq.org/spec:32/Z85/
  18. * @namespace
  19. */
  20. sjcl.codec.z85 = {
  21. /** The Z85 alphabet.
  22. * @private
  23. */
  24. _chars: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#",
  25. /** The decoder map (maps base 85 to base 256).
  26. * @private
  27. */
  28. _byteMap: [
  29. 0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
  30. 0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
  31. 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  32. 0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
  33. 0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
  34. 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
  35. 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
  36. 0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
  37. 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
  38. 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
  39. 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
  40. 0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
  41. ],
  42. /**
  43. * @summary Method to convert a bitArray to a Z85-encoded string.
  44. * The bits represented by the array MUST be multiples of 4 bytes.
  45. * @param {bitArray} arr - The input bitArray.
  46. * @return {string} The Z85-encoded string.
  47. */
  48. fromBits: function (arr) {
  49. // Sanity checks
  50. if (!arr) {
  51. return null;
  52. }
  53. // Check we have multiples of 4 bytes (32 bits)
  54. if (0 !== sjcl.bitArray.bitLength(arr) % 32) {
  55. throw new sjcl.exception.invalid("Invalid bitArray length!");
  56. }
  57. var out = "", c = sjcl.codec.z85._chars;
  58. // Convert sequences of 4 bytes (each word) to 5 characters.
  59. for (var i = 0; i < arr.length; ++i) {
  60. // Each element in the bitArray is a 32-bit (4-byte) word.
  61. var word = arr[i];
  62. var value = 0;
  63. for (var j = 0; j < 4; ++j) {
  64. // Extract each successive byte from the word from the left.
  65. var byteChunk = (word >>> 8*(4 - j - 1)) & 0xFF;
  66. // Accumulate in base-256
  67. value = value*256 + byteChunk;
  68. }
  69. var divisor = 85*85*85*85;
  70. while (divisor) {
  71. out += c.charAt(Math.floor(value/divisor) % 85);
  72. divisor = Math.floor(divisor/85);
  73. }
  74. }
  75. // Sanity check - each 4-bytes (1 word) should yield 5 characters.
  76. var encodedSize = arr.length*5;
  77. if (out.length !== encodedSize) {
  78. throw new sjcl.exception.invalid("Bad Z85 conversion!");
  79. }
  80. return out;
  81. },
  82. /**
  83. * @summary Method to convert a Z85-encoded string to a bitArray.
  84. * The length of the string MUST be a multiple of 5
  85. * (else it is not a valid Z85 string).
  86. * @param {string} str - A valid Z85-encoded string.
  87. * @return {bitArray} The decoded data represented as a bitArray.
  88. */
  89. toBits: function(str) {
  90. // Sanity check
  91. if (!str) {
  92. return [];
  93. }
  94. // Accept only strings bounded to 5 bytes
  95. if (0 !== str.length % 5) {
  96. throw new sjcl.exception.invalid("Invalid Z85 string!");
  97. }
  98. var out = [], value = 0, byteMap = sjcl.codec.z85._byteMap;
  99. var word = 0, wordSize = 0;
  100. for (var i = 0; i < str.length;) {
  101. // Accumulate value in base 85.
  102. value = value * 85 + byteMap[str[i++].charCodeAt(0) - 32];
  103. if (0 === i % 5) {
  104. // Output value in base-256
  105. var divisor = 256*256*256;
  106. while (divisor) {
  107. // The following is equivalent to a left shift by 8 bits
  108. // followed by OR-ing; however, left shift may cause sign problems
  109. // due to 2's complement interpretation,
  110. // and we're operating on unsigned values.
  111. word = (word * Math.pow(2, 8)) + (Math.floor(value/divisor) % 256);
  112. ++wordSize;
  113. // If 4 bytes have been acumulated, push the word into the bitArray.
  114. if (4 === wordSize) {
  115. out.push(word);
  116. word = 0, wordSize = 0;
  117. }
  118. divisor = Math.floor(divisor/256);
  119. }
  120. value = 0;
  121. }
  122. }
  123. return out;
  124. }
  125. }