@rust-js/runtime 0.0.2 → 0.0.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.
Files changed (2) hide show
  1. package/index.js +619 -13
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // @rust-js/runtime 0.0.2: the helpers the JS rust-js writes imports (ADR 0103).
1
+ // @rust-js/runtime 0.0.3: the helpers the JS rust-js writes imports (ADR 0103).
2
2
  // Generated by `rust-js --runtime-module`. Do not edit.
3
3
  export function $traitImpl(cache, keys, make) {
4
4
  // Each key is a dictionary or a drop, held weakly: a drop made for one call
@@ -93,6 +93,172 @@ export function $displayF64(value) {
93
93
  throw new Error('No f64 round-trip decimal found');
94
94
  }
95
95
 
96
+ // The shortest digits of the positive `f32` `x` that read back as `x`, the
97
+ // nearest to it of those, as Rust's: `[digits, point]`, `x` being `0.digits`
98
+ // times 10 to `point` (ADR 0122). A decimal reads back as `x` when it's in
99
+ // `x`'s rounding interval, at an end of it only when `x`'s significand is
100
+ // even, as a tie rounds to an even one. Tested exactly: through an `f64`,
101
+ // `Math.fround(Number(text))` rounds twice, which can miss `x`.
102
+ export function $f32Digits(x) {
103
+ const view = new DataView(new ArrayBuffer(4));
104
+ view.setFloat32(0, x);
105
+ const bits = view.getUint32(0);
106
+ const exponentBits = bits >>> 23;
107
+ const fraction = bits & 0x7fffff;
108
+ const significand = BigInt(exponentBits === 0 ? fraction : fraction | 0x800000);
109
+ const exponent2 = exponentBits === 0 ? -149 : exponentBits - 150;
110
+ // In quarters of the last place: `x` is 4m of them, and its interval's ends
111
+ // 2 either side, or 1 below a power of two, where the gap below is half.
112
+ const quarters = 4n * significand;
113
+ const below = fraction === 0 && exponentBits > 1 ? 1n : 2n;
114
+ const even = (significand & 1n) === 0n;
115
+ const quarterNum = exponent2 >= 2 ? 1n << BigInt(exponent2 - 2) : 1n;
116
+ const quarterDen = exponent2 < 2 ? 1n << BigInt(2 - exponent2) : 1n;
117
+ // `c` times 10 to `p`, against `k` quarters.
118
+ const compare = (c, p, k) => {
119
+ const left = p >= 0 ? c * 10n ** BigInt(p) * quarterDen : c * quarterDen;
120
+ const right = p >= 0 ? k * quarterNum : k * quarterNum * 10n ** BigInt(-p);
121
+ return left < right ? -1 : left > right ? 1 : 0;
122
+ };
123
+ const readsBack = (c, p) => {
124
+ const low = compare(c, p, quarters - below);
125
+ const high = compare(c, p, quarters + 2n);
126
+ return (low > 0 || (low === 0 && even)) && (high < 0 || (high === 0 && even));
127
+ };
128
+
129
+ // The first digit's power of ten, estimated, then made exact.
130
+ let exponent = Math.floor(Math.log10(x));
131
+ while (compare(1n, exponent, quarters) > 0) exponent--;
132
+ while (compare(1n, exponent + 1, quarters) <= 0) exponent++;
133
+
134
+ for (let precision = 1; precision <= 9; precision++) {
135
+ const power = exponent - precision + 1;
136
+ // `x` over 10 to `power`, as `n / d`, and the integer nearest it.
137
+ const n = power >= 0 ? quarters * quarterNum : quarters * quarterNum * 10n ** BigInt(-power);
138
+ const d = power >= 0 ? quarterDen * 10n ** BigInt(power) : quarterDen;
139
+ const rounded = n / d + (2n * (n % d) >= d ? 1n : 0n);
140
+ let best;
141
+ let bestDistance;
142
+ for (const candidate of [rounded, rounded - 1n, rounded + 1n]) {
143
+ if (candidate > 0n && readsBack(candidate, power)) {
144
+ const delta = candidate * d - n;
145
+ const distance = delta < 0n ? -delta : delta;
146
+ if (best === undefined || distance < bestDistance || (distance === bestDistance && candidate > best)) {
147
+ best = candidate;
148
+ bestDistance = distance;
149
+ }
150
+ }
151
+ }
152
+ if (best !== undefined) {
153
+ let digits = best.toString();
154
+ let decimalPower = power;
155
+ while (digits.endsWith("0")) {
156
+ digits = digits.slice(0, -1);
157
+ decimalPower++;
158
+ }
159
+ return [digits, digits.length + decimalPower];
160
+ }
161
+ }
162
+ throw new Error("No f32 round-trip decimal found");
163
+ }
164
+
165
+ export function $displayF32(value) {
166
+ if (Number.isNaN(value)) return "NaN";
167
+ if (value === Infinity) return "inf";
168
+ if (value === -Infinity) return "-inf";
169
+ const sign = value < 0 || Object.is(value, -0) ? "-" : "";
170
+ if (value === 0) return sign + "0";
171
+ const [digits, point] = $f32Digits(Math.abs(value));
172
+ const body =
173
+ point <= 0
174
+ ? "0." + "0".repeat(-point) + digits
175
+ : point >= digits.length
176
+ ? digits + "0".repeat(point - digits.length)
177
+ : digits.slice(0, point) + "." + digits.slice(point);
178
+ return sign + body;
179
+ }
180
+
181
+ export function $debugF32(value) {
182
+ const size = Math.abs(value);
183
+ if (Number.isFinite(value) && size !== 0 && (size < Math.fround(1e-4) || size >= Math.fround(1e16))) {
184
+ const [digits, point] = $f32Digits(size);
185
+ const mantissa = digits.length > 1 ? digits[0] + "." + digits.slice(1) : digits;
186
+ return (value < 0 ? "-" : "") + mantissa + "e" + (point - 1);
187
+ }
188
+ const text = $displayF32(value);
189
+ return Number.isFinite(value) && !text.includes(".") ? text + ".0" : text;
190
+ }
191
+
192
+ export function $bigToF32(n) {
193
+ const negative = n < 0n;
194
+ let size = negative ? -n : n;
195
+ const length = size.toString(2).length;
196
+ if (length > 24) {
197
+ const shift = BigInt(length - 24);
198
+ const rest = size & ((1n << shift) - 1n);
199
+ const half = 1n << (shift - 1n);
200
+ size >>= shift;
201
+ if (rest > half || (rest === half && (size & 1n) === 1n)) size++;
202
+ size <<= shift;
203
+ }
204
+ const x = Number(size);
205
+ return negative ? -x : x;
206
+ }
207
+
208
+ export function $powiF32(x, n) {
209
+ const reciprocal = n < 0;
210
+ let result = 1;
211
+ while (true) {
212
+ if (n & 1) {
213
+ result = Math.fround(result * x);
214
+ }
215
+ n = (n / 2) | 0;
216
+ if (n === 0) {
217
+ break;
218
+ }
219
+ x = Math.fround(x * x);
220
+ }
221
+ return reciprocal ? Math.fround(1 / result) : result;
222
+ }
223
+
224
+ export function $parseF32(s) {
225
+ const read = $parseF64(s);
226
+ const d = read._0;
227
+ if (read.TAG === "Err" || !Number.isFinite(d) || Math.fround(d) === d) {
228
+ return read.TAG === "Err" ? read : { TAG: "Ok", _0: Math.fround(d) };
229
+ }
230
+ const rounded = Math.fround(d);
231
+ // The `f32` this side of `d`: the largest where it rounds to infinity.
232
+ const near = Number.isFinite(rounded) ? rounded : Math.sign(d) * 3.4028234663852886e38;
233
+ const size = Math.abs(near);
234
+ // The `f32` on `d`'s other side, and the tie between them: past the
235
+ // largest, where infinity is, it's as far above as the one below is.
236
+ const view = new DataView(new ArrayBuffer(8));
237
+ view.setFloat32(0, size);
238
+ const bits = view.getUint32(0);
239
+ view.setUint32(0, Math.abs(d) > size ? bits + 1 : bits - 1);
240
+ const other = view.getFloat32(0);
241
+ view.setUint32(0, bits - 1);
242
+ const tie = other === Infinity ? size + (size - view.getFloat32(0)) / 2 : (size + other) / 2;
243
+ if (Math.abs(d) !== tie) return { TAG: "Ok", _0: rounded };
244
+ // The digits, `digits` times 10 to `exponent`, against the tie, `m`
245
+ // times 2 to `k`, in integers.
246
+ const [mantissa, power = "0"] = s.toLowerCase().replace(/^[+-]/, "").split("e");
247
+ const [whole, fraction = ""] = mantissa.split(".");
248
+ const digits = BigInt(whole + fraction || "0");
249
+ const exponent = Number(power) - fraction.length;
250
+ view.setFloat64(0, tie);
251
+ const tieBits = view.getBigUint64(0);
252
+ const m = (tieBits & ((1n << 52n) - 1n)) | (1n << 52n);
253
+ const k = Number(tieBits >> 52n) - 1075;
254
+ const left = digits * 10n ** BigInt(Math.max(exponent, 0)) * 2n ** BigInt(Math.max(-k, 0));
255
+ const right = m * 2n ** BigInt(Math.max(k, 0)) * 10n ** BigInt(Math.max(-exponent, 0));
256
+ // The tie itself goes to the even one, as `Math.fround` takes it.
257
+ if (left === right) return { TAG: "Ok", _0: rounded };
258
+ const chosen = left > right ? Math.max(size, other) : Math.min(size, other);
259
+ return { TAG: "Ok", _0: d < 0 ? -chosen : chosen };
260
+ }
261
+
96
262
  export function $f64Max(a, b) {
97
263
  return Number.isNaN(a) ? b : Number.isNaN(b) ? a : Math.max(a, b);
98
264
  }
@@ -265,6 +431,129 @@ export function $orInsertWith(map, key, make) {
265
431
  return map.get(key);
266
432
  }
267
433
 
434
+ export function $key(value) {
435
+ if (value == null) {
436
+ return "~";
437
+ }
438
+ switch (typeof value) {
439
+ case "string":
440
+ return JSON.stringify(value);
441
+ case "bigint":
442
+ return value + "n";
443
+ case "object":
444
+ if (Array.isArray(value)) {
445
+ return "[" + value.map($key).join(",") + "]";
446
+ }
447
+ return "{" + Object.keys(value).sort().map((k) => JSON.stringify(k) + ":" + $key(value[k])).join(",") + "}";
448
+ default:
449
+ return String(value);
450
+ }
451
+ }
452
+
453
+ export class $KeyMap extends Map {
454
+ constructor(entries) {
455
+ super();
456
+ if (entries) {
457
+ for (const [key, value] of entries) {
458
+ this.set(key, value);
459
+ }
460
+ }
461
+ }
462
+ get(key) {
463
+ const entry = super.get($key(key));
464
+ return entry === undefined ? undefined : entry[1];
465
+ }
466
+ set(key, value) {
467
+ const found = $key(key);
468
+ const entry = super.get(found);
469
+ if (entry === undefined) {
470
+ super.set(found, [key, value]);
471
+ } else {
472
+ entry[1] = value;
473
+ }
474
+ return this;
475
+ }
476
+ has(key) {
477
+ return super.has($key(key));
478
+ }
479
+ delete(key) {
480
+ return super.delete($key(key));
481
+ }
482
+ *entries() {
483
+ for (const [key, value] of super.values()) {
484
+ yield [key, value];
485
+ }
486
+ }
487
+ *keys() {
488
+ for (const [key] of super.values()) {
489
+ yield key;
490
+ }
491
+ }
492
+ *values() {
493
+ for (const [, value] of super.values()) {
494
+ yield value;
495
+ }
496
+ }
497
+ [Symbol.iterator]() {
498
+ return this.entries();
499
+ }
500
+ forEach(f, that) {
501
+ for (const [key, value] of this.entries()) {
502
+ f.call(that, value, key, this);
503
+ }
504
+ }
505
+ }
506
+
507
+ export class $KeySet extends Set {
508
+ #items = new Map();
509
+ constructor(items) {
510
+ super();
511
+ if (items) {
512
+ for (const item of items) {
513
+ this.add(item);
514
+ }
515
+ }
516
+ }
517
+ get size() {
518
+ return this.#items.size;
519
+ }
520
+ add(item) {
521
+ const found = $key(item);
522
+ if (!this.#items.has(found)) {
523
+ this.#items.set(found, item);
524
+ }
525
+ return this;
526
+ }
527
+ has(item) {
528
+ return this.#items.has($key(item));
529
+ }
530
+ delete(item) {
531
+ return this.#items.delete($key(item));
532
+ }
533
+ clear() {
534
+ this.#items.clear();
535
+ }
536
+ values() {
537
+ return this.#items.values();
538
+ }
539
+ keys() {
540
+ return this.#items.values();
541
+ }
542
+ *entries() {
543
+ for (const item of this.#items.values()) {
544
+ yield [item, item];
545
+ }
546
+ }
547
+ [Symbol.iterator]() {
548
+ return this.#items.values();
549
+ }
550
+ forEach(f, that) {
551
+ for (const item of this.#items.values()) {
552
+ f.call(that, item, item, this);
553
+ }
554
+ }
555
+ }
556
+
268
557
  export function $sortedEntries(map, cmp) {
269
558
  return Array.from(map).sort((a, b) => cmp(a[0], b[0]));
270
559
  }
@@ -571,6 +860,56 @@ export function $bigRange(start, end) {
571
860
  return items;
572
861
  }
573
862
 
863
+ export function* $rangeFrom(start) {
864
+ for (let i = start; ; i++) {
865
+ yield i;
866
+ }
867
+ }
868
+
869
+ export function $rangeNext(range) {
870
+ return range.start < range.end ? range.start++ : undefined;
871
+ }
872
+
873
+ export function $rangeNextBack(range) {
874
+ return range.start < range.end ? --range.end : undefined;
875
+ }
876
+
877
+ export function $rangeFromNext(range) {
878
+ return range.start++;
879
+ }
880
+
881
+ export function $asciiCase(text, upper = false) {
882
+ return upper
883
+ ? text.replace(/[a-z]+/g, (letters) => letters.toUpperCase())
884
+ : text.replace(/[A-Z]+/g, (letters) => letters.toLowerCase());
885
+ }
886
+
887
+ export function $append(items, other) {
888
+ for (const item of other) {
889
+ items.push(item);
890
+ }
891
+ other.length = 0;
892
+ }
893
+
894
+ export function $pretty(open, items, close, rest = false) {
895
+ if (items.length === 0) {
896
+ return open + (rest ? ".." : "") + close;
897
+ }
898
+ const lines = items.map((item) => " " + item.replaceAll("\n", "\n ") + ",\n").join("");
899
+ return open + "\n" + lines + (rest ? " ..\n" : "") + close;
900
+ }
901
+
902
+ export function $charRange(start, end, inclusive = false) {
903
+ const items = [];
904
+ const last = end.codePointAt(0) - (inclusive ? 0 : 1);
905
+ for (let c = start.codePointAt(0); c <= last; c++) {
906
+ if (c < 0xd800 || c > 0xdfff) {
907
+ items.push(String.fromCodePoint(c));
908
+ }
909
+ }
910
+ return items;
911
+ }
912
+
574
913
  export function $tryFromInt(x, lo, hi) {
575
914
  if (x < lo) return { TAG: "Err", _0: "NegOverflow" };
576
915
  if (x > hi) return { TAG: "Err", _0: "PosOverflow" };
@@ -634,7 +973,8 @@ export function $jsonWriter(pretty) {
634
973
  null() { this.text += "null"; },
635
974
  bool(b) { this.text += String(b); },
636
975
  int(n) { this.text += String(n); },
637
- number(x) { this.text += $jsonNumber(x); },
976
+ // An `f32`'s, `single`, with its own digits, as serde_json writes it (ADR 0122).
977
+ number(x, single) { this.text += single ? $jsonF32(x) : $jsonNumber(x); },
638
978
  char(c) { this.text += JSON.stringify(c); },
639
979
  string(s) { this.text += JSON.stringify(s); },
640
980
  // An externally tagged unit variant: its name.
@@ -666,7 +1006,7 @@ export function $jsonFlat(into) {
666
1006
  null() { if (depth > 0) into.null(); },
667
1007
  bool(b) { top("a boolean"); into.bool(b); },
668
1008
  int(n) { top("an integer"); into.int(n); },
669
- number(x) { top("a float"); into.number(x); },
1009
+ number(x, single) { top("a float"); into.number(x, single); },
670
1010
  char(c) { top("a char"); into.char(c); },
671
1011
  string(s) { top("a string"); into.string(s); },
672
1012
  variant(name) {
@@ -696,6 +1036,22 @@ export function $toJson(value, write, pretty) {
696
1036
  }
697
1037
  return { TAG: "Ok", _0: json.text };
698
1038
  }
1039
+ // An `f32` as serde_json writes one: its shortest digits, zmij's, fixed from
1040
+ // 1e-6 to 1e12, else `1.5e+16` (ADR 0122).
1041
+ export function $jsonF32(x) {
1042
+ if (!Number.isFinite(x)) return "null";
1043
+ if (x === 0) return Object.is(x, -0) ? "-0.0" : "0.0";
1044
+ const [digits, point] = $f32Digits(Math.abs(x));
1045
+ const sign = x < 0 ? "-" : "";
1046
+ const e = point - 1;
1047
+ if (e < -6 || e > 12) {
1048
+ const mantissa = digits.length > 1 ? `${digits[0]}.${digits.slice(1)}` : digits;
1049
+ return `${sign}${mantissa}e${e < 0 ? "-" : "+"}${Math.abs(e)}`;
1050
+ }
1051
+ if (point <= 0) return `${sign}0.${"0".repeat(-point)}${digits}`;
1052
+ if (point >= digits.length) return `${sign}${digits}${"0".repeat(point - digits.length)}.0`;
1053
+ return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
1054
+ }
699
1055
 
700
1056
  // What serde's impls and derives read, the same whatever they read from:
701
1057
  // the text (`$JsonReader`), or a value already read (`$JsonContent`), which
@@ -717,6 +1073,12 @@ export class $JsonDecoder {
717
1073
  return this.deserializeNumber("f64", (n) => Number(n.value));
718
1074
  }
719
1075
 
1076
+ // serde's `f32` takes any number, `as f32`: an integer rounded once, by
1077
+ // `$bigToF32`, and a float, serde_json's `f64`, rounded (ADR 0122).
1078
+ f32() {
1079
+ return this.deserializeNumber("f32", (n) => (n.kind === "f" ? Math.fround(n.value) : $bigToF32(BigInt(n.value))));
1080
+ }
1081
+
720
1082
  string() {
721
1083
  return this.deserializeStr("a string", (s) => s);
722
1084
  }
@@ -2513,7 +2875,7 @@ export class $JsonMissing {
2513
2875
  }
2514
2876
  }
2515
2877
  for (const method of [
2516
- "bool", "int", "f64", "string", "char", "borrowedStr", "unit", "unitStruct", "vec", "tuple", "array", "map",
2878
+ "bool", "int", "f64", "f32", "string", "char", "borrowedStr", "unit", "unitStruct", "vec", "tuple", "array", "map",
2517
2879
  "struct", "untaggedStruct", "tupleStruct", "enum", "taggedUnit", "internallyTagged",
2518
2880
  "adjacentlyTagged", "untagged",
2519
2881
  ]) {
@@ -2615,6 +2977,7 @@ export const $json = {
2615
2977
  u64: (json) => json.int("u64", 0n, 18446744073709551615n),
2616
2978
  i64: (json) => json.int("i64", -9223372036854775808n, 9223372036854775807n),
2617
2979
  f64: (json) => json.f64(),
2980
+ f32: (json) => json.f32(),
2618
2981
  string: (json) => json.string(),
2619
2982
  str: (json) => json.borrowedStr(),
2620
2983
  // serde_json's `Value` (ADR 0083), from whatever's there, and its `Number`.
@@ -2768,16 +3131,22 @@ export function $jsonNumberText(n) {
2768
3131
  }
2769
3132
 
2770
3133
  // serde_json's `Debug`: `Null`, `Bool(true)`, `Number(1)`, `String("a")`,
2771
- // `Array [..]` and `Object {..}`.
2772
- export function $debugJsonValue(value) {
3134
+ // `Array [..]` and `Object {..}`, whose parts `{:#?}` puts on lines of their
3135
+ // own, as a `Vec`'s and a map's.
3136
+ export function $debugJsonValue(value, alternate = false) {
2773
3137
  if (value === "Null") return "Null";
2774
3138
  const { TAG: tag, _0: inner } = value;
2775
3139
  if (tag === "Bool") return `Bool(${inner})`;
2776
3140
  if (tag === "Number") return $debugJsonNumber(inner);
2777
3141
  if (tag === "String") return `String(${$debugStr(inner)})`;
2778
- if (tag === "Array") return `Array [${inner.map($debugJsonValue).join(", ")}]`;
2779
- const entries = $sortedEntries(inner, $cmp).map(([key, item]) => `${$debugStr(key)}: ${$debugJsonValue(item)}`);
2780
- return `Object {${entries.join(", ")}}`;
3142
+ if (tag === "Array") {
3143
+ const items = inner.map((item) => $debugJsonValue(item, alternate));
3144
+ return alternate ? "Array " + $pretty("[", items, "]") : `Array [${items.join(", ")}]`;
3145
+ }
3146
+ const entries = $sortedEntries(inner, $cmp).map(
3147
+ ([key, item]) => `${$debugStr(key)}: ${$debugJsonValue(item, alternate)}`,
3148
+ );
3149
+ return alternate ? "Object " + $pretty("{", entries, "}") : `Object {${entries.join(", ")}}`;
2781
3150
  }
2782
3151
  export function $debugJsonNumber(n) {
2783
3152
  return `Number(${$jsonNumberText(n)})`;
@@ -2831,6 +3200,7 @@ export function $jsonValueEq(value, other, kind) {
2831
3200
  if (value.TAG !== "Number") return false;
2832
3201
  const n = value._0;
2833
3202
  if (kind === "f64") return Number(n.value) === other;
3203
+ if (kind === "f32") return (n.kind === "f" ? Math.fround(n.value) : $bigToF32(BigInt(n.value))) === other;
2834
3204
  if (kind === "i64") return $jsonNumberIsI64(n) && n.value == other;
2835
3205
  return n.kind === "u" && n.value == other;
2836
3206
  }
@@ -2886,6 +3256,57 @@ export function $debugJsonError(e) {
2886
3256
  return `Error(${$debugStr(e.message)}, line: ${e.line}, column: ${e.column})`;
2887
3257
  }
2888
3258
 
3259
+ // std's error of a message, which `Box<dyn Error>::from("..")` makes: a
3260
+ // `dyn Error`'s dictionary, whose `value` is the message (ADR 0141).
3261
+ export function $stringError() {
3262
+ return {
3263
+ Debug: () => ({ fmt: (message) => $debugStr(message) }),
3264
+ Display: () => ({ fmt: (message) => message }),
3265
+ source: () => undefined,
3266
+ };
3267
+ }
3268
+
3269
+ // A channel on one thread is a queue (ADR 0142), shared by its ends: both
3270
+ // are the one object, which counts its senders and knows if its receiver
3271
+ // is still there.
3272
+ export function $channel() {
3273
+ const channel = { queue: [], senders: 1, receiving: true };
3274
+ return [channel, channel];
3275
+ }
3276
+
3277
+ export function $send(channel, item) {
3278
+ if (!channel.receiving) return { TAG: "Err", _0: [item] };
3279
+ channel.queue.push(item);
3280
+ return { TAG: "Ok", _0: undefined };
3281
+ }
3282
+
3283
+ // Rust's `recv` waits for another thread to send; on one, none can, so an
3284
+ // empty channel whose senders are all there would wait forever.
3285
+ export function $recv(channel) {
3286
+ if (channel.queue.length > 0) return { TAG: "Ok", _0: channel.queue.shift() };
3287
+ if (channel.senders === 0) return { TAG: "Err", _0: undefined };
3288
+ throw new Error("`recv` of an empty channel would wait forever: no other thread can send");
3289
+ }
3290
+
3291
+ export function $tryRecv(channel) {
3292
+ if (channel.queue.length > 0) return { TAG: "Ok", _0: channel.queue.shift() };
3293
+ return { TAG: "Err", _0: channel.senders === 0 ? "Disconnected" : "Empty" };
3294
+ }
3295
+
3296
+ export function $cloneSender(channel) {
3297
+ channel.senders += 1;
3298
+ return channel;
3299
+ }
3300
+
3301
+ export function $dropSender(channel) {
3302
+ channel.senders -= 1;
3303
+ }
3304
+
3305
+ export function $dropReceiver(channel) {
3306
+ channel.receiving = false;
3307
+ channel.queue.length = 0;
3308
+ }
3309
+
2889
3310
  export function $lowerExp(x) {
2890
3311
  if (Number.isNaN(x)) return "NaN";
2891
3312
  if (!Number.isFinite(x)) return x > 0 ? "inf" : "-inf";
@@ -3100,6 +3521,64 @@ export function $sliceEnd(items, start, end = items.length) {
3100
3521
  return end;
3101
3522
  }
3102
3523
 
3524
+ export function $byteLen(s) {
3525
+ let bytes = 0;
3526
+ for (let i = 0; i < s.length; i++) {
3527
+ const unit = s.charCodeAt(i);
3528
+ if (unit < 0x80) bytes += 1;
3529
+ else if (unit < 0x800) bytes += 2;
3530
+ else if (unit >= 0xd800 && unit < 0xdc00) {
3531
+ bytes += 4;
3532
+ i++;
3533
+ } else bytes += 3;
3534
+ }
3535
+ return bytes;
3536
+ }
3537
+
3538
+ export function $strSlice(s, start, end) {
3539
+ const length = $byteLen(s);
3540
+ end ??= length;
3541
+ if (start > length) throw new Error(`start byte index ${start} is out of bounds for string of length ${length}`);
3542
+ if (end > length) throw new Error(`end byte index ${end} is out of bounds for string of length ${length}`);
3543
+ if (start > end) throw new Error(`byte range starts at ${start} but ends at ${end}`);
3544
+ return s.slice($unitAt(s, start, "start"), $unitAt(s, end, "end"));
3545
+ }
3546
+
3547
+ export function $unitAt(s, at, which) {
3548
+ let bytes = 0;
3549
+ let unit = 0;
3550
+ for (const c of s) {
3551
+ if (bytes === at) return unit;
3552
+ const next = bytes + $byteLen(c);
3553
+ if (next > at) {
3554
+ throw new Error(`${which} byte index ${at} is not a char boundary; it is inside ${$debugStr(c, "'")} (bytes ${bytes}..${next} of string)`);
3555
+ }
3556
+ bytes = next;
3557
+ unit += c.length;
3558
+ }
3559
+ return unit;
3560
+ }
3561
+
3562
+ export function $find(s, pattern) {
3563
+ const at = s.indexOf(pattern);
3564
+ return at === -1 ? undefined : $byteLen(s.slice(0, at));
3565
+ }
3566
+
3567
+ export function $rfind(s, pattern) {
3568
+ const at = s.lastIndexOf(pattern);
3569
+ return at === -1 ? undefined : $byteLen(s.slice(0, at));
3570
+ }
3571
+
3572
+ export function $charIndices(s) {
3573
+ const indices = [];
3574
+ let at = 0;
3575
+ for (const c of s) {
3576
+ indices.push([at, c]);
3577
+ at += $byteLen(c);
3578
+ }
3579
+ return indices;
3580
+ }
3581
+
3103
3582
  export function $extend(v, items) {
3104
3583
  for (const item of items) {
3105
3584
  v.push(item);
@@ -3171,6 +3650,78 @@ export function $skipWhile(items, skip) {
3171
3650
  return start < 0 ? [] : items.slice(start);
3172
3651
  }
3173
3652
 
3653
+ export function* $lazyChain(a, b) {
3654
+ yield* a;
3655
+ yield* b;
3656
+ }
3657
+
3658
+ export function* $lazyZip(a, b) {
3659
+ const right = b[Symbol.iterator]();
3660
+ for (const item of a) {
3661
+ const other = right.next();
3662
+ if (other.done) {
3663
+ return;
3664
+ }
3665
+ yield [item, other.value];
3666
+ }
3667
+ }
3668
+
3669
+ export function* $lazyTakeWhile(items, keep) {
3670
+ for (const item of items) {
3671
+ if (!keep(item)) {
3672
+ return;
3673
+ }
3674
+ yield item;
3675
+ }
3676
+ }
3677
+
3678
+ export function* $lazySkipWhile(items, skip) {
3679
+ let skipping = true;
3680
+ for (const item of items) {
3681
+ if (skipping && skip(item)) {
3682
+ continue;
3683
+ }
3684
+ skipping = false;
3685
+ yield item;
3686
+ }
3687
+ }
3688
+
3689
+ export function* $repeating(value, clone = (value) => value) {
3690
+ while (true) {
3691
+ yield clone(value);
3692
+ }
3693
+ }
3694
+
3695
+ export function* $repeatingWith(f) {
3696
+ while (true) {
3697
+ yield f();
3698
+ }
3699
+ }
3700
+
3701
+ export function* $successors(next, successor, boxed = false) {
3702
+ while (next != null) {
3703
+ const item = boxed ? $someValue(next) : next;
3704
+ next = successor(item);
3705
+ yield item;
3706
+ }
3707
+ }
3708
+
3709
+ export function $fromFn(f, boxed = false) {
3710
+ return Iterator.from({
3711
+ next() {
3712
+ const item = f();
3713
+ if (item == null) {
3714
+ return { done: true, value: undefined };
3715
+ }
3716
+ return { done: false, value: boxed ? $someValue(item) : item };
3717
+ }
3718
+ });
3719
+ }
3720
+
3721
+ export function $unzip(pairs) {
3722
+ return [pairs.map(([a]) => a), pairs.map(([, b]) => b)];
3723
+ }
3724
+
3174
3725
  export function $partition(items, keep) {
3175
3726
  const yes = [];
3176
3727
  const no = [];
@@ -3281,8 +3832,9 @@ export function $debugStr(s, quote = '"') {
3281
3832
  return out + quote;
3282
3833
  }
3283
3834
 
3284
- export function $debugFields(name, fields, values) {
3285
- return name + " { " + fields.map((field, i) => field + ": " + values[i]).join(", ") + " }";
3835
+ export function $debugFields(name, fields, values, alternate = false) {
3836
+ const shown = fields.map((field, i) => field + ": " + values[i]);
3837
+ return alternate ? $pretty(name + " {", shown, "}") : name + " { " + shown.join(", ") + " }";
3286
3838
  }
3287
3839
 
3288
3840
  export function $plus(text) {
@@ -3301,6 +3853,56 @@ export function $pad(text, width, align, fill = " ") {
3301
3853
  return fill.repeat(before) + text + fill.repeat(room - before);
3302
3854
  }
3303
3855
 
3856
+ // A `Formatter`'s options, applied to the text a number, a `bool` or a
3857
+ // string shows as std's `fmt`s apply them (ADR 0058): a number takes a
3858
+ // sign and zeros, a string is cut to its precision, and either is padded.
3859
+ export function $formatted(text, options, numeric = false) {
3860
+ if (options === undefined) return text;
3861
+ const { width, precision } = options;
3862
+ if (numeric && options.plus) text = $plus(text);
3863
+ if (!numeric && precision !== undefined) text = [...text].slice(0, precision).join("");
3864
+ if (width === undefined) return text;
3865
+ if (numeric && options.zero) return $zeroPad(text, width);
3866
+ const align = { Left: "<", Right: ">", Center: "^" }[options.align] ?? (numeric ? ">" : "<");
3867
+ return $pad(text, width, align, options.fill);
3868
+ }
3869
+
3870
+ // A float's precision is its digits; the rest is `$formatted`'s.
3871
+ export function $formatFloat(value, options, show) {
3872
+ const precision = options?.precision;
3873
+ return $formatted(precision === undefined ? show(value) : $toFixed(value, precision), options, true);
3874
+ }
3875
+
3876
+ // A JS iterator lent as a `&mut` (ADR 0071): it steps `it`, and what takes
3877
+ // from it and stops early, as `take` or a loop that breaks does, closes
3878
+ // this, not `it`, which its lender goes on stepping.
3879
+ export function $lent(it) {
3880
+ return Iterator.from({ next: () => it.next() });
3881
+ }
3882
+
3883
+ // `collect()` into a `Result`: the first `Err`, where it stops, as Rust's
3884
+ // does, or `Ok` of all the values.
3885
+ export function $collectResults(items) {
3886
+ const values = [];
3887
+ for (const item of items) {
3888
+ if (item.TAG === "Err") return item;
3889
+ values.push(item._0);
3890
+ }
3891
+ return { TAG: "Ok", _0: values };
3892
+ }
3893
+
3894
+ // `collect()` into an `Option`: `None` at the first `None`, where it stops,
3895
+ // as Rust's does, or all the values, each out of its box where it has one
3896
+ // (ADR 0051).
3897
+ export function $collectOptions(items, boxed = false) {
3898
+ const values = [];
3899
+ for (const item of items) {
3900
+ if (item == null) return undefined;
3901
+ values.push(boxed ? $someValue(item) : item);
3902
+ }
3903
+ return values;
3904
+ }
3905
+
3304
3906
  export function $cmpIn(names, a, b) {
3305
3907
  return $cmp(names.indexOf(a), names.indexOf(b));
3306
3908
  }
@@ -3349,6 +3951,10 @@ export function $min(items) {
3349
3951
  }
3350
3952
 
3351
3953
  export function $position(items, found) {
3352
- const i = items.findIndex(found);
3353
- return i < 0 ? undefined : i;
3954
+ let i = 0;
3955
+ for (const item of items) {
3956
+ if (found(item)) return i;
3957
+ i++;
3958
+ }
3959
+ return undefined;
3354
3960
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rust-js/runtime",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "The helpers the JS rust-js writes imports, released with the compiler, as @rescript/runtime is with ReScript. See docs/decisions/0103-runtime-package.md.",
5
5
  "license": "MIT",
6
6
  "repository": {