@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/src/codec/meta/Auto.luau
DELETED
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
--!strict
|
|
2
|
-
--!native
|
|
3
|
-
-- Self-describing codec. u8 type tag + value payload.
|
|
4
|
-
|
|
5
|
-
local Base = require(script.Parent.Parent.Base)
|
|
6
|
-
local BufferC = require(script.Parent.Parent.datatype.Buffer)
|
|
7
|
-
local CFrameC = require(script.Parent.Parent.datatype.CFrame)
|
|
8
|
-
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
9
|
-
local ColorC = require(script.Parent.Parent.datatype.Color)
|
|
10
|
-
local IVC = require(script.Parent.Parent.datatype.IntVector)
|
|
11
|
-
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
12
|
-
local NRC = require(script.Parent.Parent.datatype.NumberRange)
|
|
13
|
-
local NumberC = require(script.Parent.Parent.primitive.Number)
|
|
14
|
-
local RayC = require(script.Parent.Parent.datatype.Ray)
|
|
15
|
-
local RectC = require(script.Parent.Parent.datatype.Rect)
|
|
16
|
-
local RegionC = require(script.Parent.Parent.datatype.Region)
|
|
17
|
-
local SeqC = require(script.Parent.Parent.datatype.Sequence)
|
|
18
|
-
local Signed = require(script.Parent.Parent.primitive.Signed)
|
|
19
|
-
local Types = require(script.Parent.Parent.Parent.Types)
|
|
20
|
-
local UDimC = require(script.Parent.Parent.datatype.UDim)
|
|
21
|
-
local Varint = require(script.Parent.Parent.primitive.Varint)
|
|
22
|
-
local VecC = require(script.Parent.Parent.datatype.Vector)
|
|
23
|
-
|
|
24
|
-
-- Constants --------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
local TAG_NIL = 0
|
|
27
|
-
local TAG_BOOL = 1
|
|
28
|
-
local TAG_U8 = 2
|
|
29
|
-
local TAG_U16 = 3
|
|
30
|
-
local TAG_U32 = 4
|
|
31
|
-
local TAG_I8 = 5
|
|
32
|
-
local TAG_I16 = 6
|
|
33
|
-
local TAG_I32 = 7
|
|
34
|
-
local TAG_F32 = 8
|
|
35
|
-
local TAG_F64 = 9
|
|
36
|
-
local TAG_STRING = 10
|
|
37
|
-
local TAG_VEC2 = 11
|
|
38
|
-
local TAG_VEC3 = 12
|
|
39
|
-
local TAG_COLOR3 = 13
|
|
40
|
-
local TAG_CFRAME = 14
|
|
41
|
-
local TAG_BUFFER = 15
|
|
42
|
-
local TAG_UDIM = 16
|
|
43
|
-
local TAG_UDIM2 = 17
|
|
44
|
-
local TAG_NUMRANGE = 18
|
|
45
|
-
local TAG_RECT = 19
|
|
46
|
-
local TAG_V2I16 = 20
|
|
47
|
-
local TAG_V3I16 = 21
|
|
48
|
-
local TAG_REGION3 = 22
|
|
49
|
-
local TAG_R3I16 = 23
|
|
50
|
-
local TAG_RAY = 24
|
|
51
|
-
local TAG_NUMSEQ = 25
|
|
52
|
-
local TAG_COLSEQ = 26
|
|
53
|
-
|
|
54
|
-
local INLINE_MAX = Varint.INLINE_MAX
|
|
55
|
-
local F32_SCRATCH = NumberC._f32Scratch
|
|
56
|
-
|
|
57
|
-
-- Tag -> numeric primitive dispatch tables.
|
|
58
|
-
local NUM_SIZE: { [number]: number } = table.freeze({
|
|
59
|
-
[TAG_U8] = 1,
|
|
60
|
-
[TAG_I8] = 1,
|
|
61
|
-
[TAG_U16] = 2,
|
|
62
|
-
[TAG_I16] = 2,
|
|
63
|
-
[TAG_U32] = 4,
|
|
64
|
-
[TAG_I32] = 4,
|
|
65
|
-
[TAG_F32] = 4,
|
|
66
|
-
[TAG_F64] = 8,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
local NUM_WRITE: { [number]: (buffer, number, number) -> () } = table.freeze({
|
|
70
|
-
[TAG_U8] = buffer.writeu8,
|
|
71
|
-
[TAG_U16] = buffer.writeu16,
|
|
72
|
-
[TAG_U32] = buffer.writeu32,
|
|
73
|
-
[TAG_I8] = Signed.writeI8,
|
|
74
|
-
[TAG_I16] = Signed.writeI16,
|
|
75
|
-
[TAG_I32] = Signed.writeI32,
|
|
76
|
-
[TAG_F32] = buffer.writef32,
|
|
77
|
-
[TAG_F64] = buffer.writef64,
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
local NUM_READ: { [number]: (buffer, number) -> number } = table.freeze({
|
|
81
|
-
[TAG_U8] = buffer.readu8,
|
|
82
|
-
[TAG_U16] = buffer.readu16,
|
|
83
|
-
[TAG_U32] = buffer.readu32,
|
|
84
|
-
[TAG_I8] = buffer.readi8,
|
|
85
|
-
[TAG_I16] = buffer.readi16,
|
|
86
|
-
[TAG_I32] = buffer.readi32,
|
|
87
|
-
[TAG_F32] = buffer.readf32,
|
|
88
|
-
[TAG_F64] = buffer.readf64,
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
type TypeEntry = { tag: number, codec: Types.InternalCodec<any> }
|
|
92
|
-
|
|
93
|
-
local TYPE_DISPATCH: { [string]: TypeEntry } = table.freeze({
|
|
94
|
-
Vector2 = { tag = TAG_VEC2, codec = VecC.vec2 },
|
|
95
|
-
Vector3 = { tag = TAG_VEC3, codec = VecC.vec3 },
|
|
96
|
-
Color3 = { tag = TAG_COLOR3, codec = ColorC.color3 },
|
|
97
|
-
CFrame = { tag = TAG_CFRAME, codec = CFrameC.cframe },
|
|
98
|
-
UDim = { tag = TAG_UDIM, codec = UDimC.udim },
|
|
99
|
-
UDim2 = { tag = TAG_UDIM2, codec = UDimC.udim2 },
|
|
100
|
-
NumberRange = { tag = TAG_NUMRANGE, codec = NRC.numberRange },
|
|
101
|
-
Rect = { tag = TAG_RECT, codec = RectC.rect },
|
|
102
|
-
Vector2int16 = { tag = TAG_V2I16, codec = IVC.vec2int16 },
|
|
103
|
-
Vector3int16 = { tag = TAG_V3I16, codec = IVC.vec3int16 },
|
|
104
|
-
Region3 = { tag = TAG_REGION3, codec = RegionC.region3 },
|
|
105
|
-
Region3int16 = { tag = TAG_R3I16, codec = RegionC.region3int16 },
|
|
106
|
-
Ray = { tag = TAG_RAY, codec = RayC.ray },
|
|
107
|
-
NumberSequence = { tag = TAG_NUMSEQ, codec = SeqC.numberSequence },
|
|
108
|
-
ColorSequence = { tag = TAG_COLSEQ, codec = SeqC.colorSequence },
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
local READ_DISPATCH: { [number]: Types.InternalCodec<any> } = table.freeze({
|
|
112
|
-
[TAG_VEC2] = VecC.vec2,
|
|
113
|
-
[TAG_VEC3] = VecC.vec3,
|
|
114
|
-
[TAG_COLOR3] = ColorC.color3,
|
|
115
|
-
[TAG_CFRAME] = CFrameC.cframe,
|
|
116
|
-
[TAG_UDIM] = UDimC.udim,
|
|
117
|
-
[TAG_UDIM2] = UDimC.udim2,
|
|
118
|
-
[TAG_NUMRANGE] = NRC.numberRange,
|
|
119
|
-
[TAG_RECT] = RectC.rect,
|
|
120
|
-
[TAG_V2I16] = IVC.vec2int16,
|
|
121
|
-
[TAG_V3I16] = IVC.vec3int16,
|
|
122
|
-
[TAG_REGION3] = RegionC.region3,
|
|
123
|
-
[TAG_R3I16] = RegionC.region3int16,
|
|
124
|
-
[TAG_RAY] = RayC.ray,
|
|
125
|
-
[TAG_NUMSEQ] = SeqC.numberSequence,
|
|
126
|
-
[TAG_COLSEQ] = SeqC.colorSequence,
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
-- Private ----------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
local alloc = Base.alloc
|
|
132
|
-
local writeByte = Channel.writeByte
|
|
133
|
-
local writeu8 = buffer.writeu8
|
|
134
|
-
local readu8 = buffer.readu8
|
|
135
|
-
local floor = math.floor
|
|
136
|
-
local isnan = math.isnan
|
|
137
|
-
|
|
138
|
-
--[[
|
|
139
|
-
Pick the narrowest tag for a number. NaN forces F64 (no integer or F32
|
|
140
|
-
can represent it). Integers walk u8 -> u16 -> u32 / i8 -> i16 -> i32; non-
|
|
141
|
-
integers try f32 by round-tripping through scratch (see Number.luau) and
|
|
142
|
-
fall through to f64 if precision is lost.
|
|
143
|
-
]]
|
|
144
|
-
local function selectNumberTag(value: number): number
|
|
145
|
-
if isnan(value) then
|
|
146
|
-
return TAG_F64
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
if floor(value) == value then
|
|
150
|
-
if value >= 0 then
|
|
151
|
-
if value <= 0xFF then
|
|
152
|
-
return TAG_U8
|
|
153
|
-
end
|
|
154
|
-
if value <= 0xFFFF then
|
|
155
|
-
return TAG_U16
|
|
156
|
-
end
|
|
157
|
-
if value <= 0xFFFFFFFF then
|
|
158
|
-
return TAG_U32
|
|
159
|
-
end
|
|
160
|
-
else
|
|
161
|
-
if value >= -0x80 then
|
|
162
|
-
return TAG_I8
|
|
163
|
-
end
|
|
164
|
-
if value >= -0x8000 then
|
|
165
|
-
return TAG_I16
|
|
166
|
-
end
|
|
167
|
-
if value >= -0x80000000 then
|
|
168
|
-
return TAG_I32
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
buffer.writef32(F32_SCRATCH, 0, value)
|
|
174
|
-
if buffer.readf32(F32_SCRATCH, 0) == value then
|
|
175
|
-
return TAG_F32
|
|
176
|
-
end
|
|
177
|
-
return TAG_F64
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
local function writeAuto(ch: Types.ChannelState, value: any): ()
|
|
181
|
-
if value == nil then
|
|
182
|
-
writeByte(ch, TAG_NIL)
|
|
183
|
-
return
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
local t = typeof(value)
|
|
187
|
-
|
|
188
|
-
if t == "boolean" then
|
|
189
|
-
local cursor = ch.cursor
|
|
190
|
-
if cursor + 2 > ch.size then
|
|
191
|
-
alloc(ch, 2)
|
|
192
|
-
end
|
|
193
|
-
local b = ch.buff
|
|
194
|
-
writeu8(b, cursor, TAG_BOOL)
|
|
195
|
-
writeu8(b, cursor + 1, if value then 1 else 0)
|
|
196
|
-
ch.cursor = cursor + 2
|
|
197
|
-
return
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
if t == "number" then
|
|
201
|
-
local tag = selectNumberTag(value)
|
|
202
|
-
local size = NUM_SIZE[tag]
|
|
203
|
-
local total = 1 + size
|
|
204
|
-
local cursor = ch.cursor
|
|
205
|
-
if cursor + total > ch.size then
|
|
206
|
-
alloc(ch, total)
|
|
207
|
-
end
|
|
208
|
-
local b = ch.buff
|
|
209
|
-
writeu8(b, cursor, tag)
|
|
210
|
-
NUM_WRITE[tag](b, cursor + 1, value)
|
|
211
|
-
ch.cursor = cursor + total
|
|
212
|
-
return
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
if t == "string" then
|
|
216
|
-
writeByte(ch, TAG_STRING)
|
|
217
|
-
|
|
218
|
-
local len = #value
|
|
219
|
-
local cursor = ch.cursor
|
|
220
|
-
-- Inline single-byte length fast path; matches String.luau.
|
|
221
|
-
if len <= INLINE_MAX then
|
|
222
|
-
if cursor + 1 + len > ch.size then
|
|
223
|
-
alloc(ch, 1 + len)
|
|
224
|
-
end
|
|
225
|
-
local b = ch.buff
|
|
226
|
-
writeu8(b, cursor, len)
|
|
227
|
-
if len > 0 then
|
|
228
|
-
buffer.writestring(b, cursor + 1, value)
|
|
229
|
-
end
|
|
230
|
-
ch.cursor = cursor + 1 + len
|
|
231
|
-
return
|
|
232
|
-
end
|
|
233
|
-
Varint.write(ch, len)
|
|
234
|
-
cursor = ch.cursor
|
|
235
|
-
if cursor + len > ch.size then
|
|
236
|
-
alloc(ch, len)
|
|
237
|
-
end
|
|
238
|
-
buffer.writestring(ch.buff, cursor, value)
|
|
239
|
-
ch.cursor = cursor + len
|
|
240
|
-
return
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
if t == "buffer" then
|
|
244
|
-
writeByte(ch, TAG_BUFFER)
|
|
245
|
-
BufferC.buff.write(ch, value)
|
|
246
|
-
return
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
local entry = TYPE_DISPATCH[t]
|
|
250
|
-
if not entry then
|
|
251
|
-
Log.error(`unsupported type "{t}"`)
|
|
252
|
-
end
|
|
253
|
-
local codec = entry.codec
|
|
254
|
-
local size = codec._size
|
|
255
|
-
local dw = codec._directWrite
|
|
256
|
-
-- Fixed-size + direct writer: single alloc + tag + payload.
|
|
257
|
-
if size and dw then
|
|
258
|
-
local total = 1 + size
|
|
259
|
-
local cursor = ch.cursor
|
|
260
|
-
if cursor + total > ch.size then
|
|
261
|
-
alloc(ch, total)
|
|
262
|
-
end
|
|
263
|
-
local b = ch.buff
|
|
264
|
-
writeu8(b, cursor, entry.tag)
|
|
265
|
-
dw(b, cursor + 1, value)
|
|
266
|
-
ch.cursor = cursor + total
|
|
267
|
-
return
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
writeByte(ch, entry.tag)
|
|
271
|
-
codec.write(ch, value)
|
|
272
|
-
end
|
|
273
|
-
|
|
274
|
-
local function readAuto(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
275
|
-
local tag = readu8(src, pos)
|
|
276
|
-
|
|
277
|
-
if tag == TAG_NIL then
|
|
278
|
-
return nil, 1
|
|
279
|
-
end
|
|
280
|
-
if tag == TAG_BOOL then
|
|
281
|
-
return readu8(src, pos + 1) ~= 0, 2
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
if tag >= TAG_U8 and tag <= TAG_F64 then
|
|
285
|
-
return NUM_READ[tag](src, pos + 1), 1 + NUM_SIZE[tag]
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
if tag == TAG_STRING then
|
|
289
|
-
local len, lenBytes = Varint.readLengthPrefix(src, pos + 1)
|
|
290
|
-
if len == 0 then
|
|
291
|
-
return "", 1 + lenBytes
|
|
292
|
-
end
|
|
293
|
-
return buffer.readstring(src, pos + 1 + lenBytes, len), 1 + lenBytes + len
|
|
294
|
-
end
|
|
295
|
-
|
|
296
|
-
if tag == TAG_BUFFER then
|
|
297
|
-
local value, consumed = BufferC.buff.read(src, pos + 1, refs)
|
|
298
|
-
return value, 1 + consumed
|
|
299
|
-
end
|
|
300
|
-
|
|
301
|
-
local codec = READ_DISPATCH[tag]
|
|
302
|
-
if not codec then
|
|
303
|
-
Log.error(`unknown tag {tag}`)
|
|
304
|
-
end
|
|
305
|
-
local value, consumed = codec.read(src, pos + 1, refs)
|
|
306
|
-
return value, 1 + consumed
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
-- Public -----------------------------------------------------------------
|
|
310
|
-
|
|
311
|
-
local AutoModule = {}
|
|
312
|
-
|
|
313
|
-
AutoModule.auto = table.freeze({
|
|
314
|
-
write = writeAuto,
|
|
315
|
-
read = readAuto,
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
return table.freeze(AutoModule)
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
--!strict
|
|
2
|
-
--!native
|
|
3
|
-
-- Sub-byte packed bool/uint/int fields. Up to 32 bits total per record.
|
|
4
|
-
|
|
5
|
-
local Base = require(script.Parent.Parent.Base)
|
|
6
|
-
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
7
|
-
local Types = require(script.Parent.Parent.Parent.Types)
|
|
8
|
-
|
|
9
|
-
-- Constants --------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
local TYPE_BOOL = 0
|
|
12
|
-
local TYPE_UINT = 1
|
|
13
|
-
local TYPE_INT = 2
|
|
14
|
-
|
|
15
|
-
-- Private ----------------------------------------------------------------
|
|
16
|
-
|
|
17
|
-
local band = bit32.band
|
|
18
|
-
local bor = bit32.bor
|
|
19
|
-
local lshift = bit32.lshift
|
|
20
|
-
local rshift = bit32.rshift
|
|
21
|
-
|
|
22
|
-
-- bit32.lshift(1, 32) wraps to 0; force all-ones in the 32-bit case.
|
|
23
|
-
local function widthMask(width: number): number
|
|
24
|
-
if width >= 32 then
|
|
25
|
-
return 0xFFFFFFFF
|
|
26
|
-
end
|
|
27
|
-
return lshift(1, width) - 1
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
-- Public -----------------------------------------------------------------
|
|
31
|
-
|
|
32
|
-
local Bitfield = {}
|
|
33
|
-
|
|
34
|
-
export type FieldSpec = { type: "bool" | "uint" | "int", width: number? }
|
|
35
|
-
|
|
36
|
-
function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<any>
|
|
37
|
-
-- Sort field names so bit positions are stable across runs.
|
|
38
|
-
local keys: { string } = {}
|
|
39
|
-
for key in schema do
|
|
40
|
-
table.insert(keys, key)
|
|
41
|
-
end
|
|
42
|
-
table.sort(keys)
|
|
43
|
-
|
|
44
|
-
local fieldCount = #keys
|
|
45
|
-
Log.assert(fieldCount > 0, "requires at least one field")
|
|
46
|
-
|
|
47
|
-
local fieldKeys = table.create(fieldCount)
|
|
48
|
-
local fieldTypes = table.create(fieldCount)
|
|
49
|
-
local fieldOffsets = table.create(fieldCount)
|
|
50
|
-
local fieldMasks = table.create(fieldCount)
|
|
51
|
-
local fieldSignBit = table.create(fieldCount)
|
|
52
|
-
local fieldSignExt = table.create(fieldCount)
|
|
53
|
-
local totalBits = 0
|
|
54
|
-
|
|
55
|
-
for i = 1, fieldCount do
|
|
56
|
-
local key = keys[i]
|
|
57
|
-
local spec = schema[key]
|
|
58
|
-
fieldKeys[i] = key
|
|
59
|
-
fieldOffsets[i] = totalBits
|
|
60
|
-
|
|
61
|
-
if spec.type == "bool" then
|
|
62
|
-
fieldTypes[i] = TYPE_BOOL
|
|
63
|
-
fieldMasks[i] = 1
|
|
64
|
-
fieldSignBit[i] = 0
|
|
65
|
-
fieldSignExt[i] = 0
|
|
66
|
-
totalBits += 1
|
|
67
|
-
elseif spec.type == "uint" then
|
|
68
|
-
local width = spec.width
|
|
69
|
-
if not width or width < 1 or width > 32 then
|
|
70
|
-
Log.error(`field "{key}" uint width must be 1-32`)
|
|
71
|
-
end
|
|
72
|
-
fieldTypes[i] = TYPE_UINT
|
|
73
|
-
fieldMasks[i] = widthMask(width)
|
|
74
|
-
fieldSignBit[i] = 0
|
|
75
|
-
fieldSignExt[i] = 0
|
|
76
|
-
totalBits += width
|
|
77
|
-
elseif spec.type == "int" then
|
|
78
|
-
local width = spec.width
|
|
79
|
-
-- Min int width is 2: 1 bit can't represent both a sign and a value.
|
|
80
|
-
if not width or width < 2 or width > 32 then
|
|
81
|
-
Log.error(`field "{key}" int width must be 2-32`)
|
|
82
|
-
end
|
|
83
|
-
fieldTypes[i] = TYPE_INT
|
|
84
|
-
fieldMasks[i] = widthMask(width)
|
|
85
|
-
fieldSignBit[i] = 2 ^ (width - 1)
|
|
86
|
-
fieldSignExt[i] = 2 ^ width
|
|
87
|
-
totalBits += width
|
|
88
|
-
else
|
|
89
|
-
Log.error(`field "{key}" has unknown type "{spec.type}"`)
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
if totalBits > 32 then
|
|
94
|
-
Log.error(`total exceeds 32 bits ({totalBits})`)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
-- Pick the narrowest u8/u16/u32 wire form.
|
|
98
|
-
local wireBytes: number
|
|
99
|
-
local wfn: (buffer, number, number) -> ()
|
|
100
|
-
local rfn: (buffer, number) -> number
|
|
101
|
-
if totalBits <= 8 then
|
|
102
|
-
wireBytes, wfn, rfn = 1, buffer.writeu8, buffer.readu8
|
|
103
|
-
elseif totalBits <= 16 then
|
|
104
|
-
wireBytes, wfn, rfn = 2, buffer.writeu16, buffer.readu16
|
|
105
|
-
else
|
|
106
|
-
wireBytes, wfn, rfn = 4, buffer.writeu32, buffer.readu32
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
table.freeze(fieldKeys)
|
|
110
|
-
table.freeze(fieldTypes)
|
|
111
|
-
table.freeze(fieldOffsets)
|
|
112
|
-
table.freeze(fieldMasks)
|
|
113
|
-
table.freeze(fieldSignBit)
|
|
114
|
-
table.freeze(fieldSignExt)
|
|
115
|
-
|
|
116
|
-
return Base.define(wireBytes, function(b: buffer, off: number, value: any): ()
|
|
117
|
-
local packed = 0
|
|
118
|
-
for i = 1, fieldCount do
|
|
119
|
-
local v = value[fieldKeys[i]]
|
|
120
|
-
local ft = fieldTypes[i]
|
|
121
|
-
if ft == TYPE_BOOL then
|
|
122
|
-
if v then
|
|
123
|
-
packed = bor(packed, lshift(1, fieldOffsets[i]))
|
|
124
|
-
end
|
|
125
|
-
elseif ft == TYPE_UINT then
|
|
126
|
-
local mask = fieldMasks[i]
|
|
127
|
-
if v < 0 or v > mask or v % 1 ~= 0 then
|
|
128
|
-
Log.error(`field "{fieldKeys[i]}" value {v} out of uint range [0, {mask}]`)
|
|
129
|
-
end
|
|
130
|
-
packed = bor(packed, lshift(v, fieldOffsets[i]))
|
|
131
|
-
else
|
|
132
|
-
local signBit = fieldSignBit[i]
|
|
133
|
-
if v < -signBit or v >= signBit or v % 1 ~= 0 then
|
|
134
|
-
Log.error(
|
|
135
|
-
`field "{fieldKeys[i]}" value {v} out of int range [{-signBit}, {signBit - 1}]`
|
|
136
|
-
)
|
|
137
|
-
end
|
|
138
|
-
packed = bor(packed, lshift(band(v, fieldMasks[i]), fieldOffsets[i]))
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
wfn(b, off, packed)
|
|
142
|
-
end, function(b: buffer, off: number): any
|
|
143
|
-
local packed = rfn(b, off)
|
|
144
|
-
local result: { [string]: any } = {}
|
|
145
|
-
for i = 1, fieldCount do
|
|
146
|
-
local raw = band(rshift(packed, fieldOffsets[i]), fieldMasks[i])
|
|
147
|
-
if fieldTypes[i] == TYPE_BOOL then
|
|
148
|
-
result[fieldKeys[i]] = raw ~= 0
|
|
149
|
-
elseif fieldTypes[i] == TYPE_INT then
|
|
150
|
-
-- Sign-extend: subtract 2^width when the sign bit is set.
|
|
151
|
-
result[fieldKeys[i]] = if raw >= fieldSignBit[i] then raw - fieldSignExt[i] else raw
|
|
152
|
-
else
|
|
153
|
-
result[fieldKeys[i]] = raw
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
return result
|
|
157
|
-
end)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
return table.freeze(Bitfield)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
--!strict
|
|
2
|
-
--!optimize 2
|
|
3
|
-
-- User-defined fixed-size codec.
|
|
4
|
-
|
|
5
|
-
local Base = require(script.Parent.Parent.Base)
|
|
6
|
-
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
7
|
-
local Types = require(script.Parent.Parent.Parent.Types)
|
|
8
|
-
|
|
9
|
-
-- Private ----------------------------------------------------------------
|
|
10
|
-
|
|
11
|
-
local floor = math.floor
|
|
12
|
-
|
|
13
|
-
-- Public -----------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
local Custom = {}
|
|
16
|
-
|
|
17
|
-
function Custom.define<T>(
|
|
18
|
-
size: number,
|
|
19
|
-
writeFn: (b: buffer, offset: number, value: T) -> (),
|
|
20
|
-
readFn: (b: buffer, offset: number) -> T,
|
|
21
|
-
typeCheck: string?
|
|
22
|
-
): Types.InternalCodec<T>
|
|
23
|
-
if type(size) ~= "number" or size < 0 or floor(size) ~= size then
|
|
24
|
-
Log.error(`size must be a non-negative integer, got {size}`)
|
|
25
|
-
end
|
|
26
|
-
-- size == 0 collapses to constant(nil): user has no payload to encode.
|
|
27
|
-
if size == 0 then
|
|
28
|
-
return Base.constant(nil :: any) :: Types.InternalCodec<T>
|
|
29
|
-
end
|
|
30
|
-
return Base.define(size, writeFn, readFn, if typeCheck then { _typeCheck = typeCheck } else nil)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
return table.freeze(Custom)
|