@axpecter/lync 1.3.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/README.md +300 -0
- package/package.json +38 -0
- package/src/Types.luau +63 -0
- package/src/api/Group.luau +126 -0
- package/src/api/Namespace.luau +226 -0
- package/src/api/Packet.luau +147 -0
- package/src/api/Query.luau +295 -0
- package/src/api/Signal.luau +224 -0
- package/src/codec/Base.luau +49 -0
- package/src/codec/composite/Array.luau +275 -0
- package/src/codec/composite/Map.luau +395 -0
- package/src/codec/composite/Optional.luau +47 -0
- package/src/codec/composite/Shared.luau +151 -0
- package/src/codec/composite/Struct.luau +440 -0
- package/src/codec/composite/Tagged.luau +222 -0
- package/src/codec/composite/Tuple.luau +143 -0
- package/src/codec/datatype/Buffer.luau +44 -0
- package/src/codec/datatype/CFrame.luau +51 -0
- package/src/codec/datatype/Color.luau +22 -0
- package/src/codec/datatype/Instance.luau +48 -0
- package/src/codec/datatype/IntVector.luau +25 -0
- package/src/codec/datatype/NumberRange.luau +14 -0
- package/src/codec/datatype/Ray.luau +27 -0
- package/src/codec/datatype/Rect.luau +21 -0
- package/src/codec/datatype/Region.luau +58 -0
- package/src/codec/datatype/Sequence.luau +129 -0
- package/src/codec/datatype/String.luau +87 -0
- package/src/codec/datatype/UDim.luau +27 -0
- package/src/codec/datatype/Vector.luau +25 -0
- package/src/codec/meta/Auto.luau +353 -0
- package/src/codec/meta/Bitfield.luau +191 -0
- package/src/codec/meta/Custom.luau +27 -0
- package/src/codec/meta/Enum.luau +80 -0
- package/src/codec/meta/Nothing.luau +9 -0
- package/src/codec/meta/Quantized.luau +170 -0
- package/src/codec/meta/Unknown.luau +35 -0
- package/src/codec/primitive/Bool.luau +30 -0
- package/src/codec/primitive/Float16.luau +111 -0
- package/src/codec/primitive/Number.luau +48 -0
- package/src/codec/primitive/Varint.luau +76 -0
- package/src/index.d.ts +279 -0
- package/src/init.luau +161 -0
- package/src/internal/Baseline.luau +41 -0
- package/src/internal/Channel.luau +235 -0
- package/src/internal/Middleware.luau +109 -0
- package/src/internal/Pool.luau +68 -0
- package/src/internal/Registry.luau +146 -0
- package/src/transport/Bridge.luau +66 -0
- package/src/transport/Client.luau +151 -0
- package/src/transport/Gate.luau +222 -0
- package/src/transport/Reader.luau +175 -0
- package/src/transport/Server.luau +364 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- UDim (8B) and UDim2 (16B) codecs.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.Base)
|
|
6
|
+
|
|
7
|
+
return table.freeze ({
|
|
8
|
+
udim = Base.define (8, function (b: buffer, o: number, v: UDim): ()
|
|
9
|
+
buffer.writef32 (b, o, v.Scale)
|
|
10
|
+
buffer.writei32 (b, o + 4, v.Offset)
|
|
11
|
+
end, function (b: buffer, o: number): UDim
|
|
12
|
+
return UDim.new (buffer.readf32 (b, o), buffer.readi32 (b, o + 4))
|
|
13
|
+
end),
|
|
14
|
+
udim2 = Base.define (16, function (b: buffer, o: number, v: UDim2): ()
|
|
15
|
+
local x = v.X
|
|
16
|
+
local y = v.Y
|
|
17
|
+
buffer.writef32 (b, o, x.Scale)
|
|
18
|
+
buffer.writei32 (b, o + 4, x.Offset)
|
|
19
|
+
buffer.writef32 (b, o + 8, y.Scale)
|
|
20
|
+
buffer.writei32 (b, o + 12, y.Offset)
|
|
21
|
+
end, function (b: buffer, o: number): UDim2
|
|
22
|
+
return UDim2.new (
|
|
23
|
+
UDim.new (buffer.readf32 (b, o), buffer.readi32 (b, o + 4)),
|
|
24
|
+
UDim.new (buffer.readf32 (b, o + 8), buffer.readi32 (b, o + 12))
|
|
25
|
+
)
|
|
26
|
+
end),
|
|
27
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Vector2 (8B) and Vector3 (12B) codecs.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.Base)
|
|
6
|
+
|
|
7
|
+
return table.freeze ({
|
|
8
|
+
vec2 = Base.define (8, function (b: buffer, o: number, v: Vector2): ()
|
|
9
|
+
buffer.writef32 (b, o, v.X)
|
|
10
|
+
buffer.writef32 (b, o + 4, v.Y)
|
|
11
|
+
end, function (b: buffer, o: number): Vector2
|
|
12
|
+
return Vector2.new (buffer.readf32 (b, o), buffer.readf32 (b, o + 4))
|
|
13
|
+
end),
|
|
14
|
+
vec3 = Base.define (12, function (b: buffer, o: number, v: Vector3): ()
|
|
15
|
+
buffer.writef32 (b, o, v.X)
|
|
16
|
+
buffer.writef32 (b, o + 4, v.Y)
|
|
17
|
+
buffer.writef32 (b, o + 8, v.Z)
|
|
18
|
+
end, function (b: buffer, o: number): Vector3
|
|
19
|
+
return Vector3.new (
|
|
20
|
+
buffer.readf32 (b, o),
|
|
21
|
+
buffer.readf32 (b, o + 4),
|
|
22
|
+
buffer.readf32 (b, o + 8)
|
|
23
|
+
)
|
|
24
|
+
end),
|
|
25
|
+
})
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Auto (self-describing) codec. Writes a u8 type tag then the value.
|
|
4
|
+
|
|
5
|
+
local Buffer = require (script.Parent.Parent.datatype.Buffer)
|
|
6
|
+
local CFrameCodec = require (script.Parent.Parent.datatype.CFrame)
|
|
7
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
8
|
+
local alloc = Channel.alloc
|
|
9
|
+
local BoolCodec = require (script.Parent.Parent.primitive.Bool)
|
|
10
|
+
local Color = require (script.Parent.Parent.datatype.Color)
|
|
11
|
+
local IntVector = require (script.Parent.Parent.datatype.IntVector)
|
|
12
|
+
local Number = require (script.Parent.Parent.primitive.Number)
|
|
13
|
+
local NumberRangeCodec = require (script.Parent.Parent.datatype.NumberRange)
|
|
14
|
+
local RayCodec = require (script.Parent.Parent.datatype.Ray)
|
|
15
|
+
local RectCodec = require (script.Parent.Parent.datatype.Rect)
|
|
16
|
+
local Region = require (script.Parent.Parent.datatype.Region)
|
|
17
|
+
local Sequence = require (script.Parent.Parent.datatype.Sequence)
|
|
18
|
+
local String = require (script.Parent.Parent.datatype.String)
|
|
19
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
20
|
+
local UDimCodec = require (script.Parent.Parent.datatype.UDim)
|
|
21
|
+
local Varint = require (script.Parent.Parent.primitive.Varint)
|
|
22
|
+
local Vector = require (script.Parent.Parent.datatype.Vector)
|
|
23
|
+
|
|
24
|
+
type ChannelState = Types.ChannelState
|
|
25
|
+
type Codec<T> = Types.Codec<T>
|
|
26
|
+
|
|
27
|
+
-- Constants --------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
local TAG_NIL = 0
|
|
30
|
+
local TAG_BOOL = 1
|
|
31
|
+
local TAG_U8 = 2
|
|
32
|
+
local TAG_U16 = 3
|
|
33
|
+
local TAG_U32 = 4
|
|
34
|
+
local TAG_I8 = 5
|
|
35
|
+
local TAG_I16 = 6
|
|
36
|
+
local TAG_I32 = 7
|
|
37
|
+
local TAG_F32 = 8
|
|
38
|
+
local TAG_F64 = 9
|
|
39
|
+
local TAG_STRING = 10
|
|
40
|
+
local TAG_VEC2 = 11
|
|
41
|
+
local TAG_VEC3 = 12
|
|
42
|
+
local TAG_COLOR3 = 13
|
|
43
|
+
local TAG_CFRAME = 14
|
|
44
|
+
local TAG_BUFFER = 15
|
|
45
|
+
local TAG_UDIM = 16
|
|
46
|
+
local TAG_UDIM2 = 17
|
|
47
|
+
local TAG_NUMBERRANGE = 18
|
|
48
|
+
local TAG_RECT = 19
|
|
49
|
+
local TAG_VEC2INT16 = 20
|
|
50
|
+
local TAG_VEC3INT16 = 21
|
|
51
|
+
local TAG_REGION3 = 22
|
|
52
|
+
local TAG_REGION3INT16 = 23
|
|
53
|
+
local TAG_RAY = 24
|
|
54
|
+
local TAG_NUMBERSEQUENCE = 25
|
|
55
|
+
local TAG_COLORSEQUENCE = 26
|
|
56
|
+
|
|
57
|
+
local floor = math.floor
|
|
58
|
+
|
|
59
|
+
local READ_DISPATCH = table.freeze ({
|
|
60
|
+
[TAG_BOOL] = BoolCodec.bool,
|
|
61
|
+
[TAG_U8] = Number.u8,
|
|
62
|
+
[TAG_U16] = Number.u16,
|
|
63
|
+
[TAG_U32] = Number.u32,
|
|
64
|
+
[TAG_I8] = Number.i8,
|
|
65
|
+
[TAG_I16] = Number.i16,
|
|
66
|
+
[TAG_I32] = Number.i32,
|
|
67
|
+
[TAG_F32] = Number.f32,
|
|
68
|
+
[TAG_F64] = Number.f64,
|
|
69
|
+
[TAG_STRING] = String.string,
|
|
70
|
+
[TAG_VEC2] = Vector.vec2,
|
|
71
|
+
[TAG_VEC3] = Vector.vec3,
|
|
72
|
+
[TAG_COLOR3] = Color.color3,
|
|
73
|
+
[TAG_CFRAME] = CFrameCodec.cframe,
|
|
74
|
+
[TAG_BUFFER] = Buffer.buff,
|
|
75
|
+
[TAG_UDIM] = UDimCodec.udim,
|
|
76
|
+
[TAG_UDIM2] = UDimCodec.udim2,
|
|
77
|
+
[TAG_NUMBERRANGE] = NumberRangeCodec.numberRange,
|
|
78
|
+
[TAG_RECT] = RectCodec.rect,
|
|
79
|
+
[TAG_VEC2INT16] = IntVector.vec2int16,
|
|
80
|
+
[TAG_VEC3INT16] = IntVector.vec3int16,
|
|
81
|
+
[TAG_REGION3] = Region.region3,
|
|
82
|
+
[TAG_REGION3INT16] = Region.region3int16,
|
|
83
|
+
[TAG_RAY] = RayCodec.ray,
|
|
84
|
+
[TAG_NUMBERSEQUENCE] = Sequence.numberSequence,
|
|
85
|
+
[TAG_COLORSEQUENCE] = Sequence.colorSequence,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
local TYPE_DISPATCH = table.freeze ({
|
|
89
|
+
boolean = { tag = TAG_BOOL, codec = BoolCodec.bool, size = 1, dw = nil },
|
|
90
|
+
string = { tag = TAG_STRING, codec = String.string, size = nil, dw = nil },
|
|
91
|
+
Vector2 = {
|
|
92
|
+
tag = TAG_VEC2,
|
|
93
|
+
codec = Vector.vec2,
|
|
94
|
+
size = 8,
|
|
95
|
+
dw = (Vector.vec2 :: any)._directWrite,
|
|
96
|
+
},
|
|
97
|
+
Vector3 = {
|
|
98
|
+
tag = TAG_VEC3,
|
|
99
|
+
codec = Vector.vec3,
|
|
100
|
+
size = 12,
|
|
101
|
+
dw = (Vector.vec3 :: any)._directWrite,
|
|
102
|
+
},
|
|
103
|
+
Color3 = {
|
|
104
|
+
tag = TAG_COLOR3,
|
|
105
|
+
codec = Color.color3,
|
|
106
|
+
size = 3,
|
|
107
|
+
dw = (Color.color3 :: any)._directWrite,
|
|
108
|
+
},
|
|
109
|
+
CFrame = {
|
|
110
|
+
tag = TAG_CFRAME,
|
|
111
|
+
codec = CFrameCodec.cframe,
|
|
112
|
+
size = 24,
|
|
113
|
+
dw = (CFrameCodec.cframe :: any)._directWrite,
|
|
114
|
+
},
|
|
115
|
+
buffer = { tag = TAG_BUFFER, codec = Buffer.buff, size = nil, dw = nil },
|
|
116
|
+
UDim = {
|
|
117
|
+
tag = TAG_UDIM,
|
|
118
|
+
codec = UDimCodec.udim,
|
|
119
|
+
size = 8,
|
|
120
|
+
dw = (UDimCodec.udim :: any)._directWrite,
|
|
121
|
+
},
|
|
122
|
+
UDim2 = {
|
|
123
|
+
tag = TAG_UDIM2,
|
|
124
|
+
codec = UDimCodec.udim2,
|
|
125
|
+
size = 16,
|
|
126
|
+
dw = (UDimCodec.udim2 :: any)._directWrite,
|
|
127
|
+
},
|
|
128
|
+
NumberRange = {
|
|
129
|
+
tag = TAG_NUMBERRANGE,
|
|
130
|
+
codec = NumberRangeCodec.numberRange,
|
|
131
|
+
size = 8,
|
|
132
|
+
dw = (NumberRangeCodec.numberRange :: any)._directWrite,
|
|
133
|
+
},
|
|
134
|
+
Rect = {
|
|
135
|
+
tag = TAG_RECT,
|
|
136
|
+
codec = RectCodec.rect,
|
|
137
|
+
size = 16,
|
|
138
|
+
dw = (RectCodec.rect :: any)._directWrite,
|
|
139
|
+
},
|
|
140
|
+
Vector2int16 = {
|
|
141
|
+
tag = TAG_VEC2INT16,
|
|
142
|
+
codec = IntVector.vec2int16,
|
|
143
|
+
size = 4,
|
|
144
|
+
dw = (IntVector.vec2int16 :: any)._directWrite,
|
|
145
|
+
},
|
|
146
|
+
Vector3int16 = {
|
|
147
|
+
tag = TAG_VEC3INT16,
|
|
148
|
+
codec = IntVector.vec3int16,
|
|
149
|
+
size = 6,
|
|
150
|
+
dw = (IntVector.vec3int16 :: any)._directWrite,
|
|
151
|
+
},
|
|
152
|
+
Region3 = {
|
|
153
|
+
tag = TAG_REGION3,
|
|
154
|
+
codec = Region.region3,
|
|
155
|
+
size = 24,
|
|
156
|
+
dw = (Region.region3 :: any)._directWrite,
|
|
157
|
+
},
|
|
158
|
+
Region3int16 = {
|
|
159
|
+
tag = TAG_REGION3INT16,
|
|
160
|
+
codec = Region.region3int16,
|
|
161
|
+
size = 12,
|
|
162
|
+
dw = (Region.region3int16 :: any)._directWrite,
|
|
163
|
+
},
|
|
164
|
+
Ray = {
|
|
165
|
+
tag = TAG_RAY,
|
|
166
|
+
codec = RayCodec.ray,
|
|
167
|
+
size = 24,
|
|
168
|
+
dw = (RayCodec.ray :: any)._directWrite,
|
|
169
|
+
},
|
|
170
|
+
NumberSequence = {
|
|
171
|
+
tag = TAG_NUMBERSEQUENCE,
|
|
172
|
+
codec = Sequence.numberSequence,
|
|
173
|
+
size = nil,
|
|
174
|
+
dw = nil,
|
|
175
|
+
},
|
|
176
|
+
ColorSequence = {
|
|
177
|
+
tag = TAG_COLORSEQUENCE,
|
|
178
|
+
codec = Sequence.colorSequence,
|
|
179
|
+
size = nil,
|
|
180
|
+
dw = nil,
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
-- Numeric write dispatch: one lookup for both codec and payload size.
|
|
185
|
+
local NUM_SIZES = {} :: { [number]: number }
|
|
186
|
+
local NUM_DWRITES = {} :: { [number]: any }
|
|
187
|
+
|
|
188
|
+
for _, pair in
|
|
189
|
+
{
|
|
190
|
+
{ TAG_U8, Number.u8, 1 },
|
|
191
|
+
{ TAG_U16, Number.u16, 2 },
|
|
192
|
+
{ TAG_U32, Number.u32, 4 },
|
|
193
|
+
{ TAG_I8, Number.i8, 1 },
|
|
194
|
+
{ TAG_I16, Number.i16, 2 },
|
|
195
|
+
{ TAG_I32, Number.i32, 4 },
|
|
196
|
+
{ TAG_F32, Number.f32, 4 },
|
|
197
|
+
{ TAG_F64, Number.f64, 8 },
|
|
198
|
+
}
|
|
199
|
+
do
|
|
200
|
+
NUM_SIZES[pair[1]] = pair[3]
|
|
201
|
+
NUM_DWRITES[pair[1]] = (pair[2] :: any)._directWrite
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
table.freeze (NUM_SIZES)
|
|
205
|
+
table.freeze (NUM_DWRITES)
|
|
206
|
+
|
|
207
|
+
-- Private ----------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
local _f32Temp = buffer.create (4)
|
|
210
|
+
|
|
211
|
+
local function selectNumberTag (value: number): number
|
|
212
|
+
if value ~= value then
|
|
213
|
+
return TAG_F64
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
if value == floor (value) then
|
|
217
|
+
if value >= 0 then
|
|
218
|
+
if value <= 255 then
|
|
219
|
+
return TAG_U8
|
|
220
|
+
end
|
|
221
|
+
if value <= 65535 then
|
|
222
|
+
return TAG_U16
|
|
223
|
+
end
|
|
224
|
+
if value <= 4294967295 then
|
|
225
|
+
return TAG_U32
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
if value >= -128 and value <= 127 then
|
|
229
|
+
return TAG_I8
|
|
230
|
+
end
|
|
231
|
+
if value >= -32768 and value <= 32767 then
|
|
232
|
+
return TAG_I16
|
|
233
|
+
end
|
|
234
|
+
if value >= -2147483648 and value <= 2147483647 then
|
|
235
|
+
return TAG_I32
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
return TAG_F64
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
buffer.writef32 (_f32Temp, 0, value)
|
|
242
|
+
if buffer.readf32 (_f32Temp, 0) == value then
|
|
243
|
+
return TAG_F32
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
return TAG_F64
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
-- Public -----------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
local auto = table.freeze ({
|
|
252
|
+
write = function (ch: ChannelState, value: any): ()
|
|
253
|
+
if value == nil then
|
|
254
|
+
local c = ch.cursor
|
|
255
|
+
if c + 1 > ch.size then
|
|
256
|
+
alloc (ch, 1)
|
|
257
|
+
end
|
|
258
|
+
buffer.writeu8 (ch.buff, c, TAG_NIL)
|
|
259
|
+
ch.cursor = c + 1
|
|
260
|
+
return
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
local t = type (value)
|
|
264
|
+
|
|
265
|
+
if t == "number" then
|
|
266
|
+
local tag = selectNumberTag (value)
|
|
267
|
+
local payloadSize = NUM_SIZES[tag]
|
|
268
|
+
local totalSize = 1 + payloadSize
|
|
269
|
+
|
|
270
|
+
local c = ch.cursor
|
|
271
|
+
if c + totalSize > ch.size then
|
|
272
|
+
alloc (ch, totalSize)
|
|
273
|
+
end
|
|
274
|
+
local b = ch.buff
|
|
275
|
+
buffer.writeu8 (b, c, tag)
|
|
276
|
+
NUM_DWRITES[tag] (b, c + 1, value)
|
|
277
|
+
ch.cursor = c + totalSize
|
|
278
|
+
return
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
if t == "string" then
|
|
282
|
+
local len = #value
|
|
283
|
+
local total = 6 + len
|
|
284
|
+
local c = ch.cursor
|
|
285
|
+
if c + total > ch.size then
|
|
286
|
+
alloc (ch, total)
|
|
287
|
+
end
|
|
288
|
+
buffer.writeu8 (ch.buff, c, TAG_STRING)
|
|
289
|
+
ch.cursor = c + 1
|
|
290
|
+
Varint.write (ch, len)
|
|
291
|
+
local c2 = ch.cursor
|
|
292
|
+
buffer.writestring (ch.buff, c2, value)
|
|
293
|
+
ch.cursor = c2 + len
|
|
294
|
+
return
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
if t == "boolean" then
|
|
298
|
+
local c = ch.cursor
|
|
299
|
+
if c + 2 > ch.size then
|
|
300
|
+
alloc (ch, 2)
|
|
301
|
+
end
|
|
302
|
+
local b = ch.buff
|
|
303
|
+
buffer.writeu8 (b, c, TAG_BOOL)
|
|
304
|
+
buffer.writeu8 (b, c + 1, if value then 1 else 0)
|
|
305
|
+
ch.cursor = c + 2
|
|
306
|
+
return
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
local valueType = typeof (value)
|
|
310
|
+
local entry = TYPE_DISPATCH[valueType]
|
|
311
|
+
if not entry then
|
|
312
|
+
error (`[Lync] Auto unsupported type: {valueType}`)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
local dw = entry.dw
|
|
316
|
+
local size = entry.size
|
|
317
|
+
if dw and size then
|
|
318
|
+
local totalSize = 1 + size
|
|
319
|
+
local c = ch.cursor
|
|
320
|
+
if c + totalSize > ch.size then
|
|
321
|
+
alloc (ch, totalSize)
|
|
322
|
+
end
|
|
323
|
+
local b = ch.buff
|
|
324
|
+
buffer.writeu8 (b, c, entry.tag)
|
|
325
|
+
dw (b, c + 1, value)
|
|
326
|
+
ch.cursor = c + totalSize
|
|
327
|
+
else
|
|
328
|
+
local c = ch.cursor
|
|
329
|
+
if c + 1 > ch.size then
|
|
330
|
+
alloc (ch, 1)
|
|
331
|
+
end
|
|
332
|
+
buffer.writeu8 (ch.buff, c, entry.tag)
|
|
333
|
+
ch.cursor = c + 1
|
|
334
|
+
entry.codec.write (ch, value)
|
|
335
|
+
end
|
|
336
|
+
end,
|
|
337
|
+
read = function (src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
338
|
+
local tag = buffer.readu8 (src, pos)
|
|
339
|
+
if tag == TAG_NIL then
|
|
340
|
+
return nil, 1
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
local codec = READ_DISPATCH[tag]
|
|
344
|
+
if not codec then
|
|
345
|
+
error (`[Lync] Auto unknown tag: {tag}`)
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
local value, n = codec.read (src, pos + 1, refs)
|
|
349
|
+
return value, 1 + n
|
|
350
|
+
end,
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
return table.freeze ({ auto = auto })
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Pack booleans, unsigned and signed integers at the bit level.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
8
|
+
|
|
9
|
+
type ChannelState = Types.ChannelState
|
|
10
|
+
type Codec<T> = Types.Codec<T>
|
|
11
|
+
|
|
12
|
+
-- Constants --------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local band = bit32.band
|
|
15
|
+
local bor = bit32.bor
|
|
16
|
+
local lshift = bit32.lshift
|
|
17
|
+
local rshift = bit32.rshift
|
|
18
|
+
|
|
19
|
+
-- Private ----------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
type FieldSpec = {
|
|
22
|
+
type: "bool" | "uint" | "int",
|
|
23
|
+
width: number?,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
local function selectIO (bytes: number): ((b: buffer, offset: number, v: number) -> (), (
|
|
27
|
+
b: buffer,
|
|
28
|
+
offset: number
|
|
29
|
+
) -> number)
|
|
30
|
+
if bytes == 1 then
|
|
31
|
+
return buffer.writeu8, buffer.readu8
|
|
32
|
+
end
|
|
33
|
+
if bytes == 2 then
|
|
34
|
+
return buffer.writeu16, buffer.readu16
|
|
35
|
+
end
|
|
36
|
+
return buffer.writeu32, buffer.readu32
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
-- Public -----------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
local Bitfield = {}
|
|
42
|
+
|
|
43
|
+
function Bitfield.define (schema: { [string]: FieldSpec }): Codec<any>
|
|
44
|
+
local sortedKeys = {} :: { string }
|
|
45
|
+
|
|
46
|
+
for key in schema do
|
|
47
|
+
table.insert (sortedKeys, key)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
table.sort (sortedKeys)
|
|
51
|
+
|
|
52
|
+
local count = #sortedKeys
|
|
53
|
+
if count == 0 then
|
|
54
|
+
error ("[Lync] Bitfield requires at least one field")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
local keys = table.create (count) :: { string }
|
|
58
|
+
local isBool = table.create (count) :: { boolean }
|
|
59
|
+
local offsets = table.create (count) :: { number }
|
|
60
|
+
local masks = table.create (count) :: { number }
|
|
61
|
+
|
|
62
|
+
local hasSigned = false
|
|
63
|
+
local signBits = nil :: { number }?
|
|
64
|
+
local signExtends = nil :: { number }?
|
|
65
|
+
|
|
66
|
+
local totalBits = 0
|
|
67
|
+
for i = 1, count do
|
|
68
|
+
local key = sortedKeys[i]
|
|
69
|
+
local spec = schema[key]
|
|
70
|
+
local specIsBool = spec.type == "bool"
|
|
71
|
+
local specIsSigned = spec.type == "int"
|
|
72
|
+
local width: number
|
|
73
|
+
|
|
74
|
+
if specIsBool then
|
|
75
|
+
width = 1
|
|
76
|
+
else
|
|
77
|
+
if not spec.width then
|
|
78
|
+
error (`[Lync] Bitfield field requires a width: "{key}"`)
|
|
79
|
+
end
|
|
80
|
+
width = spec.width
|
|
81
|
+
if width < 1 or width > 32 then
|
|
82
|
+
error (`[Lync] Bitfield width must be 1-32: "{key}" got {width}`)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
if specIsSigned then
|
|
87
|
+
hasSigned = true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
keys[i] = key
|
|
91
|
+
isBool[i] = specIsBool
|
|
92
|
+
offsets[i] = totalBits
|
|
93
|
+
masks[i] = lshift (1, width) - 1
|
|
94
|
+
totalBits += width
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
if totalBits > 32 then
|
|
98
|
+
error (`[Lync] Bitfield exceeds 32 bits: {totalBits}`)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
-- Only build sign tables when at least one field is signed
|
|
102
|
+
if hasSigned then
|
|
103
|
+
signBits = table.create (count) :: { number }
|
|
104
|
+
signExtends = table.create (count) :: { number }
|
|
105
|
+
|
|
106
|
+
for i = 1, count do
|
|
107
|
+
local spec = schema[sortedKeys[i]]
|
|
108
|
+
if spec.type == "int" then
|
|
109
|
+
(signBits :: { number })[i] = lshift (1, (spec.width :: number) - 1);
|
|
110
|
+
(signExtends :: { number })[i] = lshift (1, spec.width :: number)
|
|
111
|
+
else
|
|
112
|
+
(signBits :: { number })[i] = 0
|
|
113
|
+
(signExtends :: { number })[i] = 0
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
table.freeze (signBits :: { number })
|
|
118
|
+
table.freeze (signExtends :: { number })
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
table.freeze (keys)
|
|
122
|
+
table.freeze (isBool)
|
|
123
|
+
table.freeze (offsets)
|
|
124
|
+
table.freeze (masks)
|
|
125
|
+
|
|
126
|
+
local bytes = if totalBits <= 8 then 1 elseif totalBits <= 16 then 2 else 4
|
|
127
|
+
local wfn, rfn = selectIO (bytes)
|
|
128
|
+
|
|
129
|
+
-- Shared pack logic
|
|
130
|
+
local function packValue (value: any): number
|
|
131
|
+
local packed = 0
|
|
132
|
+
for i = 1, count do
|
|
133
|
+
local bits = if isBool[i]
|
|
134
|
+
then (if value[keys[i]] then 1 else 0)
|
|
135
|
+
else band (value[keys[i]], masks[i])
|
|
136
|
+
packed = bor (packed, lshift (bits, offsets[i]))
|
|
137
|
+
end
|
|
138
|
+
return packed
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
-- Shared unpack logic
|
|
142
|
+
local function unpackValue (packed: number): any
|
|
143
|
+
local result = {}
|
|
144
|
+
|
|
145
|
+
if hasSigned then
|
|
146
|
+
local sb = signBits :: { number }
|
|
147
|
+
local se = signExtends :: { number }
|
|
148
|
+
|
|
149
|
+
for i = 1, count do
|
|
150
|
+
local raw = band (rshift (packed, offsets[i]), masks[i])
|
|
151
|
+
if isBool[i] then
|
|
152
|
+
result[keys[i]] = raw ~= 0
|
|
153
|
+
elseif sb[i] > 0 and raw >= sb[i] then
|
|
154
|
+
result[keys[i]] = raw - se[i]
|
|
155
|
+
else
|
|
156
|
+
result[keys[i]] = raw
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
for i = 1, count do
|
|
161
|
+
local raw = band (rshift (packed, offsets[i]), masks[i])
|
|
162
|
+
result[keys[i]] = if isBool[i] then raw ~= 0 else raw
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
return result
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
return table.freeze ({
|
|
170
|
+
_size = bytes,
|
|
171
|
+
_directWrite = function (b: buffer, offset: number, value: any): ()
|
|
172
|
+
wfn (b, offset, packValue (value))
|
|
173
|
+
end,
|
|
174
|
+
_directRead = function (b: buffer, offset: number): any
|
|
175
|
+
return unpackValue (rfn (b, offset))
|
|
176
|
+
end,
|
|
177
|
+
write = function (ch: ChannelState, value: any): ()
|
|
178
|
+
local c = ch.cursor
|
|
179
|
+
if c + bytes > ch.size then
|
|
180
|
+
alloc (ch, bytes)
|
|
181
|
+
end
|
|
182
|
+
wfn (ch.buff, c, packValue (value))
|
|
183
|
+
ch.cursor = c + bytes
|
|
184
|
+
end,
|
|
185
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (any, number)
|
|
186
|
+
return unpackValue (rfn (src, pos)), bytes
|
|
187
|
+
end,
|
|
188
|
+
})
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
return table.freeze (Bitfield)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- User-defined fixed-size codec factory. Public API wrapper over Base.define.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.Base)
|
|
6
|
+
|
|
7
|
+
local floor = math.floor
|
|
8
|
+
|
|
9
|
+
local Custom = {}
|
|
10
|
+
|
|
11
|
+
function Custom.define (
|
|
12
|
+
size: number,
|
|
13
|
+
writeFn: (b: buffer, offset: number, value: any) -> (),
|
|
14
|
+
readFn: (b: buffer, offset: number) -> any
|
|
15
|
+
): any
|
|
16
|
+
if size < 0 or size ~= floor (size) then
|
|
17
|
+
error (`[Lync] Custom codec size must be a non-negative integer, got {size}`)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if size == 0 then
|
|
21
|
+
return Base.zero ()
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
return Base.define (size, writeFn, readFn)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
return table.freeze (Custom)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- String enum codec. Maps values to u8 indices at definition time.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
8
|
+
|
|
9
|
+
type ChannelState = Types.ChannelState
|
|
10
|
+
type Codec<T> = Types.Codec<T>
|
|
11
|
+
|
|
12
|
+
-- Public -----------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local EnumCodec = {}
|
|
15
|
+
|
|
16
|
+
function EnumCodec.define (...: string): Codec<string>
|
|
17
|
+
local values = { ... }
|
|
18
|
+
local count = #values
|
|
19
|
+
if count == 0 then
|
|
20
|
+
error ("[Lync] Enum requires at least one value")
|
|
21
|
+
end
|
|
22
|
+
if count > 256 then
|
|
23
|
+
error (`[Lync] Enum exceeds 256 values: {count}`)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
local toIndex = {} :: { [string]: number }
|
|
27
|
+
local toValue = table.create (count) :: { string }
|
|
28
|
+
|
|
29
|
+
for i, value in values do
|
|
30
|
+
if toIndex[value] ~= nil then
|
|
31
|
+
error (`[Lync] Duplicate enum value: \"{value}\"`)
|
|
32
|
+
end
|
|
33
|
+
toIndex[value] = i - 1
|
|
34
|
+
toValue[i] = value
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
table.freeze (toIndex)
|
|
38
|
+
table.freeze (toValue)
|
|
39
|
+
|
|
40
|
+
return table.freeze ({
|
|
41
|
+
_size = 1,
|
|
42
|
+
_directWrite = function (b: buffer, offset: number, value: string): ()
|
|
43
|
+
local idx = toIndex[value]
|
|
44
|
+
if idx == nil then
|
|
45
|
+
error (`[Lync] Invalid enum value: \"{value}\"`)
|
|
46
|
+
end
|
|
47
|
+
buffer.writeu8 (b, offset, idx)
|
|
48
|
+
end,
|
|
49
|
+
_directRead = function (b: buffer, offset: number): string
|
|
50
|
+
local idx = buffer.readu8 (b, offset)
|
|
51
|
+
local value = toValue[idx + 1]
|
|
52
|
+
if value == nil then
|
|
53
|
+
error (`[Lync] Invalid enum index: {idx}`)
|
|
54
|
+
end
|
|
55
|
+
return value
|
|
56
|
+
end,
|
|
57
|
+
write = function (ch: ChannelState, value: string): ()
|
|
58
|
+
local idx = toIndex[value]
|
|
59
|
+
if idx == nil then
|
|
60
|
+
error (`[Lync] Invalid enum value: \"{value}\"`)
|
|
61
|
+
end
|
|
62
|
+
local c = ch.cursor
|
|
63
|
+
if c + 1 > ch.size then
|
|
64
|
+
alloc (ch, 1)
|
|
65
|
+
end
|
|
66
|
+
buffer.writeu8 (ch.buff, c, idx)
|
|
67
|
+
ch.cursor = c + 1
|
|
68
|
+
end,
|
|
69
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (string, number)
|
|
70
|
+
local idx = buffer.readu8 (src, pos)
|
|
71
|
+
local value = toValue[idx + 1]
|
|
72
|
+
if value == nil then
|
|
73
|
+
error (`[Lync] Invalid enum index: {idx}`)
|
|
74
|
+
end
|
|
75
|
+
return value, 1
|
|
76
|
+
end,
|
|
77
|
+
})
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
return table.freeze (EnumCodec)
|