functionalscript 0.9.2 → 0.9.3

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.
@@ -1,4 +1,6 @@
1
1
  declare const _default: {
2
+ rangeDecode: () => void;
3
+ rangeEncode: () => void;
2
4
  toData: (() => void)[];
3
5
  emptyTags: (() => void)[];
4
6
  variantTest: () => void;
@@ -3,7 +3,7 @@ import { stringToCodePointList } from "../../text/utf16/module.f.js";
3
3
  import { identity } from "../../types/function/module.f.js";
4
4
  import { map, toArray } from "../../types/list/module.f.js";
5
5
  import { sort } from "../../types/object/module.f.js";
6
- import { join0Plus, option, range, repeat0Plus, set } from "../module.f.js";
6
+ import { join0Plus, oneEncode, option, range, rangeDecode, repeat0Plus, set } from "../module.f.js";
7
7
  import { classic, deterministic } from "../testlib.f.js";
8
8
  import { dispatchMap, parser, parserRuleSet, toData, createEmptyTagMap, descentParser } from "./module.f.js";
9
9
  const mapCodePoint = (cp) => [cp, undefined];
@@ -12,6 +12,34 @@ const descentParserCpOnly = (m, name, cp) => {
12
12
  return m(name, cpm);
13
13
  };
14
14
  export default {
15
+ rangeDecode: () => {
16
+ const decoded1 = stringify(sort)(rangeDecode(0x000079_000087));
17
+ if (decoded1 !== '[121,135]') {
18
+ throw decoded1;
19
+ }
20
+ const decoded2 = stringify(sort)(rangeDecode(0x000080_000087));
21
+ if (decoded2 !== '[128,135]') {
22
+ throw decoded2;
23
+ }
24
+ const decoded3 = stringify(sort)(rangeDecode(0x10FFFF_10FFFF));
25
+ if (decoded3 !== '[1114111,1114111]') {
26
+ throw decoded3;
27
+ }
28
+ },
29
+ rangeEncode: () => {
30
+ const encoded1 = oneEncode(0x79);
31
+ if (encoded1 !== 0x000079_000079) {
32
+ throw encoded1;
33
+ }
34
+ const encoded2 = oneEncode(0x80);
35
+ if (encoded2 !== 0x000080_000080) {
36
+ throw encoded2;
37
+ }
38
+ const encoded3 = oneEncode(0x10FFFF);
39
+ if (encoded3 !== 0x10FFFF_10FFFF) {
40
+ throw encoded3;
41
+ }
42
+ },
15
43
  toData: [
16
44
  () => {
17
45
  const c = toData(classic());
@@ -281,7 +309,16 @@ export default {
281
309
  }
282
310
  },
283
311
  () => {
284
- const terminalRangeRule = range('AF');
312
+ const terminalRangeRule = 0x000079_000087;
313
+ const m = parser(terminalRangeRule);
314
+ const mr = m("", [64]);
315
+ const result = JSON.stringify(mr);
316
+ if (result !== '[{"sequence":[]},false,[64]]') {
317
+ throw result;
318
+ }
319
+ },
320
+ () => {
321
+ const terminalRangeRule = 0x000080_000087; //broken range
285
322
  const m = parser(terminalRangeRule);
286
323
  const mr = m("", [64]);
287
324
  const result = JSON.stringify(mr);
package/bnf/module.f.d.ts CHANGED
@@ -9,7 +9,6 @@ import { type Array2 } from '../types/array/module.f.ts';
9
9
  export type TerminalRange = number;
10
10
  export declare const fullRange: TerminalRange;
11
11
  export declare const unicodeRange: TerminalRange;
12
- export declare const eof = 1114112;
13
12
  /** A sequence of rules. */
14
13
  export type Sequence = readonly Rule[];
15
14
  /** A variant */
@@ -22,6 +21,7 @@ export type Rule = DataRule | LazyRule;
22
21
  export declare const max: string;
23
22
  export declare const rangeEncode: (a: number, b: number) => TerminalRange;
24
23
  export declare const oneEncode: (a: number) => TerminalRange;
24
+ export declare const eof: TerminalRange;
25
25
  export declare const rangeDecode: (r: number) => Array2<number>;
26
26
  export declare const toSequence: (s: string) => readonly TerminalRange[];
27
27
  export declare const str: (s: string) => readonly TerminalRange[] | TerminalRange;
package/bnf/module.f.js CHANGED
@@ -3,7 +3,6 @@ import { isArray2 } from "../types/array/module.f.js";
3
3
  import { map, toArray, repeat as listRepeat } from "../types/list/module.f.js";
4
4
  export const fullRange = 0x000000_FFFFFF;
5
5
  export const unicodeRange = 0x000000_10FFFF;
6
- export const eof = 0x110000;
7
6
  // Internals:
8
7
  const { fromEntries, values } = Object;
9
8
  const { fromCodePoint } = String;
@@ -18,10 +17,11 @@ export const rangeEncode = (a, b) => {
18
17
  if (!isValid(a) || !isValid(b) || a > b) {
19
18
  throw `Invalid range ${a} ${b}.`;
20
19
  }
21
- return (a << offset) | b;
20
+ return Number((BigInt(a) << BigInt(offset)) | BigInt(b));
22
21
  };
23
22
  export const oneEncode = (a) => rangeEncode(a, a);
24
- export const rangeDecode = (r) => [r >> offset, r & mask];
23
+ export const eof = oneEncode(0x110000);
24
+ export const rangeDecode = (r) => [Number(BigInt(r) >> BigInt(offset)), Number(BigInt(r) & BigInt(mask))];
25
25
  const mapOneEncode = map(oneEncode);
26
26
  export const toSequence = (s) => toArray(mapOneEncode(stringToCodePointList(s)));
27
27
  export const str = (s) => {
package/ci/module.f.js CHANGED
@@ -73,9 +73,7 @@ const toSteps = (m) => {
73
73
  return [
74
74
  ...(aptGet !== '' ? [{ run: `sudo apt-get update && sudo apt-get install -y ${aptGet}` }] : []),
75
75
  ...(rust ? [{
76
- // wasm32-wasip1-threads doesn't work on Rust 1.91 in the release mode.
77
- // See https://github.com/sergey-shandar/wasmtime-crash
78
- uses: 'dtolnay/rust-toolchain@1.93.0',
76
+ uses: 'dtolnay/rust-toolchain@1.93.1',
79
77
  with: {
80
78
  components: 'rustfmt,clippy',
81
79
  targets
@@ -1,5 +1,5 @@
1
1
  import { utf8 } from "../../text/module.f.js";
2
- import { length, uint, vec } from "../../types/bit_vec/module.f.js";
2
+ import { uint, vec } from "../../types/bit_vec/module.f.js";
3
3
  import { sha256, sha384, sha512 } from "../sha2/module.f.js";
4
4
  import { hmac } from "./module.f.js";
5
5
  export default {
@@ -1,7 +1,7 @@
1
1
  import { utf8 } from "../../text/module.f.js";
2
2
  import { empty, msb, repeat, vec, vec8 } from "../../types/bit_vec/module.f.js";
3
3
  import { hmac } from "../hmac/module.f.js";
4
- import { curve, secp192r1, secp256r1, secp384r1, secp521r1 } from "../secp/module.f.js";
4
+ import { secp192r1, secp256r1, secp384r1, secp521r1 } from "../secp/module.f.js";
5
5
  import { computeSync, sha224, sha256, sha384, sha512 } from "../sha2/module.f.js";
6
6
  import { all, concat, computeK, fromCurve, sign } from "./module.f.js";
7
7
  const sample = utf8("sample");
@@ -7,11 +7,11 @@ const isPlaywright = typeof process !== 'undefined' && process?.env?.PLAYWRIGHT_
7
7
  const createFramework = (fw) => (prefix, f) => fw.test(prefix, t => f((name, v) => t.test(name, v)));
8
8
  // Bun doesn't support nested tests yet.
9
9
  const createBunFramework = (fw) => (prefix, f) => f((name, v) => fw.test(`${prefix}: ${name}`, v));
10
- const createPlaywrihtFramework = async () => {
10
+ const createPlaywrightFramework = async () => {
11
11
  const pwTest = (await import('@playwright/test')).test;
12
12
  return (prefix, f) => f((name, v) => pwTest(`${prefix}: ${name}`, v));
13
13
  };
14
- const framework = isPlaywright ? await createPlaywrihtFramework() :
14
+ const framework = isPlaywright ? await createPlaywrightFramework() :
15
15
  isBun ? createBunFramework(nodeTest) :
16
16
  createFramework(nodeTest);
17
17
  const parse = parseTestSet(io.tryCatch);
@@ -1,4 +1,4 @@
1
- import { entries, fold } from "../../types/list/module.f.js";
1
+ import { fold } from "../../types/list/module.f.js";
2
2
  import { reset, fgGreen, fgRed, bold, stdio, stderr } from "../../text/sgr/module.f.js";
3
3
  import { env, loadModuleMap } from "../module.f.js";
4
4
  export const isTest = (s) => s.endsWith('test.f.js') || s.endsWith('test.f.ts');
@@ -58,7 +58,7 @@ export const test = (input) => {
58
58
  if (isGitHub) {
59
59
  // https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions
60
60
  // https://github.com/OndraM/ci-detector/blob/main/src/Ci/GitHubActions.php
61
- error(`::error file=${k},line=1,title=[3]['a']()::${r}`);
61
+ error(`::error file=${k},line=1,title=${i}()::${r}`);
62
62
  }
63
63
  else {
64
64
  error(`${i}() ${fgRed}error${reset}, ${timeFormat(delta)}`);
@@ -134,7 +134,6 @@ export const jsGrammar = () => {
134
134
  ]
135
135
  }
136
136
  ];
137
- const endOfFile = oneEncode(eof);
138
137
  const token = {
139
138
  number,
140
139
  string,
@@ -143,7 +142,7 @@ export const jsGrammar = () => {
143
142
  operator,
144
143
  ws,
145
144
  newLine,
146
- //endOfFile
145
+ eof
147
146
  };
148
147
  const tokens = repeat0Plus(token);
149
148
  return tokens;
@@ -15,9 +15,10 @@ export default {
15
15
  const mr = descentParserCpOnly(m, '', cp);
16
16
  const success = mr[1] && mr[2] === cp.length;
17
17
  if (success !== expected) {
18
- throw mr;
18
+ throw JSON.stringify([s, mr]);
19
19
  }
20
20
  };
21
+ expect('"', false);
21
22
  expect(' true ', true);
22
23
  expect(' tr2ue ', true);
23
24
  expect(' 2true ', true);
package/io/module.f.d.ts CHANGED
@@ -104,4 +104,4 @@ export type Run = (f: App) => Promise<never>;
104
104
  * Handles errors by exiting with code 1
105
105
  */
106
106
  export declare const run: (io: Io) => Run;
107
- export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, }: Io) => <T>(effect: NodeEffect<T>) => Promise<T>;
107
+ export declare const fromIo: ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }: Io) => <T>(effect: NodeEffect<T>) => Promise<T>;
package/io/module.f.js CHANGED
@@ -26,9 +26,16 @@ const tc = async (f) => {
26
26
  return error(e);
27
27
  }
28
28
  };
29
- export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, }) => asyncRun({
29
+ export const fromIo = ({ console: { error, log }, fs: { promises: { mkdir, readFile, readdir, writeFile } }, fetch, }) => asyncRun({
30
30
  error: async (message) => error(message),
31
31
  log: async (message) => log(message),
32
+ fetch: async (url) => tc(async () => {
33
+ const response = await fetch(url);
34
+ if (!response.ok) {
35
+ throw new Error(`Fetch error: ${response.status} ${response.statusText}`);
36
+ }
37
+ return toVec(new Uint8Array(await response.arrayBuffer()));
38
+ }),
32
39
  mkdir: param => tc(async () => { await mkdir(...param); }),
33
40
  readFile: path => tc(async () => toVec(await readFile(path))),
34
41
  readdir: ([path, r]) => tc(async () => (await readdir(path, { ...r, withFileTypes: true }))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.js",
@@ -9,7 +9,7 @@ export type U8 = number;
9
9
  */
10
10
  export type I32 = number;
11
11
  /**
12
- * Represents an unsigend 8-bit type - U8 or the end-of-file indicator.
12
+ * Represents an unsigned 8-bit type - U8 or the end-of-file indicator.
13
13
  * The U8 represents the byte itself, and null indicates that reading does not return anything else.
14
14
  */
15
15
  export type ByteOrEof = U8 | null;
@@ -0,0 +1,62 @@
1
+ import { type Vec } from "../bit_vec/module.f.ts";
2
+ /** ASN.1 tag number. */
3
+ type Tag = bigint;
4
+ /** ASN.1 universal BOOLEAN tag. */
5
+ export declare const boolean = 1n;
6
+ /** ASN.1 universal INTEGER tag. */
7
+ export declare const integer = 2n;
8
+ /** ASN.1 universal OCTET STRING tag. */
9
+ export declare const octetString = 4n;
10
+ /** ASN.1 universal OBJECT IDENTIFIER tag. */
11
+ export declare const objectIdentifier = 6n;
12
+ export declare const constructedSequence = 48n;
13
+ export declare const constructedSet = 49n;
14
+ /** Raw ASN.1 TLV tuple. */
15
+ export type Raw = readonly [Tag, Vec];
16
+ /** Encodes a raw ASN.1 TLV tuple into a bit vector. */
17
+ export declare const encodeRaw: ([tag, value]: Raw) => Vec;
18
+ /** Decodes a raw ASN.1 TLV tuple and returns the remaining input. */
19
+ export declare const decodeRaw: (v: Vec) => readonly [Raw, Vec];
20
+ /** Encodes a JavaScript boolean as an ASN.1 BOOLEAN value. */
21
+ export declare const encodeBoolean: (b: boolean) => Vec;
22
+ /** Decodes an ASN.1 BOOLEAN value. */
23
+ export declare const decodeBoolean: (v: Vec) => boolean;
24
+ /** Encodes a signed bigint using ASN.1 INTEGER two's complement representation. */
25
+ export declare const encodeInteger: (uint: bigint) => Vec;
26
+ /** Decodes an ASN.1 INTEGER encoded in two's complement. */
27
+ export declare const decodeInteger: (v: Vec) => bigint;
28
+ /** Encodes an OCTET STRING value. */
29
+ export declare const encodeOctetString: (v: Vec) => Vec;
30
+ /** Decodes an OCTET STRING value. */
31
+ export declare const decodeOctetString: (v: Vec) => Vec;
32
+ /** ASN.1 OBJECT IDENTIFIER components. */
33
+ export type ObjectIdentifier = readonly bigint[];
34
+ /** Encodes an OBJECT IDENTIFIER value. */
35
+ export declare const encodeObjectIdentifier: (oid: ObjectIdentifier) => Vec;
36
+ /** Decodes an OBJECT IDENTIFIER value. */
37
+ export declare const decodeObjectIdentifier: (v: Vec) => ObjectIdentifier;
38
+ /** ASN.1 ordered collection of records. */
39
+ export type Sequence = readonly Record[];
40
+ /** Encodes a SEQUENCE payload from ordered records. */
41
+ export declare const encodeSequence: (...records: Sequence) => Vec;
42
+ /** Decodes a SEQUENCE payload into records. */
43
+ export declare const decodeSequence: (v: Vec) => Sequence;
44
+ /** ASN.1 SET represented as a sequence of records. */
45
+ export type Set = Sequence;
46
+ /** Encodes a SET payload with canonical byte ordering. */
47
+ export declare const encodeSet: (...records: Sequence) => Vec;
48
+ /** Decodes a SET payload. */
49
+ export declare const decodeSet: (v: Vec) => Sequence;
50
+ /** Supported ASN.1 record variants. */
51
+ export type SupportedRecord = readonly [typeof boolean, boolean] | readonly [typeof integer, bigint] | readonly [typeof octetString, Vec] | readonly [typeof objectIdentifier, ObjectIdentifier] | readonly [typeof constructedSequence, Sequence] | readonly [typeof constructedSet, Set];
52
+ /**
53
+ * For unsupported tags, we just store the raw value including the tag and length,
54
+ * so that it can be re-encoded without loss of information.
55
+ */
56
+ export type UnsupportedRecord = Vec;
57
+ export type Record = SupportedRecord | UnsupportedRecord;
58
+ /** Encodes a supported ASN.1 record as TLV. */
59
+ export declare const encode: (record: Record) => Vec;
60
+ /** Decodes one supported ASN.1 record and returns the remaining input. */
61
+ export declare const decode: (v: Vec) => readonly [Record, Vec];
62
+ export {};
@@ -0,0 +1,276 @@
1
+ import { bitLength, max } from "../bigint/module.f.js";
2
+ import { empty, isVec, length, listToVec, msb, msbCmp, uint, unpack, vec, vec8 } from "../bit_vec/module.f.js";
3
+ import { identity } from "../function/module.f.js";
4
+ import { encode as b128encode, decode as b128decode } from "../base128/module.f.js";
5
+ const pop = msb.popFront;
6
+ const pop8 = pop(8n);
7
+ const concat = listToVec(msb);
8
+ const classPcMask = 224n;
9
+ const tagNumberMask = 31n;
10
+ const parsedTagEncode = ([classPc, number]) => {
11
+ const [firstByteNumber, rest] = number < tagNumberMask
12
+ ? [number, empty]
13
+ : [tagNumberMask, b128encode(number)];
14
+ return concat([vec8(classPc | firstByteNumber), rest]);
15
+ };
16
+ const parsedTagDecode = (v) => {
17
+ const [firstByte, rest] = pop8(v);
18
+ const classPc = (firstByte & classPcMask);
19
+ const firstByteNumber = firstByte & tagNumberMask;
20
+ const [number, rest1] = firstByteNumber < tagNumberMask
21
+ ? [firstByteNumber, rest]
22
+ : b128decode(rest);
23
+ return [[classPc, number], rest1];
24
+ };
25
+ const tagEncode = (tag) => vec(max((bitLength(tag) + 7n) >> 3n)(1n) << 3n)(tag);
26
+ const tagDecode = (v) => {
27
+ const [parsedTag, rest] = parsedTagDecode(v);
28
+ return [uint(parsedTagEncode(parsedTag)), rest];
29
+ };
30
+ //
31
+ const eoc = 0x00n;
32
+ /** ASN.1 universal BOOLEAN tag. */
33
+ export const boolean = 0x01n;
34
+ /** ASN.1 universal INTEGER tag. */
35
+ export const integer = 0x02n;
36
+ const bitString = 0x03n;
37
+ /** ASN.1 universal OCTET STRING tag. */
38
+ export const octetString = 0x04n;
39
+ const null_ = 0x05;
40
+ /** ASN.1 universal OBJECT IDENTIFIER tag. */
41
+ export const objectIdentifier = 0x06n;
42
+ const objectDescriptor = 0x07;
43
+ const external = 0x08;
44
+ const real = 0x09;
45
+ const enumerated = 0x0A;
46
+ const embeddedPdv = 0x0B;
47
+ const utf8string = 0x0C;
48
+ const relativeOid = 0x0D;
49
+ const time = 0x0E;
50
+ const sequence = 0x10;
51
+ const set = 0x11;
52
+ const numericString = 0x12;
53
+ const printableString = 0x13;
54
+ const t61String = 0x14;
55
+ const videotexString = 0x15;
56
+ const ia5String = 0x16;
57
+ const utcTime = 0x17;
58
+ const generalizedTime = 0x18;
59
+ const graphicString = 0x19;
60
+ const visibleString = 0x1A;
61
+ const generalString = 0x1B;
62
+ const universalString = 0x1C;
63
+ const characterString = 0x1D;
64
+ const bmpString = 0x1E;
65
+ const date = 0x1F;
66
+ const timeOfDay = 0x20;
67
+ const dateTime = 0x21;
68
+ const duration = 0x22;
69
+ const oidIri = 0x23;
70
+ const relativeOidIri = 0x24;
71
+ const constructed = 0x20;
72
+ export const constructedSequence = 0x30n; // constructed | sequence
73
+ export const constructedSet = 0x31n; // constructed | set
74
+ const round8 = ({ length, uint }) => {
75
+ const byteLen = (length + 7n) >> 3n;
76
+ return { byteLen, v: vec(byteLen << 3n)(uint) };
77
+ };
78
+ const lenEncode = (uint) => {
79
+ if (uint < 0x80n) {
80
+ return vec8(uint);
81
+ }
82
+ const { byteLen, v } = round8({ length: bitLength(uint), uint });
83
+ return concat([vec8(0x80n | byteLen), v]);
84
+ };
85
+ /**
86
+ * Decodes the length field of an ASN.1 TLV and returns the length in bits and the remaining input.
87
+ *
88
+ * @param v - The input bit vector starting with the length field.
89
+ * @returns A tuple containing the length in bits and the remaining input after the length field.
90
+ */
91
+ const lenDecode = (v) => {
92
+ const firstAndRest = pop8(v);
93
+ const [first, rest1] = firstAndRest;
94
+ const [byteLen, rest2] = first < 0x80n ? firstAndRest : pop((first & 0x7fn) << 3n)(rest1);
95
+ return [byteLen << 3n, rest2];
96
+ };
97
+ /** Encodes a raw ASN.1 TLV tuple into a bit vector. */
98
+ export const encodeRaw = ([tag, value]) => {
99
+ const tagVec = tagEncode(tag);
100
+ const { byteLen, v } = round8(unpack(value));
101
+ return concat([tagVec, lenEncode(byteLen), v]);
102
+ };
103
+ /** Decodes a raw ASN.1 TLV tuple and returns the remaining input. */
104
+ export const decodeRaw = (v) => {
105
+ const [tag, v1] = tagDecode(v);
106
+ const [len, v2] = lenDecode(v1);
107
+ const [result, next] = pop(len)(v2);
108
+ return [[tag, vec(len)(result)], next];
109
+ };
110
+ // boolean
111
+ /** Encodes a JavaScript boolean as an ASN.1 BOOLEAN value. */
112
+ export const encodeBoolean = (b) => vec8(b ? 0xffn : 0x00n);
113
+ /** Decodes an ASN.1 BOOLEAN value. */
114
+ export const decodeBoolean = (v) => uint(v) !== 0n;
115
+ // integer (two's compliment)
116
+ /** Encodes a signed bigint using ASN.1 INTEGER two's complement representation. */
117
+ export const encodeInteger = (uint) => {
118
+ const offset = uint < 0n ? 1n : 0n;
119
+ return round8({ length: bitLength(uint + offset) + 1n, uint }).v;
120
+ };
121
+ /** Decodes an ASN.1 INTEGER encoded in two's complement. */
122
+ export const decodeInteger = (v) => {
123
+ const { length, uint } = unpack(v);
124
+ const sign = uint >> (length - 1n);
125
+ return sign === 0n ? uint : uint - (1n << length);
126
+ };
127
+ // octet string
128
+ /** Encodes an OCTET STRING value. */
129
+ export const encodeOctetString = (v) => v;
130
+ /** Decodes an OCTET STRING value. */
131
+ export const decodeOctetString = (v) => v;
132
+ /** Encodes an OBJECT IDENTIFIER value. */
133
+ export const encodeObjectIdentifier = (oid) => {
134
+ const [first, second, ...rest] = oid;
135
+ const firstByte = first * 40n + second;
136
+ return concat([vec8(firstByte), ...rest.map(b128encode)]);
137
+ };
138
+ /** Decodes an OBJECT IDENTIFIER value. */
139
+ export const decodeObjectIdentifier = (v) => {
140
+ const [firstByte, rest] = pop8(v);
141
+ const first = firstByte / 40n;
142
+ const second = firstByte % 40n;
143
+ let result = [first, second];
144
+ let tail = rest;
145
+ while (length(tail) > 0n) {
146
+ const [value, next] = b128decode(tail);
147
+ result = [...result, value];
148
+ tail = next;
149
+ }
150
+ return result;
151
+ };
152
+ const genericEncodeSequence = (map) => (...records) => concat(map(records.map(encode)));
153
+ /** Encodes a SEQUENCE payload from ordered records. */
154
+ export const encodeSequence = genericEncodeSequence(identity);
155
+ /** Decodes a SEQUENCE payload into records. */
156
+ export const decodeSequence = (v) => {
157
+ let result = [];
158
+ while (length(v) !== 0n) {
159
+ const [record, rest] = decode(v);
160
+ result = [...result, record];
161
+ v = rest;
162
+ }
163
+ return result;
164
+ };
165
+ /** Encodes a SET payload with canonical byte ordering. */
166
+ export const encodeSet = genericEncodeSequence(v => v.toSorted((a, b) => msbCmp(a)(b)));
167
+ /** Decodes a SET payload. */
168
+ export const decodeSet = decodeSequence;
169
+ // encode
170
+ const recordToRaw = ([tag, value]) => {
171
+ switch (tag) {
172
+ case boolean: return encodeBoolean(value);
173
+ case integer: return encodeInteger(value);
174
+ case octetString: return encodeOctetString(value);
175
+ case objectIdentifier: return encodeObjectIdentifier(value);
176
+ case constructedSequence: return encodeSequence(...value);
177
+ case constructedSet: return encodeSet(...value);
178
+ }
179
+ };
180
+ /** Encodes a supported ASN.1 record as TLV. */
181
+ export const encode = (record) => isVec(record) ? record : encodeRaw([record[0], recordToRaw(record)]);
182
+ // decode
183
+ const rawToRecord = (raw) => {
184
+ const [tag, value] = raw;
185
+ switch (tag) {
186
+ case boolean: return [boolean, decodeBoolean(value)];
187
+ case integer: return [integer, decodeInteger(value)];
188
+ case octetString: return [octetString, decodeOctetString(value)];
189
+ case objectIdentifier: return [objectIdentifier, decodeObjectIdentifier(value)];
190
+ case constructedSequence: return [constructedSequence, decodeSequence(value)];
191
+ case constructedSet: return [constructedSet, decodeSet(value)];
192
+ default: return encodeRaw(raw);
193
+ }
194
+ };
195
+ /** Decodes one supported ASN.1 record and returns the remaining input. */
196
+ export const decode = (v) => {
197
+ const [raw, rest] = decodeRaw(v);
198
+ return [rawToRecord(raw), rest];
199
+ };
200
+ /*
201
+ TimeStampReq ::= SEQUENCE {
202
+ version INTEGER { v1(1) }, // [x]
203
+ messageImprint MessageImprint,
204
+ reqPolicy TSAPolicyId OPTIONAL,
205
+ nonce INTEGER OPTIONAL, // [X]
206
+ certReq BOOLEAN DEFAULT FALSE, // [X]
207
+ extensions [0] IMPLICIT Extensions OPTIONAL // [X]
208
+ }
209
+
210
+ MessageImprint ::= SEQUENCE {
211
+ hashAlgorithm AlgorithmIdentifier,
212
+ hashedMessage OCTET STRING // [X]
213
+ }
214
+
215
+ TSAPolicyId ::= OBJECT IDENTIFIER // [X]
216
+ */
217
+ /*
218
+ TimeStampResp ::= SEQUENCE {
219
+ status PKIStatusInfo,
220
+ timeStampToken TimeStampToken OPTIONAL
221
+ }
222
+
223
+ PKIStatusInfo ::= SEQUENCE {
224
+ status PKIStatus,
225
+ statusString PKIFreeText OPTIONAL,
226
+ failInfo PKIFailureInfo OPTIONAL
227
+ }
228
+
229
+ PKIStatus ::= INTEGER { // [X]
230
+ granted (0),
231
+ grantedWithMods (1),
232
+ rejection (2),
233
+ waiting (3),
234
+ revocationWarning (4),
235
+ revocationNotification (5)
236
+ }
237
+
238
+ TimeStampToken ::= ContentInfo
239
+
240
+ ContentInfo ::= SEQUENCE {
241
+ contentType ContentType,
242
+ content [0] EXPLICIT ANY DEFINED BY contentType
243
+ }
244
+
245
+ ContentType ::= OBJECT IDENTIFIER
246
+
247
+ SignedData ::= SEQUENCE {
248
+ version CMSVersion,
249
+ digestAlgorithms SET OF DigestAlgorithmIdentifier, // [X]
250
+ encapContentInfo EncapsulatedContentInfo,
251
+ certificates [0] IMPLICIT CertificateSet OPTIONAL,
252
+ crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
253
+ signerInfos SET OF SignerInfo // [X]
254
+ }
255
+
256
+ EncapsulatedContentInfo ::= SEQUENCE {
257
+ eContentType ContentType,
258
+ eContent [0] EXPLICIT OCTET STRING OPTIONAL
259
+ }
260
+
261
+ TSTInfo ::= SEQUENCE {
262
+ version INTEGER { v1(1) },
263
+ policy TSAPolicyId,
264
+ messageImprint MessageImprint,
265
+ serialNumber INTEGER,
266
+ genTime GeneralizedTime,
267
+ accuracy Accuracy OPTIONAL,
268
+ ordering BOOLEAN DEFAULT FALSE,
269
+ nonce INTEGER OPTIONAL,
270
+ tsa [0] GeneralName OPTIONAL,
271
+ extensions [1] IMPLICIT Extensions OPTIONAL
272
+ }
273
+
274
+ Bits: 8 7 | 6 | 5 4 3 2 1
275
+ Class | P/C | Tag number
276
+ */
@@ -0,0 +1,44 @@
1
+ declare const _default: {
2
+ encodeSmall: () => void;
3
+ encode7F: () => void;
4
+ encode80: () => void;
5
+ encodeFF: () => void;
6
+ encode103: () => void;
7
+ ed: ((() => void) | {
8
+ x80: () => void;
9
+ x100: () => void;
10
+ x1000: () => void;
11
+ x10000: () => void;
12
+ x20000: () => void;
13
+ x40000: () => void;
14
+ x80000: () => void;
15
+ xC0000: () => void;
16
+ xE0000: () => void;
17
+ xF0000: () => void;
18
+ xFFF00: () => void;
19
+ xFFF80: () => void;
20
+ xFFFC0: () => void;
21
+ xFFFD0: () => void;
22
+ })[];
23
+ integerValue: {
24
+ zero: () => void;
25
+ one: () => void;
26
+ minusOne: () => void;
27
+ x7F: () => void;
28
+ x80: () => void;
29
+ xFF: () => void;
30
+ nx7F: () => void;
31
+ nx80: () => void;
32
+ nx81: () => void;
33
+ nx7FFF: () => void;
34
+ nx8000: () => void;
35
+ nx8001: () => void;
36
+ };
37
+ encodeDecode: {
38
+ integer: () => void;
39
+ sequence: () => void;
40
+ set: () => void;
41
+ };
42
+ raw: (() => void)[];
43
+ };
44
+ export default _default;
@@ -0,0 +1,312 @@
1
+ import { empty, length, listToVec, msb, uint, unpack, vec, vec8 } from "../bit_vec/module.f.js";
2
+ import { asBase } from "../nominal/module.f.js";
3
+ import { decodeRaw, decodeInteger, encodeRaw, encodeInteger, integer, encode, decode, constructedSequence, octetString, boolean, constructedSet } from "./module.f.js";
4
+ const { concat, popFront: pop } = msb;
5
+ const cat = listToVec(msb);
6
+ const pop8 = pop(8n);
7
+ const check = (tag, v, rest) => {
8
+ const s = encodeRaw([tag, v]);
9
+ const [[t0, v0], r] = decodeRaw(concat(s)(rest));
10
+ if (t0 !== tag) {
11
+ throw `t0: ${t0}`;
12
+ }
13
+ if (v0 !== v) {
14
+ throw `v0: ${asBase(v0)}`;
15
+ }
16
+ if (r !== rest) {
17
+ throw `r: ${asBase(r)}`;
18
+ }
19
+ };
20
+ const integerValueCheck = (i, v) => {
21
+ const v0 = encodeInteger(i);
22
+ if (v !== v0) {
23
+ throw `encode: ${asBase(v)}, ${asBase(v0)}`;
24
+ }
25
+ const i0 = decodeInteger(v);
26
+ if (i !== i0) {
27
+ throw [i, i0];
28
+ }
29
+ };
30
+ const ch0 = (r, v, rest) => {
31
+ const [r0, rest0] = decode(concat(v)(rest));
32
+ if (rest0 !== rest) {
33
+ throw `rest: ${asBase(rest0)}`;
34
+ }
35
+ const v0 = encode(r);
36
+ const v1 = encode(r0);
37
+ if (v !== v0) {
38
+ throw `encode: ${asBase(v)}, ${asBase(v0)}`;
39
+ }
40
+ if (v !== v1) {
41
+ throw `encode: ${asBase(v)}, ${asBase(v1)}`;
42
+ }
43
+ };
44
+ const ch = (r, v) => {
45
+ ch0(r, v, empty);
46
+ ch0(r, v, vec8(0x23n));
47
+ ch0(r, v, vec(16n)(0x2345n));
48
+ };
49
+ export default {
50
+ encodeSmall: () => {
51
+ const v = vec8(0x13n);
52
+ const x = encodeRaw([integer, v]);
53
+ const lx = length(x);
54
+ if (lx !== 24n) {
55
+ throw lx;
56
+ }
57
+ const [tag, x1] = pop8(x);
58
+ if (tag !== BigInt(integer)) {
59
+ throw tag;
60
+ }
61
+ const [len, x2] = pop8(x1);
62
+ if (len !== 1n) {
63
+ throw len;
64
+ }
65
+ const { length: intLen, uint } = unpack(x2);
66
+ if (intLen !== 8n) {
67
+ throw intLen;
68
+ }
69
+ if (uint !== 0x13n) {
70
+ throw uint;
71
+ }
72
+ },
73
+ encode7F: () => {
74
+ const valueLen = 0x7fn << 3n;
75
+ const value = 0x1234n;
76
+ const v = vec(valueLen)(value);
77
+ const x = encodeRaw([integer, v]);
78
+ const lx = length(x);
79
+ if (lx !== 0x81n << 3n) {
80
+ throw lx;
81
+ }
82
+ const [tag, x1] = pop8(x);
83
+ if (tag !== BigInt(integer)) {
84
+ throw tag;
85
+ }
86
+ const [len, x2] = pop8(x1);
87
+ if (len !== 0x7fn) {
88
+ throw len;
89
+ }
90
+ const { length: intLen, uint } = unpack(x2);
91
+ if (intLen !== valueLen) {
92
+ throw intLen;
93
+ }
94
+ if (uint !== value) {
95
+ throw uint;
96
+ }
97
+ },
98
+ encode80: () => {
99
+ const valueLen = 0x80n << 3n;
100
+ const value = 0x123456n;
101
+ const v = vec(valueLen)(value);
102
+ const x = encodeRaw([integer, v]);
103
+ const lx = length(x);
104
+ if (lx !== 0x83n << 3n) {
105
+ throw lx;
106
+ }
107
+ const [tag, x1] = pop8(x);
108
+ if (tag !== BigInt(integer)) {
109
+ throw tag;
110
+ }
111
+ const [lenLen, x2] = pop8(x1);
112
+ if (lenLen !== 0x81n) {
113
+ throw lenLen;
114
+ }
115
+ const [len, x3] = pop8(x2);
116
+ if (len !== 0x80n) {
117
+ throw len;
118
+ }
119
+ const { length: intLen, uint } = unpack(x3);
120
+ if (intLen !== valueLen) {
121
+ throw intLen;
122
+ }
123
+ if (uint !== value) {
124
+ throw uint;
125
+ }
126
+ },
127
+ encodeFF: () => {
128
+ const valueLen = 0xffn << 3n;
129
+ const value = 0x123456n;
130
+ const v = vec(valueLen)(value);
131
+ const x = encodeRaw([integer, v]);
132
+ const lx = length(x);
133
+ if (lx !== valueLen + (3n << 3n)) {
134
+ throw `lx: ${lx}`;
135
+ }
136
+ const [tag, x1] = pop8(x);
137
+ if (tag !== BigInt(integer)) {
138
+ throw tag;
139
+ }
140
+ const [lenLen, x2] = pop8(x1);
141
+ if (lenLen !== 0x81n) {
142
+ throw lenLen;
143
+ }
144
+ const [len, x3] = pop8(x2);
145
+ if (len !== 0xffn) {
146
+ throw len;
147
+ }
148
+ const { length: intLen, uint } = unpack(x3);
149
+ if (intLen !== valueLen) {
150
+ throw intLen;
151
+ }
152
+ if (uint !== value) {
153
+ throw uint;
154
+ }
155
+ },
156
+ encode103: () => {
157
+ const valueLen = 0x103n << 3n;
158
+ const value = 0x123456n;
159
+ const v = vec(valueLen)(value);
160
+ const x = encodeRaw([integer, v]);
161
+ const lx = length(x);
162
+ if (lx !== valueLen + (4n << 3n)) {
163
+ throw `lx: ${lx}`;
164
+ }
165
+ const [tag, x1] = pop8(x);
166
+ if (tag !== BigInt(integer)) {
167
+ throw tag;
168
+ }
169
+ const [lenLen, x2] = pop8(x1);
170
+ if (lenLen !== 0x82n) {
171
+ throw lenLen;
172
+ }
173
+ const [len, x3] = pop(16n)(x2);
174
+ if (len !== 0x103n) {
175
+ throw len;
176
+ }
177
+ const { length: intLen, uint } = unpack(x3);
178
+ if (intLen !== valueLen) {
179
+ throw intLen;
180
+ }
181
+ if (uint !== value) {
182
+ throw uint;
183
+ }
184
+ },
185
+ ed: [
186
+ () => {
187
+ const tag = integer;
188
+ const v = vec(0x10n)(0x8234n);
189
+ if (asBase(v) !== 0x8234n) {
190
+ throw asBase(v).toString(16);
191
+ }
192
+ check(integer, v, empty);
193
+ },
194
+ {
195
+ x80: () => check(integer, vec(0x80n)(0x8234n), empty),
196
+ x100: () => check(integer, vec(0x100n)(0x8234n), vec8(0x23n)),
197
+ x1000: () => check(integer, vec(0x1000n)(0x8234n), vec8(0x23n)),
198
+ x10000: () => check(integer, vec(0x10000n)(0x8234n), vec8(0x23n)),
199
+ x20000: () => check(integer, vec(0x20000n)(0x8234n), vec8(0x23n)),
200
+ x40000: () => check(integer, vec(0x40000n)(0x8234n), empty),
201
+ x80000: () => check(integer, vec(0x80000n)(0x8234n), empty),
202
+ xC0000: () => check(integer, vec(0xc0000n)(0x8234n), empty),
203
+ xE0000: () => check(integer, vec(0xe0000n)(0x8234n), empty),
204
+ xF0000: () => check(integer, vec(0xf0000n)(0x8234n), empty),
205
+ xFFF00: () => check(integer, vec(0xfff00n)(0x8234n), empty),
206
+ xFFF80: () => check(integer, vec(0xfff80n)(0x8234n), empty),
207
+ xFFFC0: () => check(integer, vec(0xfffc0n)(0x8234n), empty),
208
+ xFFFD0: () => check(integer, vec(0xfffd0n)(0x8234n), empty),
209
+ //// fail on Bun because it has a smaller limit for BigInt
210
+ //xFFFD8: () => check(integer, vec(0xF_FFD8n)(0x8234n), empty),
211
+ //xFFFE0: () => check(integer, vec(0xF_FFE0n)(0x8234n), empty),
212
+ //e100000: () => check(integer, vec(0x10_0000n)(0x8234n), empty),
213
+ //x100000: () => check(integer, vec(0x10_0000n)(0x8234n), vec8(0x23n)),
214
+ //x1000000: () => check(integer, vec(0x100_0000n)(0x8234n), vec8(0x23n)),
215
+ //x10000000: () => check(integer, vec(0x1000_0000n)(0x8234n), vec8(0x23n)),
216
+ //x20000000: () => check(integer, vec(0x2000_0000n)(0x8234n), vec8(0x23n)),
217
+ //x30000000: () => check(integer, vec(0x3000_0000n)(0x8234n), vec8(0x23n)),
218
+ //x38000000: () => check(integer, vec(0x3800_0000n)(0x8234n), vec8(0x23n)),
219
+ //x3C000000: () => check(integer, vec(0x3800_0000n)(0x8234n), vec8(0x23n)),
220
+ //x3E000000: () => check(integer, vec(0x3E00_0000n)(0x8234n), vec8(0x23n)),
221
+ //x3F000000: () => check(integer, vec(0x3F00_0000n)(0x8234n), vec8(0x23n)),
222
+ //x3F800000: () => check(integer, vec(0x3F80_0000n)(0x8234n), vec8(0x23n)),
223
+ //x3FFFFFC0: () => check(integer, vec(0x3FFF_FFC0n)(0x8234n), vec8(0x23n)),
224
+ //e3FFFFFC8: () => check(integer, vec(0x3FFF_FFC8n)(0x8234n), empty),
225
+ //// fail on Node
226
+ //x3FFFFFC8: () => check(integer, vec(0x3FFF_FFC1n)(0x8234n), vec8(0x23n)),
227
+ //x40000000: () => check(integer, vec(0x4000_0000n)(0x8234n), vec8(0x23n)),
228
+ // check(integer, vec(0x1_0000_0000n)(0x8234n), vec8(0x23n))
229
+ },
230
+ ],
231
+ integerValue: {
232
+ zero: () => integerValueCheck(0n, vec8(0n)),
233
+ one: () => integerValueCheck(1n, vec8(1n)),
234
+ minusOne: () => integerValueCheck(-1n, vec8(0xffn)),
235
+ x7F: () => integerValueCheck(0x7fn, vec8(0x7fn)),
236
+ x80: () => integerValueCheck(0x80n, vec(16n)(0x80n)),
237
+ xFF: () => integerValueCheck(0xffn, vec(16n)(0xffn)),
238
+ nx7F: () => integerValueCheck(-0x7fn, vec8(0x81n)),
239
+ nx80: () => integerValueCheck(-0x80n, vec8(0x80n)),
240
+ nx81: () => integerValueCheck(-0x81n, vec(16n)(0xff7fn)),
241
+ nx7FFF: () => integerValueCheck(-0x7fffn, vec(16n)(0x8001n)),
242
+ nx8000: () => integerValueCheck(-0x8000n, vec(16n)(0x8000n)),
243
+ nx8001: () => integerValueCheck(-0x8001n, vec(24n)(0xff7fffn)),
244
+ },
245
+ encodeDecode: {
246
+ integer: () => {
247
+ ch([integer, 0n], cat([vec8(BigInt(integer)), vec8(1n), vec8(0n)]));
248
+ ch([integer, 1n], cat([vec8(BigInt(integer)), vec8(1n), vec8(1n)]));
249
+ },
250
+ sequence: () => {
251
+ ch([constructedSequence, []], cat([vec8(BigInt(constructedSequence)), vec8(0n)]));
252
+ ch([constructedSequence, [[integer, 0n]]], cat([vec8(BigInt(constructedSequence)), vec8(3n), encode([integer, 0n])]));
253
+ ch([constructedSequence, [[integer, 1n], [integer, 2n]]], cat([
254
+ vec8(BigInt(constructedSequence)),
255
+ vec8(6n),
256
+ encode([integer, 1n]),
257
+ encode([integer, 2n])
258
+ ]));
259
+ ch([constructedSequence, [[octetString, vec8(0x23n)], [boolean, true], [boolean, false]]], cat([
260
+ vec8(BigInt(constructedSequence)),
261
+ vec8(9n),
262
+ encode([octetString, vec8(0x23n)]),
263
+ encode([boolean, true]),
264
+ encode([boolean, false]),
265
+ ]));
266
+ },
267
+ set: () => {
268
+ ch([constructedSet, [[integer, 2n], [integer, 1n]]], cat([
269
+ vec8(BigInt(constructedSet)),
270
+ vec8(6n),
271
+ encode([integer, 1n]),
272
+ encode([integer, 2n])
273
+ ]));
274
+ },
275
+ },
276
+ raw: [
277
+ () => {
278
+ const e = encodeRaw([0x00n, vec8(0x23n)]);
279
+ if (e !== cat([vec8(0x00n), vec8(1n), vec8(0x23n)])) {
280
+ throw `encode: ${length(e)}: ${uint(e).toString(2)}`;
281
+ }
282
+ const [[tag, value], rest] = decodeRaw(e);
283
+ if (rest !== empty) {
284
+ throw `rest: ${asBase(rest)}`;
285
+ }
286
+ if (tag !== 0x00n) {
287
+ throw `tag: ${tag}`;
288
+ }
289
+ if (value !== vec8(0x23n)) {
290
+ throw `value: ${asBase(value)}`;
291
+ }
292
+ },
293
+ () => {
294
+ const e = encodeRaw([0x1f20n, vec(16n)(0x1234n)]);
295
+ if (e !== cat([vec8(0x1fn), vec8(0x20n), vec8(2n), vec(16n)(0x1234n)])) {
296
+ const l = length(e);
297
+ const u = uint(e);
298
+ throw `encode: ${l}: ${u.toString(16)}`;
299
+ }
300
+ const [[tag, value], rest] = decodeRaw(e);
301
+ if (rest !== empty) {
302
+ throw `rest: ${asBase(rest)}`;
303
+ }
304
+ if (tag !== 0x1f20n) {
305
+ throw `tag: ${tag}`;
306
+ }
307
+ if (value !== vec(16n)(0x1234n)) {
308
+ throw `value: ${asBase(value)}`;
309
+ }
310
+ }
311
+ ]
312
+ };
@@ -0,0 +1,15 @@
1
+ import { type Vec } from '../bit_vec/module.f.ts';
2
+ /**
3
+ * Encodes a bigint into an MSB Base128 vector.
4
+ *
5
+ * @param uint The bigint to encode.
6
+ * @returns The encoded MSB Base128 vector.
7
+ */
8
+ export declare const encode: (uint: bigint) => Vec;
9
+ /**
10
+ * Decodes an MSB Base128 vector into a bigint.
11
+ *
12
+ * @param v The MSB Base128 vector to decode.
13
+ * @returns A tuple containing the decoded bigint and the remaining vector.
14
+ */
15
+ export declare const decode: (v: Vec) => readonly [bigint, Vec];
@@ -0,0 +1,38 @@
1
+ import { vec8, msb, empty } from "../bit_vec/module.f.js";
2
+ const { concat, popFront } = msb;
3
+ const pop8 = popFront(8n);
4
+ /**
5
+ * Encodes a bigint into an MSB Base128 vector.
6
+ *
7
+ * @param uint The bigint to encode.
8
+ * @returns The encoded MSB Base128 vector.
9
+ */
10
+ export const encode = (uint) => {
11
+ let result = empty;
12
+ while (true) {
13
+ const item = uint & 0x7fn;
14
+ const flag = result === empty ? 0n : 0x80n;
15
+ result = concat(vec8(flag | item))(result);
16
+ uint >>= 7n;
17
+ if (uint === 0n) {
18
+ return result;
19
+ }
20
+ }
21
+ };
22
+ /**
23
+ * Decodes an MSB Base128 vector into a bigint.
24
+ *
25
+ * @param v The MSB Base128 vector to decode.
26
+ * @returns A tuple containing the decoded bigint and the remaining vector.
27
+ */
28
+ export const decode = (v) => {
29
+ let result = 0n;
30
+ while (true) {
31
+ const [byte, rest] = pop8(v);
32
+ result = (result << 7n) | (byte & 0x7fn);
33
+ if (byte < 0x80n) {
34
+ return [result, rest];
35
+ }
36
+ v = rest;
37
+ }
38
+ };
@@ -0,0 +1,2 @@
1
+ declare const _default: () => void;
2
+ export default _default;
@@ -0,0 +1,26 @@
1
+ import { empty, vec, vec8 } from "../bit_vec/module.f.js";
2
+ import { asBase } from "../nominal/module.f.js";
3
+ import { encode, decode } from "./module.f.js";
4
+ const test = (a, b) => {
5
+ const encoded = encode(a);
6
+ if (encoded !== b) {
7
+ throw `encoded: ${asBase(encoded).toString(16)}, expected: ${asBase(b).toString(16)}`;
8
+ }
9
+ const [decoded, rest] = decode(b);
10
+ if (decoded !== a) {
11
+ throw `decoded: ${decoded.toString(16)}, expected: ${a.toString(16)}`;
12
+ }
13
+ if (rest !== empty) {
14
+ throw `rest: ${asBase(rest).toString(16)}, expected: ${asBase(empty).toString(16)}`;
15
+ }
16
+ };
17
+ export default () => {
18
+ test(0n, vec8(0n));
19
+ test(1n, vec8(1n));
20
+ test(0x7fn, vec8(0x7fn));
21
+ test(0x80n, vec(16n)(0x8100n));
22
+ test(0x81n, vec(16n)(0x8101n));
23
+ test(0x82n, vec(16n)(0x8102n));
24
+ test(0x3fffn, vec(16n)(0xff7fn));
25
+ test(0x4000n, vec(24n)(0x818000n));
26
+ };
@@ -1,6 +1,7 @@
1
- import type { Fold, Reduce as OpReduce } from "../function/operator/module.f.ts";
2
- import { type List, type Thunk } from "../list/module.f.ts";
3
- import { type Nominal } from "../nominal/module.f.ts";
1
+ import type { Fold, Reduce as OpReduce } from '../function/operator/module.f.ts';
2
+ import { type List, type Thunk } from '../list/module.f.ts';
3
+ import { type Nominal } from '../nominal/module.f.ts';
4
+ import { type Sign } from '../function/compare/module.f.ts';
4
5
  /**
5
6
  * A vector of bits represented as a signed `bigint`.
6
7
  */
@@ -186,3 +187,13 @@ export declare const listToVec: ({ concat }: BitOrder) => (list: List<Vec>) => V
186
187
  */
187
188
  export declare const repeat: Fold<bigint, Vec>;
188
189
  export declare const isVec: <T>(v: Vec | T) => v is Vec;
190
+ /**
191
+ * Lexically compares two vectors based on their unsigned integer values,
192
+ * normalizing them to the same length. If the values are equal,
193
+ * the shorter vector is considered smaller.
194
+ *
195
+ * a < b => -1
196
+ * a > b => 1
197
+ * a === b => 0
198
+ */
199
+ export declare const msbCmp: (av: Vec) => (bv: Vec) => Sign;
@@ -21,11 +21,12 @@
21
21
  *
22
22
  * @module
23
23
  */
24
- import { abs, bitLength, mask, max, xor } from "../bigint/module.f.js";
24
+ import { bitLength, mask, max, min, xor } from "../bigint/module.f.js";
25
25
  import { flip } from "../function/module.f.js";
26
26
  import { fold } from "../list/module.f.js";
27
27
  import { asBase, asNominal } from "../nominal/module.f.js";
28
28
  import { repeat as mRepeat } from "../monoid/module.f.js";
29
+ import { cmp } from "../function/compare/module.f.js";
29
30
  /**
30
31
  * An empty vector of bits.
31
32
  */
@@ -55,7 +56,7 @@ export const vec = (len) => {
55
56
  const lastBit = 1n << last;
56
57
  return ui => {
57
58
  // normalize `u`
58
- const u = m & abs(ui);
59
+ const u = m & ui;
59
60
  //
60
61
  const sign = u >> last;
61
62
  const x = sign !== 0n ? u : -(u ^ lastBit);
@@ -203,3 +204,21 @@ export const listToVec = ({ concat }) => fold(flip(concat))(empty);
203
204
  */
204
205
  export const repeat = mRepeat({ identity: empty, operation: lsb.concat });
205
206
  export const isVec = (v) => typeof v === 'bigint';
207
+ /**
208
+ * Lexically compares two vectors based on their unsigned integer values,
209
+ * normalizing them to the same length. If the values are equal,
210
+ * the shorter vector is considered smaller.
211
+ *
212
+ * a < b => -1
213
+ * a > b => 1
214
+ * a === b => 0
215
+ */
216
+ export const msbCmp = (av) => (bv) => {
217
+ const au = unpack(av);
218
+ const bu = unpack(bv);
219
+ const al = au.length;
220
+ const bl = bu.length;
221
+ const { a, b } = msbNorm(au)(bu)(min(al)(bl));
222
+ const result = cmp(a)(b);
223
+ return result !== 0 ? result : cmp(al)(bl);
224
+ };
@@ -37,5 +37,6 @@ declare const _default: {
37
37
  lsbXor: () => void;
38
38
  msbXor: () => void;
39
39
  repeat: () => void;
40
+ msbCmp: () => void;
40
41
  };
41
42
  export default _default;
@@ -1,6 +1,6 @@
1
- import { abs, mask } from "../bigint/module.f.js";
1
+ import { mask } from "../bigint/module.f.js";
2
2
  import { asBase, asNominal } from "../nominal/module.f.js";
3
- import { length, empty, uint, vec, lsb, msb, repeat, vec8 } from "./module.f.js";
3
+ import { length, empty, uint, vec, lsb, msb, repeat, vec8, msbCmp } from "./module.f.js";
4
4
  const unsafeVec = (a) => asNominal(a);
5
5
  // 0x8 = 0b1000 = 0 + 8
6
6
  // 0x9 = 0b1001 = 1 + 8
@@ -347,16 +347,16 @@ export default {
347
347
  const v = vec(len)(ui);
348
348
  const x = asBase(v);
349
349
  if (x !== raw) {
350
- throw x;
350
+ throw `x: ${x}, raw: ${raw}`;
351
351
  }
352
352
  const len2 = length(v);
353
353
  if (len2 !== len) {
354
354
  throw len2;
355
355
  }
356
356
  const u = uint(v);
357
- const mui = mask(len) & abs(ui);
357
+ const mui = mask(len) & ui;
358
358
  if (u !== mui) {
359
- throw u;
359
+ throw [u, mui];
360
360
  }
361
361
  };
362
362
  // 0n
@@ -373,7 +373,15 @@ export default {
373
373
  () => c(2n)(1n)(-3n),
374
374
  () => c(2n)(2n)(2n),
375
375
  () => c(2n)(3n)(3n),
376
- () => c(2n)(-7n)(3n), //< overflow
376
+ // -1 is 0b.....1
377
+ // -2 is 0b....10
378
+ // -3 is 0b...101
379
+ // -4 is 0b...100
380
+ // -5 is 0b..1011
381
+ // -6 is 0b..1010
382
+ // -7 is 0b..1011, cut 0b11, vec: -0b11
383
+ // -8 is 0b..1100 = ~(8-1) = ~7
384
+ () => c(2n)(-7n)(-3n), //< overflow.
377
385
  ];
378
386
  },
379
387
  concat2: () => {
@@ -420,5 +428,19 @@ export default {
420
428
  if (repeat(7n)(vec(5n)(0x13n)) !== vec(35n)(21059194483n)) {
421
429
  throw 'repeat failed';
422
430
  }
431
+ },
432
+ msbCmp: () => {
433
+ const c = (a) => (b) => (r) => {
434
+ const result = msbCmp(a)(b);
435
+ if (result !== r) {
436
+ throw `result: ${result}, expected: ${r}`;
437
+ }
438
+ };
439
+ c(vec(4n)(0x5n))(vec(4n)(0x5n))(0); // 0b0101 == 0b0101
440
+ c(vec(4n)(0x5n))(vec(4n)(0x6n))(-1); // 0b0101 < 0b0110
441
+ c(vec(4n)(0x6n))(vec(4n)(0x5n))(1); // 0b0110 > 0b0101
442
+ c(vec(4n)(0x5n))(vec(5n)(0x5n))(1); // 0b0101_ < 0b00101
443
+ c(vec(5n)(0x5n))(vec(4n)(0x5n))(-1); // 0b00101 < 0b0101_
444
+ c(vec(4n)(0x5n))(vec(5n)(0xan))(-1); // 0b0101_ < 0b01010
423
445
  }
424
446
  };
@@ -2,6 +2,10 @@ import type { Vec } from '../../bit_vec/module.f.ts';
2
2
  import type { Result } from '../../result/module.f.ts';
3
3
  import { type Do, type Effect, type ToAsyncOperationMap } from '../module.f.ts';
4
4
  export type IoResult<T> = Result<T, unknown>;
5
+ export type Fetch = {
6
+ readonly fetch: readonly [string, IoResult<Vec>];
7
+ };
8
+ export declare const fetch: (url: string) => Do<Fetch, IoResult<Vec>>;
5
9
  export type MakeDirectoryOptions = {
6
10
  readonly recursive: true;
7
11
  };
@@ -46,7 +50,7 @@ export type Log = {
46
50
  };
47
51
  export declare const log: (msg: string) => Do<Log, void>;
48
52
  export type Console = Log & Error;
49
- export type NodeOperations = Console & Fs;
53
+ export type NodeOperations = Fetch & Console & Fs;
50
54
  export type NodeEffect<T> = Effect<NodeOperations, T>;
51
55
  export type NodeOperationMap = ToAsyncOperationMap<NodeOperations>;
52
56
  export type NodeProgram = (argv: readonly string[]) => NodeEffect<number>;
@@ -1,4 +1,5 @@
1
1
  import { do_ } from "../module.f.js";
2
+ export const fetch = (url) => do_('fetch', url);
2
3
  export const mkdir = (...p) => do_('mkdir', p);
3
4
  export const readFile = (path) => do_('readFile', path);
4
5
  export const readdir = (...p) => do_('readdir', p);
@@ -1,5 +1,6 @@
1
1
  declare const _default: {
2
2
  map: () => void;
3
+ fetch: () => void;
3
4
  mkdir: {
4
5
  one: () => void;
5
6
  rec: () => void;
@@ -1,6 +1,6 @@
1
1
  import { empty, isVec, uint, vec8 } from "../../bit_vec/module.f.js";
2
2
  import { run } from "../mock/module.f.js";
3
- import { mkdir, readdir, readFile, writeFile } from "./module.f.js";
3
+ import { fetch, mkdir, readdir, readFile, writeFile } from "./module.f.js";
4
4
  import { emptyState, virtual } from "./virtual/module.f.js";
5
5
  export default {
6
6
  map: () => {
@@ -29,6 +29,24 @@ export default {
29
29
  e = cont(['ok', vec8(0x15n)]);
30
30
  }
31
31
  },
32
+ fetch: () => {
33
+ const v = run(virtual);
34
+ const [_, [t, result]] = v({
35
+ ...emptyState,
36
+ internet: {
37
+ 'https://example.com/data': vec8(0x2an),
38
+ },
39
+ })(fetch('https://example.com/data'));
40
+ if (t === 'error') {
41
+ throw result;
42
+ }
43
+ if (!isVec(result)) {
44
+ throw result;
45
+ }
46
+ if (uint(result) !== 0x2an) {
47
+ throw result;
48
+ }
49
+ },
32
50
  mkdir: {
33
51
  one: () => {
34
52
  const v = run(virtual);
@@ -8,6 +8,9 @@ export type VirtualState = {
8
8
  stdout: string;
9
9
  stderr: string;
10
10
  root: VirtualDir;
11
+ internet: {
12
+ readonly [url: string]: Vec;
13
+ };
11
14
  };
12
15
  export declare const emptyState: VirtualState;
13
16
  export declare const virtual: MemOperationMap<NodeOperations, VirtualState>;
@@ -5,6 +5,7 @@ export const emptyState = {
5
5
  stdout: '',
6
6
  stderr: '',
7
7
  root: {},
8
+ internet: {},
8
9
  };
9
10
  const operation = (op) => {
10
11
  const f = (dir, path) => {
@@ -91,6 +92,10 @@ const console = (name) => (state, payload) => [{ ...state, [name]: `${state[name
91
92
  export const virtual = {
92
93
  error: console('stderr'),
93
94
  log: console('stdout'),
95
+ fetch: (state, url) => {
96
+ const result = state.internet[url];
97
+ return result === undefined ? [state, error('not found')] : [state, ok(result)];
98
+ },
94
99
  mkdir: (state, [path, p]) => mkdir(p !== undefined)(state, path),
95
100
  readFile,
96
101
  readdir: (state, [path, { recursive }]) => readdir(path, recursive === true)(state, path),