@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,395 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- map and deltaMap codecs.
|
|
4
|
+
|
|
5
|
+
local Baseline = require (script.Parent.Parent.Parent.internal.Baseline)
|
|
6
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
7
|
+
local alloc = Channel.alloc
|
|
8
|
+
local Shared = require (script.Parent.Shared)
|
|
9
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
10
|
+
|
|
11
|
+
type ChannelState = Types.ChannelState
|
|
12
|
+
type Codec<T> = Types.Codec<T>
|
|
13
|
+
local Varint = require (script.Parent.Parent.primitive.Varint)
|
|
14
|
+
|
|
15
|
+
local rangeEqual = Shared.rangeEqual
|
|
16
|
+
local acquireScratch = Shared.acquireScratch
|
|
17
|
+
local releaseScratch = Shared.releaseScratch
|
|
18
|
+
|
|
19
|
+
local FLAG_DELTA = Shared.FLAG_DELTA
|
|
20
|
+
local FLAG_FULL = Shared.FLAG_FULL
|
|
21
|
+
local FLAG_UNCHANGED = Shared.FLAG_UNCHANGED
|
|
22
|
+
|
|
23
|
+
-- Public -----------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
local Map = {}
|
|
26
|
+
|
|
27
|
+
function Map.map (
|
|
28
|
+
keyCodec: Codec<any>,
|
|
29
|
+
valueCodec: Codec<any>,
|
|
30
|
+
maxCount: number?
|
|
31
|
+
): Codec<{ [any]: any }>
|
|
32
|
+
local keySize = (keyCodec :: any)._size :: number?
|
|
33
|
+
local valSize = (valueCodec :: any)._size :: number?
|
|
34
|
+
local keyDWrite = (keyCodec :: any)._directWrite
|
|
35
|
+
local valDWrite = (valueCodec :: any)._directWrite
|
|
36
|
+
local keyDRead = (keyCodec :: any)._directRead
|
|
37
|
+
local valDRead = (valueCodec :: any)._directRead
|
|
38
|
+
|
|
39
|
+
if keySize and valSize and keyDWrite and valDWrite and keyDRead and valDRead then
|
|
40
|
+
local entrySize = keySize + valSize
|
|
41
|
+
|
|
42
|
+
return table.freeze ({
|
|
43
|
+
write = function (ch: ChannelState, value: { [any]: any }): ()
|
|
44
|
+
local count = 0
|
|
45
|
+
for _ in value do
|
|
46
|
+
count += 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Varint.write (ch, count)
|
|
50
|
+
|
|
51
|
+
if count > 0 then
|
|
52
|
+
local totalBytes = count * entrySize
|
|
53
|
+
local c = ch.cursor
|
|
54
|
+
if c + totalBytes > ch.size then
|
|
55
|
+
alloc (ch, totalBytes)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
local b = ch.buff
|
|
59
|
+
for k, v in value do
|
|
60
|
+
keyDWrite (b, c, k)
|
|
61
|
+
valDWrite (b, c + keySize, v)
|
|
62
|
+
c += entrySize
|
|
63
|
+
end
|
|
64
|
+
ch.cursor = c
|
|
65
|
+
end
|
|
66
|
+
end,
|
|
67
|
+
read = function (
|
|
68
|
+
src: buffer,
|
|
69
|
+
pos: number,
|
|
70
|
+
_refs: { Instance }?
|
|
71
|
+
): ({ [any]: any }, number)
|
|
72
|
+
local count, lenBytes = Varint.read (src, pos)
|
|
73
|
+
|
|
74
|
+
if maxCount and count > maxCount then
|
|
75
|
+
error (`[Lync] Map count {count} exceeds max {maxCount}`)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
local result = {}
|
|
79
|
+
local c = pos + lenBytes
|
|
80
|
+
|
|
81
|
+
for _ = 1, count do
|
|
82
|
+
local k = keyDRead (src, c)
|
|
83
|
+
local v = valDRead (src, c + keySize)
|
|
84
|
+
result[k] = v
|
|
85
|
+
c += entrySize
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
return result, c - pos
|
|
89
|
+
end,
|
|
90
|
+
})
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
local writeKey = keyCodec.write
|
|
94
|
+
local writeVal = valueCodec.write
|
|
95
|
+
local readKey = keyCodec.read
|
|
96
|
+
local readVal = valueCodec.read
|
|
97
|
+
|
|
98
|
+
return table.freeze ({
|
|
99
|
+
write = function (ch: ChannelState, value: { [any]: any }): ()
|
|
100
|
+
local count = 0
|
|
101
|
+
for _ in value do
|
|
102
|
+
count += 1
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Varint.write (ch, count)
|
|
106
|
+
|
|
107
|
+
for k, v in value do
|
|
108
|
+
writeKey (ch, k)
|
|
109
|
+
writeVal (ch, v)
|
|
110
|
+
end
|
|
111
|
+
end,
|
|
112
|
+
read = function (src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
|
|
113
|
+
local count, lenBytes = Varint.read (src, pos)
|
|
114
|
+
|
|
115
|
+
if maxCount and count > maxCount then
|
|
116
|
+
error (`[Lync] Map count {count} exceeds max {maxCount}`)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
local result = {}
|
|
120
|
+
local absPos = pos + lenBytes
|
|
121
|
+
|
|
122
|
+
for _ = 1, count do
|
|
123
|
+
local k, kn = readKey (src, absPos, refs)
|
|
124
|
+
absPos += kn
|
|
125
|
+
local v, vn = readVal (src, absPos, refs)
|
|
126
|
+
absPos += vn
|
|
127
|
+
result[k] = v
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
return result, absPos - pos
|
|
131
|
+
end,
|
|
132
|
+
})
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
-- Delta map: tracks per-key changes. First frame is full, then upserts + removes only.
|
|
136
|
+
--
|
|
137
|
+
-- Wire format:
|
|
138
|
+
-- FLAG_FULL: u8(1) + varint(count) + [key+value ...]
|
|
139
|
+
-- FLAG_UNCHANGED: u8(2)
|
|
140
|
+
-- FLAG_DELTA: u8(0) + varint(upsertN) + [key+value ...] + varint(removeN) + [key ...]
|
|
141
|
+
function Map.deltaMap (
|
|
142
|
+
keyCodec: Codec<any>,
|
|
143
|
+
valueCodec: Codec<any>,
|
|
144
|
+
maxCount: number?
|
|
145
|
+
): Codec<{ [any]: any }>
|
|
146
|
+
local deltaId = Shared.allocDeltaId ()
|
|
147
|
+
|
|
148
|
+
local writeKey = keyCodec.write
|
|
149
|
+
local writeVal = valueCodec.write
|
|
150
|
+
local readKey = keyCodec.read
|
|
151
|
+
local readVal = valueCodec.read
|
|
152
|
+
|
|
153
|
+
local keySize = (keyCodec :: any)._size :: number?
|
|
154
|
+
local valSize = (valueCodec :: any)._size :: number?
|
|
155
|
+
local keyDWrite = (keyCodec :: any)._directWrite
|
|
156
|
+
local valDWrite = (valueCodec :: any)._directWrite
|
|
157
|
+
local keyDRead = (keyCodec :: any)._directRead
|
|
158
|
+
local valDRead = (valueCodec :: any)._directRead
|
|
159
|
+
local scratchDirect = keySize and valSize and keyDWrite and valDWrite
|
|
160
|
+
local entrySize = if keySize and valSize then keySize + valSize else nil
|
|
161
|
+
|
|
162
|
+
return table.freeze ({
|
|
163
|
+
_isDelta = true,
|
|
164
|
+
|
|
165
|
+
write = function (ch: ChannelState, value: { [any]: any }): ()
|
|
166
|
+
local scratch = acquireScratch ()
|
|
167
|
+
|
|
168
|
+
type EntryBounds = { keyOff: number, keyLen: number, valOff: number, valLen: number }
|
|
169
|
+
|
|
170
|
+
local entries = {} :: { EntryBounds }
|
|
171
|
+
local keyLookup = {} :: { [string]: number }
|
|
172
|
+
local entryCount = 0
|
|
173
|
+
|
|
174
|
+
if scratchDirect then
|
|
175
|
+
for k, v in value do
|
|
176
|
+
entryCount += 1
|
|
177
|
+
local c = scratch.cursor
|
|
178
|
+
alloc (scratch, entrySize :: number)
|
|
179
|
+
local b = scratch.buff
|
|
180
|
+
keyDWrite (b, c, k)
|
|
181
|
+
valDWrite (b, c + (keySize :: number), v)
|
|
182
|
+
scratch.cursor = c + (entrySize :: number)
|
|
183
|
+
|
|
184
|
+
entries[entryCount] = {
|
|
185
|
+
keyOff = c,
|
|
186
|
+
keyLen = keySize :: number,
|
|
187
|
+
valOff = c + (keySize :: number),
|
|
188
|
+
valLen = valSize :: number,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
local keyId = buffer.readstring (b, c, keySize :: number)
|
|
192
|
+
keyLookup[keyId] = entryCount
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
for k, v in value do
|
|
196
|
+
entryCount += 1
|
|
197
|
+
local keyStart = scratch.cursor
|
|
198
|
+
writeKey (scratch, k)
|
|
199
|
+
local keyEnd = scratch.cursor
|
|
200
|
+
writeVal (scratch, v)
|
|
201
|
+
local valEnd = scratch.cursor
|
|
202
|
+
|
|
203
|
+
entries[entryCount] = {
|
|
204
|
+
keyOff = keyStart,
|
|
205
|
+
keyLen = keyEnd - keyStart,
|
|
206
|
+
valOff = keyEnd,
|
|
207
|
+
valLen = valEnd - keyEnd,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
local keyId = buffer.readstring (scratch.buff, keyStart, keyEnd - keyStart)
|
|
211
|
+
keyLookup[keyId] = entryCount
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
local scratchTotal = scratch.cursor
|
|
216
|
+
|
|
217
|
+
type DeltaMapCache = {
|
|
218
|
+
raw: buffer,
|
|
219
|
+
entries: { EntryBounds },
|
|
220
|
+
keyLookup: { [string]: number },
|
|
221
|
+
count: number,
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
local cache = ch.deltas[deltaId] :: DeltaMapCache?
|
|
225
|
+
|
|
226
|
+
if not cache then
|
|
227
|
+
alloc (ch, 1)
|
|
228
|
+
buffer.writeu8 (ch.buff, ch.cursor, FLAG_FULL)
|
|
229
|
+
ch.cursor += 1
|
|
230
|
+
Varint.write (ch, entryCount)
|
|
231
|
+
|
|
232
|
+
if scratchTotal > 0 then
|
|
233
|
+
alloc (ch, scratchTotal)
|
|
234
|
+
buffer.copy (ch.buff, ch.cursor, scratch.buff, 0, scratchTotal)
|
|
235
|
+
ch.cursor += scratchTotal
|
|
236
|
+
end
|
|
237
|
+
else
|
|
238
|
+
local cacheRaw = cache.raw
|
|
239
|
+
local cacheEntries = cache.entries
|
|
240
|
+
local cacheLookup = cache.keyLookup
|
|
241
|
+
|
|
242
|
+
local upsertKeys = {} :: { number }
|
|
243
|
+
local upsertN = 0
|
|
244
|
+
|
|
245
|
+
for keyId, idx in keyLookup do
|
|
246
|
+
local oldIdx = cacheLookup[keyId]
|
|
247
|
+
if not oldIdx then
|
|
248
|
+
upsertN += 1
|
|
249
|
+
upsertKeys[upsertN] = idx
|
|
250
|
+
else
|
|
251
|
+
local newE = entries[idx]
|
|
252
|
+
local oldE = cacheEntries[oldIdx]
|
|
253
|
+
if
|
|
254
|
+
newE.valLen ~= oldE.valLen
|
|
255
|
+
or not rangeEqual (
|
|
256
|
+
scratch.buff,
|
|
257
|
+
newE.valOff,
|
|
258
|
+
cacheRaw,
|
|
259
|
+
oldE.valOff,
|
|
260
|
+
newE.valLen
|
|
261
|
+
)
|
|
262
|
+
then
|
|
263
|
+
upsertN += 1
|
|
264
|
+
upsertKeys[upsertN] = idx
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
local removeKeyIds = {} :: { string }
|
|
270
|
+
local removeN = 0
|
|
271
|
+
|
|
272
|
+
for keyId in cacheLookup do
|
|
273
|
+
if not keyLookup[keyId] then
|
|
274
|
+
removeN += 1
|
|
275
|
+
removeKeyIds[removeN] = keyId
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
if upsertN == 0 and removeN == 0 then
|
|
280
|
+
alloc (ch, 1)
|
|
281
|
+
buffer.writeu8 (ch.buff, ch.cursor, FLAG_UNCHANGED)
|
|
282
|
+
ch.cursor += 1
|
|
283
|
+
releaseScratch ()
|
|
284
|
+
return
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
alloc (ch, 1)
|
|
288
|
+
buffer.writeu8 (ch.buff, ch.cursor, FLAG_DELTA)
|
|
289
|
+
ch.cursor += 1
|
|
290
|
+
|
|
291
|
+
Varint.write (ch, upsertN)
|
|
292
|
+
for i = 1, upsertN do
|
|
293
|
+
local e = entries[upsertKeys[i]]
|
|
294
|
+
local totalLen = e.keyLen + e.valLen
|
|
295
|
+
alloc (ch, totalLen)
|
|
296
|
+
buffer.copy (ch.buff, ch.cursor, scratch.buff, e.keyOff, totalLen)
|
|
297
|
+
ch.cursor += totalLen
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
Varint.write (ch, removeN)
|
|
301
|
+
for i = 1, removeN do
|
|
302
|
+
local keyId = removeKeyIds[i]
|
|
303
|
+
local oldIdx = cacheLookup[keyId]
|
|
304
|
+
local oldE = cacheEntries[oldIdx]
|
|
305
|
+
alloc (ch, oldE.keyLen)
|
|
306
|
+
buffer.copy (ch.buff, ch.cursor, cacheRaw, oldE.keyOff, oldE.keyLen)
|
|
307
|
+
ch.cursor += oldE.keyLen
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
local rawBuf = buffer.create (scratchTotal)
|
|
312
|
+
if scratchTotal > 0 then
|
|
313
|
+
buffer.copy (rawBuf, 0, scratch.buff, 0, scratchTotal)
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
ch.deltas[deltaId] = {
|
|
317
|
+
raw = rawBuf,
|
|
318
|
+
entries = entries,
|
|
319
|
+
keyLookup = keyLookup,
|
|
320
|
+
count = entryCount,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
releaseScratch ()
|
|
324
|
+
end,
|
|
325
|
+
|
|
326
|
+
read = function (src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
|
|
327
|
+
local flag = buffer.readu8 (src, pos)
|
|
328
|
+
local absPos = pos + 1
|
|
329
|
+
|
|
330
|
+
if flag == FLAG_UNCHANGED then
|
|
331
|
+
local cache = Baseline.getCache (deltaId)
|
|
332
|
+
return if cache then table.clone (cache) else {}, 1
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
if flag == FLAG_FULL then
|
|
336
|
+
local count, lenBytes = Varint.read (src, absPos)
|
|
337
|
+
absPos += lenBytes
|
|
338
|
+
|
|
339
|
+
if maxCount and count > maxCount then
|
|
340
|
+
error (`[Lync] Map count {count} exceeds max {maxCount}`)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
local result = {}
|
|
344
|
+
|
|
345
|
+
if keyDRead and valDRead and keySize and valSize then
|
|
346
|
+
for _ = 1, count do
|
|
347
|
+
local k = keyDRead (src, absPos)
|
|
348
|
+
local v = valDRead (src, absPos + keySize)
|
|
349
|
+
result[k] = v
|
|
350
|
+
absPos += entrySize :: number
|
|
351
|
+
end
|
|
352
|
+
else
|
|
353
|
+
for _ = 1, count do
|
|
354
|
+
local k, kn = readKey (src, absPos, refs)
|
|
355
|
+
absPos += kn
|
|
356
|
+
local v, vn = readVal (src, absPos, refs)
|
|
357
|
+
absPos += vn
|
|
358
|
+
result[k] = v
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
Baseline.setCache (deltaId, result)
|
|
363
|
+
return result, absPos - pos
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
local cache = Baseline.getCache (deltaId) :: { [any]: any }?
|
|
367
|
+
local result = if cache then table.clone (cache) else {}
|
|
368
|
+
|
|
369
|
+
local upsertN, unBytes = Varint.read (src, absPos)
|
|
370
|
+
absPos += unBytes
|
|
371
|
+
|
|
372
|
+
for _ = 1, upsertN do
|
|
373
|
+
local k, kn = readKey (src, absPos, refs)
|
|
374
|
+
absPos += kn
|
|
375
|
+
local v, vn = readVal (src, absPos, refs)
|
|
376
|
+
absPos += vn
|
|
377
|
+
result[k] = v
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
local removeN, rmBytes = Varint.read (src, absPos)
|
|
381
|
+
absPos += rmBytes
|
|
382
|
+
|
|
383
|
+
for _ = 1, removeN do
|
|
384
|
+
local k, kn = readKey (src, absPos, refs)
|
|
385
|
+
absPos += kn
|
|
386
|
+
result[k] = nil
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
Baseline.setCache (deltaId, result)
|
|
390
|
+
return result, absPos - pos
|
|
391
|
+
end,
|
|
392
|
+
})
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
return table.freeze (Map)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Optional codec. 1 byte flag, value only if present.
|
|
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 Optional = {}
|
|
15
|
+
|
|
16
|
+
function Optional.optional (inner: Codec<any>): Codec<any>
|
|
17
|
+
local innerWrite = inner.write
|
|
18
|
+
local innerRead = inner.read
|
|
19
|
+
|
|
20
|
+
return table.freeze ({
|
|
21
|
+
write = function (ch: ChannelState, value: any): ()
|
|
22
|
+
local c = ch.cursor
|
|
23
|
+
if c + 1 > ch.size then
|
|
24
|
+
alloc (ch, 1)
|
|
25
|
+
end
|
|
26
|
+
if value == nil then
|
|
27
|
+
buffer.writeu8 (ch.buff, c, 0)
|
|
28
|
+
ch.cursor = c + 1
|
|
29
|
+
else
|
|
30
|
+
buffer.writeu8 (ch.buff, c, 1)
|
|
31
|
+
ch.cursor = c + 1
|
|
32
|
+
innerWrite (ch, value)
|
|
33
|
+
end
|
|
34
|
+
end,
|
|
35
|
+
read = function (src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
36
|
+
local flag = buffer.readu8 (src, pos)
|
|
37
|
+
if flag == 0 then
|
|
38
|
+
return nil, 1
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
local value, n = innerRead (src, pos + 1, refs)
|
|
42
|
+
return value, 1 + n
|
|
43
|
+
end,
|
|
44
|
+
})
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
return table.freeze (Optional)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Shared helpers for composite codecs: delta flags, scratch pool, struct field extraction, bool packing.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
|
|
8
|
+
type ChannelState = any
|
|
9
|
+
|
|
10
|
+
-- Constants --------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
local band = bit32.band
|
|
13
|
+
local bor = bit32.bor
|
|
14
|
+
local lshift = bit32.lshift
|
|
15
|
+
local ceil = math.ceil
|
|
16
|
+
local min = math.min
|
|
17
|
+
|
|
18
|
+
local FLAG_DELTA = 0
|
|
19
|
+
local FLAG_FULL = 1
|
|
20
|
+
local FLAG_UNCHANGED = 2
|
|
21
|
+
|
|
22
|
+
-- State ------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
local _nextDeltaId = 1
|
|
25
|
+
local _scratchStack = {} :: { ChannelState }
|
|
26
|
+
local _scratchDepth = 0
|
|
27
|
+
|
|
28
|
+
-- Public -----------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
local Shared = {
|
|
31
|
+
FLAG_DELTA = FLAG_DELTA,
|
|
32
|
+
FLAG_FULL = FLAG_FULL,
|
|
33
|
+
FLAG_UNCHANGED = FLAG_UNCHANGED,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Shared.allocDeltaId (): number
|
|
37
|
+
local id = _nextDeltaId
|
|
38
|
+
_nextDeltaId += 1
|
|
39
|
+
return id
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
function Shared.acquireScratch (): ChannelState
|
|
43
|
+
_scratchDepth += 1
|
|
44
|
+
local scratch = _scratchStack[_scratchDepth]
|
|
45
|
+
if not scratch then
|
|
46
|
+
scratch = Channel.create ()
|
|
47
|
+
_scratchStack[_scratchDepth] = scratch
|
|
48
|
+
end
|
|
49
|
+
scratch.cursor = 0
|
|
50
|
+
table.clear (scratch.refs)
|
|
51
|
+
return scratch
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
function Shared.releaseScratch (): ()
|
|
55
|
+
_scratchDepth -= 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
function Shared.rangeEqual (a: buffer, offA: number, b: buffer, offB: number, len: number): boolean
|
|
59
|
+
local aligned = band (len, 0xFFFFFFFC)
|
|
60
|
+
for i = 0, aligned - 4, 4 do
|
|
61
|
+
if buffer.readu32 (a, offA + i) ~= buffer.readu32 (b, offB + i) then
|
|
62
|
+
return false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
for i = aligned, len - 1 do
|
|
67
|
+
if buffer.readu8 (a, offA + i) ~= buffer.readu8 (b, offB + i) then
|
|
68
|
+
return false
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
return true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
function Shared.extractFields (schema: { [string]: any }): ({ string }, { any }, { string })
|
|
76
|
+
local dataKeys = {} :: { string }
|
|
77
|
+
local dataCodecs = {} :: { any }
|
|
78
|
+
local boolKeys = {} :: { string }
|
|
79
|
+
|
|
80
|
+
for key, codec in schema do
|
|
81
|
+
if codec._isBool then
|
|
82
|
+
table.insert (boolKeys, key)
|
|
83
|
+
else
|
|
84
|
+
table.insert (dataKeys, key)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
table.sort (dataKeys)
|
|
89
|
+
table.sort (boolKeys)
|
|
90
|
+
|
|
91
|
+
if #dataKeys == 0 and #boolKeys == 0 then
|
|
92
|
+
error ("[Lync] Struct requires at least one field")
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
for i = 1, #dataKeys do
|
|
96
|
+
dataCodecs[i] = schema[dataKeys[i]]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
table.freeze (dataKeys)
|
|
100
|
+
table.freeze (dataCodecs)
|
|
101
|
+
table.freeze (boolKeys)
|
|
102
|
+
|
|
103
|
+
return dataKeys, dataCodecs, boolKeys
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
function Shared.packBools (ch: ChannelState, boolKeys: { string }, boolCount: number, value: any): ()
|
|
107
|
+
local boolByteCount = ceil (boolCount / 8)
|
|
108
|
+
alloc (ch, boolByteCount)
|
|
109
|
+
local b = ch.buff
|
|
110
|
+
local c = ch.cursor
|
|
111
|
+
|
|
112
|
+
for byteIdx = 0, boolByteCount - 1 do
|
|
113
|
+
local packed = 0
|
|
114
|
+
local base = byteIdx * 8
|
|
115
|
+
local limit = min (8, boolCount - base)
|
|
116
|
+
|
|
117
|
+
for bit = 0, limit - 1 do
|
|
118
|
+
if value[boolKeys[base + bit + 1]] then
|
|
119
|
+
packed = bor (packed, lshift (1, bit))
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
buffer.writeu8 (b, c + byteIdx, packed)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
ch.cursor = c + boolByteCount
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
function Shared.unpackBools (
|
|
130
|
+
src: buffer,
|
|
131
|
+
pos: number,
|
|
132
|
+
boolKeys: { string },
|
|
133
|
+
boolCount: number,
|
|
134
|
+
result: any
|
|
135
|
+
): number
|
|
136
|
+
local boolByteCount = ceil (boolCount / 8)
|
|
137
|
+
|
|
138
|
+
for byteIdx = 0, boolByteCount - 1 do
|
|
139
|
+
local byte = buffer.readu8 (src, pos + byteIdx)
|
|
140
|
+
local base = byteIdx * 8
|
|
141
|
+
local limit = min (8, boolCount - base)
|
|
142
|
+
|
|
143
|
+
for bit = 0, limit - 1 do
|
|
144
|
+
result[boolKeys[base + bit + 1]] = band (byte, lshift (1, bit)) ~= 0
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
return boolByteCount
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
return table.freeze (Shared)
|