@axpecter/lync 2.3.3 → 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 -317
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -193
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -473
  82. package/src/codec/composite/Map.luau +0 -485
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -311
  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 -160
  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 -484
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -66
  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 -233
  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,468 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- The state sections: one partition's flush, as the bytes it becomes.
4
+
5
+ local Bits = require (script.Parent.Parent.kernel.Bits)
6
+ local Column = require (script.Parent.Parent.shape.emit.Column)
7
+ local Compile = require (script.Parent.Parent.shape.Compile)
8
+ local Fieldset = require (script.Parent.Fieldset)
9
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
10
+ local Shape = require (script.Parent.Parent.shape)
11
+ local Types = require (script.Parent.Parent.Types)
12
+ local Varint = require (script.Parent.Parent.wire.Varint)
13
+
14
+ local Sections = {}
15
+
16
+ -- Constants -----------------------------------------------------------
17
+
18
+ const KEYFRAME = Protocol.SECTION_KEYFRAME
19
+ const DELTA = Protocol.SECTION_DELTA
20
+ const REMOVAL = Protocol.SECTION_REMOVAL
21
+ const CLEAR = Protocol.SECTION_CLEAR
22
+
23
+ -- The bits a keyframe states a slot's generation in, which is what the modulus asks for.
24
+ const GENS = Bits.width (Protocol.NEWEST_GENS - 1)
25
+
26
+ -- The presence run's element, and the value of the flag that says a field covered every slot.
27
+ const PRESENT = 1
28
+ const COVERS = 1
29
+
30
+ -- What a field's history has to be worth before a difference is taken against it.
31
+ const CHEAPER = 8
32
+
33
+ -- How wide an element a run packs, past which a column is a stride rather than a run.
34
+ const RUNS = Protocol.BIT_CEILING
35
+
36
+ -- What the widest varint spends, which is what a slot and an id are each bounded by.
37
+ const WIDEST = Protocol.VARINT_GROUPS
38
+
39
+ -- The least an entry of each kind can spend: a slot and an id, or a slot alone.
40
+ const PAIRED = 2
41
+ const LONE = 1
42
+
43
+ const TOO_MANY = "a section naming more records than its own bytes could hold"
44
+ const OUT_OF_ORDER = "a section naming its slots out of order"
45
+ const GENERATIONS = "a generation run"
46
+ const PRESENCE = "a presence run"
47
+
48
+ -- Types ---------------------------------------------------------------
49
+
50
+ -- One field's values across a partition's slots, and the last index each was sent at.
51
+ export type Columns = { { [number]: any } }
52
+ export type History = { { [number]: number }? }
53
+
54
+ -- One section, as the caller fills it and as a reader fills it back.
55
+ export type Frame = {
56
+ key: any,
57
+ count: number,
58
+ slots: { number },
59
+ ids: { number },
60
+ gens: { number },
61
+ chosen: Fieldset.Chosen,
62
+ present: { { number } },
63
+ under: { { number } },
64
+ covered: { number },
65
+ values: { { any } },
66
+ at: { { number } },
67
+ }
68
+
69
+ -- One set's sections, compiled: what each field's column is, and what a section of them costs.
70
+ export type Layout = {
71
+ read def: Compile.Set,
72
+ read set: Fieldset.Fieldset,
73
+ read fields: { Field },
74
+ read generations: Field,
75
+ read presence: Field,
76
+ read frame: Frame,
77
+ read held: { number },
78
+ read spare: { number },
79
+ read opening: number,
80
+ read entry: number,
81
+ read each: { number },
82
+ read gained: { number },
83
+ }
84
+
85
+ -- What one field of a set becomes at both ends: whole always, and as a difference where it can.
86
+ export type Field = {
87
+ read whole: Column.Write,
88
+ read back: Column.Read,
89
+ read gain: Column.Gain?,
90
+ read regain: Column.Regain?,
91
+ }
92
+
93
+ -- Private -------------------------------------------------------------
94
+
95
+ -- A run of the section's own numbers, which is a field-shaped thing with no difference to take.
96
+ local function run (width: number, said: string): Field
97
+ local whole, back = Column.bits (width, said)
98
+ return { whole = whole, back = back, gain = nil, regain = nil }
99
+ end
100
+
101
+ -- One field, as the pair or two that lay its column down.
102
+ local function made (built: Shape.Built): Field
103
+ local grid = built.grid
104
+ local deltas = grid ~= nil and built.marker ~= "newest" and grid.width > CHEAPER
105
+
106
+ local whole: Column.Write, back: Column.Read
107
+ if grid == nil or grid.width > RUNS then
108
+ -- Stating its indices only where something will take a difference against them.
109
+ whole, back = Column.each (built.write, built.read, if deltas then grid else nil)
110
+ else
111
+ whole, back = Column.grid (built.tag, built.paths, built.expected, grid)
112
+ end
113
+
114
+ if not deltas then return { whole = whole, back = back, gain = nil, regain = nil } end
115
+
116
+ local gain, regain = Column.gain (
117
+ built.tag,
118
+ built.paths,
119
+ built.expected,
120
+ grid :: any,
121
+ built.marker ~= "monotonic"
122
+ )
123
+ return { whole = whole, back = back, gain = gain, regain = regain }
124
+ end
125
+
126
+ -- The slots a field covers and where its values sit, gathered where the column reads them.
127
+ local function gathered (
128
+ of: Frame,
129
+ place: number,
130
+ field: number,
131
+ columns: Columns,
132
+ count: number
133
+ ): number
134
+ local under = of.under[place]
135
+ local values = of.values[field]
136
+ local held = columns[field]
137
+
138
+ if of.chosen.full[place] == COVERS then
139
+ for index = 1, count do
140
+ local slot = of.slots[index]
141
+ under[index] = slot
142
+ values[index] = held[slot]
143
+ end
144
+
145
+ of.covered[place] = count
146
+ return count
147
+ end
148
+
149
+ local present = of.present[place]
150
+ local reach = 0
151
+
152
+ for index = 1, count do
153
+ if present[index] == 0 then continue end
154
+
155
+ local slot = of.slots[index]
156
+ reach += 1
157
+ under[reach] = slot
158
+ values[reach] = held[slot]
159
+ end
160
+
161
+ of.covered[place] = reach
162
+ return reach
163
+ end
164
+
165
+ -- The same slots, without the values, which is what a reader has before a column has answered.
166
+ local function covering (of: Frame, place: number, count: number): number
167
+ local under = of.under[place]
168
+
169
+ if of.chosen.full[place] == COVERS then
170
+ table.move (of.slots, 1, count, 1, under)
171
+ of.covered[place] = count
172
+ return count
173
+ end
174
+
175
+ local present = of.present[place]
176
+ local reach = 0
177
+
178
+ for index = 1, count do
179
+ if present[index] == 0 then continue end
180
+
181
+ reach += 1
182
+ under[reach] = of.slots[index]
183
+ end
184
+
185
+ of.covered[place] = reach
186
+ return reach
187
+ end
188
+
189
+ -- A count against the bytes left to hold it.
190
+ local function counted (b: buffer, at: number, least: number): (number, number)
191
+ local count, after = Varint.read (b, at)
192
+ if count * least > buffer.len (b) - after then Fieldset.broken (TOO_MANY) end
193
+
194
+ return count, after
195
+ end
196
+
197
+ -- A slot list, ascending, which determinism asks for and one comparison holds it to.
198
+ local function listed (of: Frame, b: buffer, at: number, count: number): number
199
+ local held = at
200
+ local last = 0
201
+
202
+ for index = 1, count do
203
+ local slot
204
+ slot, held = Varint.read (b, held)
205
+ if slot <= last then Fieldset.broken (OUT_OF_ORDER) end
206
+
207
+ of.slots[index] = slot
208
+ last = slot
209
+ end
210
+
211
+ return held
212
+ end
213
+
214
+ -- A section's header and the key of the partition it is about, which every kind opens with.
215
+ local function opened (of: Layout, b: buffer, pos: number, kind: number): number
216
+ local def = of.def
217
+ of.held[2] = kind
218
+ def.head.write (b, pos, of.held)
219
+
220
+ local at = pos + def.head.bytes
221
+ local key = def.keyIndex
222
+ if key == nil then return at end
223
+
224
+ return def.fieldBuilt[key].write (b, at, of.frame.key)
225
+ end
226
+
227
+ -- The count and the slots under it, which three of the four kinds carry and one does not.
228
+ local function spelled (of: Layout, b: buffer, at: number): number
229
+ local frame = of.frame
230
+ local held = Varint.write (b, at, frame.count)
231
+
232
+ for index = 1, frame.count do
233
+ held = Varint.write (b, held, frame.slots[index])
234
+ end
235
+ return held
236
+ end
237
+
238
+ -- Public --------------------------------------------------------------
239
+
240
+ -- A set's sections, as the closures and the scratch one partition's flush is written through.
241
+ function Sections.of (def: Compile.Set): Layout
242
+ local reach = def.fieldCount
243
+ local fields = table.create (reach) :: { Field }
244
+ local each = table.create (reach) :: { number }
245
+ local gained = table.create (reach) :: { number }
246
+
247
+ local values = table.create (reach) :: { { any } }
248
+ local at = table.create (reach) :: { { number } }
249
+ local present = table.create (reach) :: { { number } }
250
+ local under = table.create (reach) :: { { number } }
251
+
252
+ local entry = WIDEST * 2 + if #def.newestFields == 0 then 0 else 1
253
+
254
+ for index = 1, reach do
255
+ local built = def.fieldBuilt[index]
256
+ local one = made (built)
257
+
258
+ fields[index] = one
259
+ each[index] = built.maxBytes
260
+ gained[index] = if one.gain == nil then each[index] else WIDEST
261
+ entry += each[index]
262
+
263
+ values[index] = {}
264
+ at[index] = {}
265
+ present[index] = {}
266
+ under[index] = {}
267
+ end
268
+
269
+ local key = def.keyIndex
270
+ local set = Fieldset.of (reach)
271
+
272
+ return table.freeze ({
273
+ def = def,
274
+ set = set,
275
+ fields = table.freeze (fields),
276
+ generations = table.freeze (run (GENS, GENERATIONS)),
277
+ presence = table.freeze (run (PRESENT, PRESENCE)),
278
+ frame = {
279
+ key = nil,
280
+ count = 0,
281
+ slots = {},
282
+ ids = {},
283
+ gens = {},
284
+ chosen = Fieldset.chosen (),
285
+ present = present,
286
+ under = under,
287
+ covered = {},
288
+ values = values,
289
+ at = at,
290
+ },
291
+ held = { def.id, KEYFRAME },
292
+ spare = {},
293
+ opening = def.head.bytes + (if key == nil then 0 else def.fieldBuilt[key].maxBytes) + 1,
294
+ entry = entry,
295
+ each = table.freeze (each),
296
+ gained = table.freeze (gained),
297
+ })
298
+ end
299
+
300
+ --[[
301
+ A keyframe: the listed slots, the ids they are bound to, the generations they stand at, and
302
+ every field of every one of them.
303
+ ]]
304
+ function Sections.keyframe (of: Layout, b: buffer, pos: number, columns: Columns): number
305
+ local frame = of.frame
306
+ local def = of.def
307
+ local count = frame.count
308
+
309
+ local at = spelled (of, b, opened (of, b, pos, KEYFRAME))
310
+ for index = 1, count do
311
+ at = Varint.writeSigned (b, at, frame.ids[index])
312
+ end
313
+
314
+ if #def.newestFields > 0 then
315
+ at = of.generations.whole (b, at, frame.gens :: any, of.spare, count)
316
+ end
317
+
318
+ for field = 1, def.fieldCount do
319
+ local values = frame.values[field]
320
+ local held = columns[field]
321
+
322
+ for index = 1, count do
323
+ values[index] = held[frame.slots[index] ]
324
+ end
325
+
326
+ at = of.fields[field].whole (b, at, values, frame.at[field], count)
327
+ end
328
+
329
+ return at
330
+ end
331
+
332
+ -- A delta: the fields that changed, the slots they changed on, and the differences.
333
+ function Sections.delta (of: Layout, b: buffer, pos: number, columns: Columns, was: History): number
334
+ local frame = of.frame
335
+ local chosen = frame.chosen
336
+ local count = frame.count
337
+
338
+ local at = of.set.write (b, opened (of, b, pos, DELTA), chosen)
339
+ at = spelled (of, b, at)
340
+
341
+ for place = 1, chosen.count do
342
+ if chosen.full[place] ~= COVERS then
343
+ at = of.presence.whole (b, at, frame.present[place] :: any, of.spare, count)
344
+ end
345
+
346
+ local field = chosen.fields[place]
347
+ local covered = gathered (frame, place, field, columns, count)
348
+
349
+ local one = of.fields[field]
350
+ local gain = one.gain
351
+ local values, answered = frame.values[field], frame.at[field]
352
+
353
+ if gain == nil then
354
+ at = one.whole (b, at, values, answered, covered)
355
+ continue
356
+ end
357
+
358
+ local base = was[field] :: { [number]: number }
359
+ at = gain (b, at, values, frame.under[place], base, answered, covered)
360
+ end
361
+
362
+ return at
363
+ end
364
+
365
+ --[[
366
+ A removal: the slots and nothing else, deliberately, since a cause would announce a record past
367
+ the audience that was meant to be the only thing that knew of it.
368
+ ]]
369
+ function Sections.removal (of: Layout, b: buffer, pos: number): number
370
+ return spelled (of, b, opened (of, b, pos, REMOVAL))
371
+ end
372
+
373
+ -- A clear: the partition and nothing about it, which is the whole of what emptying one says.
374
+ function Sections.clear (of: Layout, b: buffer, pos: number): number
375
+ return opened (of, b, pos, CLEAR)
376
+ end
377
+
378
+ -- Which kind of section is at this position and which partition it is about, and nothing else.
379
+ function Sections.about (
380
+ of: Layout,
381
+ b: buffer,
382
+ pos: number,
383
+ ctx: Types.ValidateContext
384
+ ): (number, any)
385
+ local def = of.def
386
+ local held = of.held
387
+
388
+ def.head.read (b, pos, held)
389
+
390
+ local key = def.keyIndex
391
+ if key == nil then return held[2], nil end
392
+ local
393
+ _, value =def.fieldBuilt[key].read (b, pos + def.head.bytes, ctx)
394
+ return held[2], value
395
+ end
396
+
397
+ -- Whatever section is at this position, as its kind and the byte past it.
398
+ function Sections.taken (
399
+ of: Layout,
400
+ b: buffer,
401
+ pos: number,
402
+ was: History,
403
+ ctx: Types.ValidateContext
404
+ ): (number, number)
405
+ local frame = of.frame
406
+ local def = of.def
407
+ local held = of.held
408
+
409
+ def.head.read (b, pos, held)
410
+ local kind = held[2]
411
+ local at = pos + def.head.bytes
412
+
413
+ local key = def.keyIndex
414
+ if key ~= nil then
415
+ at, frame.key = def.fieldBuilt[key].read (b, at, ctx)
416
+ end
417
+ if kind == CLEAR then return kind, at end
418
+
419
+ local chosen = frame.chosen
420
+ if kind == DELTA then at = of.set.read (b, at, chosen) end
421
+
422
+ local count
423
+ count, at = counted (b, at, if kind == KEYFRAME then PAIRED else LONE)
424
+ frame.count = count
425
+ at = listed (frame, b, at, count)
426
+ if kind == REMOVAL then return kind, at end
427
+
428
+ if kind == KEYFRAME then
429
+ for index = 1, count do
430
+ frame.ids[index], at = Varint.readSigned (b, at)
431
+ end
432
+
433
+ if #def.newestFields > 0 then
434
+ at = of.generations.back (b, at, frame.gens :: any, of.spare, count, ctx)
435
+ end
436
+
437
+ for field = 1, def.fieldCount do
438
+ at = of.fields[field].back (b, at, frame.values[field], frame.at[field], count, ctx)
439
+ end
440
+
441
+ return kind, at
442
+ end
443
+
444
+ for place = 1, chosen.count do
445
+ if chosen.full[place] ~= COVERS then
446
+ at = of.presence.back (b, at, frame.present[place] :: any, of.spare, count, ctx)
447
+ end
448
+
449
+ local covered = covering (frame, place, count)
450
+ local field = chosen.fields[place]
451
+
452
+ local one = of.fields[field]
453
+ local regain = one.regain
454
+ local values, answered = frame.values[field], frame.at[field]
455
+
456
+ if regain == nil then
457
+ at = one.back (b, at, values, answered, covered, ctx)
458
+ continue
459
+ end
460
+
461
+ local base = was[field] :: { [number]: number }
462
+ at = regain (b, at, values, frame.under[place], base, answered, covered)
463
+ end
464
+
465
+ return kind, at
466
+ end
467
+
468
+ return table.freeze (Sections)
@@ -0,0 +1,188 @@
1
+ --!strict
2
+ --!optimize 2
3
+ -- One partition's truth: a slot per record, the columns a wire reads and the rows a caller does.
4
+
5
+ local Compile = require (script.Parent.Parent.shape.Compile)
6
+ local Heap = require (script.Parent.Parent.kernel.Heap)
7
+ local Protocol = require (script.Parent.Parent.wire.Protocol)
8
+
9
+ local Store = {}
10
+
11
+ -- Constants -----------------------------------------------------------
12
+
13
+ -- How far a slot's generation counts before it comes round, which is what two wire bits hold.
14
+ const GENS = Protocol.NEWEST_GENS
15
+
16
+ -- What a field's marker has to be for the field to keep a last-sent index rather than a baseline.
17
+ const LATEST: Compile.Marker = "newest"
18
+
19
+ -- Types ---------------------------------------------------------------
20
+
21
+ -- A record as a caller reads one, which is the same table the callbacks are handed.
22
+ export type Record = { [string]: any }
23
+
24
+ --[[
25
+ One partition, held twice. `columns` is the wire's truth, one field across every slot, which a
26
+ section writes as one lookup and then array reads. `rows` is the caller's, one record under its
27
+ id at one lookup. One write fills both, which is why they cannot come apart. A slot is the
28
+ lowest free one, and what that costs is the generation a receiver tells a rebind by.
29
+ ]]
30
+ export type Store = {
31
+ read def: Compile.Set,
32
+ read fields: number,
33
+ read columns: { { [number]: any } },
34
+ read rows: { [number]: Record },
35
+ read ids: { [number]: number },
36
+ read slotOf: { [number]: number },
37
+ read prev: { { [number]: number }? },
38
+ read sent: { { [number]: any }? },
39
+ read gens: { [number]: number }?,
40
+ read free: Heap.Heap,
41
+ high: number,
42
+ count: number,
43
+ }
44
+
45
+ -- Private -------------------------------------------------------------
46
+
47
+ -- A slot nobody holds, and whether it has been held before.
48
+ local function taken (of: Store): number
49
+ local gens = of.gens
50
+ local _, held =Heap.pop (of.free)
51
+
52
+ if held ~= nil then
53
+ local slot = held :: number
54
+ if gens ~= nil then gens[slot] = (gens[slot] + 1) % GENS end
55
+ return slot
56
+ end
57
+
58
+ local slot = of.high + 1
59
+ of.high = slot
60
+ if gens ~= nil then gens[slot] = 0 end
61
+ return slot
62
+ end
63
+
64
+ -- Public --------------------------------------------------------------
65
+
66
+ -- A partition, empty.
67
+ function Store.new (of: Compile.Set): Store
68
+ local fields = of.fieldCount
69
+ local columns = table.create (fields) :: { { [number]: any } }
70
+ local prev = table.create (fields) :: { { [number]: number }? }
71
+ local sent = table.create (fields) :: { { [number]: any }? }
72
+
73
+ for index = 1, fields do
74
+ local built = of.fieldBuilt[index]
75
+ local newest = built.cell.marker == LATEST
76
+
77
+ columns[index] = {}
78
+ prev[index] = if built.grid ~= nil and not newest then {} else nil
79
+ sent[index] = if newest then {} else nil
80
+ end
81
+
82
+ return {
83
+ def = of,
84
+ fields = fields,
85
+ columns = columns,
86
+ rows = {},
87
+ ids = {},
88
+ slotOf = {},
89
+ prev = prev,
90
+ sent = sent,
91
+ gens = if #of.newestFields == 0 then nil else {},
92
+ free = Heap.new (),
93
+ high = 0,
94
+ count = 0,
95
+ }
96
+ end
97
+
98
+ -- A record under an id, at the slot it will keep until it is removed.
99
+ function Store.bind (of: Store, id: number): number
100
+ local slot = taken (of)
101
+
102
+ of.ids[slot] = id
103
+ of.slotOf[id] = slot
104
+ of.rows[id] = table.clone (of.def.template)
105
+ of.count += 1
106
+
107
+ return slot
108
+ end
109
+
110
+ -- A record at the slot the other machine chose for it.
111
+ function Store.hold (of: Store, id: number, slot: number)
112
+ if of.ids[slot] == nil then
113
+ of.count += 1
114
+ end
115
+
116
+ of.ids[slot] = id
117
+ of.slotOf[id] = slot
118
+ of.rows[id] = table.clone (of.def.template)
119
+
120
+ if slot > of.high then of.high = slot end
121
+ end
122
+
123
+ -- A record gone, and the slot it held.
124
+ function Store.unbind (of: Store, id: number): number?
125
+ local slot = of.slotOf[id]
126
+ if slot == nil then return nil end
127
+
128
+ of.slotOf[id] = nil
129
+ of.ids[slot] = nil
130
+ of.rows[id] = nil
131
+ of.count -= 1
132
+
133
+ return slot
134
+ end
135
+
136
+ -- A slot back where the allocator finds it, which only the machine that hands them out does.
137
+ function Store.spare (of: Store, slot: number) Heap.push (of.free, slot, slot) end
138
+
139
+ --[[
140
+ One field of one record, in both of the places it is held.
141
+
142
+ Both at once: the column and the row are one fact written twice, and they stay one fact only
143
+ because nothing can write either alone.
144
+ ]]
145
+ function Store.write (of: Store, slot: number, field: number, value: any)
146
+ of.columns[field][slot] = value
147
+
148
+ local id = of.ids[slot]
149
+ local row = of.rows[id]
150
+ --[[
151
+ A row a caller has been handed is frozen in a development build, so this writes a copy
152
+ rather than raising on it. What the caller holds stays what they were told it was: the
153
+ record as it read at the moment they took it, and borrowed rather than live.
154
+ ]]
155
+
156
+
157
+
158
+
159
+
160
+
161
+ row[of.def.fieldNames[field] ] = value
162
+ end
163
+
164
+ -- A partition emptied, and every slot it handed out back at the beginning.
165
+ function Store.clear (of: Store)
166
+ for index = 1, of.fields do
167
+ table.clear (of.columns[index])
168
+
169
+ local baseline = of.prev[index]
170
+ if baseline ~= nil then table.clear (baseline) end
171
+
172
+ local last = of.sent[index]
173
+ if last ~= nil then table.clear (last) end
174
+ end
175
+
176
+ local gens = of.gens
177
+ if gens ~= nil then table.clear (gens) end
178
+
179
+ table.clear (of.rows)
180
+ table.clear (of.ids)
181
+ table.clear (of.slotOf)
182
+
183
+ of.free.count = 0
184
+ of.high = 0
185
+ of.count = 0
186
+ end
187
+
188
+ return table.freeze (Store)