@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
package/shape/Ir.luau
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- The closed set of things the emitter knows how to write, and the one place each is built.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.codec.Base)
|
|
6
|
+
local Bits = require (script.Parent.Parent.kernel.Bits)
|
|
7
|
+
local Bytes = require (script.Parent.Parent.wire.Bytes)
|
|
8
|
+
local Grid = require (script.Parent.Grid)
|
|
9
|
+
local Protocol = require (script.Parent.Parent.wire.Protocol)
|
|
10
|
+
local Types = require (script.Parent.Parent.Types)
|
|
11
|
+
local Words = require (script.Parent.Words)
|
|
12
|
+
|
|
13
|
+
local Ir = {}
|
|
14
|
+
|
|
15
|
+
-- Constants -----------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
const BYTE = Protocol.BYTE_BITS
|
|
18
|
+
|
|
19
|
+
-- Shared by every group holding no dissolved record, since one frozen empty table is enough.
|
|
20
|
+
const NO_NESTS = table.freeze ({} :: { Nest })
|
|
21
|
+
|
|
22
|
+
-- Types ---------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
export type Op =
|
|
25
|
+
"word"
|
|
26
|
+
| "wide"
|
|
27
|
+
| "f32"
|
|
28
|
+
| "f64"
|
|
29
|
+
| "vlq"
|
|
30
|
+
| "vli"
|
|
31
|
+
| "bytes"
|
|
32
|
+
| "run"
|
|
33
|
+
| "list"
|
|
34
|
+
| "pairs"
|
|
35
|
+
| "group"
|
|
36
|
+
| "apart"
|
|
37
|
+
| "opt"
|
|
38
|
+
| "branch"
|
|
39
|
+
| "slot"
|
|
40
|
+
| "xform"
|
|
41
|
+
|
|
42
|
+
--[[
|
|
43
|
+
What a rejection at this place names, as the two indices the serializer interned it under.
|
|
44
|
+
Indices and not text, since a decode composing prose would pay for it on a path a hostile
|
|
45
|
+
client sets the rate of, and the boundary that catches the raise reads them back.
|
|
46
|
+
]]
|
|
47
|
+
export type Tag = {
|
|
48
|
+
read pathId: number,
|
|
49
|
+
read expectId: number,
|
|
50
|
+
read checks: { Base.Validate }?,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
-- The pair every op exists to become, which is the whole of what an op is for.
|
|
54
|
+
export type Write = (b: buffer, pos: number, value: any) -> number
|
|
55
|
+
export type Read = (b: buffer, pos: number, ctx: Types.ValidateContext) -> (number, any)
|
|
56
|
+
|
|
57
|
+
-- The instances that ride beside a frame, and the map holding one reference to one slot in them.
|
|
58
|
+
export type Sidecar = {
|
|
59
|
+
refs: { Instance },
|
|
60
|
+
slotOf: { [Instance]: number },
|
|
61
|
+
bears: boolean,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
--[[
|
|
65
|
+
A record that dissolved into the one holding it, as the table its fields are read out of and
|
|
66
|
+
written back into.
|
|
67
|
+
]]
|
|
68
|
+
export type Nest = {
|
|
69
|
+
read key: string,
|
|
70
|
+
read under: number?,
|
|
71
|
+
read allow: { [string]: boolean },
|
|
72
|
+
read tag: Tag,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
-- The inclusive bounds a count runs between, a ranged int of no bits at all where the two meet.
|
|
76
|
+
export type Span = {
|
|
77
|
+
read min: number,
|
|
78
|
+
read max: number,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
--[[
|
|
82
|
+
Where a run of leaves comes from, and what it goes back to. A join of one leaf whose grid is the
|
|
83
|
+
whole mapping carries no pair, which is every ordinary numeric field. Everything else carries
|
|
84
|
+
one, since its leaves come out of a single computation that asking one at a time would run once
|
|
85
|
+
per component kept.
|
|
86
|
+
]]
|
|
87
|
+
export type Join = {
|
|
88
|
+
read at: number,
|
|
89
|
+
read count: number,
|
|
90
|
+
read key: string?,
|
|
91
|
+
read nest: number?,
|
|
92
|
+
read tag: Tag,
|
|
93
|
+
read pull: ((value: any, into: { number }, at: number) -> boolean)?,
|
|
94
|
+
read push: ((from: { number }, at: number) -> any)?,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
--[[
|
|
98
|
+
A bit region and the windows it was cut into. The one node whose size is not a whole number of
|
|
99
|
+
bytes, which is why it is the one that says how many bits it is.
|
|
100
|
+
]]
|
|
101
|
+
export type Word = {
|
|
102
|
+
read op: "word",
|
|
103
|
+
read fixed: boolean,
|
|
104
|
+
read maxBytes: number,
|
|
105
|
+
read tag: Tag,
|
|
106
|
+
read bits: number,
|
|
107
|
+
read plan: { Words.Word },
|
|
108
|
+
read leaves: { Grid.Grid },
|
|
109
|
+
read joins: { Join },
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type Wide = {
|
|
113
|
+
read op: "wide",
|
|
114
|
+
read fixed: boolean,
|
|
115
|
+
read maxBytes: number,
|
|
116
|
+
read tag: Tag,
|
|
117
|
+
read grid: Grid.Grid,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type Float = {
|
|
121
|
+
read op: "f32" | "f64",
|
|
122
|
+
read fixed: boolean,
|
|
123
|
+
read maxBytes: number,
|
|
124
|
+
read tag: Tag,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export type Varint = {
|
|
128
|
+
read op: "vlq" | "vli",
|
|
129
|
+
read fixed: boolean,
|
|
130
|
+
read maxBytes: number,
|
|
131
|
+
read tag: Tag,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export type Bytes = {
|
|
135
|
+
read op: "bytes",
|
|
136
|
+
read fixed: boolean,
|
|
137
|
+
read maxBytes: number,
|
|
138
|
+
read tag: Tag,
|
|
139
|
+
read count: Span,
|
|
140
|
+
read alphabet: string?,
|
|
141
|
+
read form: "string" | "buffer",
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type Run = {
|
|
145
|
+
read op: "run",
|
|
146
|
+
read fixed: boolean,
|
|
147
|
+
read maxBytes: number,
|
|
148
|
+
read tag: Tag,
|
|
149
|
+
read count: Span,
|
|
150
|
+
read elem: Grid.Grid,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type List = {
|
|
154
|
+
read op: "list",
|
|
155
|
+
read fixed: boolean,
|
|
156
|
+
read maxBytes: number,
|
|
157
|
+
read tag: Tag,
|
|
158
|
+
read count: Span,
|
|
159
|
+
read elem: Shape,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
-- A count and then that many pairs, each a key and then the value under it.
|
|
163
|
+
export type Pairs = {
|
|
164
|
+
read op: "pairs",
|
|
165
|
+
read fixed: boolean,
|
|
166
|
+
read maxBytes: number,
|
|
167
|
+
read tag: Tag,
|
|
168
|
+
read count: Span,
|
|
169
|
+
read key: Shape,
|
|
170
|
+
read value: Shape,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
--[[
|
|
174
|
+
One thing after the region, and the field it is read off. Absent where the value handed to the
|
|
175
|
+
group is the part's own, which is a definition whose whole payload is one variable thing, and at
|
|
176
|
+
most one part of a group is keyless for the same reason at most one join is.
|
|
177
|
+
]]
|
|
178
|
+
export type Part = {
|
|
179
|
+
read key: string?,
|
|
180
|
+
read nest: number?,
|
|
181
|
+
read of: Shape,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type Group = {
|
|
185
|
+
read op: "group",
|
|
186
|
+
read fixed: boolean,
|
|
187
|
+
read maxBytes: number,
|
|
188
|
+
read tag: Tag,
|
|
189
|
+
read allow: { [string]: boolean }?,
|
|
190
|
+
read word: Word?,
|
|
191
|
+
read parts: { Part },
|
|
192
|
+
read nests: { Nest },
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type Opt = {
|
|
196
|
+
read op: "opt",
|
|
197
|
+
read fixed: boolean,
|
|
198
|
+
read maxBytes: number,
|
|
199
|
+
read tag: Tag,
|
|
200
|
+
read bit: number,
|
|
201
|
+
read inner: Shape,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
--[[
|
|
205
|
+
A tag and the arms it chooses between. The field is the one the tag rides in, because a variant
|
|
206
|
+
is a record and its name is a field of it rather than something beside it, and the names are in
|
|
207
|
+
the order the arms are, which is canonical.
|
|
208
|
+
]]
|
|
209
|
+
export type Branch = {
|
|
210
|
+
read op: "branch",
|
|
211
|
+
read fixed: boolean,
|
|
212
|
+
read maxBytes: number,
|
|
213
|
+
read tag: Tag,
|
|
214
|
+
read field: string,
|
|
215
|
+
read names: { string },
|
|
216
|
+
read tagWidth: number,
|
|
217
|
+
read arms: { Shape },
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export type Slot = {
|
|
221
|
+
read op: "slot",
|
|
222
|
+
read fixed: boolean,
|
|
223
|
+
read maxBytes: number,
|
|
224
|
+
read tag: Tag,
|
|
225
|
+
read class: string?,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export type Xform = {
|
|
229
|
+
read op: "xform",
|
|
230
|
+
read fixed: boolean,
|
|
231
|
+
read maxBytes: number,
|
|
232
|
+
read tag: Tag,
|
|
233
|
+
read inner: Shape,
|
|
234
|
+
read lift: (wire: any) -> any,
|
|
235
|
+
read lower: (domain: any) -> any,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
--[[
|
|
239
|
+
Every node there is. Closed, so the emitter's dispatch is exhaustive by the checker, and one
|
|
240
|
+
concrete record per op, so reading a field the op does not have is a compile error rather than a
|
|
241
|
+
nil at whatever rate a frame arrives.
|
|
242
|
+
]]
|
|
243
|
+
export type Shape =
|
|
244
|
+
Word
|
|
245
|
+
| Wide
|
|
246
|
+
| Float
|
|
247
|
+
| Varint
|
|
248
|
+
| Bytes
|
|
249
|
+
| Run
|
|
250
|
+
| List
|
|
251
|
+
| Pairs
|
|
252
|
+
| Group
|
|
253
|
+
| Apart
|
|
254
|
+
| Opt
|
|
255
|
+
| Branch
|
|
256
|
+
| Slot
|
|
257
|
+
| Xform
|
|
258
|
+
|
|
259
|
+
-- Private -------------------------------------------------------------
|
|
260
|
+
|
|
261
|
+
--[[
|
|
262
|
+
The bits a count is written as, which is none where the bounds meet. A caller who declared one
|
|
263
|
+
length spends nothing saying it again, and the decoder has the number without reading one.
|
|
264
|
+
]]
|
|
265
|
+
local function counted (of: Span): number return Bits.width (of.max - of.min) end
|
|
266
|
+
|
|
267
|
+
-- Public --------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
-- A bit region, its windows, and who fills them.
|
|
270
|
+
function Ir.word (tag: Tag, plan: { Words.Word }, leaves: { Grid.Grid }, joins: { Join }): Word
|
|
271
|
+
local bits = 0
|
|
272
|
+
for _, grid in leaves do
|
|
273
|
+
bits += grid.width
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
local bytes = 0
|
|
277
|
+
for _, one in plan do
|
|
278
|
+
bytes += one.bytes
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
return table.freeze ({
|
|
282
|
+
op = "word" :: "word",
|
|
283
|
+
fixed = true,
|
|
284
|
+
maxBytes = bytes,
|
|
285
|
+
tag = tag,
|
|
286
|
+
bits = bits,
|
|
287
|
+
plan = table.freeze (plan),
|
|
288
|
+
leaves = table.freeze (leaves),
|
|
289
|
+
joins = table.freeze (joins),
|
|
290
|
+
})
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
--[[
|
|
294
|
+
One fixed field too wide for the run accumulator to pack, written as whole bytes of its own. The
|
|
295
|
+
grid's width is the value's and the node's is what it costs, and past twenty-four bits those are
|
|
296
|
+
two different numbers.
|
|
297
|
+
]]
|
|
298
|
+
function Ir.wide (tag: Tag, grid: Grid.Grid): Wide
|
|
299
|
+
return table.freeze ({
|
|
300
|
+
op = "wide" :: "wide",
|
|
301
|
+
fixed = true,
|
|
302
|
+
maxBytes = Bytes.whole (grid.width),
|
|
303
|
+
tag = tag,
|
|
304
|
+
grid = grid,
|
|
305
|
+
})
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
function Ir.float (tag: Tag, wide: boolean): Float
|
|
309
|
+
return table.freeze ({
|
|
310
|
+
op = (if wide then "f64" else "f32") :: "f64",
|
|
311
|
+
fixed = true,
|
|
312
|
+
maxBytes = if wide then 8 else 4,
|
|
313
|
+
tag = tag,
|
|
314
|
+
})
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
--[[
|
|
318
|
+
A varint. Never fixed, even where a caller's values would always fit one group, because what
|
|
319
|
+
decides the length is the value and a shape is what is known before there is one.
|
|
320
|
+
]]
|
|
321
|
+
function Ir.varint (tag: Tag, signed: boolean): Varint
|
|
322
|
+
return table.freeze ({
|
|
323
|
+
op = (if signed then "vli" else "vlq") :: "vli",
|
|
324
|
+
fixed = false,
|
|
325
|
+
maxBytes = Protocol.VARINT_GROUPS,
|
|
326
|
+
tag = tag,
|
|
327
|
+
})
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
-- A string or a buffer, as its count and then its bytes.
|
|
331
|
+
function Ir.bytes (tag: Tag, count: Span, form: "string" | "buffer", alphabet: string?): Bytes
|
|
332
|
+
local width = if alphabet == nil then BYTE else Bits.width (#alphabet - 1)
|
|
333
|
+
|
|
334
|
+
return table.freeze ({
|
|
335
|
+
op = "bytes" :: "bytes",
|
|
336
|
+
fixed = count.min == count.max,
|
|
337
|
+
maxBytes = Bytes.whole (counted (count)) + Bytes.whole (count.max * width),
|
|
338
|
+
tag = tag,
|
|
339
|
+
count = table.freeze (count),
|
|
340
|
+
alphabet = alphabet,
|
|
341
|
+
form = form,
|
|
342
|
+
})
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
-- Elements of one width with no padding between them, which is the whole point of the 24-bit law.
|
|
346
|
+
function Ir.run (tag: Tag, count: Span, elem: Grid.Grid): Run
|
|
347
|
+
return table.freeze ({
|
|
348
|
+
op = "run" :: "run",
|
|
349
|
+
fixed = count.min == count.max,
|
|
350
|
+
maxBytes = Bytes.whole (counted (count)) + Bytes.whole (count.max * elem.width),
|
|
351
|
+
tag = tag,
|
|
352
|
+
count = table.freeze (count),
|
|
353
|
+
elem = elem,
|
|
354
|
+
})
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
--[[
|
|
358
|
+
Elements the run cannot take, each written whole and each starting on a byte. The worst case
|
|
359
|
+
multiplies the element's bytes rather than its bits, because every element pays its own
|
|
360
|
+
alignment whether or not the count ever varies.
|
|
361
|
+
]]
|
|
362
|
+
function Ir.list (tag: Tag, count: Span, elem: Shape): List
|
|
363
|
+
return table.freeze ({
|
|
364
|
+
op = "list" :: "list",
|
|
365
|
+
fixed = count.min == count.max and elem.fixed,
|
|
366
|
+
maxBytes = Bytes.whole (counted (count)) + count.max * elem.maxBytes,
|
|
367
|
+
tag = tag,
|
|
368
|
+
count = table.freeze (count),
|
|
369
|
+
elem = elem,
|
|
370
|
+
})
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
--[[
|
|
374
|
+
A map, as its two halves and what it may hold between them.
|
|
375
|
+
|
|
376
|
+
The worst case multiplies the pair rather than either half, since a map of no pairs writes its
|
|
377
|
+
count and nothing else and a map of many writes both of them that many times.
|
|
378
|
+
]]
|
|
379
|
+
function Ir.pairs (tag: Tag, count: Span, key: Shape, value: Shape): Pairs
|
|
380
|
+
return table.freeze ({
|
|
381
|
+
op = "pairs" :: "pairs",
|
|
382
|
+
fixed = count.min == count.max and key.fixed and value.fixed,
|
|
383
|
+
maxBytes = Bytes.whole (counted (count)) + count.max * (key.maxBytes + value.maxBytes),
|
|
384
|
+
tag = tag,
|
|
385
|
+
count = table.freeze (count),
|
|
386
|
+
key = key,
|
|
387
|
+
value = value,
|
|
388
|
+
})
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
-- A region sequence: the bit region as one word, then everything after it in wire order.
|
|
392
|
+
function Ir.group (
|
|
393
|
+
tag: Tag,
|
|
394
|
+
word: Word?,
|
|
395
|
+
parts: { Part },
|
|
396
|
+
nests: { Nest }?,
|
|
397
|
+
allow: { [string]: boolean }?
|
|
398
|
+
): Group
|
|
399
|
+
local fixed = true
|
|
400
|
+
local maxBytes = if word == nil then 0 else word.maxBytes
|
|
401
|
+
|
|
402
|
+
for _, part in parts do
|
|
403
|
+
maxBytes += part.of.maxBytes
|
|
404
|
+
if part.of.fixed then continue end
|
|
405
|
+
fixed = false
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
return table.freeze ({
|
|
409
|
+
op = "group" :: "group",
|
|
410
|
+
fixed = fixed,
|
|
411
|
+
maxBytes = maxBytes,
|
|
412
|
+
tag = tag,
|
|
413
|
+
allow = if allow == nil then nil else table.freeze (allow),
|
|
414
|
+
word = word,
|
|
415
|
+
parts = table.freeze (parts),
|
|
416
|
+
nests = if nests == nil then NO_NESTS else table.freeze (nests),
|
|
417
|
+
})
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
--[[
|
|
421
|
+
The computation that puts one of those values back together out of its parts.
|
|
422
|
+
|
|
423
|
+
Going out is a lookup and coming back is a call, which is the engine's asymmetry rather than a
|
|
424
|
+
choice here: its datatypes carry their parts as properties and are built whole or not at all.
|
|
425
|
+
]]
|
|
426
|
+
export type Build = (from: { any }) -> any
|
|
427
|
+
|
|
428
|
+
-- A value that comes apart into whole-byte fields.
|
|
429
|
+
export type Apart = {
|
|
430
|
+
read op: "apart",
|
|
431
|
+
read fixed: boolean,
|
|
432
|
+
read maxBytes: number,
|
|
433
|
+
read tag: Tag,
|
|
434
|
+
read engine: string,
|
|
435
|
+
read keys: { string },
|
|
436
|
+
read parts: { Shape },
|
|
437
|
+
read push: Build,
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
-- A value and its parts, in the order they are written.
|
|
441
|
+
function Ir.apart (tag: Tag, engine: string, keys: { string }, parts: { Shape }, push: Build): Apart
|
|
442
|
+
local fixed = true
|
|
443
|
+
local maxBytes = 0
|
|
444
|
+
|
|
445
|
+
for _, of in parts do
|
|
446
|
+
maxBytes += of.maxBytes
|
|
447
|
+
if of.fixed then continue end
|
|
448
|
+
fixed = false
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
return table.freeze ({
|
|
452
|
+
op = "apart" :: "apart",
|
|
453
|
+
fixed = fixed,
|
|
454
|
+
maxBytes = maxBytes,
|
|
455
|
+
tag = tag,
|
|
456
|
+
engine = engine,
|
|
457
|
+
keys = table.freeze (keys),
|
|
458
|
+
parts = table.freeze (parts),
|
|
459
|
+
push = push,
|
|
460
|
+
})
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
--[[
|
|
464
|
+
A value that may not be there, and the bit in the enclosing region that says so. The bit is
|
|
465
|
+
named by the leaf it is rather than read back out of the buffer, because the group has already
|
|
466
|
+
decoded the whole region into locals by the time this is reached.
|
|
467
|
+
]]
|
|
468
|
+
function Ir.opt (tag: Tag, bit: number, inner: Shape): Opt
|
|
469
|
+
return table.freeze ({
|
|
470
|
+
op = "opt" :: "opt",
|
|
471
|
+
fixed = false,
|
|
472
|
+
maxBytes = inner.maxBytes,
|
|
473
|
+
tag = tag,
|
|
474
|
+
bit = bit,
|
|
475
|
+
inner = inner,
|
|
476
|
+
})
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
--[[
|
|
480
|
+
A tag and the arm it names. The width is the ranged int the arm count needs rather than a number
|
|
481
|
+
handed in, because the two could not disagree and one of them would be the copy.
|
|
482
|
+
]]
|
|
483
|
+
function Ir.branch (tag: Tag, field: string, names: { string }, arms: { Shape }): Branch
|
|
484
|
+
local worst = 0
|
|
485
|
+
for _, arm in arms do
|
|
486
|
+
worst = math.max (worst, arm.maxBytes)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
local tagWidth = Bits.width (#arms - 1)
|
|
490
|
+
|
|
491
|
+
return table.freeze ({
|
|
492
|
+
op = "branch" :: "branch",
|
|
493
|
+
fixed = false,
|
|
494
|
+
maxBytes = Bytes.whole (tagWidth) + worst,
|
|
495
|
+
tag = tag,
|
|
496
|
+
field = field,
|
|
497
|
+
names = table.freeze (names),
|
|
498
|
+
tagWidth = tagWidth,
|
|
499
|
+
arms = table.freeze (arms),
|
|
500
|
+
})
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
--[[
|
|
504
|
+
An instance, as the index of the slot it takes in the frame's sidecar. Its presence bit is not
|
|
505
|
+
here: one that may be absent is this node inside an `opt`, which is what the surface says about
|
|
506
|
+
every other value that may be absent.
|
|
507
|
+
]]
|
|
508
|
+
function Ir.slot (tag: Tag, class: string?): Slot
|
|
509
|
+
return table.freeze ({
|
|
510
|
+
op = "slot" :: "slot",
|
|
511
|
+
fixed = false,
|
|
512
|
+
maxBytes = Protocol.VARINT_GROUPS,
|
|
513
|
+
tag = tag,
|
|
514
|
+
class = class,
|
|
515
|
+
})
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
--[[
|
|
519
|
+
A value on its way to and from the one the wire carries. It is the same bytes as its inner node
|
|
520
|
+
and says so by taking both facts off it, which is what makes a transform free to the layout and
|
|
521
|
+
impossible to see in a golden.
|
|
522
|
+
]]
|
|
523
|
+
function Ir.xform (
|
|
524
|
+
tag: Tag,
|
|
525
|
+
inner: Shape,
|
|
526
|
+
lift: (wire: any) -> any,
|
|
527
|
+
lower: (domain: any) -> any
|
|
528
|
+
): Xform
|
|
529
|
+
return table.freeze ({
|
|
530
|
+
op = "xform" :: "xform",
|
|
531
|
+
fixed = inner.fixed,
|
|
532
|
+
maxBytes = inner.maxBytes,
|
|
533
|
+
tag = tag,
|
|
534
|
+
inner = inner,
|
|
535
|
+
lift = lift,
|
|
536
|
+
lower = lower,
|
|
537
|
+
})
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
return table.freeze (Ir)
|
package/shape/Joins.luau
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!optimize 2
|
|
3
|
+
-- One declaration as the leaves it takes in a bit region and the join that fills them.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.codec.Base)
|
|
6
|
+
local Grid = require (script.Parent.Grid)
|
|
7
|
+
local Ir = require (script.Parent.Ir)
|
|
8
|
+
local Scalars = require (script.Parent.Parent.codec.Scalars)
|
|
9
|
+
|
|
10
|
+
local Joins = {}
|
|
11
|
+
|
|
12
|
+
-- Types ---------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
--[[
|
|
15
|
+
What a region field comes to: the grids it occupies, in wire order, and the join that says where
|
|
16
|
+
they are and how a value gets in and out of them.
|
|
17
|
+
]]
|
|
18
|
+
export type Field = {
|
|
19
|
+
read leaves: { Grid.Grid },
|
|
20
|
+
read join: Ir.Join,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
-- Public --------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
-- The one grid a declaration indexes through, where it has one.
|
|
26
|
+
function Joins.grid (of: Base.Cell): Grid.Grid?
|
|
27
|
+
local kind, params = of.kind, of.params
|
|
28
|
+
|
|
29
|
+
if kind == "bool" then return Grid.flag () end
|
|
30
|
+
if kind == "int" then return Grid.of (params.min :: number, params.max :: number, 1) end
|
|
31
|
+
if kind == "quant" then
|
|
32
|
+
return Grid.of (params.min :: number, params.max :: number, params.step :: number)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if kind == "angle" then
|
|
36
|
+
--[[
|
|
37
|
+
A step short of a turn, since the wrap makes the top point and the bottom the same one,
|
|
38
|
+
and cyclic so a reading is folded into the range rather than clamped to either end.
|
|
39
|
+
]]
|
|
40
|
+
local step = params.step :: number
|
|
41
|
+
return Grid.of (0, Scalars.TURN - step, step, true)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if kind == "enum" then return Grid.named (params.names :: { string }) end
|
|
45
|
+
return nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
-- The leaves a declaration takes, where its value is one the language carries.
|
|
49
|
+
function Joins.leaves (of: Base.Cell): { Grid.Grid }?
|
|
50
|
+
local single = Joins.grid (of)
|
|
51
|
+
if single ~= nil then return { single } end
|
|
52
|
+
if of.kind ~= "bitfield" then return nil end
|
|
53
|
+
|
|
54
|
+
local names = of.params.names :: { string }
|
|
55
|
+
local reach = #names
|
|
56
|
+
|
|
57
|
+
local out = table.create (reach) :: { Grid.Grid }
|
|
58
|
+
for place = 1, reach do
|
|
59
|
+
out[place] = Grid.flag ()
|
|
60
|
+
end
|
|
61
|
+
return out
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
-- A declaration, at the leaf it starts on, as the field it becomes.
|
|
65
|
+
function Joins.of (of: Base.Cell, tag: Ir.Tag, at: number, key: string?, nest: number?): Field?
|
|
66
|
+
local leaves = Joins.leaves (of)
|
|
67
|
+
if leaves == nil then return nil end
|
|
68
|
+
|
|
69
|
+
if of.kind ~= "bitfield" then
|
|
70
|
+
return {
|
|
71
|
+
leaves = leaves,
|
|
72
|
+
join = { at = at, count = 1, key = key, nest = nest, tag = tag },
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
--[[
|
|
77
|
+
A bitfield's pair reads and writes by name rather than by position, so the leaves are its
|
|
78
|
+
names in the order the cell records them and nothing about the record's own key order
|
|
79
|
+
reaches the wire. A name a record does not carry is not there, which is what a truth of
|
|
80
|
+
false already means, and a value that is no record has no name to read at all.
|
|
81
|
+
]]
|
|
82
|
+
local names = of.params.names :: { string }
|
|
83
|
+
local reach = #names
|
|
84
|
+
|
|
85
|
+
local function pull (value: any, into: { number }, opens: number): boolean
|
|
86
|
+
if type (value) ~= "table" then return false end
|
|
87
|
+
|
|
88
|
+
--[[
|
|
89
|
+
A name it does carry has to carry a truth. A flag is a `bool` declared n at a time and
|
|
90
|
+
`bool` turns away everything that is not one, so asking only whether the value is truthy
|
|
91
|
+
would let `{ dead = "no" }` write that the thing is dead. The whole field goes rather
|
|
92
|
+
than the flag, because a bitfield's twentieth bit is still the bitfield.
|
|
93
|
+
]]
|
|
94
|
+
for place = 1, reach do
|
|
95
|
+
local held = value[names[place] ]
|
|
96
|
+
if held ~= nil and type (held) ~= "boolean" then return false end
|
|
97
|
+
into[opens + place - 1] = if held then 1 else 0
|
|
98
|
+
end
|
|
99
|
+
return true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
local function push (from: { number }, opens: number): any
|
|
103
|
+
local out = table.create (reach) :: { [string]: boolean }
|
|
104
|
+
|
|
105
|
+
for place = 1, reach do
|
|
106
|
+
out[names[place] ] = from[opens + place - 1] == 1
|
|
107
|
+
end
|
|
108
|
+
return out
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
leaves = leaves,
|
|
113
|
+
join = {
|
|
114
|
+
at = at,
|
|
115
|
+
count = reach,
|
|
116
|
+
key = key,
|
|
117
|
+
nest = nest,
|
|
118
|
+
tag = tag,
|
|
119
|
+
pull = pull,
|
|
120
|
+
push = push,
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
return table.freeze (Joins)
|