@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.
- 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 -317
- package/src/api/Query.luau +0 -338
- package/src/api/Scope.luau +0 -90
- package/src/api/Signal.luau +0 -193
- package/src/codec/Base.luau +0 -136
- package/src/codec/composite/Array.luau +0 -473
- package/src/codec/composite/Map.luau +0 -485
- package/src/codec/composite/Optional.luau +0 -54
- package/src/codec/composite/Shared.luau +0 -311
- 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 -160
- 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 -484
- package/src/internal/Middleware.luau +0 -100
- package/src/internal/Pool.luau +0 -66
- 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 -233
- 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,126 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- Who sees a partition, and what has changed about that since the last flush.
|
|
4
|
+
|
|
5
|
+
local Recipients = require (script.Parent.Parent.channel.Recipients)
|
|
6
|
+
local Types = require (script.Parent.Parent.Types)
|
|
7
|
+
|
|
8
|
+
local Audience = {}
|
|
9
|
+
|
|
10
|
+
-- Constants -----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
-- What a player seeded into a key is marked with, which is a flush no diff ever runs under.
|
|
13
|
+
const BEFORE = 0
|
|
14
|
+
|
|
15
|
+
-- Types ---------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
-- Who held a key at its last diff, under the number of the diff that put them there.
|
|
18
|
+
export type Seen = { [Player]: number }
|
|
19
|
+
|
|
20
|
+
-- One set's audiences: who each key is for, and who was holding it last time anyone asked.
|
|
21
|
+
export type Audience = {
|
|
22
|
+
read of: { [any]: Types.Recipient },
|
|
23
|
+
read held: { [any]: Seen },
|
|
24
|
+
read wide: { [any]: boolean },
|
|
25
|
+
read moved: { [any]: boolean },
|
|
26
|
+
read now: { [any]: { Player } },
|
|
27
|
+
read gained: { Player },
|
|
28
|
+
read lost: { Player },
|
|
29
|
+
at: number,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
-- Public --------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
function Audience.new (): Audience
|
|
35
|
+
return {
|
|
36
|
+
of = {},
|
|
37
|
+
held = {},
|
|
38
|
+
wide = {},
|
|
39
|
+
moved = {},
|
|
40
|
+
now = {},
|
|
41
|
+
gained = {},
|
|
42
|
+
lost = {},
|
|
43
|
+
at = BEFORE,
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
-- A key's viewers, replacing whoever it had.
|
|
48
|
+
function Audience.assign (of: Audience, key: any, to: Types.Recipient)
|
|
49
|
+
of.of[key] = to
|
|
50
|
+
of.moved[key] = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
-- Whether a key goes to everybody, which is what puts its sections on the shared list.
|
|
54
|
+
function Audience.everyone (of: Audience, key: any): boolean
|
|
55
|
+
local to = of.of[key]
|
|
56
|
+
return to == nil or to == Recipients.ALL
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
-- Who holds a key, as the diff that ran this flush worked it out.
|
|
60
|
+
function Audience.members (of: Audience, key: any): { Player }
|
|
61
|
+
local now = of.now[key]
|
|
62
|
+
if now ~= nil then return now end
|
|
63
|
+
|
|
64
|
+
now = {}
|
|
65
|
+
of.now[key] = now
|
|
66
|
+
return now
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
-- Who holds a key now, and who gained or lost it since the last time this asked.
|
|
70
|
+
function Audience.diff (of: Audience, key: any, ready: Recipients.Ready)
|
|
71
|
+
table.clear (of.gained)
|
|
72
|
+
table.clear (of.lost)
|
|
73
|
+
|
|
74
|
+
local now = of.now[key]
|
|
75
|
+
if now == nil then
|
|
76
|
+
now = {}
|
|
77
|
+
of.now[key] = now
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
local to = of.of[key]
|
|
81
|
+
local open = to == nil or to == Recipients.ALL
|
|
82
|
+
|
|
83
|
+
if open and of.moved[key] ~= true then
|
|
84
|
+
table.clear (now)
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
local held = of.held[key]
|
|
89
|
+
if held == nil then
|
|
90
|
+
held = {}
|
|
91
|
+
of.held[key] = held
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if of.wide[key] ~= false then
|
|
95
|
+
for player in ready do
|
|
96
|
+
held[player] = BEFORE
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
of.moved[key] = nil
|
|
101
|
+
of.wide[key] = open
|
|
102
|
+
|
|
103
|
+
local at = of.at + 1
|
|
104
|
+
of.at = at
|
|
105
|
+
|
|
106
|
+
Recipients.resolve (now, if open then Recipients.ALL else to :: Types.Recipient, ready)
|
|
107
|
+
|
|
108
|
+
for _, player in now do
|
|
109
|
+
if held[player] == nil then table.insert (of.gained, player) end
|
|
110
|
+
held[player] = at
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
-- Who stopped holding this key, which is who is owed a removal for everything under it.
|
|
114
|
+
for player, when in held do
|
|
115
|
+
if when == at then continue end
|
|
116
|
+
|
|
117
|
+
held[player] = nil
|
|
118
|
+
if ready[player] == nil then continue end
|
|
119
|
+
|
|
120
|
+
table.insert (of.lost, player)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
if open then table.clear (held) end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return table.freeze (Audience)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- What a client is owed of a partition it has only just been given, and what a flush pays of it.
|
|
4
|
+
|
|
5
|
+
local Plan = require (script.Parent.Plan)
|
|
6
|
+
local Sections = require (script.Parent.Sections)
|
|
7
|
+
local Store = require (script.Parent.Store)
|
|
8
|
+
|
|
9
|
+
local Catchup = {}
|
|
10
|
+
|
|
11
|
+
-- Types ---------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
-- One client's backlog on one partition: the slots it is owed, in the order they will go.
|
|
14
|
+
export type Pending = {
|
|
15
|
+
read slots: { number },
|
|
16
|
+
read live: { [number]: boolean },
|
|
17
|
+
at: number,
|
|
18
|
+
count: number,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
-- Every backlog a namespace is carrying, and how many of them are not yet empty.
|
|
22
|
+
export type Catchup = {
|
|
23
|
+
read owed: { [Player]: { [any]: Pending } },
|
|
24
|
+
any: number,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
-- Private -------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
-- A partition's held slots, ascending, which is the whole of what a client meeting it is owed.
|
|
30
|
+
local function holding (store: Store.Store): { number }
|
|
31
|
+
local ids = store.ids
|
|
32
|
+
local slots = {}
|
|
33
|
+
local count = 0
|
|
34
|
+
|
|
35
|
+
for slot = 1, store.high do
|
|
36
|
+
if ids[slot] == nil then continue end
|
|
37
|
+
|
|
38
|
+
count += 1
|
|
39
|
+
slots[count] = slot
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
return slots
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
-- A backlog finished with, taken off its client and off the count the reserve is owed under.
|
|
46
|
+
local function finished (of: Catchup, owed: { [any]: Pending }, who: Player, key: any)
|
|
47
|
+
owed[key] = nil
|
|
48
|
+
of.any -= 1
|
|
49
|
+
|
|
50
|
+
if next (owed) == nil then of.owed[who] = nil end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
-- Public --------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
-- Every backlog a namespace is carrying, empty, which a session holds one of while it is open.
|
|
56
|
+
function Catchup.new (): Catchup return { owed = {}, any = 0 } end
|
|
57
|
+
|
|
58
|
+
-- The bytes a flush holds back from the shared plan while anything is catching up.
|
|
59
|
+
function Catchup.reserve (over: { Sections.Layout }): number
|
|
60
|
+
local most = 0
|
|
61
|
+
|
|
62
|
+
for _, layout in over do
|
|
63
|
+
local one = layout.opening + layout.entry
|
|
64
|
+
if one > most then most = one end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return most
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
-- Players that have just been given a key, each seeded with everything the partition holds.
|
|
71
|
+
function Catchup.gained (of: Catchup, key: any, players: { Player }, store: Store.Store)
|
|
72
|
+
if #players == 0 then return end
|
|
73
|
+
|
|
74
|
+
local slots = holding (store)
|
|
75
|
+
local total = #slots
|
|
76
|
+
if total == 0 then return end
|
|
77
|
+
|
|
78
|
+
for _, who in players do
|
|
79
|
+
local owed = of.owed[who]
|
|
80
|
+
if owed ~= nil and owed[key] ~= nil then continue end
|
|
81
|
+
|
|
82
|
+
if owed == nil then
|
|
83
|
+
owed = {}
|
|
84
|
+
of.owed[who] = owed
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
local live = {}
|
|
88
|
+
for _, slot in slots do
|
|
89
|
+
live[slot] = true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
owed[key] = { slots = slots, live = live, at = 1, count = total }
|
|
93
|
+
of.any += 1
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
-- Slots a section has just carried to everybody, taken off every backlog that was owed them.
|
|
98
|
+
function Catchup.retired (of: Catchup, key: any, slots: { number }, from: number, count: number)
|
|
99
|
+
if of.any == 0 then return end
|
|
100
|
+
|
|
101
|
+
for who, owed in of.owed do
|
|
102
|
+
local pending = owed[key]
|
|
103
|
+
if pending == nil then continue end
|
|
104
|
+
|
|
105
|
+
local live = pending.live
|
|
106
|
+
local left = pending.count
|
|
107
|
+
|
|
108
|
+
for index = from, from + count - 1 do
|
|
109
|
+
local slot = slots[index]
|
|
110
|
+
if live[slot] == nil then continue end
|
|
111
|
+
|
|
112
|
+
live[slot] = nil
|
|
113
|
+
left -= 1
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
pending.count = left
|
|
117
|
+
if left == 0 then finished (of, owed, who, key) end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
-- A partition emptied, which takes every backlog on it away with it.
|
|
122
|
+
function Catchup.cleared (of: Catchup, key: any)
|
|
123
|
+
if of.any == 0 then return end
|
|
124
|
+
|
|
125
|
+
for who, owed in of.owed do
|
|
126
|
+
if owed[key] ~= nil then finished (of, owed, who, key) end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
-- Players that have just had a key taken away, which owes them nothing further of it.
|
|
131
|
+
function Catchup.lost (of: Catchup, key: any, players: { Player })
|
|
132
|
+
if of.any == 0 then return end
|
|
133
|
+
|
|
134
|
+
for _, who in players do
|
|
135
|
+
local owed = of.owed[who]
|
|
136
|
+
if owed ~= nil and owed[key] ~= nil then finished (of, owed, who, key) end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
-- A link that has closed, which is every backlog its client was carrying and no other client's.
|
|
141
|
+
function Catchup.dropped (of: Catchup, who: Player)
|
|
142
|
+
local owed = of.owed[who]
|
|
143
|
+
if owed == nil then return end
|
|
144
|
+
|
|
145
|
+
for _ in owed do
|
|
146
|
+
of.any -= 1
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
of.owed[who] = nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
-- One chunk of what a client is owed of a partition, written where it was handed.
|
|
153
|
+
function Catchup.next (
|
|
154
|
+
of: Catchup,
|
|
155
|
+
layout: Sections.Layout,
|
|
156
|
+
store: Store.Store,
|
|
157
|
+
who: Player,
|
|
158
|
+
key: any,
|
|
159
|
+
b: buffer,
|
|
160
|
+
pos: number,
|
|
161
|
+
room: number
|
|
162
|
+
): number
|
|
163
|
+
local owed = of.owed[who]
|
|
164
|
+
if owed == nil then return pos end
|
|
165
|
+
|
|
166
|
+
local pending = owed[key]
|
|
167
|
+
if pending == nil then return pos end
|
|
168
|
+
|
|
169
|
+
local cut = Plan.keyframes (layout, room)
|
|
170
|
+
|
|
171
|
+
local frame = layout.frame
|
|
172
|
+
local slots = pending.slots
|
|
173
|
+
local live = pending.live
|
|
174
|
+
local ids = store.ids
|
|
175
|
+
local gens = store.gens
|
|
176
|
+
|
|
177
|
+
local total = #slots
|
|
178
|
+
local index = pending.at
|
|
179
|
+
local taken = 0
|
|
180
|
+
local gone = 0
|
|
181
|
+
|
|
182
|
+
while index <= total and taken < cut do
|
|
183
|
+
local slot = slots[index]
|
|
184
|
+
index += 1
|
|
185
|
+
|
|
186
|
+
if live[slot] == nil then continue end
|
|
187
|
+
|
|
188
|
+
--[[
|
|
189
|
+
A slot the store no longer holds, which is a record removed since the grant. `remove`
|
|
190
|
+
unbinds at the call and the slot retires when the removal is planned, so a backlog
|
|
191
|
+
outlives the record it names. Nobody is owed a keyframe for a record that is gone.
|
|
192
|
+
]]
|
|
193
|
+
if ids[slot] == nil then
|
|
194
|
+
live[slot] = nil
|
|
195
|
+
gone += 1
|
|
196
|
+
continue
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
taken += 1
|
|
200
|
+
frame.slots[taken] = slot
|
|
201
|
+
frame.ids[taken] = ids[slot]
|
|
202
|
+
if gens ~= nil then frame.gens[taken] = gens[slot] end
|
|
203
|
+
live[slot] = nil
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
pending.at = index
|
|
207
|
+
pending.count -= taken + gone
|
|
208
|
+
if pending.count == 0 then finished (of, owed, who, key) end
|
|
209
|
+
|
|
210
|
+
if taken == 0 then return pos end
|
|
211
|
+
|
|
212
|
+
frame.key = key
|
|
213
|
+
frame.count = taken
|
|
214
|
+
|
|
215
|
+
return Sections.keyframe (layout, b, pos, store.columns)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
return table.freeze (Catchup)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- One record's mutations across a flush: the net effect that ships, and the fields it named.
|
|
4
|
+
|
|
5
|
+
local Wide = require (script.Parent.Parent.kernel.Wide)
|
|
6
|
+
|
|
7
|
+
local Coalesce = {}
|
|
8
|
+
|
|
9
|
+
-- Types ---------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
--[[
|
|
12
|
+
What an entry has pending. `replace` is produced and never taken: an add onto a removal is a new
|
|
13
|
+
record under an old id, and a receiver has to be told the one it holds is gone before it is
|
|
14
|
+
handed another.
|
|
15
|
+
]]
|
|
16
|
+
export type Kind = "add" | "update" | "remove" | "replace"
|
|
17
|
+
|
|
18
|
+
-- Public --------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
--[[
|
|
21
|
+
Nothing pending takes whatever came. An update never changes what an entry is, since a keyframe
|
|
22
|
+
already states every field and a delta is still a delta. An add can only follow a removal, which
|
|
23
|
+
is a replace. A remove ends the entry, and ends it as nothing where the add it cancels never
|
|
24
|
+
reached anybody.
|
|
25
|
+
]]
|
|
26
|
+
function Coalesce.merge (was: Kind?, took: Kind): Kind?
|
|
27
|
+
if was == nil then return took end
|
|
28
|
+
if took == "update" then return was end
|
|
29
|
+
if took == "add" then return "replace" end
|
|
30
|
+
return if was == "add" then nil else "remove"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
function Coalesce.touched (mask: Wide.Word, field: number): Wide.Word
|
|
34
|
+
return Wide.union (mask, Wide.bit (field))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
-- Tested bit by bit and never read as a number, because the widest field is the sign.
|
|
38
|
+
function Coalesce.migrates (mask: Wide.Word, key: number?): boolean
|
|
39
|
+
if key == nil then return false end
|
|
40
|
+
return Wide.meets (mask, Wide.bit (key))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
return table.freeze (Coalesce)
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- Which fields a delta section carries, and which of them changed on every slot it lists.
|
|
4
|
+
|
|
5
|
+
local Bits = require (script.Parent.Parent.kernel.Bits)
|
|
6
|
+
local Bytes = require (script.Parent.Parent.wire.Bytes)
|
|
7
|
+
local Catalog = require (script.Parent.Parent.kernel.Catalog)
|
|
8
|
+
local Log = require (script.Parent.Parent.kernel.Log)
|
|
9
|
+
local Protocol = require (script.Parent.Parent.wire.Protocol)
|
|
10
|
+
|
|
11
|
+
local Fieldset = {}
|
|
12
|
+
|
|
13
|
+
-- Constants -----------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
const LIST = Protocol.FIELDSET_LIST
|
|
16
|
+
const MASK = Protocol.FIELDSET_MASK
|
|
17
|
+
|
|
18
|
+
-- The two one-bit fields here, named apart because they say different things at different places.
|
|
19
|
+
const MODE = 1
|
|
20
|
+
const FLAG = 1
|
|
21
|
+
|
|
22
|
+
const BYTE = Protocol.BYTE_BITS
|
|
23
|
+
|
|
24
|
+
const NO_FIELD = "a fieldset naming a field this set has not got"
|
|
25
|
+
const NO_FIELDS = "a fieldset naming no fields at all"
|
|
26
|
+
const OUT_OF_ORDER = "a fieldset naming its fields out of order"
|
|
27
|
+
const PADDED = "a fieldset leaving something in the bits above it"
|
|
28
|
+
|
|
29
|
+
-- Types ---------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
-- The fields a delta selected, and how each of them is covered.
|
|
32
|
+
export type Chosen = {
|
|
33
|
+
mode: number,
|
|
34
|
+
count: number,
|
|
35
|
+
fields: { number },
|
|
36
|
+
full: { number },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type Write = (b: buffer, at: number, of: Chosen) -> number
|
|
40
|
+
export type Read = (b: buffer, at: number, into: Chosen) -> number
|
|
41
|
+
|
|
42
|
+
-- One set's fieldset, compiled: the widths its two forms take and the pair that writes them.
|
|
43
|
+
export type Fieldset = {
|
|
44
|
+
read over: number,
|
|
45
|
+
read width: number,
|
|
46
|
+
read widest: number,
|
|
47
|
+
read write: Write,
|
|
48
|
+
read read: Read,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
-- Private -------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
--[[
|
|
54
|
+
A section this endpoint could not have written, refused under the one code every such refusal
|
|
55
|
+
carries and the phrase the site that found it holds.
|
|
56
|
+
]]
|
|
57
|
+
function Fieldset.broken (said: string): never
|
|
58
|
+
local facts: Log.Facts = { said = said }
|
|
59
|
+
return Log.reject (Catalog.SECTION, facts)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
-- Public --------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
-- A set's fieldset, as the pair that lays one down and takes it back up.
|
|
65
|
+
function Fieldset.of (over: number): Fieldset
|
|
66
|
+
local width = Bits.width (over - 1)
|
|
67
|
+
local listed = width * (over + 1)
|
|
68
|
+
|
|
69
|
+
local function write (b: buffer, at: number, of: Chosen): number
|
|
70
|
+
local count = of.count
|
|
71
|
+
local mode = if width * (count + 1) <= over then LIST else MASK
|
|
72
|
+
of.mode = mode
|
|
73
|
+
|
|
74
|
+
local held = Bytes.put (b, at * BYTE, MODE, mode)
|
|
75
|
+
|
|
76
|
+
if mode == LIST then
|
|
77
|
+
--[[
|
|
78
|
+
One less than the count, because a delta selecting nothing is not a delta. That is
|
|
79
|
+
what lets a set of one field spend nothing at all on its fieldset past the mode bit:
|
|
80
|
+
the count is a ranged int over one possibility and so is the field index, and both
|
|
81
|
+
are no bits.
|
|
82
|
+
]]
|
|
83
|
+
held = Bytes.put (b, held, width, count - 1)
|
|
84
|
+
for index = 1, count do
|
|
85
|
+
held = Bytes.put (b, held, width, of.fields[index] - 1)
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
--[[
|
|
89
|
+
The cursor is held to the count, because what sits past it is the section before
|
|
90
|
+
this one's. A leftover equal to the field being asked about would set a bit for a
|
|
91
|
+
field this delta does not carry, and the reader would then read every field after it
|
|
92
|
+
at the wrong offset.
|
|
93
|
+
]]
|
|
94
|
+
local reached = 1
|
|
95
|
+
for field = 1, over do
|
|
96
|
+
local on = if reached <= count and of.fields[reached] == field then 1 else 0
|
|
97
|
+
held = Bytes.put (b, held, FLAG, on)
|
|
98
|
+
reached += on
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
--[[
|
|
103
|
+
The coverage flags ride the same bit run rather than sitting beside the values they
|
|
104
|
+
speak for. Every boundary in a section is on a byte, so a flag there would cost a byte
|
|
105
|
+
per selected field where this costs a bit, and nothing else moves: a reader knows how
|
|
106
|
+
many follow the moment it has read the fieldset.
|
|
107
|
+
]]
|
|
108
|
+
for index = 1, count do
|
|
109
|
+
held = Bytes.put (b, held, FLAG, of.full[index])
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
local byte = Bytes.aligned (b, held)
|
|
113
|
+
return byte
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
--[[
|
|
117
|
+
A reader turns away a fieldset an encoder could not have written, and stops there. Out of
|
|
118
|
+
order and empty are one comparison each and convict a repeat as well. A count past the
|
|
119
|
+
field count needs no test: that many ascending indices inside it do not exist.
|
|
120
|
+
]]
|
|
121
|
+
local function read (b: buffer, at: number, into: Chosen): number
|
|
122
|
+
local mode, held = Bytes.take (b, at * BYTE, MODE)
|
|
123
|
+
into.mode = mode
|
|
124
|
+
|
|
125
|
+
local count = 0
|
|
126
|
+
if mode == LIST then
|
|
127
|
+
count, held = Bytes.take (b, held, width)
|
|
128
|
+
count += 1
|
|
129
|
+
|
|
130
|
+
local last = 0
|
|
131
|
+
for index = 1, count do
|
|
132
|
+
local one
|
|
133
|
+
one, held = Bytes.take (b, held, width)
|
|
134
|
+
one += 1
|
|
135
|
+
|
|
136
|
+
if one > over then Fieldset.broken (NO_FIELD) end
|
|
137
|
+
if one <= last then Fieldset.broken (OUT_OF_ORDER) end
|
|
138
|
+
|
|
139
|
+
into.fields[index] = one
|
|
140
|
+
last = one
|
|
141
|
+
end
|
|
142
|
+
else
|
|
143
|
+
for field = 1, over do
|
|
144
|
+
local on
|
|
145
|
+
on, held = Bytes.take (b, held, FLAG)
|
|
146
|
+
if on == 0 then continue end
|
|
147
|
+
|
|
148
|
+
count += 1
|
|
149
|
+
into.fields[count] = field
|
|
150
|
+
end
|
|
151
|
+
if count == 0 then Fieldset.broken (NO_FIELDS) end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
into.count = count
|
|
155
|
+
|
|
156
|
+
for index = 1, count do
|
|
157
|
+
into.full[index], held = Bytes.take (b, held, FLAG)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
local byte, clean = Bytes.aligned (b, held)
|
|
161
|
+
if not clean then Fieldset.broken (PADDED) end
|
|
162
|
+
return byte
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
return table.freeze ({
|
|
166
|
+
over = over,
|
|
167
|
+
width = width,
|
|
168
|
+
widest = math.ceil ((MODE + math.min (listed, over) + over) / BYTE),
|
|
169
|
+
write = write,
|
|
170
|
+
read = read,
|
|
171
|
+
})
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
-- The record a caller fills and a reader fills back, empty, which is one per set that has a delta.
|
|
175
|
+
function Fieldset.chosen (): Chosen return { mode = LIST, count = 0, fields = {}, full = {} } end
|
|
176
|
+
|
|
177
|
+
return table.freeze (Fieldset)
|