@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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +302 -299
  3. package/Types.luau +215 -0
  4. package/api/Define.luau +154 -0
  5. package/api/Lifecycle.luau +119 -0
  6. package/api/Packet.luau +108 -0
  7. package/api/Query.luau +149 -0
  8. package/api/Set.luau +189 -0
  9. package/channel/Blocks.luau +124 -0
  10. package/channel/Group.luau +118 -0
  11. package/channel/Link.luau +135 -0
  12. package/channel/Outbox.luau +261 -0
  13. package/channel/Recipients.luau +129 -0
  14. package/channel/Transport.luau +138 -0
  15. package/codec/Base.luau +236 -0
  16. package/codec/Composites.luau +128 -0
  17. package/codec/Datatypes.luau +162 -0
  18. package/codec/Legality.luau +61 -0
  19. package/codec/Scalars.luau +72 -0
  20. package/codec/Text.luau +89 -0
  21. package/codec/init.luau +39 -0
  22. package/index.d.ts +285 -0
  23. package/init.luau +91 -0
  24. package/kernel/Bits.luau +16 -0
  25. package/kernel/Catalog.luau +121 -0
  26. package/kernel/Heap.luau +80 -0
  27. package/kernel/Listeners.luau +95 -0
  28. package/kernel/Log.luau +197 -0
  29. package/kernel/Tally.luau +128 -0
  30. package/kernel/Threads.luau +44 -0
  31. package/kernel/Wide.luau +267 -0
  32. package/package.json +15 -32
  33. package/runtime/Boundary.luau +180 -0
  34. package/runtime/Dispatch.luau +91 -0
  35. package/runtime/Handles.luau +114 -0
  36. package/runtime/Handshake.luau +90 -0
  37. package/runtime/Pending.luau +196 -0
  38. package/runtime/Router.luau +379 -0
  39. package/runtime/Session.luau +731 -0
  40. package/shape/Compile.luau +340 -0
  41. package/shape/Folds.luau +332 -0
  42. package/shape/Frames.luau +170 -0
  43. package/shape/Grid.luau +169 -0
  44. package/shape/Ir.luau +540 -0
  45. package/shape/Joins.luau +125 -0
  46. package/shape/Legality.luau +273 -0
  47. package/shape/Lower.luau +419 -0
  48. package/shape/Order.luau +64 -0
  49. package/shape/Regions.luau +113 -0
  50. package/shape/Rotation.luau +472 -0
  51. package/shape/Serial.luau +258 -0
  52. package/shape/Words.luau +89 -0
  53. package/shape/emit/Block.luau +453 -0
  54. package/shape/emit/Column.luau +338 -0
  55. package/shape/emit/Leaf.luau +462 -0
  56. package/shape/emit/Word.luau +234 -0
  57. package/shape/emit/init.luau +222 -0
  58. package/shape/init.luau +78 -0
  59. package/state/Apply.luau +330 -0
  60. package/state/Audience.luau +126 -0
  61. package/state/Catchup.luau +218 -0
  62. package/state/Coalesce.luau +43 -0
  63. package/state/Fieldset.luau +177 -0
  64. package/state/Newest.luau +339 -0
  65. package/state/Plan.luau +441 -0
  66. package/state/Sections.luau +468 -0
  67. package/state/Store.luau +188 -0
  68. package/state/Sync.luau +762 -0
  69. package/wire/Bytes.luau +118 -0
  70. package/wire/Clock.luau +37 -0
  71. package/wire/Protocol.luau +110 -0
  72. package/wire/Sequence.luau +40 -0
  73. package/wire/Varint.luau +124 -0
  74. package/src/Types.luau +0 -180
  75. package/src/api/Group.luau +0 -156
  76. package/src/api/Packet.luau +0 -319
  77. package/src/api/Query.luau +0 -338
  78. package/src/api/Scope.luau +0 -90
  79. package/src/api/Signal.luau +0 -188
  80. package/src/codec/Base.luau +0 -136
  81. package/src/codec/composite/Array.luau +0 -456
  82. package/src/codec/composite/Map.luau +0 -469
  83. package/src/codec/composite/Optional.luau +0 -54
  84. package/src/codec/composite/Shared.luau +0 -291
  85. package/src/codec/composite/Struct.luau +0 -494
  86. package/src/codec/composite/Tagged.luau +0 -103
  87. package/src/codec/composite/Tuple.luau +0 -143
  88. package/src/codec/datatype/Buffer.luau +0 -66
  89. package/src/codec/datatype/CFrame.luau +0 -109
  90. package/src/codec/datatype/Color.luau +0 -32
  91. package/src/codec/datatype/Instance.luau +0 -52
  92. package/src/codec/datatype/IntVector.luau +0 -39
  93. package/src/codec/datatype/NumberRange.luau +0 -39
  94. package/src/codec/datatype/Ray.luau +0 -36
  95. package/src/codec/datatype/Rect.luau +0 -31
  96. package/src/codec/datatype/Region.luau +0 -62
  97. package/src/codec/datatype/Sequence.luau +0 -184
  98. package/src/codec/datatype/String.luau +0 -94
  99. package/src/codec/datatype/UDim.luau +0 -46
  100. package/src/codec/datatype/Vector.luau +0 -130
  101. package/src/codec/meta/Auto.luau +0 -318
  102. package/src/codec/meta/Bitfield.luau +0 -147
  103. package/src/codec/meta/Custom.luau +0 -33
  104. package/src/codec/meta/DeltaScalar.luau +0 -390
  105. package/src/codec/meta/Enum.luau +0 -50
  106. package/src/codec/meta/Float.luau +0 -38
  107. package/src/codec/meta/Nothing.luau +0 -13
  108. package/src/codec/meta/Unknown.luau +0 -41
  109. package/src/codec/primitive/Bool.luau +0 -26
  110. package/src/codec/primitive/Float16.luau +0 -99
  111. package/src/codec/primitive/Int.luau +0 -59
  112. package/src/codec/primitive/Number.luau +0 -24
  113. package/src/codec/primitive/Signed.luau +0 -32
  114. package/src/codec/primitive/Varint.luau +0 -165
  115. package/src/codec/primitive/Zint.luau +0 -99
  116. package/src/index.d.ts +0 -423
  117. package/src/init.luau +0 -362
  118. package/src/internal/Baseline.luau +0 -57
  119. package/src/internal/Channel.luau +0 -390
  120. package/src/internal/Middleware.luau +0 -100
  121. package/src/internal/Pool.luau +0 -65
  122. package/src/internal/Registry.luau +0 -119
  123. package/src/internal/Transport.luau +0 -28
  124. package/src/transport/Bridge.luau +0 -110
  125. package/src/transport/Client.luau +0 -97
  126. package/src/transport/Gate.luau +0 -488
  127. package/src/transport/Reader.luau +0 -331
  128. package/src/transport/Server.luau +0 -211
  129. package/src/util/Array.luau +0 -18
  130. package/src/util/Buffer.luau +0 -90
  131. package/src/util/Constants.luau +0 -30
  132. package/src/util/Log.luau +0 -68
  133. package/src/util/Player.luau +0 -14
  134. package/src/util/Quantize.luau +0 -84
  135. package/src/util/Quat.luau +0 -124
@@ -1,136 +0,0 @@
1
- --!strict
2
- --!optimize 2
3
- -- Fixed-size codec factory + channel buffer-grow primitive.
4
-
5
- local Log = require(script.Parent.Parent.util.Log)
6
- local Types = require(script.Parent.Parent.Types)
7
-
8
- -- Constants --------------------------------------------------------------
9
-
10
- local DEFAULT_MAX = 262144
11
- local INITIAL_BUF = 1024
12
-
13
- -- State ------------------------------------------------------------------
14
-
15
- local _maxSize = DEFAULT_MAX
16
-
17
- -- Private ----------------------------------------------------------------
18
-
19
- local countlz = bit32.countlz
20
- local lshift = bit32.lshift
21
-
22
- --[[
23
- Grow `ch.buff` so cursor + bytes fits. New size is the next power of two
24
- >= needed, capped by _maxSize. Callers MUST re-read `ch.buff` after
25
- return: the buffer reference is replaced.
26
- ]]
27
- local function alloc(ch: Types.ChannelState, bytes: number): ()
28
- if bytes == 0 then
29
- return
30
- end
31
-
32
- local needed = ch.cursor + bytes
33
- if needed <= ch.size then
34
- return
35
- end
36
-
37
- if needed > _maxSize then
38
- local name = ch.currentPacket
39
- if name then
40
- Log.error(`packet "{name}" overflowed at {needed}B (max {_maxSize}B)`)
41
- end
42
- Log.error(`overflowed at {needed}B (max {_maxSize}B)`)
43
- end
44
-
45
- local newSize = lshift(1, 32 - countlz(needed - 1))
46
- local newBuff = buffer.create(newSize)
47
- buffer.copy(newBuff, 0, ch.buff, 0, ch.cursor)
48
- ch.buff = newBuff
49
- ch.size = newSize
50
- end
51
-
52
- -- Public -----------------------------------------------------------------
53
-
54
- local Base = {}
55
-
56
- Base.INITIAL_BUF = INITIAL_BUF
57
- Base.alloc = alloc
58
-
59
- -- Cheap inline guard hoisted into every codec that grows a channel directly.
60
- function Base.ensure(ch: Types.ChannelState, n: number): ()
61
- if ch.cursor + n > ch.size then
62
- alloc(ch, n)
63
- end
64
- end
65
-
66
- -- Floor is INITIAL_BUF: anything smaller errors on the very first write.
67
- function Base.setMaxSize(bytes: number): ()
68
- if bytes < INITIAL_BUF then
69
- Log.error(`bytes ({bytes}) must be >= {INITIAL_BUF}`)
70
- end
71
- _maxSize = bytes
72
- end
73
-
74
- function Base.reset(): ()
75
- _maxSize = DEFAULT_MAX
76
- end
77
-
78
- --[[
79
- Build a fixed-size codec. Exposes channel-level write/read (handles
80
- buffer growth + cursor) plus raw _directWrite/_directRead for composite
81
- fast paths that pre-size the channel themselves.
82
- ]]
83
- function Base.define<T>(
84
- size: number,
85
- directWrite: (b: buffer, offset: number, value: T) -> (),
86
- directRead: (b: buffer, offset: number) -> T,
87
- meta: { [string]: any }?
88
- ): Types.InternalCodec<T>
89
- local codec: { [string]: any } = {
90
- _size = size,
91
- _directWrite = directWrite,
92
- _directRead = directRead,
93
-
94
- write = function(ch: Types.ChannelState, value: T): ()
95
- local cursor = ch.cursor
96
- if cursor + size > ch.size then
97
- alloc(ch, size)
98
- end
99
- directWrite(ch.buff, cursor, value)
100
- ch.cursor = cursor + size
101
- end,
102
-
103
- read = function(src: buffer, pos: number, _refs: { Instance }?): (T, number)
104
- return directRead(src, pos), size
105
- end,
106
- }
107
-
108
- if meta then
109
- for key, val in meta do
110
- codec[key] = val
111
- end
112
- end
113
-
114
- return table.freeze(codec) :: Types.InternalCodec<T>
115
- end
116
-
117
- --[[
118
- Zero-byte codec: writes nothing, reads always return `value`. Used by
119
- quantizers when min == max so the wire emits nothing yet decode produces
120
- a type-faithful value.
121
- ]]
122
- function Base.constant<T>(value: T): Types.InternalCodec<T>
123
- return table.freeze({
124
- _size = 0,
125
- _directWrite = function(_b: buffer, _off: number, _v: T): () end,
126
- _directRead = function(_b: buffer, _off: number): T
127
- return value
128
- end,
129
- write = function(_ch: Types.ChannelState, _v: T): () end,
130
- read = function(_src: buffer, _pos: number, _refs: { Instance }?): (T, number)
131
- return value, 0
132
- end,
133
- }) :: Types.InternalCodec<T>
134
- end
135
-
136
- return table.freeze(Base)
@@ -1,456 +0,0 @@
1
- --!strict
2
- --!native
3
- -- array() and deltaArray(). Bool elements are bit-packed.
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
- -- deltaArray wire flags. Distinct from Shared's struct/map flag spaces.
15
- local DA_UNCHANGED = 0
16
- local DA_FULL = 1
17
- local DA_PATCH = 2
18
-
19
- -- Private ----------------------------------------------------------------
20
-
21
- local alloc = Base.alloc
22
- local ensure = Base.ensure
23
- local allocDeltaId = Shared.allocDeltaId
24
- local acquireScratch = Shared.acquireScratch
25
- local releaseScratch = Shared.releaseScratch
26
- local rangeEqual = Shared.rangeEqual
27
- local copyToBuf = Shared.copyToBuf
28
- local containsDelta = Shared.containsDelta
29
- local enforceMaxCount = Shared.enforceMaxCount
30
- local boolBytes = Shared.boolBytes
31
- local packBoolArray = Shared.packBoolArray
32
- local unpackBoolArray = Shared.unpackBoolArray
33
- local varintRead = Varint.read
34
- local varintWrite = Varint.write
35
- local varintLength = Varint.length
36
- local writeu8 = buffer.writeu8
37
- local readu8 = buffer.readu8
38
- local bufCopy = buffer.copy
39
- local bufLen = buffer.len
40
-
41
- type DeltaArrayEntry = { eBuf: buffer, eLen: number }
42
-
43
- -- Bit-packed bool element fast path: 1 bit per entry instead of 1 byte.
44
- local function makeBoolArray(
45
- element: Types.InternalCodec<any>,
46
- maxCount: number?
47
- ): Types.InternalCodec<any>
48
- return {
49
- _isArray = true,
50
- _element = element,
51
- _maxCount = maxCount,
52
-
53
- write = function(ch: Types.ChannelState, value: { boolean }): ()
54
- local len = #value
55
- varintWrite(ch, len)
56
- packBoolArray(ch, value, len)
57
- end,
58
-
59
- read = function(src: buffer, pos: number, _refs: { Instance }?): ({ boolean }, number)
60
- local len, lenBytes = varintRead(src, pos)
61
- enforceMaxCount(len, maxCount)
62
- if len == 0 then
63
- return {}, lenBytes
64
- end
65
-
66
- local byteCount = boolBytes(len)
67
- local dataStart = pos + lenBytes
68
- if dataStart + byteCount > bufLen(src) then
69
- Log.error(`payload {byteCount}B exceeds remaining buffer`)
70
- end
71
- local result = table.create(len, false)
72
- unpackBoolArray(src, dataStart, len, result)
73
- return result, lenBytes + byteCount
74
- end,
75
- }
76
- end
77
-
78
- -- Fixed-size element fast path: single alloc + tight loop calling _direct fns.
79
- local function makeDirectArray(
80
- element: Types.InternalCodec<any>,
81
- elemSize: number,
82
- elemDW: (b: buffer, off: number, value: any) -> (),
83
- elemDR: (b: buffer, off: number) -> any,
84
- maxCount: number?
85
- ): Types.InternalCodec<any>
86
- return {
87
- _isArray = true,
88
- _element = element,
89
- _maxCount = maxCount,
90
-
91
- write = function(ch: Types.ChannelState, value: { any }): ()
92
- local len = #value
93
- varintWrite(ch, len)
94
- if len == 0 then
95
- return
96
- end
97
-
98
- local payloadBytes = len * elemSize
99
- local cursor = ch.cursor
100
- if cursor + payloadBytes > ch.size then
101
- alloc(ch, payloadBytes)
102
- end
103
- local b = ch.buff
104
- local off = cursor
105
- for i = 1, len do
106
- elemDW(b, off, value[i])
107
- off += elemSize
108
- end
109
- ch.cursor = cursor + payloadBytes
110
- end,
111
-
112
- read = function(src: buffer, pos: number, _refs: { Instance }?): ({ any }, number)
113
- local len, lenBytes = varintRead(src, pos)
114
- enforceMaxCount(len, maxCount)
115
- if len == 0 then
116
- return {}, lenBytes
117
- end
118
-
119
- local payloadBytes = len * elemSize
120
- local dataStart = pos + lenBytes
121
- if payloadBytes > bufLen(src) - dataStart then
122
- Log.error(`payload {payloadBytes}B exceeds remaining buffer`)
123
- end
124
-
125
- local result = table.create(len)
126
- local off = dataStart
127
- for i = 1, len do
128
- result[i] = elemDR(src, off)
129
- off += elemSize
130
- end
131
- return result, lenBytes + payloadBytes
132
- end,
133
- }
134
- end
135
-
136
- -- Generic fallback: variable-size or composite element codec.
137
- local function makeGenericArray(
138
- element: Types.InternalCodec<any>,
139
- maxCount: number?
140
- ): Types.InternalCodec<any>
141
- return {
142
- _isArray = true,
143
- _element = element,
144
- _maxCount = maxCount,
145
-
146
- write = function(ch: Types.ChannelState, value: { any }): ()
147
- local len = #value
148
- varintWrite(ch, len)
149
- for i = 1, len do
150
- element.write(ch, value[i])
151
- end
152
- end,
153
-
154
- read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
155
- local len, lenBytes = varintRead(src, pos)
156
- enforceMaxCount(len, maxCount)
157
- if len == 0 then
158
- return {}, lenBytes
159
- end
160
-
161
- local result = table.create(len)
162
- local total = lenBytes
163
- for i = 1, len do
164
- local v, consumed = element.read(src, pos + total, refs)
165
- if consumed == 0 then
166
- Log.error(`element codec consumed 0 bytes at index {i}`)
167
- end
168
- result[i] = v
169
- total += consumed
170
- end
171
- return result, total
172
- end,
173
- }
174
- end
175
-
176
- -- Public -----------------------------------------------------------------
177
-
178
- local Array = {}
179
-
180
- function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Types.InternalCodec<any>
181
- local codec: Types.InternalCodec<any>
182
- if element._isBool then
183
- codec = makeBoolArray(element, maxCount)
184
- else
185
- local elemSize = element._size
186
- local elemDW = element._directWrite
187
- local elemDR = element._directRead
188
- if elemSize and elemDW and elemDR then
189
- codec = makeDirectArray(element, elemSize, elemDW, elemDR, maxCount)
190
- else
191
- codec = makeGenericArray(element, maxCount)
192
- end
193
- end
194
- if element._hasUnknown then
195
- codec._hasUnknown = true
196
- end
197
- if containsDelta(element) then
198
- codec._hasDelta = true
199
- end
200
- return table.freeze(codec)
201
- end
202
-
203
- --[[
204
- Per-index deltaArray. Wire:
205
- [0] UNCHANGED (1 byte)
206
- [1] FULL <count varint> <element bytes ...>
207
- [2] PATCH <newLen varint> <changeCount varint>
208
- <(idx varint, element bytes) ...>
209
-
210
- Length shrinkage is implicit in newLen — entries past newLen drop from
211
- the cached map. Length growth: appended indices show up as changes.
212
- Reordered lists tend to mark every index changed; the writer falls back
213
- to FULL when changeCount * 2 >= newLen since PATCH index varints eat
214
- any savings past that ratio.
215
- ]]
216
- function Array.deltaArray(
217
- element: Types.InternalCodec<any>,
218
- maxCount: number?
219
- ): Types.InternalCodec<any>
220
- -- Inner delta would chain-diff across elements within a frame, not across frames.
221
- if containsDelta(element) then
222
- Log.error("deltaArray element cannot contain delta state")
223
- end
224
-
225
- local deltaId = allocDeltaId()
226
- local inner = Array.array(element, maxCount)
227
-
228
- --[[
229
- Encode each element into `scratch` and capture per-index byte ranges.
230
- Returns the array length plus parallel arrays of offset/length so the
231
- diff path can compare and emit individual entries without re-encoding.
232
- ]]
233
- local function encodePerIdx(
234
- scratch: Types.ChannelState,
235
- value: { any }
236
- ): (number, { number }, { number })
237
- local len = #value
238
- enforceMaxCount(len, maxCount)
239
- local off = table.create(len)
240
- local elen = table.create(len)
241
- for i = 1, len do
242
- local before = scratch.cursor
243
- element.write(scratch, value[i])
244
- off[i] = before
245
- elen[i] = scratch.cursor - before
246
- end
247
- return len, off, elen
248
- end
249
-
250
- return table.freeze({
251
- _isDelta = true,
252
- _isArray = true,
253
- _element = element,
254
- _maxCount = maxCount,
255
-
256
- write = function(ch: Types.ChannelState, value: { any }): ()
257
- if typeof(value) ~= "table" then
258
- Log.error(`expected table, got {typeof(value)}`)
259
- end
260
-
261
- local cache = ch.deltas[deltaId] :: any
262
- local scratch = acquireScratch()
263
- local len, off, elen = encodePerIdx(scratch, value)
264
- local scratchBuf = scratch.buff
265
-
266
- -- First frame: emit FULL using the per-index scratch bytes.
267
- if not cache then
268
- ensure(ch, 1)
269
- writeu8(ch.buff, ch.cursor, DA_FULL)
270
- ch.cursor += 1
271
- varintWrite(ch, len)
272
- local payloadLen = scratch.cursor
273
- ensure(ch, payloadLen)
274
- bufCopy(ch.buff, ch.cursor, scratchBuf, 0, payloadLen)
275
- ch.cursor += payloadLen
276
-
277
- local perIdx: { [number]: DeltaArrayEntry } = {}
278
- for i = 1, len do
279
- local n = elen[i]
280
- perIdx[i] = { eBuf = copyToBuf(nil, scratchBuf, off[i], n), eLen = n }
281
- end
282
- ch.deltas[deltaId] = { perIdx = perIdx, length = len } :: any
283
- releaseScratch()
284
- return
285
- end
286
-
287
- local cachedPerIdx = cache.perIdx :: { [number]: DeltaArrayEntry }
288
- local cachedLen = cache.length :: number
289
-
290
- -- Find changed indices in the overlap; appended indices count as changed.
291
- local changed: { number } = {}
292
- local changedCount = 0
293
- local overlap = if len < cachedLen then len else cachedLen
294
- for i = 1, overlap do
295
- local entry = cachedPerIdx[i]
296
- local n = elen[i]
297
- if
298
- not entry
299
- or n ~= entry.eLen
300
- or not rangeEqual(scratchBuf, off[i], entry.eBuf, 0, n)
301
- then
302
- changedCount += 1
303
- changed[changedCount] = i
304
- end
305
- end
306
- for i = overlap + 1, len do
307
- changedCount += 1
308
- changed[changedCount] = i
309
- end
310
-
311
- if changedCount == 0 and len == cachedLen then
312
- ensure(ch, 1)
313
- writeu8(ch.buff, ch.cursor, DA_UNCHANGED)
314
- ch.cursor += 1
315
- releaseScratch()
316
- return
317
- end
318
-
319
- -- Pick FULL vs PATCH on actual byte cost. Both share `1 + varintLength(len)`
320
- -- so it cancels: compare PATCH's index-header overhead + changed payload
321
- -- against FULL's full payload.
322
- local fullPayload = scratch.cursor
323
- local patchPayload = varintLength(changedCount)
324
- for i = 1, changedCount do
325
- local idx = changed[i]
326
- patchPayload += varintLength(idx) + elen[idx]
327
- end
328
-
329
- if patchPayload < fullPayload then
330
- ensure(ch, 1)
331
- writeu8(ch.buff, ch.cursor, DA_PATCH)
332
- ch.cursor += 1
333
- varintWrite(ch, len)
334
- varintWrite(ch, changedCount)
335
- for i = 1, changedCount do
336
- local idx = changed[i]
337
- varintWrite(ch, idx)
338
- local n = elen[idx]
339
- ensure(ch, n)
340
- bufCopy(ch.buff, ch.cursor, scratchBuf, off[idx], n)
341
- ch.cursor += n
342
- end
343
- else
344
- ensure(ch, 1)
345
- writeu8(ch.buff, ch.cursor, DA_FULL)
346
- ch.cursor += 1
347
- varintWrite(ch, len)
348
- ensure(ch, fullPayload)
349
- bufCopy(ch.buff, ch.cursor, scratchBuf, 0, fullPayload)
350
- ch.cursor += fullPayload
351
- end
352
-
353
- -- Update cache: refresh changed (and appended), drop truncated.
354
- for i = 1, changedCount do
355
- local idx = changed[i]
356
- local n = elen[idx]
357
- local entry = cachedPerIdx[idx]
358
- if entry then
359
- entry.eBuf = copyToBuf(entry.eBuf, scratchBuf, off[idx], n)
360
- entry.eLen = n
361
- else
362
- cachedPerIdx[idx] = { eBuf = copyToBuf(nil, scratchBuf, off[idx], n), eLen = n }
363
- end
364
- end
365
- for i = len + 1, cachedLen do
366
- cachedPerIdx[i] = nil
367
- end
368
- cache.length = len
369
-
370
- releaseScratch()
371
- end,
372
-
373
- read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
374
- if pos >= bufLen(src) then
375
- Log.error("truncated deltaArray header")
376
- end
377
- local flag = readu8(src, pos)
378
-
379
- if flag == DA_UNCHANGED then
380
- local cached = Baseline.getCache(deltaId)
381
- if cached == nil then
382
- Log.error("UNCHANGED flag before any FULL frame; baseline missing")
383
- end
384
- return cached, 1
385
- end
386
-
387
- if flag == DA_FULL then
388
- local value, consumed = inner.read(src, pos + 1, refs)
389
- Baseline.setCache(deltaId, value)
390
- return value, 1 + consumed
391
- end
392
-
393
- if flag == DA_PATCH then
394
- local cached = Baseline.getCache(deltaId)
395
- if cached == nil then
396
- Log.error("deltaArray PATCH before any FULL frame; baseline missing")
397
- end
398
- --[[
399
- Capture the cached length BEFORE clone+mutate; #result
400
- becomes unreliable once we set entries at appended indices
401
- past the original boundary.
402
- ]]
403
- local cachedLen = #cached
404
- local result: { any } = table.clone(cached)
405
- local total = 1
406
-
407
- local newLen, lLen = varintRead(src, pos + total)
408
- if lLen == 0 then
409
- Log.error("truncated deltaArray length")
410
- end
411
- enforceMaxCount(newLen, maxCount, "newLen")
412
- total += lLen
413
-
414
- local changeCount, cLen = varintRead(src, pos + total)
415
- if cLen == 0 then
416
- Log.error("truncated deltaArray change count")
417
- end
418
- -- changeCount is also bounded by newLen: every index appears at most once.
419
- if changeCount > newLen then
420
- Log.error(`deltaArray changeCount {changeCount} exceeds newLen {newLen}`)
421
- end
422
- total += cLen
423
-
424
- for _ = 1, changeCount do
425
- local idx, iLen = varintRead(src, pos + total)
426
- if iLen == 0 then
427
- Log.error("truncated deltaArray index")
428
- end
429
- -- Reject out-of-range idx so the wire can't poison the cache with
430
- -- arbitrary keys that survive truncation.
431
- if idx < 1 or idx > newLen then
432
- Log.error(`deltaArray index {idx} out of range [1, {newLen}]`)
433
- end
434
- total += iLen
435
- local v, vc = element.read(src, pos + total, refs)
436
- if vc == 0 then
437
- Log.error("truncated deltaArray element")
438
- end
439
- total += vc
440
- result[idx] = v
441
- end
442
-
443
- for i = newLen + 1, cachedLen do
444
- result[i] = nil
445
- end
446
-
447
- Baseline.setCache(deltaId, result)
448
- return result, total
449
- end
450
-
451
- Log.error(`invalid deltaArray flag {flag}`)
452
- end,
453
- }) :: Types.InternalCodec<any>
454
- end
455
-
456
- return table.freeze(Array)