@haneullabs/utils 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @mysten/utils
2
2
 
3
+ ## 0.4.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 3aa65e2: Speed up BCS parsing and serialization: faster ULEB, integer and byte reads and writes,
8
+ enum and option encoding, string decoding, and a single input conversion per write for transformed
9
+ types. Base58, base64 and hex helpers in `@mysten/utils` are faster and no longer depend on
10
+ `@scure/base`. Sui address and object digest encoding is faster.
11
+
12
+ Behaviour changes:
13
+
14
+ - `BcsReader.readBytes` and `readULEB` throw instead of reading past the end of a view into the
15
+ rest of its buffer.
16
+ - `bcs.option(T)` returns `null`/`undefined` when `T` reads as `null`/`undefined` (for example a
17
+ nested option's inner `None`), instead of `true`.
18
+ - `bcs.Address.serializedSize` and `bcs.ObjectDigest.serializedSize` throw on invalid input
19
+ instead of returning a size.
20
+
3
21
  ## 0.4.1
4
22
 
5
23
  ### Patch Changes
package/dist/b58.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  //#region src/b58.d.ts
2
- declare const toBase58: (buffer: Uint8Array) => string;
3
- declare const fromBase58: (str: string) => Uint8Array<ArrayBuffer>;
2
+ declare function toBase58(buffer: Uint8Array): string;
3
+ declare function fromBase58(str: string): Uint8Array<ArrayBuffer>;
4
4
  //#endregion
5
5
  export { fromBase58, toBase58 };
6
6
  //# sourceMappingURL=b58.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"b58.d.mts","names":[],"sources":["../src/b58.ts"],"mappings":";cAKa,WAAQ,QAAY;cACpB,aAAU,gBAA0C,WAAW"}
1
+ {"version":3,"file":"b58.d.mts","names":[],"sources":["../src/b58.ts"],"mappings":";iBAyBgB,SAAS,QAAQ;iBA4DjB,WAAW,cAAc,WAAW"}
package/dist/b58.mjs CHANGED
@@ -1,7 +1,94 @@
1
- import { base58 } from "@scure/base";
2
1
  //#region src/b58.ts
3
- const toBase58 = (buffer) => base58.encode(buffer);
4
- const fromBase58 = (str) => base58.decode(str);
2
+ const ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
3
+ const ALPHABET_CODES = Array.from(ALPHABET, (char) => char.charCodeAt(0));
4
+ const DIGIT_VALUES = (/* @__PURE__ */ new Int8Array(128)).fill(-1);
5
+ for (let i = 0; i < 58; i++) DIGIT_VALUES[ALPHABET.charCodeAt(i)] = i;
6
+ const B58_5 = 58 ** 5;
7
+ function isBytes(value) {
8
+ return value instanceof Uint8Array || ArrayBuffer.isView(value) && value.constructor.name === "Uint8Array" && value.BYTES_PER_ELEMENT === 1;
9
+ }
10
+ function toBase58(buffer) {
11
+ if (!isBytes(buffer)) throw new TypeError("toBase58 expects a Uint8Array");
12
+ const length = buffer.length;
13
+ let zeros = 0;
14
+ while (zeros < length && buffer[zeros] === 0) zeros++;
15
+ const limbs = [];
16
+ let i = zeros;
17
+ if ((length - zeros) % 2 === 1) limbs.push(buffer[i++]);
18
+ for (; i < length; i += 2) {
19
+ let carry = buffer[i] * 256 + buffer[i + 1];
20
+ for (let j = 0; j < limbs.length; j++) {
21
+ const x = limbs[j] * 65536 + carry;
22
+ carry = Math.floor(x / B58_5);
23
+ limbs[j] = x - carry * B58_5;
24
+ }
25
+ while (carry > 0) {
26
+ const next = Math.floor(carry / B58_5);
27
+ limbs.push(carry - next * B58_5);
28
+ carry = next;
29
+ }
30
+ }
31
+ const digits = [];
32
+ for (let k = 0; k < zeros; k++) digits.push(ALPHABET_CODES[0]);
33
+ for (let j = limbs.length - 1; j >= 0; j--) {
34
+ let limb = limbs[j];
35
+ const start = digits.length;
36
+ const width = j === limbs.length - 1 ? 0 : 5;
37
+ for (let k = 0; k < 5 && (limb > 0 || k < width); k++) {
38
+ const next = Math.floor(limb / 58);
39
+ digits.push(ALPHABET_CODES[limb - next * 58]);
40
+ limb = next;
41
+ }
42
+ for (let a = start, b = digits.length - 1; a < b; a++, b--) {
43
+ const tmp = digits[a];
44
+ digits[a] = digits[b];
45
+ digits[b] = tmp;
46
+ }
47
+ }
48
+ let result = "";
49
+ for (let start = 0; start < digits.length; start += 8192) result += String.fromCharCode.apply(null, digits.slice(start, start + 8192));
50
+ return result;
51
+ }
52
+ function fromBase58(str) {
53
+ if (typeof str !== "string") throw new TypeError("fromBase58 expects a string");
54
+ const length = str.length;
55
+ let zeros = 0;
56
+ while (zeros < length && str.charCodeAt(zeros) === 49) zeros++;
57
+ const limbs = [];
58
+ let i = zeros;
59
+ const head = (length - zeros) % 5;
60
+ while (i < length) {
61
+ const chunkLength = i === zeros && head !== 0 ? head : 5;
62
+ let carry = 0;
63
+ let multiplier = 1;
64
+ for (let k = 0; k < chunkLength; k++) {
65
+ const code = str.charCodeAt(i + k);
66
+ const digit = code < 128 ? DIGIT_VALUES[code] : -1;
67
+ if (digit < 0) throw new Error(`Invalid base58 character "${str[i + k]}"`);
68
+ carry = carry * 58 + digit;
69
+ multiplier *= 58;
70
+ }
71
+ i += chunkLength;
72
+ for (let j = 0; j < limbs.length; j++) {
73
+ const x = limbs[j] * multiplier + carry;
74
+ carry = Math.floor(x / 65536);
75
+ limbs[j] = x - carry * 65536;
76
+ }
77
+ while (carry > 0) {
78
+ limbs.push(carry % 65536);
79
+ carry = Math.floor(carry / 65536);
80
+ }
81
+ }
82
+ let byteLength = limbs.length * 2;
83
+ if (byteLength > 0 && limbs[limbs.length - 1] < 256) byteLength--;
84
+ const out = new Uint8Array(zeros + byteLength);
85
+ let pos = out.length - 1;
86
+ for (let j = 0; j < limbs.length; j++) {
87
+ out[pos--] = limbs[j] & 255;
88
+ if (pos >= zeros) out[pos--] = limbs[j] >> 8;
89
+ }
90
+ return out;
91
+ }
5
92
  //#endregion
6
93
  export { fromBase58, toBase58 };
7
94
 
package/dist/b58.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"b58.mjs","names":[],"sources":["../src/b58.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { base58 } from '@scure/base';\n\nexport const toBase58 = (buffer: Uint8Array) => base58.encode(buffer);\nexport const fromBase58 = (str: string) => base58.decode(str) as Uint8Array<ArrayBuffer>;\n"],"mappings":";;AAKA,MAAa,YAAY,WAAuB,OAAO,OAAO,MAAM;AACpE,MAAa,cAAc,QAAgB,OAAO,OAAO,GAAG"}
1
+ {"version":3,"file":"b58.mjs","names":[],"sources":["../src/b58.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nconst ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\nconst ALPHABET_CODES = Array.from(ALPHABET, (char) => char.charCodeAt(0));\nconst DIGIT_VALUES = new Int8Array(128).fill(-1);\nfor (let i = 0; i < ALPHABET.length; i++) {\n\tDIGIT_VALUES[ALPHABET.charCodeAt(i)] = i;\n}\n\n// Base conversion works on limbs so each step stays well inside the 2^53 float-exact range:\n// encoding accumulates base-58^5 limbs from 16-bit input chunks (< 2^46),\n// decoding accumulates base-2^16 limbs from 5-character chunks (< 2^46).\nconst B58_5 = 58 ** 5;\n\n// Same check as @scure/base, which also accepts Uint8Arrays from other realms (iframes, vm)\nfunction isBytes(value: unknown): value is Uint8Array {\n\treturn (\n\t\tvalue instanceof Uint8Array ||\n\t\t(ArrayBuffer.isView(value) &&\n\t\t\tvalue.constructor.name === 'Uint8Array' &&\n\t\t\t(value as Uint8Array).BYTES_PER_ELEMENT === 1)\n\t);\n}\n\nexport function toBase58(buffer: Uint8Array): string {\n\tif (!isBytes(buffer)) {\n\t\tthrow new TypeError('toBase58 expects a Uint8Array');\n\t}\n\n\tconst length = buffer.length;\n\tlet zeros = 0;\n\twhile (zeros < length && buffer[zeros] === 0) {\n\t\tzeros++;\n\t}\n\n\tconst limbs: number[] = [];\n\tlet i = zeros;\n\tif ((length - zeros) % 2 === 1) {\n\t\tlimbs.push(buffer[i++]);\n\t}\n\tfor (; i < length; i += 2) {\n\t\tlet carry = buffer[i] * 256 + buffer[i + 1];\n\t\tfor (let j = 0; j < limbs.length; j++) {\n\t\t\tconst x = limbs[j] * 65536 + carry;\n\t\t\tcarry = Math.floor(x / B58_5);\n\t\t\tlimbs[j] = x - carry * B58_5;\n\t\t}\n\t\twhile (carry > 0) {\n\t\t\tconst next = Math.floor(carry / B58_5);\n\t\t\tlimbs.push(carry - next * B58_5);\n\t\t\tcarry = next;\n\t\t}\n\t}\n\n\t// Each limb holds 5 base58 digits; the most significant limb is written without padding\n\tconst digits: number[] = [];\n\tfor (let k = 0; k < zeros; k++) {\n\t\tdigits.push(ALPHABET_CODES[0]);\n\t}\n\tfor (let j = limbs.length - 1; j >= 0; j--) {\n\t\tlet limb = limbs[j];\n\t\tconst start = digits.length;\n\t\tconst width = j === limbs.length - 1 ? 0 : 5;\n\t\tfor (let k = 0; k < 5 && (limb > 0 || k < width); k++) {\n\t\t\tconst next = Math.floor(limb / 58);\n\t\t\tdigits.push(ALPHABET_CODES[limb - next * 58]);\n\t\t\tlimb = next;\n\t\t}\n\t\t// Digits were produced least-significant first\n\t\tfor (let a = start, b = digits.length - 1; a < b; a++, b--) {\n\t\t\tconst tmp = digits[a];\n\t\t\tdigits[a] = digits[b];\n\t\t\tdigits[b] = tmp;\n\t\t}\n\t}\n\n\t// Chunked so long inputs stay under engine limits on function argument counts\n\tlet result = '';\n\tfor (let start = 0; start < digits.length; start += 8192) {\n\t\tresult += String.fromCharCode.apply(null, digits.slice(start, start + 8192));\n\t}\n\treturn result;\n}\n\nexport function fromBase58(str: string): Uint8Array<ArrayBuffer> {\n\tif (typeof str !== 'string') {\n\t\tthrow new TypeError('fromBase58 expects a string');\n\t}\n\n\tconst length = str.length;\n\tlet zeros = 0;\n\twhile (zeros < length && str.charCodeAt(zeros) === 49 /* '1' */) {\n\t\tzeros++;\n\t}\n\n\tconst limbs: number[] = [];\n\tlet i = zeros;\n\tconst head = (length - zeros) % 5;\n\twhile (i < length) {\n\t\tconst chunkLength = i === zeros && head !== 0 ? head : 5;\n\t\tlet carry = 0;\n\t\tlet multiplier = 1;\n\t\tfor (let k = 0; k < chunkLength; k++) {\n\t\t\tconst code = str.charCodeAt(i + k);\n\t\t\tconst digit = code < 128 ? DIGIT_VALUES[code] : -1;\n\t\t\tif (digit < 0) {\n\t\t\t\tthrow new Error(`Invalid base58 character \"${str[i + k]}\"`);\n\t\t\t}\n\t\t\tcarry = carry * 58 + digit;\n\t\t\tmultiplier *= 58;\n\t\t}\n\t\ti += chunkLength;\n\n\t\tfor (let j = 0; j < limbs.length; j++) {\n\t\t\tconst x = limbs[j] * multiplier + carry;\n\t\t\tcarry = Math.floor(x / 65536);\n\t\t\tlimbs[j] = x - carry * 65536;\n\t\t}\n\t\twhile (carry > 0) {\n\t\t\tlimbs.push(carry % 65536);\n\t\t\tcarry = Math.floor(carry / 65536);\n\t\t}\n\t}\n\n\tlet byteLength = limbs.length * 2;\n\tif (byteLength > 0 && limbs[limbs.length - 1] < 256) {\n\t\tbyteLength--;\n\t}\n\n\tconst out = new Uint8Array(zeros + byteLength);\n\tlet pos = out.length - 1;\n\tfor (let j = 0; j < limbs.length; j++) {\n\t\tout[pos--] = limbs[j] & 0xff;\n\t\tif (pos >= zeros) {\n\t\t\tout[pos--] = limbs[j] >> 8;\n\t\t}\n\t}\n\n\treturn out;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"b64.d.mts","names":[],"sources":["../src/b64.ts"],"mappings":";iBAGgB,WAAW,uBAAuB,WAAW;iBAK7C,SAAS,OAAO"}
1
+ {"version":3,"file":"b64.d.mts","names":[],"sources":["../src/b64.ts"],"mappings":";iBAGgB,WAAW,uBAAuB,WAAW;iBAU7C,SAAS,OAAO"}
package/dist/b64.mjs CHANGED
@@ -1,14 +1,17 @@
1
1
  //#region src/b64.ts
2
2
  function fromBase64(base64String) {
3
- return Uint8Array.from(atob(base64String), (char) => char.charCodeAt(0));
3
+ const binary = atob(base64String);
4
+ const bytes = new Uint8Array(binary.length);
5
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
6
+ return bytes;
4
7
  }
5
8
  const CHUNK_SIZE = 8192;
6
9
  function toBase64(bytes) {
7
- if (bytes.length < CHUNK_SIZE) return btoa(String.fromCharCode(...bytes));
10
+ if (bytes.length <= CHUNK_SIZE) return btoa(String.fromCharCode.apply(null, bytes));
8
11
  let output = "";
9
- for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
12
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
10
13
  const chunk = bytes.slice(i, i + CHUNK_SIZE);
11
- output += String.fromCharCode(...chunk);
14
+ output += String.fromCharCode.apply(null, chunk);
12
15
  }
13
16
  return btoa(output);
14
17
  }
package/dist/b64.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"b64.mjs","names":[],"sources":["../src/b64.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport function fromBase64(base64String: string): Uint8Array<ArrayBuffer> {\n\treturn Uint8Array.from(atob(base64String), (char) => char.charCodeAt(0));\n}\n\nconst CHUNK_SIZE = 8192;\nexport function toBase64(bytes: Uint8Array): string {\n\t// Special-case the simple case for speed's sake.\n\tif (bytes.length < CHUNK_SIZE) {\n\t\treturn btoa(String.fromCharCode(...bytes));\n\t}\n\n\tlet output = '';\n\tfor (var i = 0; i < bytes.length; i += CHUNK_SIZE) {\n\t\tconst chunk = bytes.slice(i, i + CHUNK_SIZE);\n\t\toutput += String.fromCharCode(...chunk);\n\t}\n\n\treturn btoa(output);\n}\n"],"mappings":";AAGA,SAAgB,WAAW,cAA+C;CACzE,OAAO,WAAW,KAAK,KAAK,YAAY,IAAI,SAAS,KAAK,WAAW,CAAC,CAAC;AACxE;AAEA,MAAM,aAAa;AACnB,SAAgB,SAAS,OAA2B;CAEnD,IAAI,MAAM,SAAS,YAClB,OAAO,KAAK,OAAO,aAAa,GAAG,KAAK,CAAC;CAG1C,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,YAAY;EAClD,MAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,UAAU;EAC3C,UAAU,OAAO,aAAa,GAAG,KAAK;CACvC;CAEA,OAAO,KAAK,MAAM;AACnB"}
1
+ {"version":3,"file":"b64.mjs","names":[],"sources":["../src/b64.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport function fromBase64(base64String: string): Uint8Array<ArrayBuffer> {\n\tconst binary = atob(base64String);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i);\n\t}\n\treturn bytes;\n}\n\nconst CHUNK_SIZE = 8192;\nexport function toBase64(bytes: Uint8Array): string {\n\tif (bytes.length <= CHUNK_SIZE) {\n\t\treturn btoa(String.fromCharCode.apply(null, bytes as unknown as number[]));\n\t}\n\n\tlet output = '';\n\tfor (let i = 0; i < bytes.length; i += CHUNK_SIZE) {\n\t\tconst chunk = bytes.slice(i, i + CHUNK_SIZE);\n\t\toutput += String.fromCharCode.apply(null, chunk as unknown as number[]);\n\t}\n\n\treturn btoa(output);\n}\n"],"mappings":";AAGA,SAAgB,WAAW,cAA+C;CACzE,MAAM,SAAS,KAAK,YAAY;CAChC,MAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;CAC1C,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAClC,MAAM,KAAK,OAAO,WAAW,CAAC;CAE/B,OAAO;AACR;AAEA,MAAM,aAAa;AACnB,SAAgB,SAAS,OAA2B;CACnD,IAAI,MAAM,UAAU,YACnB,OAAO,KAAK,OAAO,aAAa,MAAM,MAAM,KAA4B,CAAC;CAG1E,IAAI,SAAS;CACb,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,YAAY;EAClD,MAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,UAAU;EAC3C,UAAU,OAAO,aAAa,MAAM,MAAM,KAA4B;CACvE;CAEA,OAAO,KAAK,MAAM;AACnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"hex.d.mts","names":[],"sources":["../src/hex.ts"],"mappings":";iBAGgB,QAAQ,iBAAiB,WAAW;iBAYpC,MAAM,OAAO"}
1
+ {"version":3,"file":"hex.d.mts","names":[],"sources":["../src/hex.ts"],"mappings":";iBAGgB,QAAQ,iBAAiB,WAAW;iBA0BpC,MAAM,OAAO"}
package/dist/hex.mjs CHANGED
@@ -2,12 +2,26 @@
2
2
  function fromHex(hexStr) {
3
3
  const normalized = hexStr.startsWith("0x") ? hexStr.slice(2) : hexStr;
4
4
  const padded = normalized.length % 2 === 0 ? normalized : `0${normalized}`;
5
- const intArr = padded.match(/[0-9a-fA-F]{2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
6
- if (intArr.length !== padded.length / 2) throw new Error(`Invalid hex string ${hexStr}`);
7
- return Uint8Array.from(intArr);
5
+ const bytes = new Uint8Array(padded.length / 2);
6
+ for (let i = 0; i < bytes.length; i++) {
7
+ const high = hexDigit(padded.charCodeAt(i * 2));
8
+ const low = hexDigit(padded.charCodeAt(i * 2 + 1));
9
+ if (high < 0 || low < 0) throw new Error(`Invalid hex string ${hexStr}`);
10
+ bytes[i] = high * 16 + low;
11
+ }
12
+ return bytes;
8
13
  }
14
+ function hexDigit(code) {
15
+ if (code >= 48 && code <= 57) return code - 48;
16
+ if (code >= 97 && code <= 102) return code - 87;
17
+ if (code >= 65 && code <= 70) return code - 55;
18
+ return -1;
19
+ }
20
+ const HEX_BYTES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
9
21
  function toHex(bytes) {
10
- return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
22
+ let hex = "";
23
+ for (let i = 0; i < bytes.length; i++) hex += HEX_BYTES[bytes[i]];
24
+ return hex;
11
25
  }
12
26
  //#endregion
13
27
  export { fromHex, toHex };
package/dist/hex.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"hex.mjs","names":[],"sources":["../src/hex.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport function fromHex(hexStr: string): Uint8Array<ArrayBuffer> {\n\tconst normalized = hexStr.startsWith('0x') ? hexStr.slice(2) : hexStr;\n\tconst padded = normalized.length % 2 === 0 ? normalized : `0${normalized}`;\n\tconst intArr = padded.match(/[0-9a-fA-F]{2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];\n\n\tif (intArr.length !== padded.length / 2) {\n\t\tthrow new Error(`Invalid hex string ${hexStr}`);\n\t}\n\n\treturn Uint8Array.from(intArr);\n}\n\nexport function toHex(bytes: Uint8Array): string {\n\treturn bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');\n}\n"],"mappings":";AAGA,SAAgB,QAAQ,QAAyC;CAChE,MAAM,aAAa,OAAO,WAAW,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI;CAC/D,MAAM,SAAS,WAAW,SAAS,MAAM,IAAI,aAAa,IAAI;CAC9D,MAAM,SAAS,OAAO,MAAM,iBAAiB,CAAC,EAAE,KAAK,SAAS,SAAS,MAAM,EAAE,CAAC,KAAK,CAAC;CAEtF,IAAI,OAAO,WAAW,OAAO,SAAS,GACrC,MAAM,IAAI,MAAM,sBAAsB,QAAQ;CAG/C,OAAO,WAAW,KAAK,MAAM;AAC9B;AAEA,SAAgB,MAAM,OAA2B;CAChD,OAAO,MAAM,QAAQ,KAAK,SAAS,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAAE;AAChF"}
1
+ {"version":3,"file":"hex.mjs","names":[],"sources":["../src/hex.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nexport function fromHex(hexStr: string): Uint8Array<ArrayBuffer> {\n\tconst normalized = hexStr.startsWith('0x') ? hexStr.slice(2) : hexStr;\n\tconst padded = normalized.length % 2 === 0 ? normalized : `0${normalized}`;\n\tconst bytes = new Uint8Array(padded.length / 2);\n\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tconst high = hexDigit(padded.charCodeAt(i * 2));\n\t\tconst low = hexDigit(padded.charCodeAt(i * 2 + 1));\n\t\tif (high < 0 || low < 0) {\n\t\t\tthrow new Error(`Invalid hex string ${hexStr}`);\n\t\t}\n\t\tbytes[i] = high * 16 + low;\n\t}\n\n\treturn bytes;\n}\n\nfunction hexDigit(code: number): number {\n\tif (code >= 48 && code <= 57) return code - 48;\n\tif (code >= 97 && code <= 102) return code - 87;\n\tif (code >= 65 && code <= 70) return code - 55;\n\treturn -1;\n}\n\nconst HEX_BYTES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));\n\nexport function toHex(bytes: Uint8Array): string {\n\tlet hex = '';\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\thex += HEX_BYTES[bytes[i]];\n\t}\n\treturn hex;\n}\n"],"mappings":";AAGA,SAAgB,QAAQ,QAAyC;CAChE,MAAM,aAAa,OAAO,WAAW,IAAI,IAAI,OAAO,MAAM,CAAC,IAAI;CAC/D,MAAM,SAAS,WAAW,SAAS,MAAM,IAAI,aAAa,IAAI;CAC9D,MAAM,QAAQ,IAAI,WAAW,OAAO,SAAS,CAAC;CAE9C,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACtC,MAAM,OAAO,SAAS,OAAO,WAAW,IAAI,CAAC,CAAC;EAC9C,MAAM,MAAM,SAAS,OAAO,WAAW,IAAI,IAAI,CAAC,CAAC;EACjD,IAAI,OAAO,KAAK,MAAM,GACrB,MAAM,IAAI,MAAM,sBAAsB,QAAQ;EAE/C,MAAM,KAAK,OAAO,KAAK;CACxB;CAEA,OAAO;AACR;AAEA,SAAS,SAAS,MAAsB;CACvC,IAAI,QAAQ,MAAM,QAAQ,IAAI,OAAO,OAAO;CAC5C,IAAI,QAAQ,MAAM,QAAQ,KAAK,OAAO,OAAO;CAC7C,IAAI,QAAQ,MAAM,QAAQ,IAAI,OAAO,OAAO;CAC5C,OAAO;AACR;AAEA,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,IAAI,IAAI,GAAG,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC;AAEvF,SAAgB,MAAM,OAA2B;CAChD,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KACjC,OAAO,UAAU,MAAM;CAExB,OAAO;AACR"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haneullabs/utils",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Shared utilities for @haneullabs/* packages",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Haneul Labs <build@haneul-labs.com>",
@@ -35,12 +35,10 @@
35
35
  "typescript": "^7.0.2",
36
36
  "vitest": "^4.1.11"
37
37
  },
38
- "dependencies": {
39
- "@scure/base": "^2.3.0"
40
- },
41
38
  "scripts": {
42
39
  "clean": "rm -rf tsconfig.tsbuildinfo ./dist",
43
40
  "build": "rm -rf dist && tsc --noEmit && tsdown",
41
+ "test": "vitest run",
44
42
  "prettier:check": "prettier -c --ignore-unknown .",
45
43
  "prettier:fix": "prettier -w --ignore-unknown .",
46
44
  "oxlint:check": "oxlint .",
package/src/b58.ts CHANGED
@@ -1,7 +1,141 @@
1
1
  // Copyright (c) Mysten Labs, Inc.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { base58 } from '@scure/base';
4
+ const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
5
+ const ALPHABET_CODES = Array.from(ALPHABET, (char) => char.charCodeAt(0));
6
+ const DIGIT_VALUES = new Int8Array(128).fill(-1);
7
+ for (let i = 0; i < ALPHABET.length; i++) {
8
+ DIGIT_VALUES[ALPHABET.charCodeAt(i)] = i;
9
+ }
5
10
 
6
- export const toBase58 = (buffer: Uint8Array) => base58.encode(buffer);
7
- export const fromBase58 = (str: string) => base58.decode(str) as Uint8Array<ArrayBuffer>;
11
+ // Base conversion works on limbs so each step stays well inside the 2^53 float-exact range:
12
+ // encoding accumulates base-58^5 limbs from 16-bit input chunks (< 2^46),
13
+ // decoding accumulates base-2^16 limbs from 5-character chunks (< 2^46).
14
+ const B58_5 = 58 ** 5;
15
+
16
+ // Same check as @scure/base, which also accepts Uint8Arrays from other realms (iframes, vm)
17
+ function isBytes(value: unknown): value is Uint8Array {
18
+ return (
19
+ value instanceof Uint8Array ||
20
+ (ArrayBuffer.isView(value) &&
21
+ value.constructor.name === 'Uint8Array' &&
22
+ (value as Uint8Array).BYTES_PER_ELEMENT === 1)
23
+ );
24
+ }
25
+
26
+ export function toBase58(buffer: Uint8Array): string {
27
+ if (!isBytes(buffer)) {
28
+ throw new TypeError('toBase58 expects a Uint8Array');
29
+ }
30
+
31
+ const length = buffer.length;
32
+ let zeros = 0;
33
+ while (zeros < length && buffer[zeros] === 0) {
34
+ zeros++;
35
+ }
36
+
37
+ const limbs: number[] = [];
38
+ let i = zeros;
39
+ if ((length - zeros) % 2 === 1) {
40
+ limbs.push(buffer[i++]);
41
+ }
42
+ for (; i < length; i += 2) {
43
+ let carry = buffer[i] * 256 + buffer[i + 1];
44
+ for (let j = 0; j < limbs.length; j++) {
45
+ const x = limbs[j] * 65536 + carry;
46
+ carry = Math.floor(x / B58_5);
47
+ limbs[j] = x - carry * B58_5;
48
+ }
49
+ while (carry > 0) {
50
+ const next = Math.floor(carry / B58_5);
51
+ limbs.push(carry - next * B58_5);
52
+ carry = next;
53
+ }
54
+ }
55
+
56
+ // Each limb holds 5 base58 digits; the most significant limb is written without padding
57
+ const digits: number[] = [];
58
+ for (let k = 0; k < zeros; k++) {
59
+ digits.push(ALPHABET_CODES[0]);
60
+ }
61
+ for (let j = limbs.length - 1; j >= 0; j--) {
62
+ let limb = limbs[j];
63
+ const start = digits.length;
64
+ const width = j === limbs.length - 1 ? 0 : 5;
65
+ for (let k = 0; k < 5 && (limb > 0 || k < width); k++) {
66
+ const next = Math.floor(limb / 58);
67
+ digits.push(ALPHABET_CODES[limb - next * 58]);
68
+ limb = next;
69
+ }
70
+ // Digits were produced least-significant first
71
+ for (let a = start, b = digits.length - 1; a < b; a++, b--) {
72
+ const tmp = digits[a];
73
+ digits[a] = digits[b];
74
+ digits[b] = tmp;
75
+ }
76
+ }
77
+
78
+ // Chunked so long inputs stay under engine limits on function argument counts
79
+ let result = '';
80
+ for (let start = 0; start < digits.length; start += 8192) {
81
+ result += String.fromCharCode.apply(null, digits.slice(start, start + 8192));
82
+ }
83
+ return result;
84
+ }
85
+
86
+ export function fromBase58(str: string): Uint8Array<ArrayBuffer> {
87
+ if (typeof str !== 'string') {
88
+ throw new TypeError('fromBase58 expects a string');
89
+ }
90
+
91
+ const length = str.length;
92
+ let zeros = 0;
93
+ while (zeros < length && str.charCodeAt(zeros) === 49 /* '1' */) {
94
+ zeros++;
95
+ }
96
+
97
+ const limbs: number[] = [];
98
+ let i = zeros;
99
+ const head = (length - zeros) % 5;
100
+ while (i < length) {
101
+ const chunkLength = i === zeros && head !== 0 ? head : 5;
102
+ let carry = 0;
103
+ let multiplier = 1;
104
+ for (let k = 0; k < chunkLength; k++) {
105
+ const code = str.charCodeAt(i + k);
106
+ const digit = code < 128 ? DIGIT_VALUES[code] : -1;
107
+ if (digit < 0) {
108
+ throw new Error(`Invalid base58 character "${str[i + k]}"`);
109
+ }
110
+ carry = carry * 58 + digit;
111
+ multiplier *= 58;
112
+ }
113
+ i += chunkLength;
114
+
115
+ for (let j = 0; j < limbs.length; j++) {
116
+ const x = limbs[j] * multiplier + carry;
117
+ carry = Math.floor(x / 65536);
118
+ limbs[j] = x - carry * 65536;
119
+ }
120
+ while (carry > 0) {
121
+ limbs.push(carry % 65536);
122
+ carry = Math.floor(carry / 65536);
123
+ }
124
+ }
125
+
126
+ let byteLength = limbs.length * 2;
127
+ if (byteLength > 0 && limbs[limbs.length - 1] < 256) {
128
+ byteLength--;
129
+ }
130
+
131
+ const out = new Uint8Array(zeros + byteLength);
132
+ let pos = out.length - 1;
133
+ for (let j = 0; j < limbs.length; j++) {
134
+ out[pos--] = limbs[j] & 0xff;
135
+ if (pos >= zeros) {
136
+ out[pos--] = limbs[j] >> 8;
137
+ }
138
+ }
139
+
140
+ return out;
141
+ }
package/src/b64.ts CHANGED
@@ -2,20 +2,24 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  export function fromBase64(base64String: string): Uint8Array<ArrayBuffer> {
5
- return Uint8Array.from(atob(base64String), (char) => char.charCodeAt(0));
5
+ const binary = atob(base64String);
6
+ const bytes = new Uint8Array(binary.length);
7
+ for (let i = 0; i < binary.length; i++) {
8
+ bytes[i] = binary.charCodeAt(i);
9
+ }
10
+ return bytes;
6
11
  }
7
12
 
8
13
  const CHUNK_SIZE = 8192;
9
14
  export function toBase64(bytes: Uint8Array): string {
10
- // Special-case the simple case for speed's sake.
11
- if (bytes.length < CHUNK_SIZE) {
12
- return btoa(String.fromCharCode(...bytes));
15
+ if (bytes.length <= CHUNK_SIZE) {
16
+ return btoa(String.fromCharCode.apply(null, bytes as unknown as number[]));
13
17
  }
14
18
 
15
19
  let output = '';
16
- for (var i = 0; i < bytes.length; i += CHUNK_SIZE) {
20
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
17
21
  const chunk = bytes.slice(i, i + CHUNK_SIZE);
18
- output += String.fromCharCode(...chunk);
22
+ output += String.fromCharCode.apply(null, chunk as unknown as number[]);
19
23
  }
20
24
 
21
25
  return btoa(output);
package/src/hex.ts CHANGED
@@ -4,15 +4,33 @@
4
4
  export function fromHex(hexStr: string): Uint8Array<ArrayBuffer> {
5
5
  const normalized = hexStr.startsWith('0x') ? hexStr.slice(2) : hexStr;
6
6
  const padded = normalized.length % 2 === 0 ? normalized : `0${normalized}`;
7
- const intArr = padded.match(/[0-9a-fA-F]{2}/g)?.map((byte) => parseInt(byte, 16)) ?? [];
7
+ const bytes = new Uint8Array(padded.length / 2);
8
8
 
9
- if (intArr.length !== padded.length / 2) {
10
- throw new Error(`Invalid hex string ${hexStr}`);
9
+ for (let i = 0; i < bytes.length; i++) {
10
+ const high = hexDigit(padded.charCodeAt(i * 2));
11
+ const low = hexDigit(padded.charCodeAt(i * 2 + 1));
12
+ if (high < 0 || low < 0) {
13
+ throw new Error(`Invalid hex string ${hexStr}`);
14
+ }
15
+ bytes[i] = high * 16 + low;
11
16
  }
12
17
 
13
- return Uint8Array.from(intArr);
18
+ return bytes;
14
19
  }
15
20
 
21
+ function hexDigit(code: number): number {
22
+ if (code >= 48 && code <= 57) return code - 48;
23
+ if (code >= 97 && code <= 102) return code - 87;
24
+ if (code >= 65 && code <= 70) return code - 55;
25
+ return -1;
26
+ }
27
+
28
+ const HEX_BYTES = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
29
+
16
30
  export function toHex(bytes: Uint8Array): string {
17
- return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
31
+ let hex = '';
32
+ for (let i = 0; i < bytes.length; i++) {
33
+ hex += HEX_BYTES[bytes[i]];
34
+ }
35
+ return hex;
18
36
  }