···11ISC License
2233-Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
33+Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
4455Permission to use, copy, modify, and/or distribute this software for any
66purpose with or without fee is hereby granted, provided that the above
+3-3
README.md
···3344asn1js is a JavaScript generic ASN.1 parser/decoder that can decode any valid ASN.1 DER or BER structures.
5566-An example page that can decode Base64-encoded (raw base64, PEM armoring and `begin-base64` are recognized) or Hex-encoded (or local files with some browsers) is included and can be used both [online on the official website](https://lapo.it/asn1js/) or [offline (ZIP file)](https://lapo.it/asn1js/asn1js.zip).
66+An example page that can decode Base64-encoded (raw base64, PEM armoring and `begin-base64` are recognized) or Hex-encoded (or local files with some browsers) is included and can be used both [online on the official website](https://asn1js.eu/) or [offline (ZIP file)](https://lapo.it/asn1js/asn1js.zip) by opening `index-local.html`.
7788Usage with `nodejs`
99-------------------
···6767Local usage
6868--------------------
69697070-Since unfortunately ESM modules are not working on `file:` protocol due to [CORS issues](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts), there is a bundled [single-file version working locally](https://asn1js.eu/index-local.html). It doesn't work online (due to [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) restrictions about inline content) but can be saved locally and opened in a browser. ([known bug](https://github.com/lapo-luchini/asn1js/issues/89): dark mode is currently broken in this mode)
7070+Since unfortunately ESM modules are not working on `file:` protocol due to [CORS issues](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#other_differences_between_modules_and_standard_scripts), there is a bundled [single-file version working locally](https://asn1js.eu/index-local.html). It doesn't work online (due to [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) restrictions about inline content) but can be saved locally and opened in a browser.
71717272Usage from CLI
7373--------------------
···8181ISC license
8282-----------
83838484-ASN.1 JavaScript decoder Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
8484+ASN.1 JavaScript decoder Copyright (c) 2008-2025 Lapo Luchini <lapo@lapo.it>
85858686Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
8787
+50-22
asn1.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···2121 reTimeS = /^(\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2222 reTimeL = /^(\d\d\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|(-(?:0\d|1[0-2])|[+](?:0\d|1[0-4]))([0-5]\d)?)?$/,
2323 hexDigits = '0123456789ABCDEF',
2424- b64Safe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
2424+ b64Std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
2525+ b64URL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_',
2526 tableT61 = [
2627 ['', ''],
2728 ['AEIOUaeiou', 'รรรรรร รจรฌรฒรน'], // Grave
···5657 }
5758}
58595959-class Stream {
6060+/** Class to manage a stream of bytes, with a zero-copy approach.
6161+ * It uses an existing array or binary string and advances a position index. */
6262+export class Stream {
60636464+ /**
6565+ * @param {Stream|array|string} enc data (will not be copied)
6666+ * @param {?number} pos starting position (mandatory when `end` is not a Stream)
6767+ */
6168 constructor(enc, pos) {
6269 if (enc instanceof Stream) {
6370 this.enc = enc.enc;
6471 this.pos = enc.pos;
6572 } else {
6666- // enc should be an array or a binary string
6773 this.enc = enc;
6874 this.pos = pos;
6975 }
7676+ if (typeof this.pos != 'number')
7777+ throw new Error('"pos" must be a numeric value');
7878+ if (typeof this.enc == 'string')
7979+ this.getRaw = pos => this.enc.charCodeAt(pos);
8080+ else if (typeof this.enc[0] == 'number')
8181+ this.getRaw = pos => this.enc[pos];
8282+ else
8383+ throw new Error('"enc" must be a numeric array or a string');
7084 }
8585+ /** Get the byte at current position (and increment it) or at a specified position (and avoid moving current position).
8686+ * @param {?number} pos read position if specified, else current position (and increment it) */
7187 get(pos) {
7288 if (pos === undefined)
7389 pos = this.pos++;
7490 if (pos >= this.enc.length)
7591 throw new Error('Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length);
7676- return (typeof this.enc == 'string') ? this.enc.charCodeAt(pos) : this.enc[pos];
9292+ return this.getRaw(pos);
7793 }
7878- hexByte(b) {
9494+ /** Convert a single byte to an hexadcimal string (of length 2).
9595+ * @param {number} b */
9696+ static hexByte(b) {
7997 return hexDigits.charAt((b >> 4) & 0xF) + hexDigits.charAt(b & 0xF);
8098 }
8181- /** Hexadecimal dump.
8282- * @param type 'raw', 'byte' or 'dump' */
9999+ /** Hexadecimal dump of a specified region of the stream.
100100+ * @param {number} start starting position (included)
101101+ * @param {number} end ending position (excluded)
102102+ * @param {string} type 'raw', 'byte' or 'dump' (default) */
83103 hexDump(start, end, type = 'dump') {
84104 let s = '';
85105 for (let i = start; i < end; ++i) {
86106 if (type == 'byte' && i > start)
87107 s += ' ';
8888- s += this.hexByte(this.get(i));
108108+ s += Stream.hexByte(this.get(i));
89109 if (type == 'dump')
90110 switch (i & 0xF) {
91111 case 0x7: s += ' '; break;
···95115 }
96116 return s;
97117 }
9898- b64Dump(start, end) {
118118+ /** Base64url dump of a specified region of the stream (according to RFC 4648 section 5).
119119+ * @param {number} start starting position (included)
120120+ * @param {number} end ending position (excluded)
121121+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding) */
122122+ b64Dump(start, end, type = 'url') {
123123+ const b64 = type === 'url' ? b64URL : b64Std;
99124 let extra = (end - start) % 3,
100125 s = '',
101126 i, c;
102127 for (i = start; i + 2 < end; i += 3) {
103128 c = this.get(i) << 16 | this.get(i + 1) << 8 | this.get(i + 2);
104104- s += b64Safe.charAt(c >> 18 & 0x3F);
105105- s += b64Safe.charAt(c >> 12 & 0x3F);
106106- s += b64Safe.charAt(c >> 6 & 0x3F);
107107- s += b64Safe.charAt(c & 0x3F);
129129+ s += b64.charAt(c >> 18 & 0x3F);
130130+ s += b64.charAt(c >> 12 & 0x3F);
131131+ s += b64.charAt(c >> 6 & 0x3F);
132132+ s += b64.charAt(c & 0x3F);
108133 }
109134 if (extra > 0) {
110135 c = this.get(i) << 16;
111136 if (extra > 1) c |= this.get(i + 1) << 8;
112112- s += b64Safe.charAt(c >> 18 & 0x3F);
113113- s += b64Safe.charAt(c >> 12 & 0x3F);
114114- if (extra == 2) s += b64Safe.charAt(c >> 6 & 0x3F);
137137+ s += b64.charAt(c >> 18 & 0x3F);
138138+ s += b64.charAt(c >> 12 & 0x3F);
139139+ if (extra == 2) s += b64.charAt(c >> 6 & 0x3F);
140140+ if (b64 === b64Std) s += '==='.slice(0, 3 - extra);
115141 }
116142 return s;
117143 }
···277303 end = start + maxLength;
278304 s = '';
279305 for (let i = start; i < end; ++i)
280280- s += this.hexByte(this.get(i));
306306+ s += Stream.hexByte(this.get(i));
281307 if (len > maxLength)
282308 s += ellipsis;
283309 return { size: len, str: s };
···535561 toHexString(type = 'raw') {
536562 return this.stream.hexDump(this.posStart(), this.posEnd(), type);
537563 }
538538- /** Base64 dump of the node. */
539539- toB64String() {
540540- return this.stream.b64Dump(this.posStart(), this.posEnd());
564564+ /** Base64url dump of the node (according to RFC 4648 section 5).
565565+ * @param {string} type 'url' (default, section 5 without padding) or 'std' (section 4 with padding)
566566+ */
567567+ toB64String(type = 'url') {
568568+ return this.stream.b64Dump(this.posStart(), this.posEnd(), type);
541569 }
542570 static decodeLength(stream) {
543571 let buf = stream.get(),
+8-7
base64.js
···11// Base64 JavaScript decoder
22-// Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···3131 decoder[b64.charCodeAt(i)] = i;
3232 for (i = 0; i < ignore.length; ++i)
3333 decoder[ignore.charCodeAt(i)] = -1;
3434- // RFC 3548 URL & file safe encoding
3434+ // also support decoding Base64url (RFC 4648 section 5)
3535 decoder['-'.charCodeAt(0)] = decoder['+'.charCodeAt(0)];
3636 decoder['_'.charCodeAt(0)] = decoder['/'.charCodeAt(0)];
3737 }
···75757676 static pretty(str) {
7777 // fix padding
7878- if (str.length % 4 > 0)
7979- str = (str + '===').slice(0, str.length + str.length % 4);
8080- // convert RFC 3548 to standard Base64
7878+ let pad = 4 - str.length % 4;
7979+ if (pad < 4)
8080+ str += '==='.slice(0, pad);
8181+ // convert Base64url (RFC 4648 section 5) to standard Base64 (RFC 4648 section 4)
8182 str = str.replace(/-/g, '+').replace(/_/g, '/');
8283 // 80 column width
8383- return str.replace(/(.{80})/g, '$1\n');
8484+ return str.replace(/.{80}/g, '$&\n');
8485 }
85868687 static unarmor(a) {
···11// ASN.1 RFC definitions matcher
22-// Copyright (c) 2023-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2023 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···2929 def = Object.assign({}, def);
3030 def.type = Defs.searchType(name).type;
3131 }
3232- if (def?.type?.name == 'CHOICE') {
3333- for (let c of def.type.content) {
3232+ if (def?.name == 'CHOICE' || def?.type?.name == 'CHOICE') {
3333+ for (let c of def.content ?? def.type.content) {
3434 if (tn != c.type.name && tn != c.name)
3535 c = translate(c);
3636 if (tn == c.type.name || tn == c.name) {
3737 def = Object.assign({}, def);
3838+ if (c.id) def.id = c.id;
3839 def.type = c.type.name ? c.type : c;
3940 break;
4041 }
···127128Defs.RFC = rfcdef;
128129129130Defs.commonTypes = [
130130- [ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
131131+ [ 'X.509 certificate', '1.3.6.1.5.5.7.0.18', 'Certificate' ],
131132 [ 'X.509 public key info', '1.3.6.1.5.5.7.0.18', 'SubjectPublicKeyInfo' ],
133133+ [ 'X.509 certificate revocation list', '1.3.6.1.5.5.7.0.18', 'CertificateList' ],
132134 [ 'CMS / PKCS#7 envelope', '1.2.840.113549.1.9.16.0.14', 'ContentInfo' ],
133135 [ 'PKCS#1 RSA private key', '1.2.840.113549.1.1.0.1', 'RSAPrivateKey' ],
134136 [ 'PKCS#8 encrypted private key', '1.2.840.113549.1.8.1.1', 'EncryptedPrivateKeyInfo' ],
135137 [ 'PKCS#8 private key', '1.2.840.113549.1.8.1.1', 'PrivateKeyInfo' ],
136138 [ 'PKCS#10 certification request', '1.2.840.113549.1.10.1.1', 'CertificationRequest' ],
137139 [ 'CMP PKI Message', '1.3.6.1.5.5.7.0.16', 'PKIMessage' ],
140140+ [ 'LDAP Message', '1.3.6.1.1.18', 'LDAPMessage' ],
138141].map(arr => ({ description: arr[0], ...Defs.moduleAndType(rfcdef[arr[1]], arr[2]) }));
+27-11
dom.js
···11// ASN.1 JavaScript decoder
22-// Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···2424 ellipsis: '\u2026',
2525 tag: function (tagName, className, text) {
2626 let t = document.createElement(tagName);
2727- t.className = className;
2727+ if (className) t.className = className;
2828 if (text) t.innerText = text;
2929 return t;
3030 },
···5656 toDOM(spaces) {
5757 spaces = spaces || '';
5858 let isOID = (typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06) || (this.tag.tagNumber == 0x0D));
5959- let node = DOM.tag('div', 'node');
5959+ let node = DOM.tag('li');
6060 node.asn1 = this;
6161 let head = DOM.tag('span', 'head');
6262- head.appendChild(DOM.tag('span', 'spaces', spaces));
6362 const typeName = this.typeName().replace(/_/g, ' ');
6463 if (this.def) {
6564 if (this.def.id) {
···111110 content = content.replace(/</g, '<');
112111 content = content.replace(/\n/g, '<br>');
113112 }
114114- node.appendChild(head);
115115- this.node = node;
113113+ // add the li and details section for this node
114114+ let contentNode;
115115+ let childNode;
116116+ if (this.sub !== null) {
117117+ let details = DOM.tag('details');
118118+ details.setAttribute('open', '');
119119+ node.appendChild(details);
120120+ let summary = DOM.tag('summary', 'node');
121121+ details.appendChild(summary);
122122+ summary.appendChild(head);
123123+ contentNode = summary;
124124+ childNode = details;
125125+ } else {
126126+ contentNode = node;
127127+ contentNode.classList.add('node');
128128+ contentNode.appendChild(head);
129129+ }
130130+ this.node = contentNode;
116131 this.head = head;
117132 let value = DOM.tag('div', 'value');
118133 let s = 'Offset: ' + this.stream.pos + '<br>';
···135150 }
136151 }
137152 value.innerHTML = s;
138138- node.appendChild(value);
139139- let sub = DOM.tag('div', 'sub');
153153+ contentNode.appendChild(value);
140154 if (this.sub !== null) {
155155+ let sub = DOM.tag('ul');
156156+ childNode.appendChild(sub);
141157 spaces += '\xA0 ';
142158 for (let i = 0, max = this.sub.length; i < max; ++i)
143159 sub.appendChild(this.sub[i].toDOM(spaces));
144160 }
145145- node.appendChild(sub);
146161 bindContextMenu(node);
147162 return node;
148163 }
···169184 this.head.onmouseover = function () { this.hexNode.className = 'hexCurrent'; };
170185 this.head.onmouseout = function () { this.hexNode.className = 'hex'; };
171186 node.asn1 = this;
172172- node.onmouseover = function () {
187187+ node.onmouseover = function (event) {
173188 let current = !root.selected;
174189 if (current) {
175190 root.selected = this.asn1;
176191 this.className = 'hexCurrent';
177192 }
178193 this.asn1.fakeHover(current);
194194+ event.stopPropagation();
179195 };
180196 node.onmouseout = function () {
181197 let current = (root.selected == this.asn1);
+4
dumpASN1.js
···11#!/usr/bin/env node
2233+// usage:
44+// ./dumpASN1.js filename
55+// ./dumpASN1.js data:base64,MDMCAQFjLgQACgEACgEAAgEAAgEAAQEAoA+jDQQFTnRWZXIEBAEAAAAwCgQITmV0bG9nb24===
66+37import * as fs from 'node:fs';
48import { Base64 } from './base64.js';
59import { ASN1 } from './asn1.js';
+12
examples/crl-rfc5280.b64
···11+CRL example from RFC5280 as found here:
22+https://csrc.nist.gov/projects/pki-testing/sample-certificates-and-crls
33+44+begin-base64 644 crl-rfc5280.der
55+MIIBYDCBygIBATANBgkqhkiG9w0BAQUFADBDMRMwEQYKCZImiZPyLGQBGRYDY29tMRcwFQYKCZIm
66+iZPyLGQBGRYHZXhhbXBsZTETMBEGA1UEAxMKRXhhbXBsZSBDQRcNMDUwMjA1MTIwMDAwWhcNMDUw
77+MjA2MTIwMDAwWjAiMCACARIXDTA0MTExOTE1NTcwM1owDDAKBgNVHRUEAwoBAaAvMC0wHwYDVR0j
88+BBgwFoAUCGivhTPIOUp6+IKTjnBqSiCELDIwCgYDVR0UBAMCAQwwDQYJKoZIhvcNAQEFBQADgYEA
99+ItwYffcIzsx10NBqm60Q9HYjtIFutW2+DvsVFGzIF20f7pAXom9g5L2qjFXejoRvkvifEBInr0rU
1010+L4XiNkR9qqNMJTgV/wD9Pn7uPSYS69jnK2LiK8NGgO94gtEVxtCccmrLznrtZ5mLbnCBfUNCdMGm
1111+r8FVF6IzTNYGmCuk/C4=
1212+====
+8
examples/ldapmessage.b64
···11+LDAPMessage example as found on ldap.com.
22+33+Original link:
44+https://ldap.com/ldapv3-wire-protocol-reference-ldap-message/
55+66+begin-base64 644 ldapmessage.der
77+MDUCAQVKEWRjPWV4YW1wbGUsZGM9Y29toB0wGwQWMS4yLjg0MC4xMTM1NTYuMS40LjgwNQEB/w==
88+====
···11// Hex JavaScript decoder
22-// Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···11+import './theme.js';
12import { ASN1DOM } from './dom.js';
23import { Base64 } from './base64.js';
34import { Hex } from './hex.js';
···1516 area = id('area'),
1617 file = id('file'),
1718 examples = id('examples'),
1818- selectTheme = id('theme-select'),
1919 selectDefs = id('definitions'),
2020 selectTag = id('tags');
2121···4141function show(asn1) {
4242 tree.innerHTML = '';
4343 dump.innerHTML = '';
4444- tree.appendChild(asn1.toDOM());
4444+ let ul = document.createElement('ul');
4545+ ul.className = 'treecollapse';
4646+ tree.appendChild(ul);
4747+ ul.appendChild(asn1.toDOM());
4548 if (wantHex.checked) dump.appendChild(asn1.toHexDOM(undefined, trimHex.checked));
4649}
4750export function decode(der, offset) {
···157160 let elem = id(name);
158161 if (elem)
159162 elem.onclick = onClick;
160160-}
161161-// set dark theme depending on OS settings
162162-function setTheme() {
163163- if (!selectTheme) {
164164- console.log('Themes are currently not working with single file version.');
165165- return;
166166- }
167167- let storedTheme = localStorage.getItem('theme');
168168- let theme = 'os';
169169- if (storedTheme)
170170- theme = storedTheme;
171171- selectTheme.value = theme;
172172- if (theme == 'os') {
173173- let prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
174174- theme = prefersDarkScheme.matches ? 'dark': 'light';
175175- }
176176- if (theme == 'dark') {
177177- const css1 = id('theme-base');
178178- const css2 = css1.cloneNode();
179179- css2.id = 'theme-override';
180180- css2.href = 'index-' + theme + '.css';
181181- css1.parentElement.appendChild(css2);
182182- } else {
183183- const css2 = id('theme-override');
184184- if (css2) css2.remove();
185185- }
186186-}
187187-setTheme();
188188-if (selectTheme) {
189189- selectTheme.addEventListener('change', function () {
190190- localStorage.setItem('theme', selectTheme.value);
191191- setTheme();
192192- });
193163}
194164// this is only used if window.FileReader
195165function read(f) {
+15-9
int10.js
···11// Big integer base-10 printing library
22-// Copyright (c) 2008-2024 Lapo Luchini <lapo@lapo.it>
22+// Copyright (c) 2008 Lapo Luchini <lapo@lapo.it>
3344// Permission to use, copy, modify, and/or distribute this software for any
55// purpose with or without fee is hereby granted, provided that the above
66// copyright notice and this permission notice appear in all copies.
77-//
77+//
88// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
99// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1010// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
···1313// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1414// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15151616-let max = 10000000000000; // biggest 10^n integer that can still fit 2^53 when multiplied by 256
1616+/** Biggest 10^n integer that can still fit 2^53 when multiplied by 256. */
1717+const max = 10000000000000;
17181819export class Int10 {
1920 /**
···26272728 /**
2829 * Multiply value by m and add c.
2929- * @param {number} m - multiplier, must be < =256
3030- * @param {number} c - value to add
3030+ * @param {number} m - multiplier, must be 0<m<=256
3131+ * @param {number} c - value to add, must be c>=0
3132 */
3233 mulAdd(m, c) {
3434+ // assert(m > 0)
3335 // assert(m <= 256)
3636+ // assert(c >= 0)
3437 let b = this.buf,
3538 l = b.length,
3639 i, t;
···71747275 /**
7376 * Convert to decimal string representation.
7474- * @param {*} base - optional value, only value accepted is 10
7777+ * @param {number} [base=10] - optional value, only value accepted is 10
7878+ * @returns {string} The decimal string representation.
7579 */
7676- toString(base) {
7777- if ((base || 10) != 10)
7878- throw 'only base 10 is supported';
8080+ toString(base = 10) {
8181+ if (base != 10)
8282+ throw new Error('only base 10 is supported');
7983 let b = this.buf,
8084 s = b[b.length - 1].toString();
8185 for (let i = b.length - 2; i >= 0; --i)
···8690 /**
8791 * Convert to Number value representation.
8892 * Will probably overflow 2^53 and thus become approximate.
9393+ * @returns {number} The numeric value.
8994 */
9095 valueOf() {
9196 let b = this.buf,
···9710298103 /**
99104 * Return value as a simple Number (if it is <= 10000000000000), or return this.
105105+ * @returns {number | Int10} The simplified value.
100106 */
101107 simplify() {
102108 let b = this.buf;