@msb235/blits 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -0
- package/README.md +1 -1
- package/dist/bare.js +22 -7
- package/dist/columns.d.ts +2 -1
- package/dist/columns.js +55 -35
- package/dist/crowd.d.ts +8 -4
- package/dist/crowd.js +17 -7
- package/dist/delay.d.ts +3 -0
- package/dist/delay.js +7 -0
- package/dist/due.js +1 -1
- package/dist/easing.d.ts +2 -0
- package/dist/easing.js +4 -0
- package/dist/everyone.d.ts +5 -0
- package/dist/everyone.js +30 -0
- package/dist/fill.js +1 -1
- package/dist/held.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/lane.d.ts +2 -5
- package/dist/lane.js +14 -10
- package/dist/lanes.d.ts +7 -4
- package/dist/lanes.js +27 -28
- package/dist/meet.js +5 -3
- package/dist/mixer.d.ts +2 -1
- package/dist/mixer.js +4 -0
- package/dist/named.d.ts +6 -1
- package/dist/named.js +29 -1
- package/dist/numbers.d.ts +5 -0
- package/dist/numbers.js +29 -0
- package/dist/pull.js +11 -1
- package/dist/qualify.js +9 -10
- package/dist/renumber.d.ts +10 -0
- package/dist/renumber.js +75 -0
- package/dist/rows.js +27 -19
- package/dist/seat.d.ts +17 -0
- package/dist/seat.js +25 -0
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/strays.js +3 -2
- package/dist/trace.d.ts +4 -0
- package/dist/trace.js +255 -0
- package/dist/types/mix.d.ts +15 -0
- package/dist/types/trace.d.ts +51 -0
- package/dist/types/trace.js +1 -0
- package/dist/types/voice.d.ts +10 -5
- package/dist/types.d.ts +1 -0
- package/dist/unreached.d.ts +2 -0
- package/dist/unreached.js +7 -0
- package/dist/voice.d.ts +3 -0
- package/dist/voice.js +9 -3
- package/docs/schema.html +11 -0
- package/package.json +1 -1
package/dist/lanes.js
CHANGED
|
@@ -10,6 +10,7 @@ import { Placed } from './placed.js';
|
|
|
10
10
|
import { requalify, retouch } from './qualify.js';
|
|
11
11
|
import { reading } from './reading.js';
|
|
12
12
|
import { reportedRow, unplace } from './rows.js';
|
|
13
|
+
import { seatOf } from './seat.js';
|
|
13
14
|
/**
|
|
14
15
|
* The lanes of one mix: subject numbers, which channels and voices run on lanes, and the values a
|
|
15
16
|
* frame's fill leaves for probes to copy.
|
|
@@ -24,7 +25,7 @@ export class Lanes {
|
|
|
24
25
|
byId = new Map();
|
|
25
26
|
/** The crowds of single-subject voices, one per list of channels, and which holds each voice. */
|
|
26
27
|
crowds = [];
|
|
27
|
-
|
|
28
|
+
seats = new Map();
|
|
28
29
|
/** Which crowd voices' rows are placed on each subject. */
|
|
29
30
|
placed = new Placed();
|
|
30
31
|
/**
|
|
@@ -115,14 +116,16 @@ export class Lanes {
|
|
|
115
116
|
*/
|
|
116
117
|
late = [];
|
|
117
118
|
/**
|
|
118
|
-
* What `writeLater` and `pullRun` have queued, for one set of columns, as runs: each
|
|
119
|
-
*
|
|
119
|
+
* What `writeLater` and `pullRun` have queued, for one set of columns, as runs of rows: each
|
|
120
|
+
* run's first row and how many follow, and its first lane slot where its slots follow one apart
|
|
121
|
+
* too, else -1, when each row's slot is read from `queueFrom` by row.
|
|
120
122
|
*/
|
|
121
123
|
queueSlots = new Int32Array(0);
|
|
122
124
|
queueRows = new Int32Array(0);
|
|
123
125
|
queueCounts = new Int32Array(0);
|
|
124
126
|
queued = 0;
|
|
125
127
|
queuedFor = null;
|
|
128
|
+
queueFrom = null;
|
|
126
129
|
keeps = false;
|
|
127
130
|
now = Number.NaN;
|
|
128
131
|
filledAt = Number.NaN;
|
|
@@ -233,10 +236,9 @@ export class Lanes {
|
|
|
233
236
|
lane.remove(slot);
|
|
234
237
|
return;
|
|
235
238
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
unplace(this, c, p);
|
|
239
|
+
const s = this.seats.get(id);
|
|
240
|
+
if (s !== undefined && s.crowd.list[s.row] === slot)
|
|
241
|
+
unplace(this, s.crowd, s.row);
|
|
240
242
|
}
|
|
241
243
|
/** A voice brought a subject back: its next probe meets every lane again, as it met them first. */
|
|
242
244
|
rejoin(slot) {
|
|
@@ -254,9 +256,8 @@ export class Lanes {
|
|
|
254
256
|
const lane = this.byId.get(id);
|
|
255
257
|
if (lane !== undefined)
|
|
256
258
|
return this.reported(lane, slot);
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
return c === undefined || p === undefined ? undefined : reportedRow(this, c, p, slot);
|
|
259
|
+
const s = this.seats.get(id);
|
|
260
|
+
return s === undefined ? undefined : reportedRow(this, s.crowd, s.row, slot);
|
|
260
261
|
}
|
|
261
262
|
reported(lane, slot) {
|
|
262
263
|
const o = slot * 6 /* Per.SLOT */;
|
|
@@ -439,10 +440,9 @@ export class Lanes {
|
|
|
439
440
|
lane.remove(slot);
|
|
440
441
|
const placed = this.placed;
|
|
441
442
|
for (let id = placed.any(slot); id !== 0; id = placed.any(slot)) {
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
unplace(this, c, p);
|
|
443
|
+
const s = this.seats.get(id);
|
|
444
|
+
if (s !== undefined && s.crowd.list[s.row] === slot)
|
|
445
|
+
unplace(this, s.crowd, s.row);
|
|
446
446
|
else
|
|
447
447
|
placed.remove(slot, id);
|
|
448
448
|
}
|
|
@@ -474,10 +474,10 @@ export class Lanes {
|
|
|
474
474
|
* next fill, and a lane keeps the sample the old one gave.
|
|
475
475
|
*/
|
|
476
476
|
stretchChanged(id, s) {
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
c.restale(
|
|
477
|
+
const seat = this.seats.get(id);
|
|
478
|
+
if (seat !== undefined) {
|
|
479
|
+
const c = seat.crowd;
|
|
480
|
+
c.restale(seat.row, c.hot[seat.row * c.stride + 0 /* Hot.FLAGS */]);
|
|
481
481
|
return;
|
|
482
482
|
}
|
|
483
483
|
const lane = this.byId.get(id);
|
|
@@ -502,12 +502,11 @@ export class Lanes {
|
|
|
502
502
|
}
|
|
503
503
|
/** A voice's clock moved the `shown` of records it had already placed: copies them again. */
|
|
504
504
|
reshown(id) {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
const held = c.records[p];
|
|
505
|
+
const s = this.seats.get(id);
|
|
506
|
+
if (s !== undefined) {
|
|
507
|
+
const held = s.crowd.records[s.row];
|
|
509
508
|
if (held !== undefined)
|
|
510
|
-
|
|
509
|
+
s.crowd.data[s.row * 8 /* Row.STRIDE */ + 1 /* Row.SINCE */] = held.shown;
|
|
511
510
|
}
|
|
512
511
|
const lane = this.byId.get(id);
|
|
513
512
|
if (lane === undefined)
|
|
@@ -519,12 +518,12 @@ export class Lanes {
|
|
|
519
518
|
}
|
|
520
519
|
}
|
|
521
520
|
/** Something on a voice changed: a crowd copies it again at the next fill. */
|
|
522
|
-
voiceChanged(
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
if (c === undefined || p === undefined)
|
|
521
|
+
voiceChanged(voice) {
|
|
522
|
+
const s = seatOf(this, voice);
|
|
523
|
+
if (s === null)
|
|
526
524
|
return;
|
|
527
|
-
const
|
|
525
|
+
const c = s.crowd;
|
|
526
|
+
const f = s.row * c.stride + 0 /* Hot.FLAGS */;
|
|
528
527
|
c.hot[f] = c.hot[f] | 64 /* Flag.VOICE */;
|
|
529
528
|
}
|
|
530
529
|
}
|
package/dist/meet.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { open, shut } from './everyone.js';
|
|
2
|
+
import { seatOf } from './seat.js';
|
|
2
3
|
/**
|
|
3
4
|
* Whether the lanes a probe of the subject at `slot` just met (`met`) can be folded on the general
|
|
4
5
|
* path after the lanes' values: none is in a role or a crowd, the subject owes none already this
|
|
@@ -183,10 +184,11 @@ function foldNow(lanes, slot, subject, again) {
|
|
|
183
184
|
}
|
|
184
185
|
/** Places a crowd voice's row on the subject a probe met, as `Lane.add` adds a position. */
|
|
185
186
|
function place(lanes, voice, slot, subject, from, head) {
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
if (c === undefined || p === undefined)
|
|
187
|
+
const s = seatOf(lanes, voice);
|
|
188
|
+
if (s === null)
|
|
189
189
|
return false;
|
|
190
|
+
const c = s.crowd;
|
|
191
|
+
const p = s.row;
|
|
190
192
|
const h = p * c.stride;
|
|
191
193
|
const flags = c.hot[h + 0 /* Hot.FLAGS */];
|
|
192
194
|
if (c.hot[h + 6 /* Hot.EPOCH */] <= from || (flags & 16 /* Flag.PLACED */) !== 0)
|
package/dist/mixer.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { Store } from './store.js';
|
|
|
32
32
|
import type { MotionOwner } from './stretch.js';
|
|
33
33
|
import type { Subject } from './subject.js';
|
|
34
34
|
import { type Announced, Transport } from './transport.js';
|
|
35
|
-
import type { ArcHandle, ArcSpec, BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, VoiceSpec } from './types.js';
|
|
35
|
+
import type { ArcHandle, ArcSpec, BlendMember, Booker, BookOptions, Channel, Columns, Engine, Handle, Kit, Marked, Mix, MixOptions, OwnerSpec, Patch, Projection, Sent, Signal, SpanHandle, SpanSpec, Tape, Trace, TraceOptions, VoiceSpec } from './types.js';
|
|
36
36
|
import { type Voice } from './voice.js';
|
|
37
37
|
import { base, capped, fadeOf, horizonFor, ownedBy, ownerBase, prime, primeLaned, weigh, weighOwned } from './weigh.js';
|
|
38
38
|
/**
|
|
@@ -266,6 +266,7 @@ export declare class Mixer<I, O> implements Mix<I, O> {
|
|
|
266
266
|
project(at: number | {
|
|
267
267
|
readonly frame: number;
|
|
268
268
|
}): Projection<I, O>;
|
|
269
|
+
trace(from: number, to: number, opts?: TraceOptions): Trace<I, O>;
|
|
269
270
|
announce(name: string, opts?: {
|
|
270
271
|
at?: number;
|
|
271
272
|
score?: string;
|
package/dist/mixer.js
CHANGED
|
@@ -33,6 +33,7 @@ import { Fitting, refit } from './spans.js';
|
|
|
33
33
|
import { Store } from './store.js';
|
|
34
34
|
import { record } from './tape.js';
|
|
35
35
|
import { touch } from './touch.js';
|
|
36
|
+
import { traceMix } from './trace.js';
|
|
36
37
|
import { announce, Transport } from './transport.js';
|
|
37
38
|
import { none } from './voice.js';
|
|
38
39
|
import { gave, historyFor, register, unregister } from './watch.js';
|
|
@@ -435,6 +436,9 @@ export class Mixer {
|
|
|
435
436
|
project(at) {
|
|
436
437
|
return projectAll(this.transport, at).of(this);
|
|
437
438
|
}
|
|
439
|
+
trace(from, to, opts) {
|
|
440
|
+
return traceMix(this, from, to, opts);
|
|
441
|
+
}
|
|
438
442
|
announce(name, opts = {}) {
|
|
439
443
|
this.attached();
|
|
440
444
|
announce(this.transport, name, opts, this.slot, this.name);
|
package/dist/named.d.ts
CHANGED
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
export declare class Named<I> {
|
|
6
6
|
readonly list: readonly I[];
|
|
7
7
|
private readonly set;
|
|
8
|
-
|
|
8
|
+
/** Each subject's `stagger` answer, asked at cue; null for a voice with no stagger. */
|
|
9
|
+
readonly delays: Map<I, number> | null;
|
|
10
|
+
/** The longest of `delays`, 0 at least, and the least, 0 at most: a voice's bounds at cue. */
|
|
11
|
+
readonly latest: number;
|
|
12
|
+
readonly soonest: number;
|
|
13
|
+
constructor(subjects: Iterable<I>, stagger?: (subject: I) => number);
|
|
9
14
|
get size(): number;
|
|
10
15
|
has(subject: I): boolean;
|
|
11
16
|
}
|
package/dist/named.js
CHANGED
|
@@ -7,11 +7,19 @@ const FEW = 8;
|
|
|
7
7
|
export class Named {
|
|
8
8
|
list;
|
|
9
9
|
set;
|
|
10
|
-
|
|
10
|
+
/** Each subject's `stagger` answer, asked at cue; null for a voice with no stagger. */
|
|
11
|
+
delays = null;
|
|
12
|
+
/** The longest of `delays`, 0 at least, and the least, 0 at most: a voice's bounds at cue. */
|
|
13
|
+
latest = 0;
|
|
14
|
+
soonest = 0;
|
|
15
|
+
constructor(subjects, stagger) {
|
|
11
16
|
// An array of distinct subjects, as nearly every voice names, is copied without iterating it.
|
|
12
17
|
if (Array.isArray(subjects) && distinct(subjects)) {
|
|
13
18
|
this.list = subjects.slice();
|
|
14
19
|
this.set = null;
|
|
20
|
+
if (stagger)
|
|
21
|
+
this.delays = delaysOf(this.list, stagger);
|
|
22
|
+
[this.latest, this.soonest] = boundsOf(this.delays);
|
|
15
23
|
return;
|
|
16
24
|
}
|
|
17
25
|
const list = [];
|
|
@@ -32,6 +40,9 @@ export class Named {
|
|
|
32
40
|
// Exactly as long as it is: a list grown by push keeps room for 17.
|
|
33
41
|
this.list = list.slice();
|
|
34
42
|
this.set = set;
|
|
43
|
+
if (stagger)
|
|
44
|
+
this.delays = delaysOf(this.list, stagger);
|
|
45
|
+
[this.latest, this.soonest] = boundsOf(this.delays);
|
|
35
46
|
}
|
|
36
47
|
get size() {
|
|
37
48
|
return this.list.length;
|
|
@@ -40,6 +51,23 @@ export class Named {
|
|
|
40
51
|
return this.set === null ? this.list.includes(subject) : this.set.has(subject);
|
|
41
52
|
}
|
|
42
53
|
}
|
|
54
|
+
function delaysOf(list, stagger) {
|
|
55
|
+
const delays = new Map();
|
|
56
|
+
for (const s of list)
|
|
57
|
+
delays.set(s, stagger(s));
|
|
58
|
+
return delays;
|
|
59
|
+
}
|
|
60
|
+
function boundsOf(delays) {
|
|
61
|
+
let latest = 0;
|
|
62
|
+
let soonest = 0;
|
|
63
|
+
for (const delay of delays?.values() ?? []) {
|
|
64
|
+
if (delay > latest)
|
|
65
|
+
latest = delay;
|
|
66
|
+
if (delay < soonest)
|
|
67
|
+
soonest = delay;
|
|
68
|
+
}
|
|
69
|
+
return [latest, soonest];
|
|
70
|
+
}
|
|
43
71
|
/** Whether a short array names no subject twice, as `includes` compares; a long one says no. */
|
|
44
72
|
function distinct(subjects) {
|
|
45
73
|
const n = subjects.length;
|
package/dist/numbers.d.ts
CHANGED
|
@@ -36,5 +36,10 @@ export declare class Numbers<I> {
|
|
|
36
36
|
/** Whether `slot` is a number in use, its subject perhaps collected and not yet released. */
|
|
37
37
|
has(slot: number): boolean;
|
|
38
38
|
subject(slot: number): I | typeof absent;
|
|
39
|
+
/**
|
|
40
|
+
* Hands the subject numbered `from` the free number `to`, and frees `from` without telling the
|
|
41
|
+
* owner, whose stores the caller moves; false, changing nothing, where either does not hold.
|
|
42
|
+
*/
|
|
43
|
+
move(from: number, to: number): boolean;
|
|
39
44
|
release(slot: number): void;
|
|
40
45
|
}
|
package/dist/numbers.js
CHANGED
|
@@ -82,6 +82,35 @@ export class Numbers {
|
|
|
82
82
|
}
|
|
83
83
|
return ref;
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Hands the subject numbered `from` the free number `to`, and frees `from` without telling the
|
|
87
|
+
* owner, whose stores the caller moves; false, changing nothing, where either does not hold.
|
|
88
|
+
*/
|
|
89
|
+
move(from, to) {
|
|
90
|
+
const ref = this.refs[from];
|
|
91
|
+
const free = this.free;
|
|
92
|
+
if (ref === undefined || to >= this.next || this.refs[to] !== undefined || free === null)
|
|
93
|
+
return false;
|
|
94
|
+
const at = free.lastIndexOf(to);
|
|
95
|
+
if (at < 0)
|
|
96
|
+
return false;
|
|
97
|
+
if (ref instanceof WeakRef) {
|
|
98
|
+
const subject = ref.deref();
|
|
99
|
+
if (subject === undefined)
|
|
100
|
+
return false;
|
|
101
|
+
if (ref === this.lone)
|
|
102
|
+
this.loneSlot = to;
|
|
103
|
+
else if (this.registry !== null) {
|
|
104
|
+
this.registry.unregister(ref);
|
|
105
|
+
this.registry.register(subject, to, ref);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
free.splice(at, 1);
|
|
109
|
+
free.push(from);
|
|
110
|
+
this.refs[to] = ref;
|
|
111
|
+
this.refs[from] = undefined;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
85
114
|
release(slot) {
|
|
86
115
|
const ref = this.refs[slot];
|
|
87
116
|
if (ref === undefined)
|
package/dist/pull.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { axesOf } from './channels.js';
|
|
2
2
|
import { copy } from './clone.js';
|
|
3
3
|
import { clampRun, flush, pullRun, writeLater } from './columns.js';
|
|
4
|
+
import { renumber } from './renumber.js';
|
|
4
5
|
/** What `pull` remembers at a position it has read no subject at. */
|
|
5
6
|
const UNREAD = Symbol('unread');
|
|
6
7
|
/** Writes a folded pose's value into subject `n`'s places in a column. */
|
|
@@ -152,12 +153,21 @@ function readAll(mix, list, columns, live, current, since, whole) {
|
|
|
152
153
|
was[n] = subject;
|
|
153
154
|
heads[n] = head;
|
|
154
155
|
slot = head.slot;
|
|
156
|
+
// Numbered out of the list's order, as swap-remove leaves a subject: renumbered into it.
|
|
157
|
+
const to = n === 0 ? 0 : slots[n - 1] + 1;
|
|
158
|
+
if (runs && slot >= 0 && slot !== to && to >= 0) {
|
|
159
|
+
if (renumber(mix, subject, head, to)) {
|
|
160
|
+
for (let j = was.indexOf(subject); j >= 0; j = was.indexOf(subject, j + 1))
|
|
161
|
+
slots[j] = to;
|
|
162
|
+
slot = to;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
155
165
|
slots[n] = slot;
|
|
156
166
|
}
|
|
157
167
|
}
|
|
158
168
|
const laned = live && lanes?.prepare(slot, subject, mix.version, heads[n] ?? null) === true;
|
|
159
169
|
if (laned && whole && lanes.whole && !lanes.owes(slot)) {
|
|
160
|
-
writeLater(lanes, slot, columns, n);
|
|
170
|
+
writeLater(lanes, slot, columns, n, slots);
|
|
161
171
|
continue;
|
|
162
172
|
}
|
|
163
173
|
const head = live ? heads[n] : null;
|
package/dist/qualify.js
CHANGED
|
@@ -8,6 +8,7 @@ import { Lane } from './lane.js';
|
|
|
8
8
|
import { reach } from './meet.js';
|
|
9
9
|
import { bury, compactSparse, join, regroup, retouchRow, sameCrowds } from './rows.js';
|
|
10
10
|
import { settle } from './sample.js';
|
|
11
|
+
import { seated, seatOf } from './seat.js';
|
|
11
12
|
/**
|
|
12
13
|
* Which channels run as lanes and which voices run on them: the fixed point where every voice
|
|
13
14
|
* writing a laned channel fits and writes only laned channels. Only channels some laned voice
|
|
@@ -150,17 +151,16 @@ export function requalify(lanes, version) {
|
|
|
150
151
|
for (const c of lanes.crowds)
|
|
151
152
|
for (let p = 0; p < c.size; p++) {
|
|
152
153
|
const v = c.voices[p];
|
|
153
|
-
if (
|
|
154
|
+
if (seated(lanes, v, c, p) && v.state === 'done')
|
|
154
155
|
bury(lanes, c, p);
|
|
155
156
|
}
|
|
156
157
|
// A crowd voice that started or began fading takes it up on its row, as `retouch` does.
|
|
157
158
|
for (const v of touched) {
|
|
158
159
|
if (v.state === 'done')
|
|
159
160
|
continue;
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
retouchRow(lanes, c, p, v);
|
|
161
|
+
const s = seatOf(lanes, v);
|
|
162
|
+
if (s !== null)
|
|
163
|
+
retouchRow(lanes, s.crowd, s.row, v);
|
|
164
164
|
}
|
|
165
165
|
compactSparse(lanes);
|
|
166
166
|
return;
|
|
@@ -306,14 +306,13 @@ export function retouch(lanes) {
|
|
|
306
306
|
const known = v.id <= lanes.known;
|
|
307
307
|
if (v.id > lanes.known)
|
|
308
308
|
lanes.known = v.id;
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
if (c !== undefined && p !== undefined) {
|
|
309
|
+
const s = seatOf(lanes, v);
|
|
310
|
+
if (s !== null) {
|
|
312
311
|
if (v.state === 'done') {
|
|
313
|
-
bury(lanes,
|
|
312
|
+
bury(lanes, s.crowd, s.row);
|
|
314
313
|
continue;
|
|
315
314
|
}
|
|
316
|
-
retouchRow(lanes,
|
|
315
|
+
retouchRow(lanes, s.crowd, s.row, v);
|
|
317
316
|
continue;
|
|
318
317
|
}
|
|
319
318
|
if (v.state === 'done' && !known)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Mixer } from './mixer.js';
|
|
2
|
+
import type { Subject } from './subject.js';
|
|
3
|
+
/**
|
|
4
|
+
* Gives a subject the free number `to` in place of its own, carrying everything kept under the old
|
|
5
|
+
* one, so `pull` can keep a list's numbers in its order: a list kept dense by swap-remove moves its
|
|
6
|
+
* last subject into the place a dropped one left, whose number the drop just freed. Says whether it
|
|
7
|
+
* did; it does not where some store it would have to carry is in use: while a fill runs, under
|
|
8
|
+
* history, in a read ahead, or with roles, a bound or anything owed on the lanes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renumber<I, O>(mix: Mixer<I, O>, subject: I, head: Subject<unknown>, to: number): boolean;
|
package/dist/renumber.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { resight } from './everyone.js';
|
|
2
|
+
import { goneIndex } from './gone.js';
|
|
3
|
+
import { reunreach } from './unreached.js';
|
|
4
|
+
/**
|
|
5
|
+
* Gives a subject the free number `to` in place of its own, carrying everything kept under the old
|
|
6
|
+
* one, so `pull` can keep a list's numbers in its order: a list kept dense by swap-remove moves its
|
|
7
|
+
* last subject into the place a dropped one left, whose number the drop just freed. Says whether it
|
|
8
|
+
* did; it does not where some store it would have to carry is in use: while a fill runs, under
|
|
9
|
+
* history, in a read ahead, or with roles, a bound or anything owed on the lanes.
|
|
10
|
+
*/
|
|
11
|
+
export function renumber(mix, subject, head, to) {
|
|
12
|
+
const lanes = mix.lanes;
|
|
13
|
+
const from = head.slot;
|
|
14
|
+
if (from < 0 ||
|
|
15
|
+
lanes.filling ||
|
|
16
|
+
lanes.host.keeps ||
|
|
17
|
+
mix.projecting ||
|
|
18
|
+
lanes.roles.length > 0 ||
|
|
19
|
+
lanes.owed.any() ||
|
|
20
|
+
lanes.lanes.some((lane) => lane.voice.bound !== null) ||
|
|
21
|
+
!lanes.numbers.move(from, to))
|
|
22
|
+
return false;
|
|
23
|
+
for (const lane of lanes.lanes)
|
|
24
|
+
lane.renumber(from, to);
|
|
25
|
+
const placed = lanes.placed;
|
|
26
|
+
for (const id of placed.all(from)) {
|
|
27
|
+
placed.remove(from, id);
|
|
28
|
+
const s = lanes.seats.get(id);
|
|
29
|
+
if (s === undefined || s.crowd.list[s.row] !== from)
|
|
30
|
+
continue;
|
|
31
|
+
s.crowd.list[s.row] = to;
|
|
32
|
+
placed.add(to, id);
|
|
33
|
+
}
|
|
34
|
+
const per = lanes.per;
|
|
35
|
+
per.copyWithin(to * 6 /* Per.SLOT */, from * 6 /* Per.SLOT */, (from + 1) * 6 /* Per.SLOT */);
|
|
36
|
+
per[from * 6 /* Per.SLOT */ + 0 /* Per.FILLED */] = lanes.fills;
|
|
37
|
+
lanes.unlinked[to] = lanes.unlinked[from];
|
|
38
|
+
lanes.subjects[to] = lanes.subjects[from];
|
|
39
|
+
lanes.subjects[from] = undefined;
|
|
40
|
+
for (const ch of lanes.laned)
|
|
41
|
+
ch.values.copyWithin(to * ch.axes, from * ch.axes, (from + 1) * ch.axes);
|
|
42
|
+
mix.places?.forgot(from);
|
|
43
|
+
mix.places?.number(subject, to);
|
|
44
|
+
const cap = lanes.cap;
|
|
45
|
+
for (const voice of mix.general) {
|
|
46
|
+
reunreach(voice, from, to);
|
|
47
|
+
resight(voice, from, to, cap);
|
|
48
|
+
reblend(mix, voice, from, to);
|
|
49
|
+
}
|
|
50
|
+
for (const voice of goneIndex(mix).general)
|
|
51
|
+
reunreach(voice, from, to);
|
|
52
|
+
for (let held = head; held !== null; held = held.next)
|
|
53
|
+
if (held.voice !== null)
|
|
54
|
+
reblend(mix, held.voice, from, to);
|
|
55
|
+
head.slot = to;
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
/** Moves a blend's read for the subject this frame, if it has made one, to its new number. */
|
|
59
|
+
function reblend(mix, voice, from, to) {
|
|
60
|
+
const of = voice.blend?.of;
|
|
61
|
+
if (of === undefined || from >= of.frames.length || of.frames[from] !== mix.frame)
|
|
62
|
+
return;
|
|
63
|
+
if (to >= of.frames.length) {
|
|
64
|
+
const size = Math.max(to + 1, of.frames.length * 2, 64);
|
|
65
|
+
const frames = new Float64Array(size);
|
|
66
|
+
const values = new Float64Array(size);
|
|
67
|
+
frames.set(of.frames);
|
|
68
|
+
values.set(of.values);
|
|
69
|
+
of.frames = frames;
|
|
70
|
+
of.values = values;
|
|
71
|
+
}
|
|
72
|
+
of.frames[to] = mix.frame;
|
|
73
|
+
of.values[to] = of.values[from];
|
|
74
|
+
of.frames[from] = Number.NaN;
|
|
75
|
+
}
|
package/dist/rows.js
CHANGED
|
@@ -4,6 +4,7 @@ import { reach } from './meet.js';
|
|
|
4
4
|
import { motionOf } from './motion.js';
|
|
5
5
|
import { open } from './qualify.js';
|
|
6
6
|
import { settle } from './sample.js';
|
|
7
|
+
import { Seat, seatOf } from './seat.js';
|
|
7
8
|
/** Whether two crowds' channels are the same list. */
|
|
8
9
|
function same(a, b) {
|
|
9
10
|
if (a.length !== b.length)
|
|
@@ -38,14 +39,13 @@ export function unplace(lanes, c, p) {
|
|
|
38
39
|
export function sameCrowds(lanes, members) {
|
|
39
40
|
let live = 0;
|
|
40
41
|
for (const v of members) {
|
|
41
|
-
if (!lanes.
|
|
42
|
+
if (!lanes.seats.has(v.id))
|
|
42
43
|
return false;
|
|
43
44
|
live++;
|
|
44
45
|
}
|
|
45
46
|
let rows = 0;
|
|
46
|
-
for (const
|
|
47
|
-
|
|
48
|
-
if (c.voices[p].state !== 'done')
|
|
47
|
+
for (const s of lanes.seats.values()) {
|
|
48
|
+
if (s.crowd.voices[s.row].state !== 'done')
|
|
49
49
|
rows++;
|
|
50
50
|
}
|
|
51
51
|
return rows === live;
|
|
@@ -64,13 +64,15 @@ export function regroup(lanes, members) {
|
|
|
64
64
|
leaveCrowd(lanes, c, p);
|
|
65
65
|
}
|
|
66
66
|
const old = lanes.crowds;
|
|
67
|
-
const was = new Map(lanes.
|
|
67
|
+
const was = new Map(lanes.seats);
|
|
68
|
+
for (const s of was.values())
|
|
69
|
+
s.lanes = null;
|
|
68
70
|
lanes.crowds = [];
|
|
69
|
-
lanes.
|
|
71
|
+
lanes.seats.clear();
|
|
70
72
|
lanes.overlap = false;
|
|
71
73
|
for (const v of members) {
|
|
72
74
|
const from = was.get(v.id);
|
|
73
|
-
join(lanes, v, from, from?.
|
|
75
|
+
join(lanes, v, from?.crowd, from?.row, old);
|
|
74
76
|
}
|
|
75
77
|
if (lanes.crowds.length === 0) {
|
|
76
78
|
lanes.lawsByKey.clear();
|
|
@@ -104,8 +106,9 @@ export function join(lanes, v, from, at, old) {
|
|
|
104
106
|
newRow(lanes, c, p, v);
|
|
105
107
|
if (c.hot[p * c.stride + 6 /* Hot.EPOCH */] === 0 && v.state !== 'pending')
|
|
106
108
|
c.hot[p * c.stride + 6 /* Hot.EPOCH */] = open(lanes, v);
|
|
107
|
-
|
|
108
|
-
lanes.
|
|
109
|
+
const seat = new Seat(lanes, c, p);
|
|
110
|
+
lanes.seats.set(v.id, seat);
|
|
111
|
+
v.seat = seat;
|
|
109
112
|
v.laned = true;
|
|
110
113
|
}
|
|
111
114
|
/** A crowd row takes up its voice as it now stands: opened once it starts, its fields copied again. */
|
|
@@ -122,27 +125,28 @@ export function retouchRow(lanes, c, p, v) {
|
|
|
122
125
|
export function compactSparse(lanes) {
|
|
123
126
|
for (const c of lanes.crowds)
|
|
124
127
|
if (c.dead > 64 && c.dead * 4 >= c.size)
|
|
125
|
-
compact(c);
|
|
128
|
+
compact(lanes, c);
|
|
126
129
|
}
|
|
127
130
|
/**
|
|
128
131
|
* Slides a crowd's rows down over the empty ones its departed voices left, in order, so its rows
|
|
129
132
|
* stay in voice order; a qualify did this by rebuilding every crowd, a dropped frame at 10k rows.
|
|
130
133
|
*/
|
|
131
|
-
function compact(c) {
|
|
134
|
+
function compact(lanes, c) {
|
|
132
135
|
// The rows' stops are copied into a fresh pool, leaving the departed rows' behind.
|
|
133
136
|
const keys = c.keys;
|
|
134
137
|
c.keys = new KeyRows();
|
|
135
138
|
let q = 0;
|
|
136
139
|
for (let p = 0; p < c.size; p++) {
|
|
137
140
|
const v = c.voices[p];
|
|
138
|
-
|
|
141
|
+
const s = seatOf(lanes, v);
|
|
142
|
+
if (s === null || s.crowd !== c || s.row !== p) {
|
|
139
143
|
c.odd?.delete(p);
|
|
140
144
|
continue;
|
|
141
145
|
}
|
|
142
146
|
if (q !== p) {
|
|
143
147
|
copyRow(c, p, c, q, keys);
|
|
144
148
|
c.odd?.delete(p);
|
|
145
|
-
|
|
149
|
+
s.row = q;
|
|
146
150
|
}
|
|
147
151
|
else
|
|
148
152
|
copyKeys(keys, c, q);
|
|
@@ -166,8 +170,12 @@ export function bury(lanes, c, p) {
|
|
|
166
170
|
c.deltas[p] = null;
|
|
167
171
|
c.motions[p] = undefined;
|
|
168
172
|
c.hot[p * c.stride + 0 /* Hot.FLAGS */] = 0;
|
|
169
|
-
|
|
170
|
-
|
|
173
|
+
lanes.seats.delete(v.id);
|
|
174
|
+
const s = v.seat;
|
|
175
|
+
if (s !== null && s.lanes === lanes) {
|
|
176
|
+
s.lanes = null;
|
|
177
|
+
v.seat = null;
|
|
178
|
+
}
|
|
171
179
|
c.dead++;
|
|
172
180
|
}
|
|
173
181
|
/** Copies `n` numbers of a row; a view of them to hand `set` was three objects a row moved. */
|
|
@@ -202,7 +210,7 @@ function copyRow(from, p, to, q, keys = from.keys) {
|
|
|
202
210
|
function copyKeys(keys, c, q) {
|
|
203
211
|
const h = q * c.stride;
|
|
204
212
|
if ((c.hot[h + 0 /* Hot.FLAGS */] & 512 /* Flag.FLAT */) !== 0)
|
|
205
|
-
c.hot[h +
|
|
213
|
+
c.hot[h + 9 /* Hot.AT */] = c.keys.copy(keys, c.hot[h + 9 /* Hot.AT */]);
|
|
206
214
|
}
|
|
207
215
|
function newRow(lanes, c, p, v) {
|
|
208
216
|
const run = motionOf(v.patch);
|
|
@@ -210,9 +218,9 @@ function newRow(lanes, c, p, v) {
|
|
|
210
218
|
c.hot.fill(0, h, h + c.stride);
|
|
211
219
|
const at = v.built === null ? -1 : c.keys.add(v.built, c.chans);
|
|
212
220
|
if (at >= 0) {
|
|
213
|
-
c.hot[h +
|
|
214
|
-
c.hot[h +
|
|
215
|
-
c.hot[h +
|
|
221
|
+
c.hot[h + 9 /* Hot.AT */] = at;
|
|
222
|
+
c.hot[h + 11 /* Hot.X0 */] = v.duration;
|
|
223
|
+
c.hot[h + 10 /* Hot.MS */] = v.passes;
|
|
216
224
|
}
|
|
217
225
|
c.restale(p, 64 /* Flag.VOICE */ |
|
|
218
226
|
(run === undefined ? 0 : 128 /* Flag.MOTION */) |
|
package/dist/seat.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Crowd } from './crowd.js';
|
|
2
|
+
import type { Voice } from './voice.js';
|
|
3
|
+
/**
|
|
4
|
+
* A crowd voice's row, held on the voice and by its id in its lanes' `seats`: one place to read
|
|
5
|
+
* both its crowd and its row, where two maps by id were two cache misses a stop at 40,000 voices.
|
|
6
|
+
* `lanes` is the lanes it was given in, null once they let it go.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Seat<I, O> {
|
|
9
|
+
lanes: object | null;
|
|
10
|
+
readonly crowd: Crowd<I, O>;
|
|
11
|
+
row: number;
|
|
12
|
+
constructor(lanes: object | null, crowd: Crowd<I, O>, row: number);
|
|
13
|
+
}
|
|
14
|
+
/** `voice`'s seat in `lanes`, null where it has none there. */
|
|
15
|
+
export declare function seatOf<I, O>(lanes: object, voice: Voice<I, O>): Seat<I, O> | null;
|
|
16
|
+
/** Whether row `p` of `c` is `voice`'s seat in `lanes`, not a row it has left. */
|
|
17
|
+
export declare function seated<I, O>(lanes: object, voice: Voice<I, O>, c: Crowd<I, O>, p: number): boolean;
|
package/dist/seat.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A crowd voice's row, held on the voice and by its id in its lanes' `seats`: one place to read
|
|
3
|
+
* both its crowd and its row, where two maps by id were two cache misses a stop at 40,000 voices.
|
|
4
|
+
* `lanes` is the lanes it was given in, null once they let it go.
|
|
5
|
+
*/
|
|
6
|
+
export class Seat {
|
|
7
|
+
lanes;
|
|
8
|
+
crowd;
|
|
9
|
+
row;
|
|
10
|
+
constructor(lanes, crowd, row) {
|
|
11
|
+
this.lanes = lanes;
|
|
12
|
+
this.crowd = crowd;
|
|
13
|
+
this.row = row;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** `voice`'s seat in `lanes`, null where it has none there. */
|
|
17
|
+
export function seatOf(lanes, voice) {
|
|
18
|
+
const s = voice.seat;
|
|
19
|
+
return s !== null && s.lanes === lanes ? s : null;
|
|
20
|
+
}
|
|
21
|
+
/** Whether row `p` of `c` is `voice`'s seat in `lanes`, not a row it has left. */
|
|
22
|
+
export function seated(lanes, voice, c, p) {
|
|
23
|
+
const s = seatOf(lanes, voice);
|
|
24
|
+
return s !== null && s.crowd === c && s.row === p;
|
|
25
|
+
}
|
package/dist/shared.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The package version, for a fact whose shape follows the code too closely to version by hand:
|
|
3
3
|
* copies of different releases keep apart. `test/copies.test.ts` holds it to `package.json`.
|
|
4
4
|
*/
|
|
5
|
-
export declare const release = "0.15.
|
|
5
|
+
export declare const release = "0.15.1";
|
|
6
6
|
/**
|
|
7
7
|
* A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
|
|
8
8
|
* patch or channel it made is read by another copy's mix. The name carries a version, bumped when
|
package/dist/shared.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The package version, for a fact whose shape follows the code too closely to version by hand:
|
|
3
3
|
* copies of different releases keep apart. `test/copies.test.ts` holds it to `package.json`.
|
|
4
4
|
*/
|
|
5
|
-
export const release = '0.15.
|
|
5
|
+
export const release = '0.15.1';
|
|
6
6
|
/**
|
|
7
7
|
* A WeakMap every copy of blits loaded in one realm shares, so a fact one copy records about a
|
|
8
8
|
* patch or channel it made is read by another copy's mix. The name carries a version, bumped when
|