]>
Commit | Line | Data |
---|---|---|
ac537983 IC |
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 | // @ts-ignore | |
8 | const _Buffer = require('safe-buffer').Buffer; | |
9 | ||
10 | function base (ALPHABET: string): base.BaseConverter { | |
11 | if (ALPHABET.length >= 255) throw new TypeError('Alphabet too long') | |
12 | ||
13 | const BASE_MAP = new Uint8Array(256) | |
14 | BASE_MAP.fill(255) | |
15 | ||
16 | for (let i = 0; i < ALPHABET.length; i++) { | |
17 | const x = ALPHABET.charAt(i) | |
18 | const xc = x.charCodeAt(0) | |
19 | ||
20 | if (BASE_MAP[xc] !== 255) throw new TypeError(x + ' is ambiguous') | |
21 | BASE_MAP[xc] = i | |
22 | } | |
23 | ||
24 | const BASE = ALPHABET.length | |
25 | const LEADER = ALPHABET.charAt(0) | |
26 | const FACTOR = Math.log(BASE) / Math.log(256) // log(BASE) / log(256), rounded up | |
27 | const iFACTOR = Math.log(256) / Math.log(BASE) // log(256) / log(BASE), rounded up | |
28 | ||
29 | function encode (source: Buffer): string { | |
30 | if (!_Buffer.isBuffer(source)) throw new TypeError('Expected Buffer') | |
31 | if (source.length === 0) return '' | |
32 | ||
33 | // Skip & count leading zeroes. | |
34 | let zeroes = 0 | |
35 | let length = 0 | |
36 | let pbegin = 0 | |
37 | const pend = source.length | |
38 | ||
39 | while (pbegin !== pend && source[pbegin] === 0) { | |
40 | pbegin++ | |
41 | zeroes++ | |
42 | } | |
43 | ||
44 | // Allocate enough space in big-endian base58 representation. | |
45 | const size = ((pend - pbegin) * iFACTOR + 1) >>> 0 | |
46 | const b58 = new Uint8Array(size) | |
47 | ||
48 | // Process the bytes. | |
49 | while (pbegin !== pend) { | |
50 | let carry = source[pbegin] | |
51 | ||
52 | // Apply "b58 = b58 * 256 + ch". | |
53 | let i = 0 | |
54 | for (let it1 = size - 1; (carry !== 0 || i < length) && (it1 !== -1); it1--, i++) { | |
55 | carry += (256 * b58[it1]) >>> 0 | |
56 | b58[it1] = (carry % BASE) >>> 0 | |
57 | carry = (carry / BASE) >>> 0 | |
58 | } | |
59 | ||
60 | if (carry !== 0) throw new Error('Non-zero carry') | |
61 | length = i | |
62 | pbegin++ | |
63 | } | |
64 | ||
65 | // Skip leading zeroes in base58 result. | |
66 | let it2 = size - length | |
67 | while (it2 !== size && b58[it2] === 0) { | |
68 | it2++ | |
69 | } | |
70 | ||
71 | // Translate the result into a string. | |
72 | let str = LEADER.repeat(zeroes) | |
73 | for (; it2 < size; ++it2) str += ALPHABET.charAt(b58[it2]) | |
74 | ||
75 | return str | |
76 | } | |
77 | ||
78 | function decodeUnsafe (source: string): Buffer | undefined { | |
79 | if (typeof source !== 'string') throw new TypeError('Expected String') | |
80 | if (source.length === 0) return _Buffer.alloc(0) | |
81 | ||
82 | let psz = 0 | |
83 | ||
84 | // Skip leading spaces. | |
85 | if (source[psz] === ' ') return | |
86 | ||
87 | // Skip and count leading '1's. | |
88 | let zeroes = 0 | |
89 | let length = 0 | |
90 | while (source[psz] === LEADER) { | |
91 | zeroes++ | |
92 | psz++ | |
93 | } | |
94 | ||
95 | // Allocate enough space in big-endian base256 representation. | |
96 | const size = (((source.length - psz) * FACTOR) + 1) >>> 0 // log(58) / log(256), rounded up. | |
97 | const b256 = new Uint8Array(size) | |
98 | ||
99 | // Process the characters. | |
100 | while (source[psz]) { | |
101 | // Decode character | |
102 | let carry = BASE_MAP[source.charCodeAt(psz)] | |
103 | ||
104 | // Invalid character | |
105 | if (carry === 255) return | |
106 | ||
107 | let i = 0 | |
108 | for (let it3 = size - 1; (carry !== 0 || i < length) && (it3 !== -1); it3--, i++) { | |
109 | carry += (BASE * b256[it3]) >>> 0 | |
110 | b256[it3] = (carry % 256) >>> 0 | |
111 | carry = (carry / 256) >>> 0 | |
112 | } | |
113 | ||
114 | if (carry !== 0) throw new Error('Non-zero carry') | |
115 | length = i | |
116 | psz++ | |
117 | } | |
118 | ||
119 | // Skip trailing spaces. | |
120 | if (source[psz] === ' ') return | |
121 | ||
122 | // Skip leading zeroes in b256. | |
123 | let it4 = size - length | |
124 | while (it4 !== size && b256[it4] === 0) { | |
125 | it4++ | |
126 | } | |
127 | ||
128 | const vch = _Buffer.allocUnsafe(zeroes + (size - it4)) | |
129 | vch.fill(0x00, 0, zeroes) | |
130 | ||
131 | let j = zeroes | |
132 | while (it4 !== size) { | |
133 | vch[j++] = b256[it4++] | |
134 | } | |
135 | ||
136 | return vch | |
137 | } | |
138 | ||
139 | function decode (string: string): Buffer { | |
140 | const buffer = decodeUnsafe(string) | |
141 | if (buffer) return buffer | |
142 | ||
143 | throw new Error('Non-base' + BASE + ' character') | |
144 | } | |
145 | ||
146 | return { | |
147 | encode: encode, | |
148 | decodeUnsafe: decodeUnsafe, | |
149 | decode: decode | |
150 | } | |
151 | } | |
152 | ||
153 | export = base; | |
154 | ||
155 | declare namespace base { | |
156 | interface BaseConverter { | |
157 | encode(buffer: Buffer): string; | |
158 | decodeUnsafe(string: string): Buffer | undefined; | |
159 | decode(string: string): Buffer; | |
160 | } | |
161 | } |