@axpecter/lync 2.3.1 → 2.3.3
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 +29 -19
- package/package.json +1 -1
- package/src/Types.luau +12 -6
- package/src/api/Packet.luau +93 -35
- package/src/api/Query.luau +2 -2
- package/src/api/Signal.luau +6 -1
- package/src/codec/Base.luau +8 -1
- package/src/codec/composite/Array.luau +293 -17
- package/src/codec/composite/Map.luau +345 -40
- package/src/codec/composite/Optional.luau +4 -0
- package/src/codec/composite/Shared.luau +101 -102
- package/src/codec/composite/Struct.luau +20 -10
- package/src/codec/composite/Tagged.luau +8 -0
- package/src/codec/composite/Tuple.luau +12 -1
- package/src/codec/datatype/Buffer.luau +1 -3
- package/src/codec/datatype/CFrame.luau +6 -106
- package/src/codec/meta/Bitfield.luau +14 -1
- package/src/codec/meta/DeltaScalar.luau +390 -0
- package/src/codec/primitive/Varint.luau +13 -7
- package/src/codec/primitive/Zint.luau +99 -0
- package/src/index.d.ts +46 -0
- package/src/init.luau +7 -0
- package/src/internal/Channel.luau +187 -58
- package/src/internal/Pool.luau +4 -3
- package/src/transport/Reader.luau +19 -8
- package/src/transport/Server.luau +35 -13
- package/src/util/Buffer.luau +2 -4
- package/src/util/Quantize.luau +27 -3
- package/src/util/Quat.luau +124 -0
|
@@ -9,20 +9,43 @@ local Shared = require(script.Parent.Parent.composite.Shared)
|
|
|
9
9
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
10
10
|
local Varint = require(script.Parent.Parent.primitive.Varint)
|
|
11
11
|
|
|
12
|
+
-- Constants --------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
-- deltaMap wire flags. Distinct from Shared's struct/array flag spaces.
|
|
15
|
+
local DM_UNCHANGED = 0
|
|
16
|
+
local DM_FULL = 1
|
|
17
|
+
local DM_DIFF = 2
|
|
18
|
+
|
|
12
19
|
-- State ------------------------------------------------------------------
|
|
13
20
|
|
|
14
21
|
--[[
|
|
15
22
|
Depth-stacked key scratch. A nested Map.write (Map<K, Map<K2, V>>) would
|
|
16
23
|
clobber a single shared array mid-iteration; the stack gives each frame
|
|
17
|
-
its own slot.
|
|
24
|
+
its own slot. _keyTails parallels _keyStacks because `#keys` after
|
|
25
|
+
`keys[count] = k` is undefined when count < the prior count.
|
|
18
26
|
]]
|
|
19
27
|
local _keyStacks: { { any } } = {}
|
|
28
|
+
local _keyTails: { number } = {}
|
|
20
29
|
local _keyDepth = 0
|
|
21
30
|
|
|
22
31
|
-- Private ----------------------------------------------------------------
|
|
23
32
|
|
|
24
|
-
local
|
|
33
|
+
local ensure = Base.ensure
|
|
25
34
|
local allocDeltaId = Shared.allocDeltaId
|
|
35
|
+
local acquireScratch = Shared.acquireScratch
|
|
36
|
+
local releaseScratch = Shared.releaseScratch
|
|
37
|
+
local rangeEqual = Shared.rangeEqual
|
|
38
|
+
local copyToBuf = Shared.copyToBuf
|
|
39
|
+
local containsDelta = Shared.containsDelta
|
|
40
|
+
local enforceMaxCount = Shared.enforceMaxCount
|
|
41
|
+
local writeu8 = buffer.writeu8
|
|
42
|
+
local readu8 = buffer.readu8
|
|
43
|
+
local bufCopy = buffer.copy
|
|
44
|
+
local bufLen = buffer.len
|
|
45
|
+
local varintRead = Varint.read
|
|
46
|
+
local varintWrite = Varint.write
|
|
47
|
+
|
|
48
|
+
type DeltaMapEntry = { kBuf: buffer, kLen: number, vBuf: buffer, vLen: number }
|
|
26
49
|
|
|
27
50
|
local function acquireKeys(): { any }
|
|
28
51
|
_keyDepth += 1
|
|
@@ -44,6 +67,34 @@ local function tostringLess(a: any, b: any): boolean
|
|
|
44
67
|
return tostring(a) < tostring(b)
|
|
45
68
|
end
|
|
46
69
|
|
|
70
|
+
local function sortKeys(keys: { any }, canSortDirect: boolean): ()
|
|
71
|
+
if canSortDirect then
|
|
72
|
+
table.sort(keys :: { any })
|
|
73
|
+
else
|
|
74
|
+
table.sort(keys, tostringLess)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
local function collectAndSortKeys(value: { [any]: any }, canSortDirect: boolean): ({ any }, number)
|
|
79
|
+
local keys = acquireKeys()
|
|
80
|
+
local prevTail = _keyTails[_keyDepth] or 0
|
|
81
|
+
local count = 0
|
|
82
|
+
for k in value do
|
|
83
|
+
count += 1
|
|
84
|
+
keys[count] = k
|
|
85
|
+
end
|
|
86
|
+
if count < prevTail then
|
|
87
|
+
for i = count + 1, prevTail do
|
|
88
|
+
keys[i] = nil
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
_keyTails[_keyDepth] = count
|
|
92
|
+
if count > 1 then
|
|
93
|
+
sortKeys(keys, canSortDirect)
|
|
94
|
+
end
|
|
95
|
+
return keys, count
|
|
96
|
+
end
|
|
97
|
+
|
|
47
98
|
-- Public -----------------------------------------------------------------
|
|
48
99
|
|
|
49
100
|
local Map = {}
|
|
@@ -56,6 +107,7 @@ function Map.map(
|
|
|
56
107
|
local fixedKey = keyCodec._size
|
|
57
108
|
local fixedValue = valueCodec._size
|
|
58
109
|
local hasUnknown = keyCodec._hasUnknown or valueCodec._hasUnknown
|
|
110
|
+
local hasDelta = containsDelta(keyCodec) or containsDelta(valueCodec)
|
|
59
111
|
|
|
60
112
|
--[[
|
|
61
113
|
Numbers and strings sort by `<` directly; everything else (Instance,
|
|
@@ -82,63 +134,42 @@ function Map.map(
|
|
|
82
134
|
Log.error(`expected table, got {typeof(value)}`)
|
|
83
135
|
end
|
|
84
136
|
|
|
85
|
-
local keys =
|
|
86
|
-
local count = 0
|
|
87
|
-
for k in value do
|
|
88
|
-
count += 1
|
|
89
|
-
keys[count] = k
|
|
90
|
-
end
|
|
137
|
+
local keys, count = collectAndSortKeys(value, canSortDirect)
|
|
91
138
|
|
|
92
|
-
|
|
93
|
-
local prevTail = #keys
|
|
94
|
-
for i = count + 1, prevTail do
|
|
95
|
-
keys[i] = nil
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
Varint.write(ch, count)
|
|
139
|
+
varintWrite(ch, count)
|
|
99
140
|
if count == 0 then
|
|
100
141
|
releaseKeys()
|
|
101
142
|
return
|
|
102
143
|
end
|
|
103
144
|
|
|
104
|
-
if canSortDirect then
|
|
105
|
-
table.sort(keys :: { any })
|
|
106
|
-
else
|
|
107
|
-
table.sort(keys, tostringLess)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
145
|
-- Single alloc when both halves are fixed-size.
|
|
111
146
|
if fixedKey and fixedValue then
|
|
112
|
-
|
|
113
|
-
local cursor = ch.cursor
|
|
114
|
-
if cursor + count * entrySize > ch.size then
|
|
115
|
-
alloc(ch, count * entrySize)
|
|
116
|
-
end
|
|
147
|
+
ensure(ch, count * (fixedKey + fixedValue))
|
|
117
148
|
end
|
|
118
149
|
|
|
119
150
|
for i = 1, count do
|
|
120
151
|
local k = keys[i]
|
|
121
152
|
keyCodec.write(ch, k)
|
|
122
153
|
valueCodec.write(ch, value[k])
|
|
123
|
-
keys[i] = nil
|
|
124
154
|
end
|
|
125
155
|
releaseKeys()
|
|
126
156
|
end,
|
|
127
157
|
|
|
128
158
|
read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
|
|
129
|
-
local count, lenBytes =
|
|
130
|
-
|
|
131
|
-
Log.error(`count {count} exceeds max {maxCount}`)
|
|
132
|
-
end
|
|
159
|
+
local count, lenBytes = varintRead(src, pos)
|
|
160
|
+
enforceMaxCount(count, maxCount)
|
|
133
161
|
|
|
134
162
|
-- Fixed-size: reject corrupt counts before per-entry decode.
|
|
135
163
|
if fixedKey and fixedValue and count > 0 then
|
|
136
|
-
local
|
|
137
|
-
|
|
138
|
-
local payloadBytes = count * entrySize
|
|
139
|
-
if payloadBytes > buffer.len(src) - dataStart then
|
|
164
|
+
local payloadBytes = count * (fixedKey + fixedValue)
|
|
165
|
+
if payloadBytes > bufLen(src) - (pos + lenBytes) then
|
|
140
166
|
Log.error(`payload {payloadBytes}B exceeds remaining buffer`)
|
|
141
167
|
end
|
|
168
|
+
elseif count > 0 then
|
|
169
|
+
-- Variable-size: each pair is at least 2 bytes (key + value).
|
|
170
|
+
if count * 2 > bufLen(src) - (pos + lenBytes) then
|
|
171
|
+
Log.error(`map count {count} exceeds remaining buffer`)
|
|
172
|
+
end
|
|
142
173
|
end
|
|
143
174
|
|
|
144
175
|
local result: { [any]: any } = {}
|
|
@@ -162,19 +193,293 @@ function Map.map(
|
|
|
162
193
|
if hasUnknown then
|
|
163
194
|
codec._hasUnknown = true
|
|
164
195
|
end
|
|
196
|
+
if hasDelta then
|
|
197
|
+
codec._hasDelta = true
|
|
198
|
+
end
|
|
165
199
|
return table.freeze(codec)
|
|
166
200
|
end
|
|
167
201
|
|
|
202
|
+
--[[
|
|
203
|
+
Per-key deltaMap. Wire:
|
|
204
|
+
[0] UNCHANGED (1 byte)
|
|
205
|
+
[1] FULL <count varint> <(k,v) pairs ...>
|
|
206
|
+
[2] DIFF <removeCount varint> <removed key bytes ...>
|
|
207
|
+
<changeCount varint> <(k,v) pair bytes ...>
|
|
208
|
+
|
|
209
|
+
Cache: per-key encoded value bytes for byte-equality diffing plus per-key
|
|
210
|
+
encoded key bytes so removed-key emission doesn't re-encode the key codec
|
|
211
|
+
for keys that no longer live in the map.
|
|
212
|
+
]]
|
|
168
213
|
function Map.deltaMap(
|
|
169
214
|
keyCodec: Types.InternalCodec<any>,
|
|
170
215
|
valueCodec: Types.InternalCodec<any>,
|
|
171
216
|
maxCount: number?
|
|
172
217
|
): Types.InternalCodec<any>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
)
|
|
218
|
+
-- Inner delta would chain-diff across pairs within a frame, not across frames.
|
|
219
|
+
if containsDelta(keyCodec) then
|
|
220
|
+
Log.error("deltaMap key codec cannot contain delta state")
|
|
221
|
+
end
|
|
222
|
+
if containsDelta(valueCodec) then
|
|
223
|
+
Log.error("deltaMap value codec cannot contain delta state")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
local deltaId = allocDeltaId()
|
|
227
|
+
local typeCheck = keyCodec._typeCheck
|
|
228
|
+
local canSortDirect = typeCheck == "number" or typeCheck == "string"
|
|
229
|
+
local inner = Map.map(keyCodec, valueCodec, maxCount)
|
|
230
|
+
|
|
231
|
+
--[[
|
|
232
|
+
Encode each (key, value) pair into `scratch` and capture per-pair byte
|
|
233
|
+
ranges. Returns the sorted-keys list plus four parallel arrays of
|
|
234
|
+
offset/length so the diff path can compare and emit individual entries
|
|
235
|
+
without re-encoding.
|
|
236
|
+
]]
|
|
237
|
+
local function encodePerKey(
|
|
238
|
+
scratch: Types.ChannelState,
|
|
239
|
+
value: { [any]: any }
|
|
240
|
+
): ({ any }, { number }, { number }, { number }, { number })
|
|
241
|
+
local keys, count = collectAndSortKeys(value, canSortDirect)
|
|
242
|
+
local kOff = table.create(count)
|
|
243
|
+
local kLen = table.create(count)
|
|
244
|
+
local vOff = table.create(count)
|
|
245
|
+
local vLen = table.create(count)
|
|
246
|
+
|
|
247
|
+
for i = 1, count do
|
|
248
|
+
local k = keys[i]
|
|
249
|
+
local before = scratch.cursor
|
|
250
|
+
keyCodec.write(scratch, k)
|
|
251
|
+
local mid = scratch.cursor
|
|
252
|
+
kOff[i] = before
|
|
253
|
+
kLen[i] = mid - before
|
|
254
|
+
valueCodec.write(scratch, value[k])
|
|
255
|
+
vOff[i] = mid
|
|
256
|
+
vLen[i] = scratch.cursor - mid
|
|
257
|
+
end
|
|
258
|
+
return keys, kOff, kLen, vOff, vLen
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
return table.freeze({
|
|
262
|
+
_isDelta = true,
|
|
263
|
+
_isMap = true,
|
|
264
|
+
_keyCodec = keyCodec,
|
|
265
|
+
_valueCodec = valueCodec,
|
|
266
|
+
_maxCount = maxCount,
|
|
267
|
+
|
|
268
|
+
write = function(ch: Types.ChannelState, value: { [any]: any }): ()
|
|
269
|
+
if typeof(value) ~= "table" then
|
|
270
|
+
Log.error(`expected table, got {typeof(value)}`)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
local cache = ch.deltas[deltaId] :: any
|
|
274
|
+
local scratch = acquireScratch()
|
|
275
|
+
local keys, kOff, kLen, vOff, vLen = encodePerKey(scratch, value)
|
|
276
|
+
local count = #keys
|
|
277
|
+
local scratchBuf = scratch.buff
|
|
278
|
+
|
|
279
|
+
-- First frame: emit FULL. Per-key scratch bytes ARE the inner map payload.
|
|
280
|
+
if not cache then
|
|
281
|
+
ensure(ch, 1)
|
|
282
|
+
writeu8(ch.buff, ch.cursor, DM_FULL)
|
|
283
|
+
ch.cursor += 1
|
|
284
|
+
varintWrite(ch, count)
|
|
285
|
+
local payloadLen = scratch.cursor
|
|
286
|
+
ensure(ch, payloadLen)
|
|
287
|
+
bufCopy(ch.buff, ch.cursor, scratchBuf, 0, payloadLen)
|
|
288
|
+
ch.cursor += payloadLen
|
|
289
|
+
|
|
290
|
+
local perKey: { [any]: DeltaMapEntry } = {}
|
|
291
|
+
for i = 1, count do
|
|
292
|
+
local kbLen = kLen[i]
|
|
293
|
+
local vbLen = vLen[i]
|
|
294
|
+
perKey[keys[i]] = {
|
|
295
|
+
kBuf = copyToBuf(nil, scratchBuf, kOff[i], kbLen),
|
|
296
|
+
kLen = kbLen,
|
|
297
|
+
vBuf = copyToBuf(nil, scratchBuf, vOff[i], vbLen),
|
|
298
|
+
vLen = vbLen,
|
|
299
|
+
}
|
|
300
|
+
end
|
|
301
|
+
ch.deltas[deltaId] = { perKey = perKey } :: any
|
|
302
|
+
releaseKeys()
|
|
303
|
+
releaseScratch()
|
|
304
|
+
return
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
local cachedPerKey = cache.perKey :: { [any]: DeltaMapEntry }
|
|
308
|
+
|
|
309
|
+
local seen: { [any]: boolean } = {}
|
|
310
|
+
local changed: { number } = {}
|
|
311
|
+
local changedCount = 0
|
|
312
|
+
for i = 1, count do
|
|
313
|
+
local k = keys[i]
|
|
314
|
+
seen[k] = true
|
|
315
|
+
local entry = cachedPerKey[k]
|
|
316
|
+
local newLen = vLen[i]
|
|
317
|
+
if
|
|
318
|
+
not entry
|
|
319
|
+
or newLen ~= entry.vLen
|
|
320
|
+
or not rangeEqual(scratchBuf, vOff[i], entry.vBuf, 0, newLen)
|
|
321
|
+
then
|
|
322
|
+
changedCount += 1
|
|
323
|
+
changed[changedCount] = i
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
local removed: { any } = {}
|
|
328
|
+
local removedCount = 0
|
|
329
|
+
for k in cachedPerKey do
|
|
330
|
+
if not seen[k] then
|
|
331
|
+
removedCount += 1
|
|
332
|
+
removed[removedCount] = k
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
if removedCount == 0 and changedCount == 0 then
|
|
337
|
+
ensure(ch, 1)
|
|
338
|
+
writeu8(ch.buff, ch.cursor, DM_UNCHANGED)
|
|
339
|
+
ch.cursor += 1
|
|
340
|
+
releaseKeys()
|
|
341
|
+
releaseScratch()
|
|
342
|
+
return
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
-- Sort removed keys so the XOR baseline can collapse identical drops.
|
|
346
|
+
sortKeys(removed, canSortDirect)
|
|
347
|
+
|
|
348
|
+
ensure(ch, 1)
|
|
349
|
+
writeu8(ch.buff, ch.cursor, DM_DIFF)
|
|
350
|
+
ch.cursor += 1
|
|
351
|
+
|
|
352
|
+
varintWrite(ch, removedCount)
|
|
353
|
+
for i = 1, removedCount do
|
|
354
|
+
local entry = cachedPerKey[removed[i]]
|
|
355
|
+
local n = entry.kLen
|
|
356
|
+
ensure(ch, n)
|
|
357
|
+
bufCopy(ch.buff, ch.cursor, entry.kBuf, 0, n)
|
|
358
|
+
ch.cursor += n
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
varintWrite(ch, changedCount)
|
|
362
|
+
for i = 1, changedCount do
|
|
363
|
+
local idx = changed[i]
|
|
364
|
+
local kbLen = kLen[idx]
|
|
365
|
+
local vbLen = vLen[idx]
|
|
366
|
+
ensure(ch, kbLen + vbLen)
|
|
367
|
+
bufCopy(ch.buff, ch.cursor, scratchBuf, kOff[idx], kbLen)
|
|
368
|
+
bufCopy(ch.buff, ch.cursor + kbLen, scratchBuf, vOff[idx], vbLen)
|
|
369
|
+
ch.cursor += kbLen + vbLen
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
-- Update cache: drop removed, refresh changed, leave untouched in place.
|
|
373
|
+
for i = 1, removedCount do
|
|
374
|
+
cachedPerKey[removed[i]] = nil
|
|
375
|
+
end
|
|
376
|
+
for i = 1, changedCount do
|
|
377
|
+
local idx = changed[i]
|
|
378
|
+
local k = keys[idx]
|
|
379
|
+
local kbLen = kLen[idx]
|
|
380
|
+
local vbLen = vLen[idx]
|
|
381
|
+
local entry = cachedPerKey[k]
|
|
382
|
+
if entry then
|
|
383
|
+
entry.kBuf = copyToBuf(entry.kBuf, scratchBuf, kOff[idx], kbLen)
|
|
384
|
+
entry.kLen = kbLen
|
|
385
|
+
entry.vBuf = copyToBuf(entry.vBuf, scratchBuf, vOff[idx], vbLen)
|
|
386
|
+
entry.vLen = vbLen
|
|
387
|
+
else
|
|
388
|
+
cachedPerKey[k] = {
|
|
389
|
+
kBuf = copyToBuf(nil, scratchBuf, kOff[idx], kbLen),
|
|
390
|
+
kLen = kbLen,
|
|
391
|
+
vBuf = copyToBuf(nil, scratchBuf, vOff[idx], vbLen),
|
|
392
|
+
vLen = vbLen,
|
|
393
|
+
}
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
releaseKeys()
|
|
398
|
+
releaseScratch()
|
|
399
|
+
end,
|
|
400
|
+
|
|
401
|
+
read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
|
|
402
|
+
if pos >= bufLen(src) then
|
|
403
|
+
Log.error("truncated deltaMap header")
|
|
404
|
+
end
|
|
405
|
+
local flag = readu8(src, pos)
|
|
406
|
+
|
|
407
|
+
if flag == DM_UNCHANGED then
|
|
408
|
+
local cached = Baseline.getCache(deltaId)
|
|
409
|
+
if cached == nil then
|
|
410
|
+
Log.error("UNCHANGED flag before any FULL frame; baseline missing")
|
|
411
|
+
end
|
|
412
|
+
return cached, 1
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
if flag == DM_FULL then
|
|
416
|
+
local value, consumed = inner.read(src, pos + 1, refs)
|
|
417
|
+
Baseline.setCache(deltaId, value)
|
|
418
|
+
return value, 1 + consumed
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
if flag == DM_DIFF then
|
|
422
|
+
local cached = Baseline.getCache(deltaId)
|
|
423
|
+
if cached == nil then
|
|
424
|
+
Log.error("deltaMap DIFF before any FULL frame; baseline missing")
|
|
425
|
+
end
|
|
426
|
+
local result: { [any]: any } = table.clone(cached)
|
|
427
|
+
local total = 1
|
|
428
|
+
|
|
429
|
+
local cachedSize = 0
|
|
430
|
+
for _ in cached do
|
|
431
|
+
cachedSize += 1
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
local removeCount, rcLen = varintRead(src, pos + total)
|
|
435
|
+
if rcLen == 0 then
|
|
436
|
+
Log.error("truncated deltaMap remove count")
|
|
437
|
+
end
|
|
438
|
+
enforceMaxCount(removeCount, maxCount, "removeCount")
|
|
439
|
+
if removeCount > cachedSize then
|
|
440
|
+
Log.error(`deltaMap removeCount {removeCount} exceeds cached size {cachedSize}`)
|
|
441
|
+
end
|
|
442
|
+
total += rcLen
|
|
443
|
+
for _ = 1, removeCount do
|
|
444
|
+
local k, kc = keyCodec.read(src, pos + total, refs)
|
|
445
|
+
if kc == 0 then
|
|
446
|
+
Log.error("truncated deltaMap removed key")
|
|
447
|
+
end
|
|
448
|
+
total += kc
|
|
449
|
+
result[k] = nil
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
local changeCount, ccLen = varintRead(src, pos + total)
|
|
453
|
+
if ccLen == 0 then
|
|
454
|
+
Log.error("truncated deltaMap change count")
|
|
455
|
+
end
|
|
456
|
+
enforceMaxCount(changeCount, maxCount, "changeCount")
|
|
457
|
+
-- Joint bound: post-DIFF size can't exceed maxCount either.
|
|
458
|
+
if maxCount and (cachedSize - removeCount + changeCount) > maxCount then
|
|
459
|
+
Log.error(`deltaMap DIFF would grow map past maxCount {maxCount}`)
|
|
460
|
+
end
|
|
461
|
+
total += ccLen
|
|
462
|
+
for _ = 1, changeCount do
|
|
463
|
+
local k, kc = keyCodec.read(src, pos + total, refs)
|
|
464
|
+
if kc == 0 then
|
|
465
|
+
Log.error("truncated deltaMap changed key")
|
|
466
|
+
end
|
|
467
|
+
total += kc
|
|
468
|
+
local v, vc = valueCodec.read(src, pos + total, refs)
|
|
469
|
+
if vc == 0 then
|
|
470
|
+
Log.error("truncated deltaMap changed value")
|
|
471
|
+
end
|
|
472
|
+
total += vc
|
|
473
|
+
result[k] = v
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
Baseline.setCache(deltaId, result)
|
|
477
|
+
return result, total
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
Log.error(`invalid deltaMap flag {flag}`)
|
|
481
|
+
end,
|
|
482
|
+
}) :: Types.InternalCodec<any>
|
|
178
483
|
end
|
|
179
484
|
|
|
180
485
|
return table.freeze(Map)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
6
6
|
local Log = require(script.Parent.Parent.Parent.util.Log)
|
|
7
|
+
local Shared = require(script.Parent.Parent.composite.Shared)
|
|
7
8
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
8
9
|
|
|
9
10
|
-- Private ----------------------------------------------------------------
|
|
@@ -44,6 +45,9 @@ function Optional.optional(inner: Types.InternalCodec<any>): Types.InternalCodec
|
|
|
44
45
|
if inner._hasUnknown then
|
|
45
46
|
codec._hasUnknown = true
|
|
46
47
|
end
|
|
48
|
+
if Shared.containsDelta(inner) then
|
|
49
|
+
codec._hasDelta = true
|
|
50
|
+
end
|
|
47
51
|
return table.freeze(codec)
|
|
48
52
|
end
|
|
49
53
|
|