basen.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Numeric base conversion library. Works for arbitrary bases and
  16. * arbitrary length numbers.
  17. *
  18. * For base-64 conversion use base64.js because it is optimized for the specific
  19. * conversion to base-64 while this module is generic. Base-64 is defined here
  20. * mostly for demonstration purpose.
  21. *
  22. * TODO: Make base64 and baseN classes that have common interface. (Perhaps...)
  23. *
  24. */
  25. goog.provide('goog.crypt.baseN');
  26. /**
  27. * Base-2, i.e. '01'.
  28. * @type {string}
  29. */
  30. goog.crypt.baseN.BASE_BINARY = '01';
  31. /**
  32. * Base-8, i.e. '01234567'.
  33. * @type {string}
  34. */
  35. goog.crypt.baseN.BASE_OCTAL = '01234567';
  36. /**
  37. * Base-10, i.e. '0123456789'.
  38. * @type {string}
  39. */
  40. goog.crypt.baseN.BASE_DECIMAL = '0123456789';
  41. /**
  42. * Base-16 using lower case, i.e. '0123456789abcdef'.
  43. * @type {string}
  44. */
  45. goog.crypt.baseN.BASE_LOWERCASE_HEXADECIMAL = '0123456789abcdef';
  46. /**
  47. * Base-16 using upper case, i.e. '0123456789ABCDEF'.
  48. * @type {string}
  49. */
  50. goog.crypt.baseN.BASE_UPPERCASE_HEXADECIMAL = '0123456789ABCDEF';
  51. /**
  52. * The more-known version of the BASE-64 encoding. Uses + and / characters.
  53. * @type {string}
  54. */
  55. goog.crypt.baseN.BASE_64 =
  56. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  57. /**
  58. * URL-safe version of the BASE-64 encoding.
  59. * @type {string}
  60. */
  61. goog.crypt.baseN.BASE_64_URL_SAFE =
  62. 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
  63. /**
  64. * Converts a number from one numeric base to another.
  65. *
  66. * The bases are represented as strings, which list allowed digits. Each digit
  67. * should be unique. The bases can either be user defined, or any of
  68. * goog.crypt.baseN.BASE_xxx.
  69. *
  70. * The number is in human-readable format, most significant digit first, and is
  71. * a non-negative integer. Base designators such as $, 0x, d, b or h (at end)
  72. * will be interpreted as digits, so avoid them. Leading zeros will be trimmed.
  73. *
  74. * Note: for huge bases the result may be inaccurate because of overflowing
  75. * 64-bit doubles used by JavaScript for integer calculus. This may happen
  76. * if the product of the number of digits in the input and output bases comes
  77. * close to 10^16, which is VERY unlikely (100M digits in each base), but
  78. * may be possible in the future unicode world. (Unicode 3.2 has less than 100K
  79. * characters. However, it reserves some more, close to 1M.)
  80. *
  81. * @param {string} number The number to convert.
  82. * @param {string} inputBase The numeric base the number is in (all digits).
  83. * @param {string} outputBase Requested numeric base.
  84. * @return {string} The converted number.
  85. */
  86. goog.crypt.baseN.recodeString = function(number, inputBase, outputBase) {
  87. if (outputBase == '') {
  88. throw Error('Empty output base');
  89. }
  90. // Check if number is 0 (special case when we don't want to return '').
  91. var isZero = true;
  92. for (var i = 0, n = number.length; i < n; i++) {
  93. if (number.charAt(i) != inputBase.charAt(0)) {
  94. isZero = false;
  95. break;
  96. }
  97. }
  98. if (isZero) {
  99. return outputBase.charAt(0);
  100. }
  101. var numberDigits = goog.crypt.baseN.stringToArray_(number, inputBase);
  102. var inputBaseSize = inputBase.length;
  103. var outputBaseSize = outputBase.length;
  104. // result = 0.
  105. var result = [];
  106. // For all digits of number, starting with the most significant ...
  107. for (var i = numberDigits.length - 1; i >= 0; i--) {
  108. // result *= number.base.
  109. var carry = 0;
  110. for (var j = 0, n = result.length; j < n; j++) {
  111. var digit = result[j];
  112. // This may overflow for huge bases. See function comment.
  113. digit = digit * inputBaseSize + carry;
  114. if (digit >= outputBaseSize) {
  115. var remainder = digit % outputBaseSize;
  116. carry = (digit - remainder) / outputBaseSize;
  117. digit = remainder;
  118. } else {
  119. carry = 0;
  120. }
  121. result[j] = digit;
  122. }
  123. while (carry) {
  124. var remainder = carry % outputBaseSize;
  125. result.push(remainder);
  126. carry = (carry - remainder) / outputBaseSize;
  127. }
  128. // result += number[i].
  129. carry = numberDigits[i];
  130. var j = 0;
  131. while (carry) {
  132. if (j >= result.length) {
  133. // Extend result with a leading zero which will be overwritten below.
  134. result.push(0);
  135. }
  136. var digit = result[j];
  137. digit += carry;
  138. if (digit >= outputBaseSize) {
  139. var remainder = digit % outputBaseSize;
  140. carry = (digit - remainder) / outputBaseSize;
  141. digit = remainder;
  142. } else {
  143. carry = 0;
  144. }
  145. result[j] = digit;
  146. j++;
  147. }
  148. }
  149. return goog.crypt.baseN.arrayToString_(result, outputBase);
  150. };
  151. /**
  152. * Converts a string representation of a number to an array of digit values.
  153. *
  154. * More precisely, the digit values are indices into the number base, which
  155. * is represented as a string, which can either be user defined or one of the
  156. * BASE_xxx constants.
  157. *
  158. * Throws an Error if the number contains a digit not found in the base.
  159. *
  160. * @param {string} number The string to convert, most significant digit first.
  161. * @param {string} base Digits in the base.
  162. * @return {!Array<number>} Array of digit values, least significant digit
  163. * first.
  164. * @private
  165. */
  166. goog.crypt.baseN.stringToArray_ = function(number, base) {
  167. var index = {};
  168. for (var i = 0, n = base.length; i < n; i++) {
  169. index[base.charAt(i)] = i;
  170. }
  171. var result = [];
  172. for (var i = number.length - 1; i >= 0; i--) {
  173. var character = number.charAt(i);
  174. var digit = index[character];
  175. if (typeof digit == 'undefined') {
  176. throw Error(
  177. 'Number ' + number + ' contains a character not found in base ' +
  178. base + ', which is ' + character);
  179. }
  180. result.push(digit);
  181. }
  182. return result;
  183. };
  184. /**
  185. * Converts an array representation of a number to a string.
  186. *
  187. * More precisely, the elements of the input array are indices into the base,
  188. * which is represented as a string, which can either be user defined or one of
  189. * the BASE_xxx constants.
  190. *
  191. * Throws an Error if the number contains a digit which is outside the range
  192. * 0 ... base.length - 1.
  193. *
  194. * @param {Array<number>} number Array of digit values, least significant
  195. * first.
  196. * @param {string} base Digits in the base.
  197. * @return {string} Number as a string, most significant digit first.
  198. * @private
  199. */
  200. goog.crypt.baseN.arrayToString_ = function(number, base) {
  201. var n = number.length;
  202. var chars = [];
  203. var baseSize = base.length;
  204. for (var i = n - 1; i >= 0; i--) {
  205. var digit = number[i];
  206. if (digit >= baseSize || digit < 0) {
  207. throw Error('Number ' + number + ' contains an invalid digit: ' + digit);
  208. }
  209. chars.push(base.charAt(digit));
  210. }
  211. return chars.join('');
  212. };