├── .github └── workflows │ └── main_ci.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── benchmark ├── index.js ├── package-lock.json └── package.json ├── package-lock.json ├── package.json ├── src ├── cjs │ ├── index.cjs │ └── index.d.ts └── esm │ └── index.js ├── test ├── fixtures.json └── index.js ├── ts_src └── index.ts ├── tsconfig.base.json ├── tsconfig.cjs.json └── tsconfig.json /.github/workflows/main_ci.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | unit: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 18 17 | registry-url: https://registry.npmjs.org/ 18 | - run: npm ci 19 | - run: npm run unit 20 | format: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: 18 27 | registry-url: https://registry.npmjs.org/ 28 | - run: npm ci 29 | - run: npm run standard 30 | gitdiff: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v3 34 | - uses: actions/setup-node@v4 35 | with: 36 | node-version: 18 37 | registry-url: https://registry.npmjs.org/ 38 | - run: npm ci 39 | - run: npm run gitdiff -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 base-x contributors 4 | Copyright (c) 2014-2018 The Bitcoin Core developers 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base-x 2 | 3 | [![NPM Package](https://img.shields.io/npm/v/base-x.svg?style=flat-square)](https://www.npmjs.org/package/base-x) 4 | [![Build Status](https://img.shields.io/travis/cryptocoinjs/base-x.svg?branch=master&style=flat-square)](https://travis-ci.org/cryptocoinjs/base-x) 5 | 6 | [![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) 7 | 8 | Fast base encoding / decoding of any given alphabet using bitcoin style leading 9 | zero compression. 10 | 11 | **WARNING:** This module is **NOT RFC3548** compliant, it cannot be used for base16 (hex), base32, or base64 encoding in a standards compliant manner. 12 | 13 | ## Example 14 | 15 | Base58 16 | 17 | ``` javascript 18 | var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 19 | import basex from 'base-x' 20 | var bs58 = basex(BASE58) 21 | 22 | var decoded = bs58.decode('5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr') 23 | 24 | console.log(decoded) 25 | // => Uint8Array(33) [ 26 | // 128, 237, 219, 220, 17, 104, 241, 218, 27 | // 234, 219, 211, 228, 76, 30, 63, 143, 28 | // 90, 40, 76, 32, 41, 247, 138, 210, 29 | // 106, 249, 133, 131, 164, 153, 222, 91, 30 | // 25 31 | // ] 32 | 33 | console.log(bs58.encode(decoded)) 34 | // => 5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr 35 | ``` 36 | 37 | ### Alphabets 38 | 39 | See below for a list of commonly recognized alphabets, and their respective base. 40 | 41 | Base | Alphabet 42 | ------------- | ------------- 43 | 2 | `01` 44 | 8 | `01234567` 45 | 11 | `0123456789a` 46 | 16 | `0123456789abcdef` 47 | 32 | `0123456789ABCDEFGHJKMNPQRSTVWXYZ` 48 | 32 | `ybndrfg8ejkmcpqxot1uwisza345h769` (z-base-32) 49 | 36 | `0123456789abcdefghijklmnopqrstuvwxyz` 50 | 58 | `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz` 51 | 62 | `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` 52 | 64 | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/` 53 | 67 | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~` 54 | 55 | 56 | ## How it works 57 | 58 | It encodes octet arrays by doing long divisions on all significant digits in the 59 | array, creating a representation of that number in the new base. Then for every 60 | leading zero in the input (not significant as a number) it will encode as a 61 | single leader character. This is the first in the alphabet and will decode as 8 62 | bits. The other characters depend upon the base. For example, a base58 alphabet 63 | packs roughly 5.858 bits per character. 64 | 65 | This means the encoded string 000f (using a base16, 0-f alphabet) will actually decode 66 | to 4 bytes unlike a canonical hex encoding which uniformly packs 4 bits into each 67 | character. 68 | 69 | While unusual, this does mean that no padding is required and it works for bases 70 | like 43. 71 | 72 | 73 | ## LICENSE [MIT](LICENSE) 74 | A direct derivation of the base58 implementation from [`bitcoin/bitcoin`](https://github.com/bitcoin/bitcoin/blob/f1e2f2a85962c1664e4e55471061af0eaa798d40/src/base58.cpp), generalized for variable length alphabets. 75 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const crypto = require('crypto') 3 | const benchmark = require('benchmark') 4 | const XorShift128Plus = require('xorshift.js').XorShift128Plus 5 | 6 | const bs58ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 7 | const basex = require('../src/cjs/index.cjs').default 8 | const bs58 = basex(bs58ALPHABET) 9 | 10 | // const bs58 = basex(bs58ALPHABET) 11 | 12 | let fixtureIndex = 0 13 | const resetFixtureIndex = function () { fixtureIndex = 0 } 14 | const fixtures = new Array(10000) 15 | const getNextFixture = function () { 16 | const fixture = fixtures[fixtureIndex++] 17 | if (fixtureIndex === fixtures.length) { 18 | fixtureIndex = 0 19 | } 20 | 21 | return fixture 22 | } 23 | 24 | const seed = process.env.SEED || crypto.randomBytes(16).toString('hex') 25 | console.log('Seed: ' + seed) 26 | const prng = new XorShift128Plus(seed) 27 | for (let i = 0; i < fixtures.length; ++i) { 28 | const source = prng.randomBytes(32) 29 | fixtures[i] = { source, string: bs58.encode(source) } 30 | } 31 | 32 | if (/fast/i.test(process.argv[2])) { 33 | console.log('Running in fast mode...') 34 | benchmark.options.minTime = 0.3 35 | benchmark.options.maxTime = 1 36 | benchmark.options.minSamples = 3 37 | } else { 38 | benchmark.options.minTime = 1 39 | } 40 | 41 | new benchmark.Suite({ 42 | onStart: function () { 43 | console.log('--------------------------------------------------') 44 | }, 45 | onCycle: function (event) { 46 | console.log(String(event.target)) 47 | }, 48 | onError: function (event) { 49 | console.error(event.target.error) 50 | }, 51 | onComplete: function () { 52 | console.log('==================================================') 53 | } 54 | }) 55 | .add('encode', function () { 56 | const fixture = getNextFixture() 57 | bs58.encode(fixture.source) 58 | }, { onStart: resetFixtureIndex, onCycle: resetFixtureIndex }) 59 | .add('decode', function () { 60 | const fixture = getNextFixture() 61 | bs58.decode(fixture.string) 62 | }, { onStart: resetFixtureIndex, onCycle: resetFixtureIndex }) 63 | .run() 64 | -------------------------------------------------------------------------------- /benchmark/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-x-benchmark", 3 | "version": "0.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "base-x-benchmark", 9 | "version": "0.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "benchmark": "^1.0.0", 13 | "xorshift.js": "^1.0.3" 14 | } 15 | }, 16 | "node_modules/benchmark": { 17 | "version": "1.0.0", 18 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz", 19 | "integrity": "sha512-qSlOi0If8sI+icu3l/W5rd4R0etJz9orLPWpDdt1lPgEFzEHYYnkfMuotj+Lx5SyMkmfawlPoW9RmoEm19ziHA==", 20 | "engines": [ 21 | "node", 22 | "rhino" 23 | ] 24 | }, 25 | "node_modules/inherits": { 26 | "version": "2.0.4", 27 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 28 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 29 | }, 30 | "node_modules/xorshift.js": { 31 | "version": "1.0.5", 32 | "resolved": "https://registry.npmjs.org/xorshift.js/-/xorshift.js-1.0.5.tgz", 33 | "integrity": "sha512-izfjE9osPb9sb8/jGf98AMOTdqdzYR+Jr2dD+tOWS9CpUNmAJ0w08TNcIidVzywpKehjzwqrFOAvx2WKJy/uGw==", 34 | "dependencies": { 35 | "inherits": "^2.0.1" 36 | } 37 | } 38 | }, 39 | "dependencies": { 40 | "benchmark": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz", 43 | "integrity": "sha512-qSlOi0If8sI+icu3l/W5rd4R0etJz9orLPWpDdt1lPgEFzEHYYnkfMuotj+Lx5SyMkmfawlPoW9RmoEm19ziHA==" 44 | }, 45 | "inherits": { 46 | "version": "2.0.4", 47 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 48 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 49 | }, 50 | "xorshift.js": { 51 | "version": "1.0.5", 52 | "resolved": "https://registry.npmjs.org/xorshift.js/-/xorshift.js-1.0.5.tgz", 53 | "integrity": "sha512-izfjE9osPb9sb8/jGf98AMOTdqdzYR+Jr2dD+tOWS9CpUNmAJ0w08TNcIidVzywpKehjzwqrFOAvx2WKJy/uGw==", 54 | "requires": { 55 | "inherits": "^2.0.1" 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-x-benchmark", 3 | "version": "0.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "node index.js", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "benchmark": "^1.0.0", 13 | "xorshift.js": "^1.0.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base-x", 3 | "version": "5.0.1", 4 | "description": "Fast base encoding / decoding of any given alphabet", 5 | "type": "module", 6 | "keywords": [ 7 | "base-x", 8 | "base58", 9 | "base62", 10 | "base64", 11 | "crypto", 12 | "crytography", 13 | "decode", 14 | "decoding", 15 | "encode", 16 | "encoding" 17 | ], 18 | "homepage": "https://github.com/cryptocoinjs/base-x", 19 | "bugs": { 20 | "url": "https://github.com/cryptocoinjs/base-x/issues" 21 | }, 22 | "license": "MIT", 23 | "author": "Daniel Cousens", 24 | "files": [ 25 | "src" 26 | ], 27 | "main": "src/cjs/index.cjs", 28 | "module": "src/esm/index.js", 29 | "types": "src/cjs/index.d.ts", 30 | "exports": { 31 | ".": { 32 | "require": "./src/cjs/index.cjs", 33 | "import": "./src/esm/index.js", 34 | "types": "./src/cjs/index.d.ts" 35 | } 36 | }, 37 | "repository": { 38 | "type": "git", 39 | "url": "https://github.com/cryptocoinjs/base-x.git" 40 | }, 41 | "scripts": { 42 | "build": "npm run clean && tsc -p ./tsconfig.json && tsc -p ./tsconfig.cjs.json; npm run standard -- --fix", 43 | "clean": "rimraf src", 44 | "postbuild": "find src/cjs -type f -name \"*.js\" -exec bash -c 'mv \"$0\" \"${0%.js}.cjs\"' {} \\;", 45 | "gitdiff": "npm run build && git diff --exit-code", 46 | "prepublish": "npm run gitdiff", 47 | "standard": "standard --ignore test", 48 | "test": "npm run unit && npm run standard", 49 | "unit": "tape test/*.js" 50 | }, 51 | "devDependencies": { 52 | "@types/node": "12.0.10", 53 | "rimraf": "^3.0.2", 54 | "standard": "^17.1.0", 55 | "tape": "^5.3.0", 56 | "typescript": "^5.4.5" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/cjs/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // base-x encoding / decoding 3 | // Copyright (c) 2018 base-x contributors 4 | // Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp) 5 | // Distributed under the MIT software license, see the accompanying 6 | // file LICENSE or http://www.opensource.org/licenses/mit-license.php. 7 | Object.defineProperty(exports, '__esModule', { value: true }) 8 | function base (ALPHABET) { 9 | if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') } 10 | const BASE_MAP = new Uint8Array(256) 11 | for (let j = 0; j < BASE_MAP.length; j++) { 12 | BASE_MAP[j] = 255 13 | } 14 | for (let i = 0; i < ALPHABET.length; i++) { 15 | const x = ALPHABET.charAt(i) 16 | const xc = x.charCodeAt(0) 17 | if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') } 18 | BASE_MAP[xc] = i 19 | } 20 | const BASE = ALPHABET.length 21 | const LEADER = ALPHABET.charAt(0) 22 | const FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up 23 | const iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up 24 | function encode (source) { 25 | // eslint-disable-next-line no-empty 26 | if (source instanceof Uint8Array) { } else if (ArrayBuffer.isView(source)) { 27 | source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength) 28 | } else if (Array.isArray(source)) { 29 | source = Uint8Array.from(source) 30 | } 31 | if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') } 32 | if (source.length === 0) { return '' } 33 | // Skip & count leading zeroes. 34 | let zeroes = 0 35 | let length = 0 36 | let pbegin = 0 37 | const pend = source.length 38 | while (pbegin !== pend && source[pbegin] === 0) { 39 | pbegin++ 40 | zeroes++ 41 | } 42 | // Allocate enough space in big-endian base58 representation. 43 | const size = ((pend - pbegin) * iFACTOR + 1) >>> 0 44 | const b58 = new Uint8Array(size) 45 | // Process the bytes. 46 | while (pbegin !== pend) { 47 | let carry = source[pbegin] 48 | // Apply "b58 = b58 * 256 + ch". 49 | let i = 0 50 | for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { 51 | carry += (256 * b58[it1]) >>> 0 52 | b58[it1] = (carry % BASE) >>> 0 53 | carry = (carry / BASE) >>> 0 54 | } 55 | if (carry !== 0) { throw new Error('Non-zero carry') } 56 | length = i 57 | pbegin++ 58 | } 59 | // Skip leading zeroes in base58 result. 60 | let it2 = size - length 61 | while (it2 !== size && b58[it2] === 0) { 62 | it2++ 63 | } 64 | // Translate the result into a string. 65 | let str = LEADER.repeat(zeroes) 66 | for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]) } 67 | return str 68 | } 69 | function decodeUnsafe (source) { 70 | if (typeof source !== 'string') { throw new TypeError('Expected String') } 71 | if (source.length === 0) { return new Uint8Array() } 72 | let psz = 0 73 | // Skip and count leading '1's. 74 | let zeroes = 0 75 | let length = 0 76 | while (source[psz] === LEADER) { 77 | zeroes++ 78 | psz++ 79 | } 80 | // Allocate enough space in big-endian base256 representation. 81 | const size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up. 82 | const b256 = new Uint8Array(size) 83 | // Process the characters. 84 | while (psz < source.length) { 85 | // Find code of next character 86 | const charCode = source.charCodeAt(psz) 87 | // Base map can not be indexed using char code 88 | if (charCode > 255) { return } 89 | // Decode character 90 | let carry = BASE_MAP[charCode] 91 | // Invalid character 92 | if (carry === 255) { return } 93 | let i = 0 94 | for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { 95 | carry += (BASE * b256[it3]) >>> 0 96 | b256[it3] = (carry % 256) >>> 0 97 | carry = (carry / 256) >>> 0 98 | } 99 | if (carry !== 0) { throw new Error('Non-zero carry') } 100 | length = i 101 | psz++ 102 | } 103 | // Skip leading zeroes in b256. 104 | let it4 = size - length 105 | while (it4 !== size && b256[it4] === 0) { 106 | it4++ 107 | } 108 | const vch = new Uint8Array(zeroes + (size - it4)) 109 | let j = zeroes 110 | while (it4 !== size) { 111 | vch[j++] = b256[it4++] 112 | } 113 | return vch 114 | } 115 | function decode (string) { 116 | const buffer = decodeUnsafe(string) 117 | if (buffer) { return buffer } 118 | throw new Error('Non-base' + BASE + ' character') 119 | } 120 | return { 121 | encode, 122 | decodeUnsafe, 123 | decode 124 | } 125 | } 126 | exports.default = base 127 | -------------------------------------------------------------------------------- /src/cjs/index.d.ts: -------------------------------------------------------------------------------- 1 | declare function base(ALPHABET: string): base.BaseConverter; 2 | export default base; 3 | declare namespace base { 4 | interface BaseConverter { 5 | encode(buffer: Uint8Array | number[]): string; 6 | decodeUnsafe(string: string): Uint8Array | undefined; 7 | decode(string: string): Uint8Array; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/esm/index.js: -------------------------------------------------------------------------------- 1 | // base-x encoding / decoding 2 | // Copyright (c) 2018 base-x contributors 3 | // Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp) 4 | // Distributed under the MIT software license, see the accompanying 5 | // file LICENSE or http://www.opensource.org/licenses/mit-license.php. 6 | function base (ALPHABET) { 7 | if (ALPHABET.length >= 255) { throw new TypeError('Alphabet too long') } 8 | const BASE_MAP = new Uint8Array(256) 9 | for (let j = 0; j < BASE_MAP.length; j++) { 10 | BASE_MAP[j] = 255 11 | } 12 | for (let i = 0; i < ALPHABET.length; i++) { 13 | const x = ALPHABET.charAt(i) 14 | const xc = x.charCodeAt(0) 15 | if (BASE_MAP[xc] !== 255) { throw new TypeError(x + ' is ambiguous') } 16 | BASE_MAP[xc] = i 17 | } 18 | const BASE = ALPHABET.length 19 | const LEADER = ALPHABET.charAt(0) 20 | const FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up 21 | const iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up 22 | function encode (source) { 23 | // eslint-disable-next-line no-empty 24 | if (source instanceof Uint8Array) { } else if (ArrayBuffer.isView(source)) { 25 | source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength) 26 | } else if (Array.isArray(source)) { 27 | source = Uint8Array.from(source) 28 | } 29 | if (!(source instanceof Uint8Array)) { throw new TypeError('Expected Uint8Array') } 30 | if (source.length === 0) { return '' } 31 | // Skip & count leading zeroes. 32 | let zeroes = 0 33 | let length = 0 34 | let pbegin = 0 35 | const pend = source.length 36 | while (pbegin !== pend && source[pbegin] === 0) { 37 | pbegin++ 38 | zeroes++ 39 | } 40 | // Allocate enough space in big-endian base58 representation. 41 | const size = ((pend - pbegin) * iFACTOR + 1) >>> 0 42 | const b58 = new Uint8Array(size) 43 | // Process the bytes. 44 | while (pbegin !== pend) { 45 | let carry = source[pbegin] 46 | // Apply "b58 = b58 * 256 + ch". 47 | let i = 0 48 | for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { 49 | carry += (256 * b58[it1]) >>> 0 50 | b58[it1] = (carry % BASE) >>> 0 51 | carry = (carry / BASE) >>> 0 52 | } 53 | if (carry !== 0) { throw new Error('Non-zero carry') } 54 | length = i 55 | pbegin++ 56 | } 57 | // Skip leading zeroes in base58 result. 58 | let it2 = size - length 59 | while (it2 !== size && b58[it2] === 0) { 60 | it2++ 61 | } 62 | // Translate the result into a string. 63 | let str = LEADER.repeat(zeroes) 64 | for (; it2 < size; ++it2) { str += ALPHABET.charAt(b58[it2]) } 65 | return str 66 | } 67 | function decodeUnsafe (source) { 68 | if (typeof source !== 'string') { throw new TypeError('Expected String') } 69 | if (source.length === 0) { return new Uint8Array() } 70 | let psz = 0 71 | // Skip and count leading '1's. 72 | let zeroes = 0 73 | let length = 0 74 | while (source[psz] === LEADER) { 75 | zeroes++ 76 | psz++ 77 | } 78 | // Allocate enough space in big-endian base256 representation. 79 | const size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up. 80 | const b256 = new Uint8Array(size) 81 | // Process the characters. 82 | while (psz < source.length) { 83 | // Find code of next character 84 | const charCode = source.charCodeAt(psz) 85 | // Base map can not be indexed using char code 86 | if (charCode > 255) { return } 87 | // Decode character 88 | let carry = BASE_MAP[charCode] 89 | // Invalid character 90 | if (carry === 255) { return } 91 | let i = 0 92 | for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { 93 | carry += (BASE * b256[it3]) >>> 0 94 | b256[it3] = (carry % 256) >>> 0 95 | carry = (carry / 256) >>> 0 96 | } 97 | if (carry !== 0) { throw new Error('Non-zero carry') } 98 | length = i 99 | psz++ 100 | } 101 | // Skip leading zeroes in b256. 102 | let it4 = size - length 103 | while (it4 !== size && b256[it4] === 0) { 104 | it4++ 105 | } 106 | const vch = new Uint8Array(zeroes + (size - it4)) 107 | let j = zeroes 108 | while (it4 !== size) { 109 | vch[j++] = b256[it4++] 110 | } 111 | return vch 112 | } 113 | function decode (string) { 114 | const buffer = decodeUnsafe(string) 115 | if (buffer) { return buffer } 116 | throw new Error('Non-base' + BASE + ' character') 117 | } 118 | return { 119 | encode, 120 | decodeUnsafe, 121 | decode 122 | } 123 | } 124 | export default base 125 | -------------------------------------------------------------------------------- /test/fixtures.json: -------------------------------------------------------------------------------- 1 | { 2 | "alphabets": { 3 | "base2": "01", 4 | "base16": "0123456789abcdef", 5 | "base45": "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", 6 | "base58": "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 7 | }, 8 | "valid": [ 9 | { 10 | "alphabet": "base2", 11 | "hex": "000f", 12 | "string": "01111" 13 | }, 14 | { 15 | "alphabet": "base2", 16 | "hex": "00ff", 17 | "comment": "Note the first leading zero byte is compressed into 1 char", 18 | "string": "011111111" 19 | }, 20 | { 21 | "alphabet": "base2", 22 | "hex": "0fff", 23 | "string": "111111111111" 24 | }, 25 | { 26 | "alphabet": "base2", 27 | "hex": "ff00ff00", 28 | "string": "11111111000000001111111100000000" 29 | }, 30 | { 31 | "alphabet": "base16", 32 | "hex": "0000000f", 33 | "string": "000f" 34 | }, 35 | { 36 | "alphabet": "base16", 37 | "hex": "000fff", 38 | "string": "0fff" 39 | }, 40 | { 41 | "alphabet": "base16", 42 | "hex": "ffff", 43 | "string": "ffff" 44 | }, 45 | { 46 | "alphabet": "base58", 47 | "hex": "", 48 | "string": "" 49 | }, 50 | { 51 | "alphabet": "base58", 52 | "hex": "61", 53 | "string": "2g" 54 | }, 55 | { 56 | "alphabet": "base58", 57 | "hex": "626262", 58 | "string": "a3gV" 59 | }, 60 | { 61 | "alphabet": "base58", 62 | "hex": "636363", 63 | "string": "aPEr" 64 | }, 65 | { 66 | "alphabet": "base58", 67 | "hex": "73696d706c792061206c6f6e6720737472696e67", 68 | "string": "2cFupjhnEsSn59qHXstmK2ffpLv2" 69 | }, 70 | { 71 | "alphabet": "base58", 72 | "hex": "00eb15231dfceb60925886b67d065299925915aeb172c06647", 73 | "string": "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L" 74 | }, 75 | { 76 | "alphabet": "base58", 77 | "hex": "516b6fcd0f", 78 | "string": "ABnLTmg" 79 | }, 80 | { 81 | "alphabet": "base58", 82 | "hex": "bf4f89001e670274dd", 83 | "string": "3SEo3LWLoPntC" 84 | }, 85 | { 86 | "alphabet": "base58", 87 | "hex": "572e4794", 88 | "string": "3EFU7m" 89 | }, 90 | { 91 | "alphabet": "base58", 92 | "hex": "ecac89cad93923c02321", 93 | "string": "EJDM8drfXA6uyA" 94 | }, 95 | { 96 | "alphabet": "base58", 97 | "hex": "10c8511e", 98 | "string": "Rt5zm" 99 | }, 100 | { 101 | "alphabet": "base58", 102 | "hex": "00000000000000000000", 103 | "string": "1111111111" 104 | }, 105 | { 106 | "alphabet": "base58", 107 | "hex": "801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e", 108 | "string": "5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD" 109 | }, 110 | { 111 | "alphabet": "base58", 112 | "hex": "003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187", 113 | "string": "16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS" 114 | }, 115 | { 116 | "alphabet": "base58", 117 | "hex": "ffffffffffffffffffff", 118 | "string": "FPBt6CHo3fovdL" 119 | }, 120 | { 121 | "alphabet": "base58", 122 | "hex": "ffffffffffffffffffffffffff", 123 | "string": "NKioeUVktgzXLJ1B3t" 124 | }, 125 | { 126 | "alphabet": "base58", 127 | "hex": "ffffffffffffffffffffffffffffffff", 128 | "string": "YcVfxkQb6JRzqk5kF2tNLv" 129 | }, 130 | { 131 | "alphabet": "base2", 132 | "hex": "fb6f9ac3", 133 | "string": "11111011011011111001101011000011" 134 | }, 135 | { 136 | "alphabet": "base2", 137 | "hex": "179eea7a", 138 | "string": "10111100111101110101001111010" 139 | }, 140 | { 141 | "alphabet": "base2", 142 | "hex": "6db825db", 143 | "string": "1101101101110000010010111011011" 144 | }, 145 | { 146 | "alphabet": "base2", 147 | "hex": "93976aa7", 148 | "string": "10010011100101110110101010100111" 149 | }, 150 | { 151 | "alphabet": "base58", 152 | "hex": "ef41b9ce7e830af7", 153 | "string": "h26E62FyLQN" 154 | }, 155 | { 156 | "alphabet": "base58", 157 | "hex": "606cbc791036d2e9", 158 | "string": "H8Sa62HVULG" 159 | }, 160 | { 161 | "alphabet": "base58", 162 | "hex": "bdcb0ea69c2c8ec8", 163 | "string": "YkESUPpnfoD" 164 | }, 165 | { 166 | "alphabet": "base58", 167 | "hex": "1a2358ba67fb71d5", 168 | "string": "5NaBN89ajtQ" 169 | }, 170 | { 171 | "alphabet": "base58", 172 | "hex": "e6173f0f4d5fb5d7", 173 | "string": "fVAoezT1ZkS" 174 | }, 175 | { 176 | "alphabet": "base58", 177 | "hex": "91c81cbfdd58bbd2", 178 | "string": "RPGNSU3bqTX" 179 | }, 180 | { 181 | "alphabet": "base58", 182 | "hex": "329e0bf0e388dbfe", 183 | "string": "9U41ZkwwysT" 184 | }, 185 | { 186 | "alphabet": "base58", 187 | "hex": "30b10393210fa65b", 188 | "string": "99NMW3WHjjY" 189 | }, 190 | { 191 | "alphabet": "base58", 192 | "hex": "ab3bdd18e3623654", 193 | "string": "VeBbqBb4rCT" 194 | }, 195 | { 196 | "alphabet": "base58", 197 | "hex": "fe29d1751ec4af8a", 198 | "string": "jWhmYLN9dUm" 199 | }, 200 | { 201 | "alphabet": "base58", 202 | "hex": "c1273ab5488769807d", 203 | "string": "3Tbh4kL3WKW6g" 204 | }, 205 | { 206 | "alphabet": "base58", 207 | "hex": "6c7907904de934f852", 208 | "string": "2P5jNYhfpTJxy" 209 | }, 210 | { 211 | "alphabet": "base58", 212 | "hex": "05f0be055db47a0dc9", 213 | "string": "5PN768Kr5oEp" 214 | }, 215 | { 216 | "alphabet": "base58", 217 | "hex": "3511e6206829b35b12", 218 | "string": "gBREojGaJ6DF" 219 | }, 220 | { 221 | "alphabet": "base58", 222 | "hex": "d1c7c2ddc4a459d503", 223 | "string": "3fsekq5Esq2KC" 224 | }, 225 | { 226 | "alphabet": "base58", 227 | "hex": "1f88efd17ab073e9a1", 228 | "string": "QHJbmW9ZY7jn" 229 | }, 230 | { 231 | "alphabet": "base58", 232 | "hex": "0f45dadf4e64c5d5c2", 233 | "string": "CGyVUMmCKLRf" 234 | }, 235 | { 236 | "alphabet": "base58", 237 | "hex": "de1e5c5f718bb7fafa", 238 | "string": "3pyy8U7w3KUa5" 239 | }, 240 | { 241 | "alphabet": "base58", 242 | "hex": "123190b93e9a49a46c", 243 | "string": "ES3DeFrG1zbd" 244 | }, 245 | { 246 | "alphabet": "base58", 247 | "hex": "8bee94a543e7242e5a", 248 | "string": "2nJnuWyLpGf6y" 249 | }, 250 | { 251 | "alphabet": "base58", 252 | "hex": "9fd5f2285362f5cfd834", 253 | "string": "9yqFhqeewcW3pF" 254 | }, 255 | { 256 | "alphabet": "base58", 257 | "hex": "6987bac63ad23828bb31", 258 | "string": "6vskE5Y1LhS3U4" 259 | }, 260 | { 261 | "alphabet": "base58", 262 | "hex": "19d4a0f9d459cc2a08b0", 263 | "string": "2TAsHPuaLhh5Aw" 264 | }, 265 | { 266 | "alphabet": "base58", 267 | "hex": "a1e47ffdbea5a807ab26", 268 | "string": "A6XzPgSUJDf1W5" 269 | }, 270 | { 271 | "alphabet": "base58", 272 | "hex": "35c231e5b3a86a9b83db", 273 | "string": "42B8reRwPAAoAa" 274 | }, 275 | { 276 | "alphabet": "base58", 277 | "hex": "b2351012a48b8347c351", 278 | "string": "B1hPyomGx4Vhqa" 279 | }, 280 | { 281 | "alphabet": "base58", 282 | "hex": "71d402694dd9517ea653", 283 | "string": "7Pv2SyAQx2Upu8" 284 | }, 285 | { 286 | "alphabet": "base58", 287 | "hex": "55227c0ec7955c2bd6e8", 288 | "string": "5nR64BkskyjHMq" 289 | }, 290 | { 291 | "alphabet": "base58", 292 | "hex": "17b3d8ee7907c1be34df", 293 | "string": "2LEg7TxosoxTGS" 294 | }, 295 | { 296 | "alphabet": "base58", 297 | "hex": "7e7bba7b68bb8e95827f", 298 | "string": "879o2ATGnmYyAW" 299 | }, 300 | { 301 | "alphabet": "base58", 302 | "hex": "db9c13f5ba7654b01407fb", 303 | "string": "wTYfxjDVbiks874" 304 | }, 305 | { 306 | "alphabet": "base58", 307 | "hex": "6186449d20f5fd1e6c4393", 308 | "string": "RBeiWhzZNL6VtMG" 309 | }, 310 | { 311 | "alphabet": "base58", 312 | "hex": "5248751cebf4ad1c1a83c3", 313 | "string": "MQSVNnc8ehFCqtW" 314 | }, 315 | { 316 | "alphabet": "base58", 317 | "hex": "32090ef18cd479fc376a74", 318 | "string": "DQdu351ExDaeYeX" 319 | }, 320 | { 321 | "alphabet": "base58", 322 | "hex": "7cfa5d6ed1e467d986c426", 323 | "string": "XzW67T5qfEnFcaZ" 324 | }, 325 | { 326 | "alphabet": "base58", 327 | "hex": "9d8707723c7ede51103b6d", 328 | "string": "g4eTCg6QJnB1UU4" 329 | }, 330 | { 331 | "alphabet": "base58", 332 | "hex": "6f4d1e392d6a9b4ed8b223", 333 | "string": "Ubo7kZY5aDpAJp2" 334 | }, 335 | { 336 | "alphabet": "base58", 337 | "hex": "38057d98797cd39f80a0c9", 338 | "string": "EtjQ2feamJvuqse" 339 | }, 340 | { 341 | "alphabet": "base58", 342 | "hex": "de7e59903177e20880e915", 343 | "string": "xB2N7yRBnDYEoT2" 344 | }, 345 | { 346 | "alphabet": "base58", 347 | "hex": "b2ea24a28bc4a60b5c4b8d", 348 | "string": "mNFMpJ2P3TGYqhv" 349 | }, 350 | { 351 | "alphabet": "base58", 352 | "hex": "cf84938958589b6ffba6114d", 353 | "string": "4v8ZbsGh2ePz5sipt" 354 | }, 355 | { 356 | "alphabet": "base58", 357 | "hex": "dee13be7b8d8a08c94a3c02a", 358 | "string": "5CwmE9jQqwtHkTF45" 359 | }, 360 | { 361 | "alphabet": "base58", 362 | "hex": "14cb9c6b3f8cd2e02710f569", 363 | "string": "Pm85JHVAAdeUdxtp" 364 | }, 365 | { 366 | "alphabet": "base58", 367 | "hex": "ca3f2d558266bdcc44c79cb5", 368 | "string": "4pMwomBAQHuUnoLUC" 369 | }, 370 | { 371 | "alphabet": "base58", 372 | "hex": "c031215be44cbad745f38982", 373 | "string": "4dMeTrcxiVw9RWvj3" 374 | }, 375 | { 376 | "alphabet": "base58", 377 | "hex": "1435ab1dbc403111946270a5", 378 | "string": "P7wX3sCWNrbqhBEC" 379 | }, 380 | { 381 | "alphabet": "base58", 382 | "hex": "d8c6e4d775e7a66a0d0f9f41", 383 | "string": "56GLoRDGWGuGJJwPN" 384 | }, 385 | { 386 | "alphabet": "base58", 387 | "hex": "dcee35e74f0fd74176fce2f4", 388 | "string": "5Ap1zyuYiJJFwWcMR" 389 | }, 390 | { 391 | "alphabet": "base58", 392 | "hex": "bfcc0ca4b4855d1cf8993fc0", 393 | "string": "4cvafQW4PEhARKv9D" 394 | }, 395 | { 396 | "alphabet": "base58", 397 | "hex": "e02a3ac25ece7b54584b670a", 398 | "string": "5EMM28xkpxZ1kkVUM" 399 | }, 400 | { 401 | "alphabet": "base58", 402 | "hex": "fe4d938fc3719f064cabb4bfff", 403 | "string": "NBXKkbHwrAsiWTLAk6" 404 | }, 405 | { 406 | "alphabet": "base58", 407 | "hex": "9289cb4f6b15c57e6086b87ea5", 408 | "string": "DCvDpjEXEbHjZqskKv" 409 | }, 410 | { 411 | "alphabet": "base58", 412 | "hex": "fc266f35626b3612bfe978537b", 413 | "string": "N186PVoBWrNre35BGE" 414 | }, 415 | { 416 | "alphabet": "base58", 417 | "hex": "33ff08c06d92502bf258c07166", 418 | "string": "5LC4SoW6jmTtbkbePw" 419 | }, 420 | { 421 | "alphabet": "base58", 422 | "hex": "6a81cac1f3666bc59dc67b1c3c", 423 | "string": "9sXgUySUzwiqDU5WHy" 424 | }, 425 | { 426 | "alphabet": "base58", 427 | "hex": "9dfb8e7e744c544c0f323ea729", 428 | "string": "EACsmGmkgcwsrPFzLg" 429 | }, 430 | { 431 | "alphabet": "base58", 432 | "hex": "1e7a1e284f70838b38442b682b", 433 | "string": "3YEVk9bE7rw5qExMkv" 434 | }, 435 | { 436 | "alphabet": "base58", 437 | "hex": "2a862ad57901a8235f5dc74eaf", 438 | "string": "4YS259nuTLfeXa5Wuc" 439 | }, 440 | { 441 | "alphabet": "base58", 442 | "hex": "74c82096baef21f9d3089e5462", 443 | "string": "AjAcKEhUfrqm8smvM7" 444 | }, 445 | { 446 | "alphabet": "base58", 447 | "hex": "7a3edbc23d7b600263920261cc", 448 | "string": "BBZXyRgey5S5DDZkcK" 449 | }, 450 | { 451 | "alphabet": "base58", 452 | "hex": "20435664c357d25a9c8df751cf4f", 453 | "string": "CrwNL6Fbv4pbRx1zd9g" 454 | }, 455 | { 456 | "alphabet": "base58", 457 | "hex": "51a7aa87cf5cb1c12d045ec3422d", 458 | "string": "X27NHGgKXmGzzQvDtpC" 459 | }, 460 | { 461 | "alphabet": "base58", 462 | "hex": "344d2e116aa26f1062a2cb6ebbef", 463 | "string": "LEDLDvL1Hg4qt1efVXt" 464 | }, 465 | { 466 | "alphabet": "base58", 467 | "hex": "6941add7be4c0b5c7163e4928f8e", 468 | "string": "fhMyN6gwoxE3uYraVzV" 469 | }, 470 | { 471 | "alphabet": "base58", 472 | "hex": "10938fcbb7c4ab991649734a14bf", 473 | "string": "76TPrSDxzGQfSzMu974" 474 | }, 475 | { 476 | "alphabet": "base58", 477 | "hex": "eafe04d944ba504e9af9117b07de", 478 | "string": "2VPgov563ryfe4L2Bj6M" 479 | }, 480 | { 481 | "alphabet": "base58", 482 | "hex": "58d0aeed4d35da20b6f052127edf", 483 | "string": "ZenZhXF9YwP8nQvNtNz" 484 | }, 485 | { 486 | "alphabet": "base58", 487 | "hex": "d734984e2f5aecf25f7a3e353f8a", 488 | "string": "2N7n3jFsTdyN49Faoq6h" 489 | }, 490 | { 491 | "alphabet": "base58", 492 | "hex": "57d873fdb405b7daf4bafa62068a", 493 | "string": "ZJ7NwoP4wHvwyZg3Wjs" 494 | }, 495 | { 496 | "alphabet": "base58", 497 | "hex": "bda4ec7b40d0d65ca95dec4c4d3b", 498 | "string": "2CijxjsNyvqTwPCfDcpA" 499 | }, 500 | { 501 | "alphabet": "base58", 502 | "hex": "826c4abdceb1b91f0d4ad665f86d2e", 503 | "string": "4edfvuDQu9KzVxLuXHfMo" 504 | }, 505 | { 506 | "alphabet": "base58", 507 | "hex": "e7ecb35d07e65b960cb10574a4f51a", 508 | "string": "7VLRYdB4cToipp2J2p3v9" 509 | }, 510 | { 511 | "alphabet": "base58", 512 | "hex": "4f2d72ead87b31d6869fba39eac6dc", 513 | "string": "3DUjqJRcfdWhpsrLrGcQs" 514 | }, 515 | { 516 | "alphabet": "base58", 517 | "hex": "8b4f5788d60030950d5dfbf94c585d", 518 | "string": "4u44JSRH5jP5X39YhPsmE" 519 | }, 520 | { 521 | "alphabet": "base58", 522 | "hex": "ee4c0a0025d1a74ace9fe349355cc5", 523 | "string": "7fgACjABRQUGUEpN6VBBA" 524 | }, 525 | { 526 | "alphabet": "base58", 527 | "hex": "58ac05b9a0b4b66083ff1d489b8d84", 528 | "string": "3UtJPyTwGXapcxHx8Rom5" 529 | }, 530 | { 531 | "alphabet": "base58", 532 | "hex": "1aa35c05e1132e8e049aafaef035d8", 533 | "string": "kE2eSU7gM2619pT82iGP" 534 | }, 535 | { 536 | "alphabet": "base58", 537 | "hex": "771b0c28608484562a292e5d5d2b30", 538 | "string": "4LGYeWhyfrjUByibUqdVR" 539 | }, 540 | { 541 | "alphabet": "base58", 542 | "hex": "78ff9a0e56f9e88dc1cd654b40d019", 543 | "string": "4PLggs66qAdbmZgkaPihe" 544 | }, 545 | { 546 | "alphabet": "base58", 547 | "hex": "6d691bdd736346aa5a0a95b373b2ab", 548 | "string": "44Y6qTgSvRMkdqpQ5ufkN" 549 | }, 550 | { 551 | "alphabet": "base45", 552 | "hex": "d71c91867aa08cc96b361f29", 553 | "string": "5AKG9ZNTTK/JJP2/$D" 554 | }, 555 | { 556 | "alphabet": "base45", 557 | "hex": "e70558f0d75915b457b3e356", 558 | "string": "5R%/+F5SZ6LLW+J60D" 559 | }, 560 | { 561 | "alphabet": "base45", 562 | "hex": "821b8ac934dca0a7627683b7", 563 | "string": "37ILC1NGCLFRNPB2QK" 564 | }, 565 | { 566 | "alphabet": "base45", 567 | "hex": "925e5d9120eb85e3f47de98c", 568 | "string": "3P9IPF01 DWZ5TA/$W" 569 | }, 570 | { 571 | "alphabet": "base45", 572 | "hex": "55543281883fa0e4b67ebfb4", 573 | "string": "23H+2M%XIGQ32UJ/YO" 574 | }, 575 | { 576 | "alphabet": "base45", 577 | "hex": "d419c3781964d6873931178c", 578 | "string": "5772LW:CX0%LBPD.OZ" 579 | }, 580 | { 581 | "alphabet": "base45", 582 | "hex": "c6d10718e79b1171fb71af6f", 583 | "string": "4$RXK2XNP WOWGL0 E" 584 | }, 585 | { 586 | "alphabet": "base45", 587 | "hex": "f67db5bed2d32445f7da2c44", 588 | "string": "5:Z/BRXGU-0NV3G3TU" 589 | }, 590 | { 591 | "alphabet": "base45", 592 | "hex": "7496e6d6346165fd2deb88ec", 593 | "string": "2$RSPPBNCP5-RE3S*K" 594 | }, 595 | { 596 | "alphabet": "base45", 597 | "hex": "df10b11c5f90c825285c72f9", 598 | "string": "5J75BHERI1$04F/LS5" 599 | }, 600 | { 601 | "alphabet": "base45", 602 | "hex": "2174a83619bb46aacca7beec", 603 | "string": " R%/+F5SZ6LLW+J60" 604 | }, 605 | { 606 | "alphabet": "base45", 607 | "hex": "05e9b0213ba6e5e9fc62aa2906", 608 | "string": " /+F5SZ6LLW+ " 609 | }, 610 | { 611 | "alphabet": "base45", 612 | "hex": "7496e6d6346165fd2debc7bd", 613 | "string": "2$RSPPBNCP5-RE3 " 614 | } 615 | ], 616 | "invalid": [ 617 | { 618 | "alphabet": "base58", 619 | "description": "non-base58 string", 620 | "exception": "^TypeError: Expected String$", 621 | "string": {} 622 | }, 623 | { 624 | "alphabet": "base58", 625 | "description": "non-base58 string", 626 | "exception": "^Error: Non-base58 character$", 627 | "string": "#####" 628 | }, 629 | { 630 | "alphabet": "base58", 631 | "description": "non-base58 string", 632 | "exception": "^Error: Non-base58 character$", 633 | "string": "invalid" 634 | }, 635 | { 636 | "alphabet": "base58", 637 | "description": "non-base58 alphabet", 638 | "exception": "^Error: Non-base58 character$", 639 | "string": "c2F0b3NoaQo=" 640 | }, 641 | { 642 | "alphabet": "base58", 643 | "description": "leading whitespace is not base58 character", 644 | "exception": "^Error: Non-base58 character$", 645 | "string": " 1111111111" 646 | }, 647 | { 648 | "alphabet": "base58", 649 | "description": "trailing whitespace is not base58 character", 650 | "exception": "^Error: Non-base58 character$", 651 | "string": "1111111111 " 652 | }, 653 | { 654 | "alphabet": "base58", 655 | "description": "unexpected character after whitespace", 656 | "exception": "^Error: Non-base58 character$", 657 | "string": " \t\n\u000b\f\r skip \r\f\u000b\n\t a" 658 | }, 659 | { 660 | "alphabet": "0123456789fabcdef", 661 | "description": "poorly formed alphabet", 662 | "exception": "^TypeError: f is ambiguous$" 663 | }, 664 | { 665 | "alphabet": "base58", 666 | "description": "character whose code exceeds the highest index of base map (>=256)", 667 | "exception": "^Error: Non-base58 character$", 668 | "string": "\u1000" 669 | } 670 | ] 671 | } 672 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | import basex from '../src/esm/index.js' 2 | import tape from 'tape' 3 | import fixtures from './fixtures.json' assert { type: 'json' } 4 | 5 | const { alphabets, invalid, valid } = fixtures 6 | 7 | const uint8ArrayToHexString = (uint8) => { 8 | return Array.from(uint8).reduce((acc, curr) => `${acc}${curr.toString(16).padStart(2, '0')}`, '') 9 | } 10 | 11 | const uint8ArrayFromHexString = (string) => { 12 | if (!string.length) { 13 | return new Uint8Array(0) 14 | } 15 | 16 | if (string.length % 2 !== 0) { 17 | throw new Error('Invalid hex string') 18 | } 19 | 20 | return new Uint8Array(string.match(/.{1,2}/g).map(byte => parseInt(byte, 16))) 21 | } 22 | 23 | const bases = Object.keys(alphabets).reduce(function (bases, alphabetName) { 24 | bases[alphabetName] = basex(alphabets[alphabetName]) 25 | return bases 26 | }, {}) 27 | 28 | valid.forEach(function (f) { 29 | tape('can encode ' + f.alphabet + ': ' + f.hex, function (t) { 30 | const base = bases[f.alphabet] 31 | const actual = base.encode(uint8ArrayFromHexString(f.hex)) 32 | 33 | t.plan(1) 34 | t.same(actual, f.string) 35 | }) 36 | }) 37 | 38 | valid.forEach(function (f) { 39 | tape('can decode ' + f.alphabet + ': ' + f.string, function (t) { 40 | const base = bases[f.alphabet] 41 | const actual = uint8ArrayToHexString(base.decode(f.string)) 42 | 43 | t.plan(1) 44 | t.same(actual, f.hex) 45 | }) 46 | }) 47 | 48 | invalid.forEach(function (f) { 49 | tape('decode throws on ' + f.description, function (t) { 50 | let base = bases[f.alphabet] 51 | 52 | t.plan(1) 53 | t.throws(function () { 54 | if (!base) base = basex(f.alphabet) 55 | 56 | base.decode(f.string) 57 | }, new RegExp(f.exception)) 58 | }) 59 | }) 60 | 61 | tape('decode should return Uint8Array', function (t) { 62 | t.plan(2) 63 | t.true(bases.base2.decode('') instanceof Uint8Array) 64 | t.true(bases.base2.decode('01') instanceof Uint8Array) 65 | }) 66 | 67 | tape('encode throws on string', function (t) { 68 | const base = bases.base58 69 | 70 | t.plan(1) 71 | t.throws(function () { 72 | base.encode('a') 73 | }, /^TypeError: Expected Uint8Array$/) 74 | }) 75 | 76 | tape('encode not throw on Array or Uint8Array', function (t) { 77 | const base = bases.base58 78 | 79 | t.plan(2) 80 | t.same(base.encode([42, 12, 34]), 'F89f') 81 | t.same(base.encode(new Uint8Array([42, 12, 34])), 'F89f') 82 | }) 83 | -------------------------------------------------------------------------------- /ts_src/index.ts: -------------------------------------------------------------------------------- 1 | // base-x encoding / decoding 2 | // Copyright (c) 2018 base-x contributors 3 | // Copyright (c) 2014-2018 The Bitcoin Core developers (base58.cpp) 4 | // Distributed under the MIT software license, see the accompanying 5 | // file LICENSE or http://www.opensource.org/licenses/mit-license.php. 6 | 7 | function base (ALPHABET: string): base.BaseConverter { 8 | if (ALPHABET.length >= 255) throw new TypeError('Alphabet too long') 9 | 10 | const BASE_MAP = new Uint8Array(256) 11 | for (let j = 0; j < BASE_MAP.length; j++) { 12 | BASE_MAP[j] = 255 13 | } 14 | 15 | for (let i = 0; i < ALPHABET.length; i++) { 16 | const x = ALPHABET.charAt(i) 17 | const xc = x.charCodeAt(0) 18 | 19 | if (BASE_MAP[xc] !== 255) throw new TypeError(x + ' is ambiguous') 20 | BASE_MAP[xc] = i 21 | } 22 | 23 | const BASE = ALPHABET.length 24 | const LEADER = ALPHABET.charAt(0) 25 | const FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up 26 | const iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up 27 | 28 | function encode (source: Uint8Array | number[]): string { 29 | //eslint-disable-next-line no-empty 30 | if (source instanceof Uint8Array) {} 31 | else if (ArrayBuffer.isView(source)) { 32 | source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength) 33 | } else if (Array.isArray(source)) { 34 | source = Uint8Array.from(source) 35 | } 36 | if (!(source instanceof Uint8Array)) throw new TypeError('Expected Uint8Array') 37 | if (source.length === 0) return '' 38 | 39 | // Skip & count leading zeroes. 40 | let zeroes = 0 41 | let length = 0 42 | let pbegin = 0 43 | const pend = source.length 44 | 45 | while (pbegin !== pend && source[pbegin] === 0) { 46 | pbegin++ 47 | zeroes++ 48 | } 49 | 50 | // Allocate enough space in big-endian base58 representation. 51 | const size = ((pend - pbegin) * iFACTOR + 1) >>> 0 52 | const b58 = new Uint8Array(size) 53 | 54 | // Process the bytes. 55 | while (pbegin !== pend) { 56 | let carry = source[pbegin] 57 | 58 | // Apply "b58 = b58 * 256 + ch". 59 | let i = 0 60 | for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { 61 | carry += (256 * b58[it1]) >>> 0 62 | b58[it1] = (carry % BASE) >>> 0 63 | carry = (carry / BASE) >>> 0 64 | } 65 | 66 | if (carry !== 0) throw new Error('Non-zero carry') 67 | length = i 68 | pbegin++ 69 | } 70 | 71 | // Skip leading zeroes in base58 result. 72 | let it2 = size - length 73 | while (it2 !== size && b58[it2] === 0) { 74 | it2++ 75 | } 76 | 77 | // Translate the result into a string. 78 | let str = LEADER.repeat(zeroes) 79 | for (; it2 < size; ++it2) str += ALPHABET.charAt(b58[it2]) 80 | 81 | return str 82 | } 83 | 84 | function decodeUnsafe (source: string): Uint8Array | undefined { 85 | if (typeof source !== 'string') throw new TypeError('Expected String') 86 | if (source.length === 0) return new Uint8Array() 87 | 88 | let psz = 0 89 | 90 | // Skip and count leading '1's. 91 | let zeroes = 0 92 | let length = 0 93 | while (source[psz] === LEADER) { 94 | zeroes++ 95 | psz++ 96 | } 97 | 98 | // Allocate enough space in big-endian base256 representation. 99 | const size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up. 100 | const b256 = new Uint8Array(size) 101 | 102 | // Process the characters. 103 | while (psz < source.length) { 104 | // Find code of next character 105 | const charCode = source.charCodeAt(psz) 106 | 107 | // Base map can not be indexed using char code 108 | if (charCode > 255) return 109 | 110 | // Decode character 111 | let carry = BASE_MAP[charCode] 112 | 113 | // Invalid character 114 | if (carry === 255) return 115 | 116 | let i = 0 117 | for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { 118 | carry += (BASE * b256[it3]) >>> 0 119 | b256[it3] = (carry % 256) >>> 0 120 | carry = (carry / 256) >>> 0 121 | } 122 | 123 | if (carry !== 0) throw new Error('Non-zero carry') 124 | length = i 125 | psz++ 126 | } 127 | 128 | // Skip leading zeroes in b256. 129 | let it4 = size - length 130 | while (it4 !== size && b256[it4] === 0) { 131 | it4++ 132 | } 133 | 134 | const vch = new Uint8Array(zeroes + (size - it4)) 135 | 136 | let j = zeroes 137 | while (it4 !== size) { 138 | vch[j++] = b256[it4++] 139 | } 140 | 141 | return vch 142 | } 143 | 144 | function decode (string: string): Uint8Array { 145 | const buffer = decodeUnsafe(string) 146 | if (buffer) return buffer 147 | 148 | throw new Error('Non-base' + BASE + ' character') 149 | } 150 | 151 | return { 152 | encode: encode, 153 | decodeUnsafe: decodeUnsafe, 154 | decode: decode 155 | } 156 | } 157 | 158 | export default base; 159 | 160 | declare namespace base { 161 | interface BaseConverter { 162 | encode(buffer: Uint8Array | number[]): string; 163 | decodeUnsafe(string: string): Uint8Array | undefined; 164 | decode(string: string): Uint8Array; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "outDir": "./src", 6 | "rootDir": "./ts_src", 7 | "types": ["node"], 8 | "allowJs": false, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "strictFunctionTypes": true, 13 | "strictBindCallApply": true, 14 | "strictPropertyInitialization": true, 15 | "noImplicitThis": true, 16 | "alwaysStrict": true, 17 | "esModuleInterop": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true 20 | }, 21 | "include": ["ts_src/**/*.ts"], 22 | "exclude": ["node_modules/**/*"] 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "emitDeclarationOnly": false, 6 | "outDir": "src/cjs", 7 | "module": "commonjs" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "src/esm", 5 | "esModuleInterop": true, 6 | "resolveJsonModule": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node" 9 | }, 10 | } 11 | --------------------------------------------------------------------------------