You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

186 lines
4.9 KiB

4 years ago
  1. /**
  2. * Base-N/Base-X encoding/decoding functions.
  3. *
  4. * Original implementation from base-x:
  5. * https://github.com/cryptocoinjs/base-x
  6. *
  7. * Which is MIT licensed:
  8. *
  9. * The MIT License (MIT)
  10. *
  11. * Copyright base-x contributors (c) 2016
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a copy
  14. * of this software and associated documentation files (the "Software"), to deal
  15. * in the Software without restriction, including without limitation the rights
  16. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  17. * copies of the Software, and to permit persons to whom the Software is
  18. * furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included in
  21. * all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  24. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  26. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  28. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  29. * DEALINGS IN THE SOFTWARE.
  30. */
  31. var api = {};
  32. module.exports = api;
  33. // baseN alphabet indexes
  34. var _reverseAlphabets = {};
  35. /**
  36. * BaseN-encodes a Uint8Array using the given alphabet.
  37. *
  38. * @param input the Uint8Array to encode.
  39. * @param maxline the maximum number of encoded characters per line to use,
  40. * defaults to none.
  41. *
  42. * @return the baseN-encoded output string.
  43. */
  44. api.encode = function(input, alphabet, maxline) {
  45. if(typeof alphabet !== 'string') {
  46. throw new TypeError('"alphabet" must be a string.');
  47. }
  48. if(maxline !== undefined && typeof maxline !== 'number') {
  49. throw new TypeError('"maxline" must be a number.');
  50. }
  51. var output = '';
  52. if(!(input instanceof Uint8Array)) {
  53. // assume forge byte buffer
  54. output = _encodeWithByteBuffer(input, alphabet);
  55. } else {
  56. var i = 0;
  57. var base = alphabet.length;
  58. var first = alphabet.charAt(0);
  59. var digits = [0];
  60. for(i = 0; i < input.length; ++i) {
  61. for(var j = 0, carry = input[i]; j < digits.length; ++j) {
  62. carry += digits[j] << 8;
  63. digits[j] = carry % base;
  64. carry = (carry / base) | 0;
  65. }
  66. while(carry > 0) {
  67. digits.push(carry % base);
  68. carry = (carry / base) | 0;
  69. }
  70. }
  71. // deal with leading zeros
  72. for(i = 0; input[i] === 0 && i < input.length - 1; ++i) {
  73. output += first;
  74. }
  75. // convert digits to a string
  76. for(i = digits.length - 1; i >= 0; --i) {
  77. output += alphabet[digits[i]];
  78. }
  79. }
  80. if(maxline) {
  81. var regex = new RegExp('.{1,' + maxline + '}', 'g');
  82. output = output.match(regex).join('\r\n');
  83. }
  84. return output;
  85. };
  86. /**
  87. * Decodes a baseN-encoded (using the given alphabet) string to a
  88. * Uint8Array.
  89. *
  90. * @param input the baseN-encoded input string.
  91. *
  92. * @return the Uint8Array.
  93. */
  94. api.decode = function(input, alphabet) {
  95. if(typeof input !== 'string') {
  96. throw new TypeError('"input" must be a string.');
  97. }
  98. if(typeof alphabet !== 'string') {
  99. throw new TypeError('"alphabet" must be a string.');
  100. }
  101. var table = _reverseAlphabets[alphabet];
  102. if(!table) {
  103. // compute reverse alphabet
  104. table = _reverseAlphabets[alphabet] = [];
  105. for(var i = 0; i < alphabet.length; ++i) {
  106. table[alphabet.charCodeAt(i)] = i;
  107. }
  108. }
  109. // remove whitespace characters
  110. input = input.replace(/\s/g, '');
  111. var base = alphabet.length;
  112. var first = alphabet.charAt(0);
  113. var bytes = [0];
  114. for(var i = 0; i < input.length; i++) {
  115. var value = table[input.charCodeAt(i)];
  116. if(value === undefined) {
  117. return;
  118. }
  119. for(var j = 0, carry = value; j < bytes.length; ++j) {
  120. carry += bytes[j] * base;
  121. bytes[j] = carry & 0xff;
  122. carry >>= 8;
  123. }
  124. while(carry > 0) {
  125. bytes.push(carry & 0xff);
  126. carry >>= 8;
  127. }
  128. }
  129. // deal with leading zeros
  130. for(var k = 0; input[k] === first && k < input.length - 1; ++k) {
  131. bytes.push(0);
  132. }
  133. if(typeof Buffer !== 'undefined') {
  134. return Buffer.from(bytes.reverse());
  135. }
  136. return new Uint8Array(bytes.reverse());
  137. };
  138. function _encodeWithByteBuffer(input, alphabet) {
  139. var i = 0;
  140. var base = alphabet.length;
  141. var first = alphabet.charAt(0);
  142. var digits = [0];
  143. for(i = 0; i < input.length(); ++i) {
  144. for(var j = 0, carry = input.at(i); j < digits.length; ++j) {
  145. carry += digits[j] << 8;
  146. digits[j] = carry % base;
  147. carry = (carry / base) | 0;
  148. }
  149. while(carry > 0) {
  150. digits.push(carry % base);
  151. carry = (carry / base) | 0;
  152. }
  153. }
  154. var output = '';
  155. // deal with leading zeros
  156. for(i = 0; input.at(i) === 0 && i < input.length() - 1; ++i) {
  157. output += first;
  158. }
  159. // convert digits to a string
  160. for(i = digits.length - 1; i >= 0; --i) {
  161. output += alphabet[digits[i]];
  162. }
  163. return output;
  164. }