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