@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.
- package/LICENSE +21 -0
- package/README.md +302 -299
- package/Types.luau +215 -0
- package/api/Define.luau +154 -0
- package/api/Lifecycle.luau +119 -0
- package/api/Packet.luau +108 -0
- package/api/Query.luau +149 -0
- package/api/Set.luau +189 -0
- package/channel/Blocks.luau +124 -0
- package/channel/Group.luau +118 -0
- package/channel/Link.luau +135 -0
- package/channel/Outbox.luau +261 -0
- package/channel/Recipients.luau +129 -0
- package/channel/Transport.luau +138 -0
- package/codec/Base.luau +236 -0
- package/codec/Composites.luau +128 -0
- package/codec/Datatypes.luau +162 -0
- package/codec/Legality.luau +61 -0
- package/codec/Scalars.luau +72 -0
- package/codec/Text.luau +89 -0
- package/codec/init.luau +39 -0
- package/index.d.ts +285 -0
- package/init.luau +91 -0
- package/kernel/Bits.luau +16 -0
- package/kernel/Catalog.luau +121 -0
- package/kernel/Heap.luau +80 -0
- package/kernel/Listeners.luau +95 -0
- package/kernel/Log.luau +197 -0
- package/kernel/Tally.luau +128 -0
- package/kernel/Threads.luau +44 -0
- package/kernel/Wide.luau +267 -0
- package/package.json +15 -32
- package/runtime/Boundary.luau +180 -0
- package/runtime/Dispatch.luau +91 -0
- package/runtime/Handles.luau +114 -0
- package/runtime/Handshake.luau +90 -0
- package/runtime/Pending.luau +196 -0
- package/runtime/Router.luau +379 -0
- package/runtime/Session.luau +731 -0
- package/shape/Compile.luau +340 -0
- package/shape/Folds.luau +332 -0
- package/shape/Frames.luau +170 -0
- package/shape/Grid.luau +169 -0
- package/shape/Ir.luau +540 -0
- package/shape/Joins.luau +125 -0
- package/shape/Legality.luau +273 -0
- package/shape/Lower.luau +419 -0
- package/shape/Order.luau +64 -0
- package/shape/Regions.luau +113 -0
- package/shape/Rotation.luau +472 -0
- package/shape/Serial.luau +258 -0
- package/shape/Words.luau +89 -0
- package/shape/emit/Block.luau +453 -0
- package/shape/emit/Column.luau +338 -0
- package/shape/emit/Leaf.luau +462 -0
- package/shape/emit/Word.luau +234 -0
- package/shape/emit/init.luau +222 -0
- package/shape/init.luau +78 -0
- package/state/Apply.luau +330 -0
- package/state/Audience.luau +126 -0
- package/state/Catchup.luau +218 -0
- package/state/Coalesce.luau +43 -0
- package/state/Fieldset.luau +177 -0
- package/state/Newest.luau +339 -0
- package/state/Plan.luau +441 -0
- package/state/Sections.luau +468 -0
- package/state/Store.luau +188 -0
- package/state/Sync.luau +762 -0
- package/wire/Bytes.luau +118 -0
- package/wire/Clock.luau +37 -0
- package/wire/Protocol.luau +110 -0
- package/wire/Sequence.luau +40 -0
- package/wire/Varint.luau +124 -0
- package/src/Types.luau +0 -180
- package/src/api/Group.luau +0 -156
- package/src/api/Packet.luau +0 -319
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -188
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -456
- package/src/codec/composite/Map.luau +0 -469
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -291
- package/src/codec/composite/Struct.luau +0 -494
- package/src/codec/composite/Tagged.luau +0 -103
- package/src/codec/composite/Tuple.luau +0 -143
- package/src/codec/datatype/Buffer.luau +0 -66
- package/src/codec/datatype/CFrame.luau +0 -109
- package/src/codec/datatype/Color.luau +0 -32
- package/src/codec/datatype/Instance.luau +0 -52
- package/src/codec/datatype/IntVector.luau +0 -39
- package/src/codec/datatype/NumberRange.luau +0 -39
- package/src/codec/datatype/Ray.luau +0 -36
- package/src/codec/datatype/Rect.luau +0 -31
- package/src/codec/datatype/Region.luau +0 -62
- package/src/codec/datatype/Sequence.luau +0 -184
- package/src/codec/datatype/String.luau +0 -94
- package/src/codec/datatype/UDim.luau +0 -46
- package/src/codec/datatype/Vector.luau +0 -130
- package/src/codec/meta/Auto.luau +0 -318
- package/src/codec/meta/Bitfield.luau +0 -147
- package/src/codec/meta/Custom.luau +0 -33
- package/src/codec/meta/DeltaScalar.luau +0 -390
- package/src/codec/meta/Enum.luau +0 -50
- package/src/codec/meta/Float.luau +0 -38
- package/src/codec/meta/Nothing.luau +0 -13
- package/src/codec/meta/Unknown.luau +0 -41
- package/src/codec/primitive/Bool.luau +0 -26
- package/src/codec/primitive/Float16.luau +0 -99
- package/src/codec/primitive/Int.luau +0 -59
- package/src/codec/primitive/Number.luau +0 -24
- package/src/codec/primitive/Signed.luau +0 -32
- package/src/codec/primitive/Varint.luau +0 -165
- package/src/codec/primitive/Zint.luau +0 -99
- package/src/index.d.ts +0 -423
- package/src/init.luau +0 -362
- package/src/internal/Baseline.luau +0 -57
- package/src/internal/Channel.luau +0 -390
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -65
- package/src/internal/Registry.luau +0 -119
- package/src/internal/Transport.luau +0 -28
- package/src/transport/Bridge.luau +0 -110
- package/src/transport/Client.luau +0 -97
- package/src/transport/Gate.luau +0 -488
- package/src/transport/Reader.luau +0 -331
- package/src/transport/Server.luau +0 -211
- package/src/util/Array.luau +0 -18
- package/src/util/Buffer.luau +0 -90
- package/src/util/Constants.luau +0 -30
- package/src/util/Log.luau +0 -68
- package/src/util/Player.luau +0 -14
- package/src/util/Quantize.luau +0 -84
- 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)
|
package/state/Store.luau
ADDED
|
@@ -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)
|