@axpecter/lync 2.3.2 → 3.0.0

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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -319
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -188
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -456
  82. package/src/codec/composite/Map.luau +0 -469
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -291
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -147
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -390
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -65
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -211
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
@@ -0,0 +1,222 @@
1
+ --!strict
2
+ --!native
3
+ --!optimize 2
4
+ -- A shape and everything under it, as the one pair of closures the whole tree became.
5
+
6
+ local Base = require (script.Parent.Parent.codec.Base)
7
+ local Block = require (script.Block)
8
+ local Catalog = require (script.Parent.Parent.kernel.Catalog)
9
+ local Ir = require (script.Parent.Ir)
10
+ local Leaf = require (script.Leaf)
11
+ local Log = require (script.Parent.Parent.kernel.Log)
12
+ local Serial = require (script.Parent.Serial)
13
+ local Types = require (script.Parent.Parent.Types)
14
+ local Word = require (script.Word)
15
+
16
+ local Emit = {}
17
+
18
+ -- Constants -----------------------------------------------------------
19
+
20
+ -- Types ---------------------------------------------------------------
21
+
22
+ -- Everything a walk needs that is not the node it is on.
23
+ export type Site = {
24
+ read paths: Serial.Interner,
25
+ read expected: Serial.Interner,
26
+ read sidecar: Ir.Sidecar,
27
+ }
28
+
29
+ -- State ---------------------------------------------------------------
30
+
31
+ -- The first reason a check gave while one payload was decoding, held until that payload is done.
32
+ local parkedPath: number? = nil
33
+ local parkedReason: string? = nil
34
+
35
+ -- Private -------------------------------------------------------------
36
+
37
+ -- Every check a place declared, in the order they were declared, and the first reason one gives.
38
+ local function turned (tag: Ir.Tag, value: any, ctx: Types.ValidateContext)
39
+ local checks = tag.checks :: { Base.Validate }
40
+
41
+ for index = 1, #checks do
42
+ local why = checks[index] (value, ctx)
43
+ if why == nil then continue end
44
+
45
+ if parkedPath == nil then
46
+ parkedPath = tag.pathId
47
+ parkedReason = why
48
+ end
49
+ return
50
+ end
51
+ end
52
+
53
+ -- A node's own checks, wrapped round the read once here so a node without any pays for nothing.
54
+ local function checked (read: Ir.Read, tag: Ir.Tag): Ir.Read
55
+ return function (b: buffer, pos: number, ctx: Types.ValidateContext): (number, any)
56
+ local at, value = read (b, pos, ctx)
57
+ turned (tag, value, ctx)
58
+ return at, value
59
+ end
60
+ end
61
+
62
+ -- Where a region's field sits in the record the group answers, as the names to walk to reach it.
63
+ local function chained (of: Ir.Group, join: Ir.Join): { string }
64
+ local key = join.key
65
+ local out: { string } = if key == nil then {} else { key }
66
+
67
+ local at = join.nest
68
+ while at ~= nil do
69
+ local nest = of.nests[at]
70
+ table.insert (out, 1, nest.key)
71
+ at = nest.under
72
+ end
73
+ return out
74
+ end
75
+
76
+ -- The checks of a region's fields, wrapped round the group that holds the region.
77
+ local function fielded (read: Ir.Read, of: Ir.Group): Ir.Read
78
+ local word = of.word
79
+ if word == nil then return read end
80
+
81
+ local looks: { Ir.Join } = {}
82
+ local within: { { string } } = {}
83
+
84
+ for _, join in word.joins do
85
+ if join.tag.checks == nil then continue end
86
+
87
+ table.insert (looks, join)
88
+ table.insert (within, chained (of, join))
89
+ end
90
+
91
+ local reach = #looks
92
+ if reach == 0 then return read end
93
+
94
+ return function (b: buffer, pos: number, ctx: Types.ValidateContext): (number, any)
95
+ local at, value = read (b, pos, ctx)
96
+
97
+ for index = 1, reach do
98
+ local one = value
99
+ for _, name in within[index] do
100
+ one = one[name]
101
+ end
102
+
103
+ turned (looks[index].tag, one, ctx)
104
+ end
105
+
106
+ return at, value
107
+ end
108
+ end
109
+
110
+ -- One node to its pair, with its children already theirs.
111
+ local function made (of: Ir.Shape, at: Site): (Ir.Write, Ir.Read)
112
+ local paths, expected = at.paths, at.expected
113
+
114
+ --[[
115
+ A region alone is a record with no fields after it. It has joins and so it has a value, but
116
+ the pair that lays a region down fills an array rather than answering, and the thing that
117
+ turns that into a value is the group. So a bare one is emitted as the group it means.
118
+ ]]
119
+ if of.op == "word" then return made (Ir.group (of.tag, of, {}), at) end
120
+
121
+ if of.op == "wide" then return Leaf.wide (of, paths, expected) end
122
+ if of.op == "f32" or of.op == "f64" then return Leaf.float (of, paths, expected) end
123
+ if of.op == "vlq" or of.op == "vli" then return Leaf.varint (of, paths, expected) end
124
+ if of.op == "bytes" then return Leaf.bytes (of, paths, expected) end
125
+ if of.op == "run" then return Leaf.run (of, paths, expected) end
126
+
127
+ if of.op == "group" then
128
+ local word = of.word
129
+ local pack: Word.Pack?, unpack: Word.Unpack?
130
+ if word ~= nil then
131
+ pack, unpack = Word.emit (word, paths, expected)
132
+ end
133
+
134
+ local reach = #of.parts
135
+ local writes = table.create (reach) :: { Ir.Write }
136
+ local reads = table.create (reach) :: { Ir.Read }
137
+
138
+ for index, part in of.parts do
139
+ writes[index], reads[index] = Emit.emit (part.of, at)
140
+ end
141
+
142
+ local write, read = Block.group (of, pack, unpack, writes, reads, paths, expected)
143
+ return write, fielded (read, of)
144
+ end
145
+
146
+ if of.op == "apart" then
147
+ local reach = #of.parts
148
+ local writes = table.create (reach) :: { Ir.Write }
149
+ local reads = table.create (reach) :: { Ir.Read }
150
+
151
+ for index, part in of.parts do
152
+ writes[index], reads[index] = Emit.emit (part, at)
153
+ end
154
+
155
+ return Block.apart (of, writes, reads, paths, expected)
156
+ end
157
+
158
+ if of.op == "list" then
159
+ local write, read = Emit.emit (of.elem, at)
160
+ return Block.list (of, write, read, paths, expected)
161
+ end
162
+
163
+ if of.op == "pairs" then
164
+ local writeKey, readKey = Emit.emit (of.key, at)
165
+ local writeValue, readValue = Emit.emit (of.value, at)
166
+
167
+ return Block.pairs (of, writeKey, readKey, writeValue, readValue, paths, expected)
168
+ end
169
+
170
+ --[[
171
+ An optional is its own value and a bit of the region beside it, and the bit is the group's.
172
+ So there is no closure here that a present field does not already have, and an absent one is
173
+ a call the group does not make.
174
+ ]]
175
+ if of.op == "opt" then return Emit.emit (of.inner, at) end
176
+
177
+ if of.op == "branch" then
178
+ local reach = #of.arms
179
+ local writes = table.create (reach) :: { Ir.Write }
180
+ local reads = table.create (reach) :: { Ir.Read }
181
+
182
+ for index, arm in of.arms do
183
+ writes[index], reads[index] = Emit.emit (arm, at)
184
+ end
185
+
186
+ return Block.branch (of, writes, reads, paths, expected)
187
+ end
188
+
189
+ if of.op == "xform" then
190
+ local write, read = Emit.emit (of.inner, at)
191
+ return Block.xform (of, write, read)
192
+ end
193
+
194
+ return Leaf.slot (of, paths, expected, at.sidecar)
195
+ end
196
+
197
+ -- Public --------------------------------------------------------------
198
+
199
+ -- A shape and everything under it, as the pair the whole tree became.
200
+ function Emit.emit (of: Ir.Shape, at: Site): (Ir.Write, Ir.Read)
201
+ local write, read = made (of, at)
202
+ local held = if of.tag.checks == nil then read else checked (read, of.tag)
203
+
204
+ -- The root, which is the one place a parked reason becomes a refusal.
205
+ local function taken (b: buffer, pos: number, ctx: Types.ValidateContext): (number, any)
206
+ parkedPath = nil
207
+ parkedReason = nil
208
+
209
+ local ends, value = held (b, pos, ctx)
210
+ if parkedPath == nil then return ends, value end
211
+
212
+ local path, reason = parkedPath, parkedReason
213
+ parkedPath = nil
214
+ parkedReason = nil
215
+
216
+ return Log.reject (Catalog.VALIDATE, { pathId = path, reason = reason, at = ends })
217
+ end
218
+
219
+ return write, taken
220
+ end
221
+
222
+ return table.freeze (Emit)
@@ -0,0 +1,78 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- One declaration to the pair that writes it and reads it back, which is the whole compile path.
4
+
5
+ local Base = require (script.Parent.codec.Base)
6
+ local Emit = require (script.emit)
7
+ local Grid = require (script.Grid)
8
+ local Ir = require (script.Ir)
9
+ local Lower = require (script.Lower)
10
+ local Serial = require (script.Serial)
11
+ local Types = require (script.Parent.Types)
12
+
13
+ local Shape = {}
14
+
15
+ -- Types ---------------------------------------------------------------
16
+
17
+ -- What a build interns and what it hands a sidecar, declared below and passed on unchanged.
18
+ export type Site = Emit.Site
19
+
20
+ -- A declaration compiled: the pair, what it costs, and the facts everything above reads off it.
21
+ export type Built = {
22
+ read cell: Base.Cell,
23
+ read write: Ir.Write,
24
+ read read: Ir.Read,
25
+ read maxBytes: number,
26
+ read fixed: boolean,
27
+ read tag: Ir.Tag,
28
+ read grid: Grid.Grid?,
29
+ read marker: Base.Marker,
30
+ read sidecar: Ir.Sidecar,
31
+ read paths: Serial.Interner,
32
+ read expected: Serial.Interner,
33
+ }
34
+
35
+ -- Private -------------------------------------------------------------
36
+
37
+ -- The grid a whole declaration is one index on, where it is one, and nothing where it is not.
38
+ local function gridded (of: Ir.Shape): Grid.Grid?
39
+ if of.op == "xform" then return gridded (of.inner) end
40
+ if of.op == "wide" then return of.grid end
41
+ if of.op ~= "word" then return nil end
42
+
43
+ -- A pull spreads one value over several leaves, so there is no single index to answer with.
44
+ if of.joins[1].pull ~= nil then return nil end
45
+ return of.leaves[1]
46
+ end
47
+
48
+ -- Public --------------------------------------------------------------
49
+
50
+ -- A codec and where a refusal inside it says it is, as the build everything above holds.
51
+ function Shape.build (of: Types.Codec<any>, at: string, shared: Site?): Built
52
+ local cell = Base.cell (of)
53
+ local site: Site = shared
54
+ or {
55
+ paths = Serial.interner (),
56
+ expected = Serial.interner (),
57
+ sidecar = { refs = {}, slotOf = {}, bears = false },
58
+ }
59
+
60
+ local shape = Lower.of (cell, at, site)
61
+ local write, read = Emit.emit (shape, site)
62
+
63
+ return table.freeze ({
64
+ cell = cell,
65
+ write = write,
66
+ read = read,
67
+ maxBytes = shape.maxBytes,
68
+ fixed = shape.fixed,
69
+ tag = shape.tag,
70
+ grid = gridded (shape),
71
+ marker = cell.marker,
72
+ sidecar = site.sidecar,
73
+ paths = site.paths,
74
+ expected = site.expected,
75
+ })
76
+ end
77
+
78
+ return table.freeze (Shape)
@@ -0,0 +1,330 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The one path a section becomes a store by, which both machines run over the same sections.
4
+
5
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
6
+ local Sections = require (script.Parent.Sections)
7
+ local Store = require (script.Parent.Store)
8
+ local Types = require (script.Parent.Parent.Types)
9
+
10
+ local Apply = {}
11
+
12
+ -- Constants -----------------------------------------------------------
13
+
14
+ const KEYFRAME = Protocol.SECTION_KEYFRAME
15
+ const DELTA = Protocol.SECTION_DELTA
16
+ const REMOVAL = Protocol.SECTION_REMOVAL
17
+
18
+ -- The two causes a section can be read as, which is every cause there is.
19
+ const REMOVED: Types.Cause = "removed"
20
+ const CLEARED: Types.Cause = "cleared"
21
+
22
+ -- What a frame came to for one record, which is everything a caller can be told about one.
23
+ const NOTHING = 0
24
+ const ARRIVED = NOTHING + 1
25
+ const CHANGED = ARRIVED + 1
26
+ const GONE = CHANGED + 1
27
+ const REPLACED = GONE + 1
28
+
29
+ -- Types ---------------------------------------------------------------
30
+
31
+ --[[
32
+ What a frame did to one set, held per set rather than per partition because an id is unique
33
+ across the set and a slot is not. `net` is what the frame came to for each record and the whole
34
+ of what a caller is told, `added`, `changed` and `gone` are that taken apart for the fan, `olds`
35
+ is the record as it stood, and `shadow` is the slots a keyframe spoke for.
36
+ ]]
37
+ export type Heard = {
38
+ watching: boolean,
39
+ net: { [number]: number },
40
+ left: { [number]: Store.Store },
41
+ added: { number },
42
+ changed: { number },
43
+ gone: { number },
44
+ causes: { [number]: Types.Cause },
45
+ olds: { [number]: Store.Record },
46
+ shadow: { [Store.Store]: { [number]: boolean } },
47
+ owed: number,
48
+ holds: { { [number]: number } },
49
+ where: { number },
50
+ marks: { number },
51
+ }
52
+
53
+ -- Private -------------------------------------------------------------
54
+
55
+ -- What a partition's keyframes have spoken for this frame, made where the first one is applied.
56
+ local function shadowed (of: Heard, store: Store.Store): { [number]: boolean }
57
+ local held = of.shadow[store]
58
+ if held ~= nil then return held end
59
+
60
+ local made: { [number]: boolean } = {}
61
+ of.shadow[store] = made
62
+ return made
63
+ end
64
+
65
+ -- A baseline a keyframe stated, kept until every section of the frame has been read.
66
+ local function owes (of: Heard, held: { [number]: number }, slot: number, mark: number)
67
+ local at = of.owed + 1
68
+
69
+ of.owed = at
70
+ of.holds[at] = held
71
+ of.where[at] = slot
72
+ of.marks[at] = mark
73
+ end
74
+
75
+ -- Whether the record as it stood before this frame is worth keeping at all.
76
+ local function kept (of: Heard, id: number): boolean return of.watching and of.olds[id] == nil end
77
+
78
+ -- The record as it stood, kept where it lies, because this one is on its way out.
79
+ local function shed (of: Heard, store: Store.Store, id: number)
80
+ if not kept (of, id) then return end
81
+
82
+ local row = store.rows[id]
83
+ if row == nil then return end
84
+
85
+ of.olds[id] = row
86
+ end
87
+
88
+ -- The record as it stood before this frame, taken once and only where something wants it.
89
+ local function snapped (of: Heard, store: Store.Store, id: number)
90
+ if not kept (of, id) then return end
91
+
92
+ local row = store.rows[id]
93
+ if row == nil then return end
94
+
95
+ of.olds[id] = table.clone (row)
96
+ end
97
+
98
+ -- The same, for the lane that does not come through a section.
99
+ function Apply.snapped (of: Heard, store: Store.Store, id: number) snapped (of, store, id) end
100
+
101
+ -- A record established, which is an arrival unless this frame has already taken it away.
102
+ local function born (of: Heard, store: Store.Store, id: number)
103
+ local was = of.net[id]
104
+
105
+ if was == GONE then
106
+ of.net[id] = if of.left[id] == store then REPLACED else CHANGED
107
+ return
108
+ end
109
+ if was == CHANGED or was == REPLACED then return end
110
+
111
+ of.net[id] = ARRIVED
112
+ end
113
+
114
+ local function moved (of: Heard, id: number) Apply.moved (of, id) end
115
+
116
+ -- A record taken away, which cancels an arrival rather than following it.
117
+ local function lost (of: Heard, store: Store.Store, id: number, why: Types.Cause)
118
+ if of.net[id] == ARRIVED then
119
+ of.net[id] = NOTHING
120
+ return
121
+ end
122
+
123
+ of.net[id] = GONE
124
+ of.left[id] = store
125
+ of.causes[id] = why
126
+ end
127
+
128
+ --[[
129
+ A partition emptied, and every record it held reported gone.
130
+
131
+ Read before the store is emptied rather than after, which is the only order there is: what a
132
+ clear says is every record this receiver held, and afterwards it holds none.
133
+ ]]
134
+ local function emptied (store: Store.Store, of: Heard)
135
+ for id in store.rows do
136
+ shed (of, store, id)
137
+ lost (of, store, id, CLEARED)
138
+ end
139
+
140
+ Store.clear (store)
141
+ end
142
+
143
+ --[[
144
+ The listed records gone. A slot this receiver does not hold is a specified no-op rather than a
145
+ fault, since a removal is broadcast and an audience is what decides who was holding one.
146
+ ]]
147
+ local function dropped (store: Store.Store, frame: Sections.Frame, of: Heard)
148
+ for index = 1, frame.count do
149
+ local id = store.ids[frame.slots[index] ]
150
+ if id == nil then continue end
151
+
152
+ shed (of, store, id)
153
+ Store.unbind (store, id)
154
+ lost (of, store, id, REMOVED)
155
+ end
156
+ end
157
+
158
+ --[[
159
+ The listed records established: bound to the slots the other machine chose, at the generations
160
+ it stated, with every field of every one of them written whole.
161
+ ]]
162
+ local function established (store: Store.Store, layout: Sections.Layout, of: Heard)
163
+ local frame = layout.frame
164
+ local shadow = shadowed (of, store)
165
+ local gens = store.gens
166
+
167
+ for index = 1, frame.count do
168
+ local slot = frame.slots[index]
169
+ local id = frame.ids[index]
170
+ local was = store.slotOf[id]
171
+
172
+ snapped (of, store, id)
173
+ Store.hold (store, id, slot)
174
+ if gens ~= nil then gens[slot] = frame.gens[index] end
175
+
176
+ shadow[slot] = true
177
+ if was == nil then
178
+ born (of, store, id)
179
+ else
180
+ moved (of, id)
181
+ end
182
+ end
183
+
184
+ for field = 1, layout.def.fieldCount do
185
+ local values = frame.values[field]
186
+ local marks = frame.at[field]
187
+ local held = store.prev[field]
188
+
189
+ for index = 1, frame.count do
190
+ local slot = frame.slots[index]
191
+
192
+ Store.write (store, slot, field, values[index])
193
+ if held ~= nil then owes (of, held, slot, marks[index]) end
194
+ end
195
+ end
196
+ end
197
+
198
+ --[[
199
+ The listed differences applied, for the slots this receiver holds and this frame has not already
200
+ spoken for.
201
+ ]]
202
+ local function advanced (store: Store.Store, layout: Sections.Layout, of: Heard)
203
+ local frame = layout.frame
204
+ local chosen = frame.chosen
205
+ local shadow = shadowed (of, store)
206
+ local ids = store.ids
207
+
208
+ for place = 1, chosen.count do
209
+ local field = chosen.fields[place]
210
+ local under = frame.under[place]
211
+ local values = frame.values[field]
212
+ local marks = frame.at[field]
213
+ local held = store.prev[field]
214
+
215
+ for index = 1, frame.covered[place] do
216
+ local slot = under[index]
217
+ local id = ids[slot]
218
+ if id == nil or shadow[slot] then continue end
219
+
220
+ local before = store.columns[field][slot]
221
+
222
+ snapped (of, store, id)
223
+ Store.write (store, slot, field, values[index])
224
+ if held ~= nil then held[slot] = marks[index] end
225
+
226
+ -- A value that arrived as what the field already held is no change to report.
227
+ if before ~= values[index] then moved (of, id) end
228
+ end
229
+ end
230
+ end
231
+
232
+ -- Public --------------------------------------------------------------
233
+
234
+ -- One record changed, said once however many times a frame says it.
235
+ function Apply.moved (of: Heard, id: number)
236
+ local was = of.net[id]
237
+ if was == ARRIVED or was == REPLACED then return end
238
+
239
+ of.net[id] = CHANGED
240
+ end
241
+
242
+ -- What the frame came to, as the three lists the fan reads, each in id order.
243
+ function Apply.parted (of: Heard): ({ number }, { number }, { number })
244
+ local gone, added, changed = of.gone, of.added, of.changed
245
+
246
+ table.clear (gone)
247
+ table.clear (added)
248
+ table.clear (changed)
249
+
250
+ for id, net in of.net do
251
+ if net == GONE or net == REPLACED then table.insert (gone, id) end
252
+ if net == ARRIVED or net == REPLACED then table.insert (added, id) end
253
+ if net == CHANGED then table.insert (changed, id) end
254
+ end
255
+
256
+ table.sort (gone)
257
+ table.sort (added)
258
+ table.sort (changed)
259
+
260
+ return gone, added, changed
261
+ end
262
+
263
+ -- What a frame did to one set, empty, which a set holds one of for as long as it exists.
264
+ function Apply.heard (): Heard
265
+ return {
266
+ watching = false,
267
+ net = {},
268
+ left = {},
269
+ added = {},
270
+ changed = {},
271
+ gone = {},
272
+ causes = {},
273
+ olds = {},
274
+ shadow = {},
275
+ owed = 0,
276
+ holds = {},
277
+ where = {},
278
+ marks = {},
279
+ }
280
+ end
281
+
282
+ -- One section, into the partition it is about.
283
+ function Apply.section (store: Store.Store, layout: Sections.Layout, kind: number, of: Heard)
284
+ if kind == KEYFRAME then
285
+ established (store, layout, of)
286
+ return
287
+ end
288
+
289
+ if kind == DELTA then
290
+ advanced (store, layout, of)
291
+ return
292
+ end
293
+
294
+ if kind == REMOVAL then
295
+ dropped (store, layout.frame, of)
296
+ return
297
+ end
298
+
299
+ emptied (store, of)
300
+ end
301
+
302
+ -- The frame over: what its keyframes owe, paid, and what they spoke for, forgotten.
303
+ function Apply.settle (of: Heard)
304
+ for index = 1, of.owed do
305
+ of.holds[index][of.where[index] ] = of.marks[index]
306
+ end
307
+ of.owed = 0
308
+
309
+ for _, held in of.shadow do
310
+ table.clear (held)
311
+ end
312
+ end
313
+
314
+ --[[
315
+ What the frame did, forgotten, which is what a caller does once it has said so.
316
+
317
+ Apart from the settling because the two answer to different things: the baselines are owed to
318
+ the next frame and cannot wait on anybody, and this waits on every listener having run.
319
+ ]]
320
+ function Apply.forget (of: Heard)
321
+ table.clear (of.net)
322
+ table.clear (of.left)
323
+ table.clear (of.added)
324
+ table.clear (of.changed)
325
+ table.clear (of.gone)
326
+ table.clear (of.causes)
327
+ table.clear (of.olds)
328
+ end
329
+
330
+ return table.freeze (Apply)