aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/@exodus/bytes/base58.js
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-13 21:34:48 -0800
commit76cb9c2a39d477a64824a985ade40507e3bbade1 (patch)
tree41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/@exodus/bytes/base58.js
parent819a39a21ac992b1393244a4c283bbb125208c69 (diff)
downloadneko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2
neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/@exodus/bytes/base58.js')
-rw-r--r--vanilla/node_modules/@exodus/bytes/base58.js220
1 files changed, 220 insertions, 0 deletions
diff --git a/vanilla/node_modules/@exodus/bytes/base58.js b/vanilla/node_modules/@exodus/bytes/base58.js
new file mode 100644
index 0000000..c032aba
--- /dev/null
+++ b/vanilla/node_modules/@exodus/bytes/base58.js
@@ -0,0 +1,220 @@
+import { typedView } from './array.js'
+import { assertU8, E_STRING } from './fallback/_utils.js'
+import { nativeDecoder, nativeEncoder, isHermes } from './fallback/platform.js'
+import { encodeAscii, decodeAscii } from './fallback/latin1.js'
+
+const alphabet58 = [...'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz']
+const alphabetXRP = [...'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz']
+const codes58 = new Uint8Array(alphabet58.map((x) => x.charCodeAt(0)))
+const codesXRP = new Uint8Array(alphabetXRP.map((x) => x.charCodeAt(0)))
+
+const _0n = BigInt(0)
+const _1n = BigInt(1)
+const _8n = BigInt(8)
+const _32n = BigInt(32)
+const _58n = BigInt(58)
+const _0xffffffffn = BigInt(0xff_ff_ff_ff)
+
+let table // 15 * 82, diagonal, <1kb
+const fromMaps = new Map()
+
+const E_CHAR = 'Invalid character in base58 input'
+
+const shouldUseBigIntFrom = isHermes // faster only on Hermes, numbers path beats it on normal engines
+
+function toBase58core(arr, alphabet, codes) {
+ assertU8(arr)
+ const length = arr.length
+ if (length === 0) return ''
+
+ const ZERO = alphabet[0]
+ let zeros = 0
+ while (zeros < length && arr[zeros] === 0) zeros++
+
+ if (length > 60) {
+ // Slow path. Can be optimized ~10%, but the main factor is /58n division anyway, so doesn't matter much
+ let x = _0n
+ for (let i = 0; i < arr.length; i++) x = (x << _8n) | BigInt(arr[i])
+
+ let out = ''
+ while (x) {
+ const d = x / _58n
+ out = alphabet[Number(x - _58n * d)] + out
+ x = d
+ }
+
+ return ZERO.repeat(zeros) + out
+ }
+
+ // We run fast mode operations only on short (<=60 bytes) inputs, via precomputation table
+ if (!table) {
+ table = []
+ let x = _1n
+ for (let i = 0; i < 15; i++) {
+ // Convert x to base 58 digits
+ const in58 = []
+ let y = x
+ while (y) {
+ const d = y / _58n
+ in58.push(Number(y - _58n * d))
+ y = d
+ }
+
+ table.push(new Uint8Array(in58))
+ x <<= _32n
+ }
+ }
+
+ const res = []
+ {
+ let j = 0
+ // We group each 4 bytes into 32-bit chunks
+ // Not using u32arr to not deal with remainder + BE/LE differences
+ for (let i = length - 1; i >= 0; i -= 4) {
+ let c
+ if (i > 2) {
+ c = (arr[i] | (arr[i - 1] << 8) | (arr[i - 2] << 16) | (arr[i - 3] << 24)) >>> 0
+ } else if (i > 1) {
+ c = arr[i] | (arr[i - 1] << 8) | (arr[i - 2] << 16)
+ } else {
+ c = i === 1 ? arr[i] | (arr[i - 1] << 8) : arr[i]
+ }
+
+ const row = table[j++]
+ if (c === 0) continue
+ const olen = res.length
+ const nlen = row.length
+ let k = 0
+ for (; k < olen; k++) res[k] += c * row[k]
+ while (k < nlen) res.push(c * row[k++])
+ }
+ }
+
+ // We can now do a single scan over regular numbers under MAX_SAFE_INTEGER
+ // Note: can't use int32 operations on them, as they are outside of 2**32 range
+ // This is faster though
+ {
+ let carry = 0
+ let i = 0
+ while (i < res.length) {
+ const c = res[i] + carry
+ carry = Math.floor(c / 58)
+ res[i++] = c - carry * 58
+ }
+
+ while (carry) {
+ const c = carry
+ carry = Math.floor(c / 58)
+ res.push(c - carry * 58)
+ }
+ }
+
+ if (nativeDecoder) {
+ const oa = new Uint8Array(res.length)
+ let j = 0
+ for (let i = res.length - 1; i >= 0; i--) oa[j++] = codes[res[i]]
+ return ZERO.repeat(zeros) + decodeAscii(oa)
+ }
+
+ let out = ''
+ for (let i = res.length - 1; i >= 0; i--) out += alphabet[res[i]]
+ return ZERO.repeat(zeros) + out
+}
+
+function fromBase58core(str, alphabet, codes, format = 'uint8') {
+ if (typeof str !== 'string') throw new TypeError(E_STRING)
+ const length = str.length
+ if (length === 0) return typedView(new Uint8Array(), format)
+
+ const zeroC = codes[0]
+ let zeros = 0
+ while (zeros < length && str.charCodeAt(zeros) === zeroC) zeros++
+
+ let fromMap = fromMaps.get(alphabet)
+ if (!fromMap) {
+ fromMap = new Int8Array(256).fill(-1)
+ for (let i = 0; i < 58; i++) fromMap[alphabet[i].charCodeAt(0)] = i
+ fromMaps.set(alphabet, fromMap)
+ }
+
+ const size = zeros + (((length - zeros + 1) * 3) >> 2) // 3/4 rounded up, larger than ~0.73 coef to fit everything
+ const res = new Uint8Array(size)
+ let at = size // where is the first significant byte written
+
+ if (shouldUseBigIntFrom) {
+ let x = _0n
+
+ // nativeEncoder gives a benefit here
+ if (nativeEncoder) {
+ const codes = encodeAscii(str, E_CHAR)
+ for (let i = zeros; i < length; i++) {
+ const c = fromMap[codes[i]]
+ if (c < 0) throw new SyntaxError(E_CHAR)
+ x = x * _58n + BigInt(c)
+ }
+ } else {
+ for (let i = zeros; i < length; i++) {
+ const charCode = str.charCodeAt(i)
+ const c = fromMap[charCode]
+ if (charCode > 255 || c < 0) throw new SyntaxError(E_CHAR)
+ x = x * _58n + BigInt(c)
+ }
+ }
+
+ while (x) {
+ let y = Number(x & _0xffffffffn)
+ x >>= 32n
+ res[--at] = y & 0xff
+ y >>>= 8
+ if (!x && !y) break
+ res[--at] = y & 0xff
+ y >>>= 8
+ if (!x && !y) break
+ res[--at] = y & 0xff
+ y >>>= 8
+ if (!x && !y) break
+ res[--at] = y & 0xff
+ }
+ } else {
+ for (let i = zeros; i < length; i++) {
+ const charCode = str.charCodeAt(i)
+ let c = fromMap[charCode]
+ if (charCode > 255 || c < 0) throw new SyntaxError(E_CHAR)
+
+ let k = size - 1
+ for (;;) {
+ if (c === 0 && k < at) break
+ c += 58 * res[k]
+ res[k] = c & 0xff
+ c >>>= 8
+ k--
+ // unroll a bit
+ if (c === 0 && k < at) break
+ c += 58 * res[k]
+ res[k] = c & 0xff
+ c >>>= 8
+ k--
+ if (c === 0 && k < at) break
+ c += 58 * res[k]
+ res[k] = c & 0xff
+ c >>>= 8
+ k--
+ if (c === 0 && k < at) break
+ c += 58 * res[k]
+ res[k] = c & 0xff
+ c >>>= 8
+ k--
+ }
+
+ at = k + 1
+ if (c !== 0 || at < zeros) /* c8 ignore next */ throw new Error('Unexpected') // unreachable
+ }
+ }
+
+ return typedView(res.slice(at - zeros), format) // slice is faster for small sizes than subarray
+}
+
+export const toBase58 = (arr) => toBase58core(arr, alphabet58, codes58)
+export const fromBase58 = (str, format) => fromBase58core(str, alphabet58, codes58, format)
+export const toBase58xrp = (arr) => toBase58core(arr, alphabetXRP, codesXRP)
+export const fromBase58xrp = (str, format) => fromBase58core(str, alphabetXRP, codesXRP, format)