UNPKG

bases

Version:

Utility for converting numbers to/from different bases/alphabets.

115 lines (93 loc) 4.32 kB
// bases.js // Utility for converting numbers to/from different bases/alphabets. // See README.md for details. var bases = (typeof exports !== 'undefined' ? exports : (window.Bases = {})); // Returns a string representation of the given number for the given alphabet: bases.toAlphabet = function (num, alphabet) { var base = alphabet.length; var digits = []; // these will be in reverse order since arrays are stacks // execute at least once, even if num is 0, since we should return the '0': do { digits.push(num % base); // TODO handle negatives properly? num = Math.floor(num / base); } while (num > 0); var chars = []; while (digits.length) { chars.push(alphabet[digits.pop()]); } return chars.join(''); }; // Returns an integer representation of the given string for the given alphabet: bases.fromAlphabet = function (str, alphabet) { var base = alphabet.length; var pos = 0; var num = 0; var c; while (str.length) { c = str[str.length - 1]; str = str.substr(0, str.length - 1); num += Math.pow(base, pos) * alphabet.indexOf(c); pos++; } return num; }; // Known alphabets: bases.NUMERALS = '0123456789'; bases.LETTERS_LOWERCASE = 'abcdefghijklmnopqrstuvwxyz'; bases.LETTERS_UPPERCASE = bases.LETTERS_LOWERCASE.toUpperCase(); bases.KNOWN_ALPHABETS = {}; // Each of the number ones, starting from base-2 (base-1 doesn't make sense?): for (var i = 2; i <= 10; i++) { bases.KNOWN_ALPHABETS[i] = bases.NUMERALS.substr(0, i); } // Node's native hex is 0-9 followed by *lowercase* a-f, so we'll take that // approach for everything from base-11 to base-16: for (var i = 11; i <= 16; i++) { bases.KNOWN_ALPHABETS[i] = bases.NUMERALS + bases.LETTERS_LOWERCASE.substr(0, i - 10); } // We also model base-36 off of that, just using the full letter alphabet: bases.KNOWN_ALPHABETS[36] = bases.NUMERALS + bases.LETTERS_LOWERCASE; // And base-62 will be the uppercase letters added: bases.KNOWN_ALPHABETS[62] = bases.NUMERALS + bases.LETTERS_LOWERCASE + bases.LETTERS_UPPERCASE; // For base-26, we'll assume the user wants just the letter alphabet: bases.KNOWN_ALPHABETS[26] = bases.LETTERS_LOWERCASE; // We'll also add a similar base-52, just letters, lowercase then uppercase: bases.KNOWN_ALPHABETS[52] = bases.LETTERS_LOWERCASE + bases.LETTERS_UPPERCASE; // Base-64 is a formally-specified alphabet that has a particular order: // http://en.wikipedia.org/wiki/Base64 (and Node.js follows this too) // TODO FIXME But our code above doesn't add padding! Don't use this yet... bases.KNOWN_ALPHABETS[64] = bases.LETTERS_UPPERCASE + bases.LETTERS_LOWERCASE + bases.NUMERALS + '+/'; // Flickr and others also have a base-58 that removes confusing characters, but // there isn't consensus on the order of lowercase vs. uppercase... =/ // http://www.flickr.com/groups/api/discuss/72157616713786392/ // https://en.bitcoin.it/wiki/Base58Check_encoding#Base58_symbol_chart // https://github.com/dougal/base58/blob/master/lib/base58.rb // http://icoloma.blogspot.com/2010/03/create-your-own-bitly-using-base58.html // We'll arbitrarily stay consistent with the above and using lowercase first: bases.KNOWN_ALPHABETS[58] = bases.KNOWN_ALPHABETS[62].replace(/[0OlI]/g, ''); // And Douglas Crockford shared a similar base-32 from base-36: // http://www.crockford.com/wrmg/base32.html // Unlike our base-36, he explicitly specifies uppercase letters bases.KNOWN_ALPHABETS[32] = bases.NUMERALS + bases.LETTERS_UPPERCASE.replace(/[ILOU]/g, ''); // Closure helper for convenience aliases like bases.toBase36(): function makeAlias (base, alphabet) { bases['toBase' + base] = function (num) { return bases.toAlphabet(num, alphabet); }; bases['fromBase' + base] = function (str) { return bases.fromAlphabet(str, alphabet); }; } // Do this for all known alphabets: for (var base in bases.KNOWN_ALPHABETS) { if (bases.KNOWN_ALPHABETS.hasOwnProperty(base)) { makeAlias(base, bases.KNOWN_ALPHABETS[base]); } } // And a generic alias too: bases.toBase = function (num, base) { return bases.toAlphabet(num, bases.KNOWN_ALPHABETS[base]); }; bases.fromBase = function (str, base) { return bases.fromAlphabet(str, bases.KNOWN_ALPHABETS[base]); };