@axpecter/lync 2.2.1 → 2.3.2
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 +109 -147
- package/package.json +1 -1
- package/src/Types.luau +34 -22
- package/src/api/Group.luau +40 -33
- package/src/api/Packet.luau +140 -66
- package/src/api/Query.luau +103 -65
- package/src/api/Scope.luau +26 -10
- package/src/api/Signal.luau +43 -52
- package/src/codec/Base.luau +28 -14
- package/src/codec/composite/Array.luau +296 -115
- package/src/codec/composite/Map.luau +377 -57
- package/src/codec/composite/Optional.luau +10 -2
- package/src/codec/composite/Shared.luau +134 -64
- package/src/codec/composite/Struct.luau +294 -43
- package/src/codec/composite/Tagged.luau +21 -16
- package/src/codec/composite/Tuple.luau +32 -15
- package/src/codec/datatype/Buffer.luau +7 -7
- package/src/codec/datatype/CFrame.luau +34 -122
- package/src/codec/datatype/Color.luau +10 -10
- package/src/codec/datatype/Instance.luau +15 -12
- package/src/codec/datatype/IntVector.luau +2 -2
- package/src/codec/datatype/NumberRange.luau +15 -8
- package/src/codec/datatype/Ray.luau +13 -14
- package/src/codec/datatype/Rect.luau +12 -12
- package/src/codec/datatype/Region.luau +13 -14
- package/src/codec/datatype/Sequence.luau +87 -65
- package/src/codec/datatype/String.luau +17 -10
- package/src/codec/datatype/UDim.luau +10 -8
- package/src/codec/datatype/Vector.luau +20 -59
- package/src/codec/meta/Auto.luau +94 -126
- package/src/codec/meta/Bitfield.luau +12 -14
- package/src/codec/meta/Custom.luau +3 -1
- package/src/codec/meta/DeltaScalar.luau +390 -0
- package/src/codec/meta/Enum.luau +9 -9
- package/src/codec/meta/Float.luau +6 -28
- package/src/codec/meta/Nothing.luau +1 -1
- package/src/codec/meta/Unknown.luau +10 -7
- package/src/codec/primitive/Bool.luau +6 -4
- package/src/codec/primitive/Float16.luau +5 -2
- package/src/codec/primitive/Int.luau +5 -6
- package/src/codec/primitive/Number.luau +6 -4
- package/src/codec/primitive/Signed.luau +2 -2
- package/src/codec/primitive/Varint.luau +76 -39
- package/src/codec/primitive/Zint.luau +99 -0
- package/src/index.d.ts +207 -53
- package/src/init.luau +116 -103
- package/src/internal/Baseline.luau +9 -1
- package/src/internal/Channel.luau +191 -157
- package/src/internal/Middleware.luau +22 -6
- package/src/internal/Pool.luau +12 -4
- package/src/internal/Registry.luau +25 -16
- package/src/internal/Transport.luau +1 -1
- package/src/transport/Bridge.luau +47 -33
- package/src/transport/Client.luau +25 -21
- package/src/transport/Gate.luau +227 -172
- package/src/transport/Reader.luau +174 -106
- package/src/transport/Server.luau +46 -57
- package/src/util/Array.luau +18 -0
- package/src/util/Buffer.luau +90 -0
- package/src/util/Constants.luau +30 -0
- package/src/util/Log.luau +68 -0
- package/src/util/Player.luau +14 -0
- package/src/util/Quantize.luau +84 -0
- package/src/util/Quat.luau +124 -0
- package/src/internal/Util.luau +0 -26
package/src/codec/meta/Auto.luau
CHANGED
|
@@ -8,6 +8,7 @@ local CFrameC = require(script.Parent.Parent.datatype.CFrame)
|
|
|
8
8
|
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
9
9
|
local ColorC = require(script.Parent.Parent.datatype.Color)
|
|
10
10
|
local IVC = require(script.Parent.Parent.datatype.IntVector)
|
|
11
|
+
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
11
12
|
local NRC = require(script.Parent.Parent.datatype.NumberRange)
|
|
12
13
|
local NumberC = require(script.Parent.Parent.primitive.Number)
|
|
13
14
|
local RayC = require(script.Parent.Parent.datatype.Ray)
|
|
@@ -53,6 +54,78 @@ local TAG_COLSEQ = 26
|
|
|
53
54
|
local INLINE_MAX = Varint.INLINE_MAX
|
|
54
55
|
local F32_SCRATCH = NumberC._f32Scratch
|
|
55
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
|
+
|
|
56
129
|
-- Private ----------------------------------------------------------------
|
|
57
130
|
|
|
58
131
|
local alloc = Base.alloc
|
|
@@ -62,10 +135,12 @@ local readu8 = buffer.readu8
|
|
|
62
135
|
local floor = math.floor
|
|
63
136
|
local isnan = math.isnan
|
|
64
137
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
]]
|
|
69
144
|
local function selectNumberTag(value: number): number
|
|
70
145
|
if isnan(value) then
|
|
71
146
|
return TAG_F64
|
|
@@ -95,7 +170,6 @@ local function selectNumberTag(value: number): number
|
|
|
95
170
|
end
|
|
96
171
|
end
|
|
97
172
|
|
|
98
|
-
-- Roblox is single-threaded so a module scratch cannot be aliased.
|
|
99
173
|
buffer.writef32(F32_SCRATCH, 0, value)
|
|
100
174
|
if buffer.readf32(F32_SCRATCH, 0) == value then
|
|
101
175
|
return TAG_F32
|
|
@@ -103,114 +177,6 @@ local function selectNumberTag(value: number): number
|
|
|
103
177
|
return TAG_F64
|
|
104
178
|
end
|
|
105
179
|
|
|
106
|
-
local function numberSize(tag: number): number
|
|
107
|
-
if tag == TAG_U8 or tag == TAG_I8 then
|
|
108
|
-
return 1
|
|
109
|
-
end
|
|
110
|
-
if tag == TAG_U16 or tag == TAG_I16 then
|
|
111
|
-
return 2
|
|
112
|
-
end
|
|
113
|
-
if tag == TAG_U32 or tag == TAG_I32 or tag == TAG_F32 then
|
|
114
|
-
return 4
|
|
115
|
-
end
|
|
116
|
-
return 8
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
local function writeNumber(b: buffer, off: number, tag: number, value: number): ()
|
|
120
|
-
if tag == TAG_U8 then
|
|
121
|
-
buffer.writeu8(b, off, value)
|
|
122
|
-
return
|
|
123
|
-
end
|
|
124
|
-
if tag == TAG_U16 then
|
|
125
|
-
buffer.writeu16(b, off, value)
|
|
126
|
-
return
|
|
127
|
-
end
|
|
128
|
-
if tag == TAG_U32 then
|
|
129
|
-
buffer.writeu32(b, off, value)
|
|
130
|
-
return
|
|
131
|
-
end
|
|
132
|
-
if tag == TAG_I8 then
|
|
133
|
-
writeI8(b, off, value)
|
|
134
|
-
return
|
|
135
|
-
end
|
|
136
|
-
if tag == TAG_I16 then
|
|
137
|
-
writeI16(b, off, value)
|
|
138
|
-
return
|
|
139
|
-
end
|
|
140
|
-
if tag == TAG_I32 then
|
|
141
|
-
writeI32(b, off, value)
|
|
142
|
-
return
|
|
143
|
-
end
|
|
144
|
-
if tag == TAG_F32 then
|
|
145
|
-
buffer.writef32(b, off, value)
|
|
146
|
-
return
|
|
147
|
-
end
|
|
148
|
-
buffer.writef64(b, off, value)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
local function readNumber(b: buffer, off: number, tag: number): number
|
|
152
|
-
if tag == TAG_U8 then
|
|
153
|
-
return buffer.readu8(b, off)
|
|
154
|
-
end
|
|
155
|
-
if tag == TAG_U16 then
|
|
156
|
-
return buffer.readu16(b, off)
|
|
157
|
-
end
|
|
158
|
-
if tag == TAG_U32 then
|
|
159
|
-
return buffer.readu32(b, off)
|
|
160
|
-
end
|
|
161
|
-
if tag == TAG_I8 then
|
|
162
|
-
return buffer.readi8(b, off)
|
|
163
|
-
end
|
|
164
|
-
if tag == TAG_I16 then
|
|
165
|
-
return buffer.readi16(b, off)
|
|
166
|
-
end
|
|
167
|
-
if tag == TAG_I32 then
|
|
168
|
-
return buffer.readi32(b, off)
|
|
169
|
-
end
|
|
170
|
-
if tag == TAG_F32 then
|
|
171
|
-
return buffer.readf32(b, off)
|
|
172
|
-
end
|
|
173
|
-
return buffer.readf64(b, off)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
type TypeEntry = { tag: number, codec: Types.InternalCodec<any> }
|
|
177
|
-
|
|
178
|
-
local TYPE_DISPATCH: { [string]: TypeEntry } = table.freeze({
|
|
179
|
-
Vector2 = { tag = TAG_VEC2, codec = VecC.vec2 },
|
|
180
|
-
Vector3 = { tag = TAG_VEC3, codec = VecC.vec3 },
|
|
181
|
-
Color3 = { tag = TAG_COLOR3, codec = ColorC.color3 },
|
|
182
|
-
CFrame = { tag = TAG_CFRAME, codec = CFrameC.cframe },
|
|
183
|
-
UDim = { tag = TAG_UDIM, codec = UDimC.udim },
|
|
184
|
-
UDim2 = { tag = TAG_UDIM2, codec = UDimC.udim2 },
|
|
185
|
-
NumberRange = { tag = TAG_NUMRANGE, codec = NRC.numberRange },
|
|
186
|
-
Rect = { tag = TAG_RECT, codec = RectC.rect },
|
|
187
|
-
Vector2int16 = { tag = TAG_V2I16, codec = IVC.vec2int16 },
|
|
188
|
-
Vector3int16 = { tag = TAG_V3I16, codec = IVC.vec3int16 },
|
|
189
|
-
Region3 = { tag = TAG_REGION3, codec = RegionC.region3 },
|
|
190
|
-
Region3int16 = { tag = TAG_R3I16, codec = RegionC.region3int16 },
|
|
191
|
-
Ray = { tag = TAG_RAY, codec = RayC.ray },
|
|
192
|
-
NumberSequence = { tag = TAG_NUMSEQ, codec = SeqC.numberSequence },
|
|
193
|
-
ColorSequence = { tag = TAG_COLSEQ, codec = SeqC.colorSequence },
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
local READ_DISPATCH: { [number]: Types.InternalCodec<any> } = table.freeze({
|
|
197
|
-
[TAG_VEC2] = VecC.vec2,
|
|
198
|
-
[TAG_VEC3] = VecC.vec3,
|
|
199
|
-
[TAG_COLOR3] = ColorC.color3,
|
|
200
|
-
[TAG_CFRAME] = CFrameC.cframe,
|
|
201
|
-
[TAG_UDIM] = UDimC.udim,
|
|
202
|
-
[TAG_UDIM2] = UDimC.udim2,
|
|
203
|
-
[TAG_NUMRANGE] = NRC.numberRange,
|
|
204
|
-
[TAG_RECT] = RectC.rect,
|
|
205
|
-
[TAG_V2I16] = IVC.vec2int16,
|
|
206
|
-
[TAG_V3I16] = IVC.vec3int16,
|
|
207
|
-
[TAG_REGION3] = RegionC.region3,
|
|
208
|
-
[TAG_R3I16] = RegionC.region3int16,
|
|
209
|
-
[TAG_RAY] = RayC.ray,
|
|
210
|
-
[TAG_NUMSEQ] = SeqC.numberSequence,
|
|
211
|
-
[TAG_COLSEQ] = SeqC.colorSequence,
|
|
212
|
-
})
|
|
213
|
-
|
|
214
180
|
local function writeAuto(ch: Types.ChannelState, value: any): ()
|
|
215
181
|
if value == nil then
|
|
216
182
|
writeByte(ch, TAG_NIL)
|
|
@@ -233,15 +199,16 @@ local function writeAuto(ch: Types.ChannelState, value: any): ()
|
|
|
233
199
|
|
|
234
200
|
if t == "number" then
|
|
235
201
|
local tag = selectNumberTag(value)
|
|
236
|
-
local
|
|
202
|
+
local size = NUM_SIZE[tag]
|
|
203
|
+
local total = 1 + size
|
|
237
204
|
local cursor = ch.cursor
|
|
238
|
-
if cursor +
|
|
239
|
-
alloc(ch,
|
|
205
|
+
if cursor + total > ch.size then
|
|
206
|
+
alloc(ch, total)
|
|
240
207
|
end
|
|
241
208
|
local b = ch.buff
|
|
242
209
|
writeu8(b, cursor, tag)
|
|
243
|
-
|
|
244
|
-
ch.cursor = cursor +
|
|
210
|
+
NUM_WRITE[tag](b, cursor + 1, value)
|
|
211
|
+
ch.cursor = cursor + total
|
|
245
212
|
return
|
|
246
213
|
end
|
|
247
214
|
|
|
@@ -250,6 +217,7 @@ local function writeAuto(ch: Types.ChannelState, value: any): ()
|
|
|
250
217
|
|
|
251
218
|
local len = #value
|
|
252
219
|
local cursor = ch.cursor
|
|
220
|
+
-- Inline single-byte length fast path; matches String.luau.
|
|
253
221
|
if len <= INLINE_MAX then
|
|
254
222
|
if cursor + 1 + len > ch.size then
|
|
255
223
|
alloc(ch, 1 + len)
|
|
@@ -280,22 +248,22 @@ local function writeAuto(ch: Types.ChannelState, value: any): ()
|
|
|
280
248
|
|
|
281
249
|
local entry = TYPE_DISPATCH[t]
|
|
282
250
|
if not entry then
|
|
283
|
-
error(`
|
|
251
|
+
Log.error(`unsupported type "{t}"`)
|
|
284
252
|
end
|
|
285
|
-
|
|
286
253
|
local codec = entry.codec
|
|
287
254
|
local size = codec._size
|
|
288
255
|
local dw = codec._directWrite
|
|
256
|
+
-- Fixed-size + direct writer: single alloc + tag + payload.
|
|
289
257
|
if size and dw then
|
|
290
|
-
local
|
|
258
|
+
local total = 1 + size
|
|
291
259
|
local cursor = ch.cursor
|
|
292
|
-
if cursor +
|
|
293
|
-
alloc(ch,
|
|
260
|
+
if cursor + total > ch.size then
|
|
261
|
+
alloc(ch, total)
|
|
294
262
|
end
|
|
295
263
|
local b = ch.buff
|
|
296
264
|
writeu8(b, cursor, entry.tag)
|
|
297
265
|
dw(b, cursor + 1, value)
|
|
298
|
-
ch.cursor = cursor +
|
|
266
|
+
ch.cursor = cursor + total
|
|
299
267
|
return
|
|
300
268
|
end
|
|
301
269
|
|
|
@@ -314,7 +282,7 @@ local function readAuto(src: buffer, pos: number, refs: { Instance }?): (any, nu
|
|
|
314
282
|
end
|
|
315
283
|
|
|
316
284
|
if tag >= TAG_U8 and tag <= TAG_F64 then
|
|
317
|
-
return
|
|
285
|
+
return NUM_READ[tag](src, pos + 1), 1 + NUM_SIZE[tag]
|
|
318
286
|
end
|
|
319
287
|
|
|
320
288
|
if tag == TAG_STRING then
|
|
@@ -332,7 +300,7 @@ local function readAuto(src: buffer, pos: number, refs: { Instance }?): (any, nu
|
|
|
332
300
|
|
|
333
301
|
local codec = READ_DISPATCH[tag]
|
|
334
302
|
if not codec then
|
|
335
|
-
error(`
|
|
303
|
+
Log.error(`unknown tag {tag}`)
|
|
336
304
|
end
|
|
337
305
|
local value, consumed = codec.read(src, pos + 1, refs)
|
|
338
306
|
return value, 1 + consumed
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
-- Sub-byte
|
|
3
|
+
-- Sub-byte packed bool/uint/int fields. Up to 32 bits total per record.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
6
7
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
7
8
|
|
|
8
9
|
-- Constants --------------------------------------------------------------
|
|
@@ -18,12 +19,7 @@ local bor = bit32.bor
|
|
|
18
19
|
local lshift = bit32.lshift
|
|
19
20
|
local rshift = bit32.rshift
|
|
20
21
|
|
|
21
|
-
--
|
|
22
|
-
Width-aware mask: bit32.lshift(1, 32) returns 0, so 32-bit fields
|
|
23
|
-
need the all-ones mask explicitly. Sign-extension constants for int
|
|
24
|
-
fields use 2^(width-1) and 2^width via numeric exponentiation so
|
|
25
|
-
width=32 still produces 0x80000000 / 0x100000000 correctly.
|
|
26
|
-
]]
|
|
22
|
+
-- bit32.lshift(1, 32) wraps to 0; force all-ones in the 32-bit case.
|
|
27
23
|
local function widthMask(width: number): number
|
|
28
24
|
if width >= 32 then
|
|
29
25
|
return 0xFFFFFFFF
|
|
@@ -38,6 +34,7 @@ local Bitfield = {}
|
|
|
38
34
|
export type FieldSpec = { type: "bool" | "uint" | "int", width: number? }
|
|
39
35
|
|
|
40
36
|
function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<any>
|
|
37
|
+
-- Sort field names so bit positions are stable across runs.
|
|
41
38
|
local keys: { string } = {}
|
|
42
39
|
for key in schema do
|
|
43
40
|
table.insert(keys, key)
|
|
@@ -45,9 +42,7 @@ function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<a
|
|
|
45
42
|
table.sort(keys)
|
|
46
43
|
|
|
47
44
|
local fieldCount = #keys
|
|
48
|
-
|
|
49
|
-
error("[Lync] Lync.bitfield: requires at least one field")
|
|
50
|
-
end
|
|
45
|
+
Log.assert(fieldCount > 0, "requires at least one field")
|
|
51
46
|
|
|
52
47
|
local fieldKeys = table.create(fieldCount)
|
|
53
48
|
local fieldTypes = table.create(fieldCount)
|
|
@@ -72,7 +67,7 @@ function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<a
|
|
|
72
67
|
elseif spec.type == "uint" then
|
|
73
68
|
local width = spec.width
|
|
74
69
|
if not width or width < 1 or width > 32 then
|
|
75
|
-
error(`
|
|
70
|
+
Log.error(`field "{key}" uint width must be 1-32`)
|
|
76
71
|
end
|
|
77
72
|
fieldTypes[i] = TYPE_UINT
|
|
78
73
|
fieldMasks[i] = widthMask(width)
|
|
@@ -81,8 +76,9 @@ function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<a
|
|
|
81
76
|
totalBits += width
|
|
82
77
|
elseif spec.type == "int" then
|
|
83
78
|
local width = spec.width
|
|
79
|
+
-- Min int width is 2: 1 bit can't represent both a sign and a value.
|
|
84
80
|
if not width or width < 2 or width > 32 then
|
|
85
|
-
error(`
|
|
81
|
+
Log.error(`field "{key}" int width must be 2-32`)
|
|
86
82
|
end
|
|
87
83
|
fieldTypes[i] = TYPE_INT
|
|
88
84
|
fieldMasks[i] = widthMask(width)
|
|
@@ -90,14 +86,15 @@ function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<a
|
|
|
90
86
|
fieldSignExt[i] = 2 ^ width
|
|
91
87
|
totalBits += width
|
|
92
88
|
else
|
|
93
|
-
error(`
|
|
89
|
+
Log.error(`field "{key}" has unknown type "{spec.type}"`)
|
|
94
90
|
end
|
|
95
91
|
end
|
|
96
92
|
|
|
97
93
|
if totalBits > 32 then
|
|
98
|
-
error(`
|
|
94
|
+
Log.error(`total exceeds 32 bits ({totalBits})`)
|
|
99
95
|
end
|
|
100
96
|
|
|
97
|
+
-- Pick the narrowest u8/u16/u32 wire form.
|
|
101
98
|
local wireBytes: number
|
|
102
99
|
local wfn: (buffer, number, number) -> ()
|
|
103
100
|
local rfn: (buffer, number) -> number
|
|
@@ -137,6 +134,7 @@ function Bitfield.define(schema: { [string]: FieldSpec }): Types.InternalCodec<a
|
|
|
137
134
|
if fieldTypes[i] == TYPE_BOOL then
|
|
138
135
|
result[fieldKeys[i]] = raw ~= 0
|
|
139
136
|
elseif fieldTypes[i] == TYPE_INT then
|
|
137
|
+
-- Sign-extend: subtract 2^width when the sign bit is set.
|
|
140
138
|
result[fieldKeys[i]] = if raw >= fieldSignBit[i] then raw - fieldSignExt[i] else raw
|
|
141
139
|
else
|
|
142
140
|
result[fieldKeys[i]] = raw
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
-- User-defined fixed-size codec.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
6
7
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
7
8
|
|
|
8
9
|
-- Private ----------------------------------------------------------------
|
|
@@ -20,8 +21,9 @@ function Custom.define<T>(
|
|
|
20
21
|
typeCheck: string?
|
|
21
22
|
): Types.InternalCodec<T>
|
|
22
23
|
if type(size) ~= "number" or size < 0 or floor(size) ~= size then
|
|
23
|
-
error(
|
|
24
|
+
Log.error(`size must be a non-negative integer, got {size}`)
|
|
24
25
|
end
|
|
26
|
+
-- size == 0 collapses to constant(nil): user has no payload to encode.
|
|
25
27
|
if size == 0 then
|
|
26
28
|
return Base.constant(nil :: any) :: Types.InternalCodec<T>
|
|
27
29
|
end
|