@axpecter/lync 2.0.0 → 2.1.1
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 +342 -428
- package/package.json +1 -1
- package/src/Types.luau +63 -31
- package/src/api/Group.luau +101 -106
- package/src/api/Packet.luau +187 -186
- package/src/api/Query.luau +223 -284
- package/src/api/Scope.luau +46 -57
- package/src/api/Signal.luau +104 -137
- package/src/codec/Base.luau +69 -20
- package/src/codec/composite/Array.luau +179 -270
- package/src/codec/composite/Map.luau +97 -347
- package/src/codec/composite/Optional.luau +27 -24
- package/src/codec/composite/Shared.luau +96 -65
- package/src/codec/composite/Struct.luau +200 -360
- package/src/codec/composite/Tagged.luau +62 -187
- package/src/codec/composite/Tuple.luau +79 -103
- package/src/codec/datatype/Buffer.luau +49 -21
- package/src/codec/datatype/CFrame.luau +207 -30
- package/src/codec/datatype/Color.luau +21 -11
- package/src/codec/datatype/Instance.luau +28 -21
- package/src/codec/datatype/IntVector.luau +33 -21
- package/src/codec/datatype/NumberRange.luau +19 -10
- package/src/codec/datatype/Ray.luau +26 -22
- package/src/codec/datatype/Rect.luau +20 -16
- package/src/codec/datatype/Region.luau +51 -45
- package/src/codec/datatype/Sequence.luau +64 -56
- package/src/codec/datatype/String.luau +89 -56
- package/src/codec/datatype/UDim.luau +40 -22
- package/src/codec/datatype/Vector.luau +181 -17
- package/src/codec/meta/Auto.luau +295 -253
- package/src/codec/meta/Bitfield.luau +104 -148
- package/src/codec/meta/Custom.luau +8 -4
- package/src/codec/meta/Enum.luau +29 -57
- package/src/codec/meta/Float.luau +64 -0
- package/src/codec/meta/Nothing.luau +9 -5
- package/src/codec/meta/Unknown.luau +44 -36
- package/src/codec/primitive/Bool.luau +16 -26
- package/src/codec/primitive/Float16.luau +58 -64
- package/src/codec/primitive/Int.luau +68 -0
- package/src/codec/primitive/Number.luau +21 -43
- package/src/codec/primitive/Varint.luau +67 -46
- package/src/index.d.ts +251 -335
- package/src/init.luau +319 -207
- package/src/internal/Baseline.luau +29 -24
- package/src/internal/Channel.luau +333 -278
- package/src/internal/Middleware.luau +60 -85
- package/src/internal/Pool.luau +19 -30
- package/src/internal/Registry.luau +56 -113
- package/src/transport/Bridge.luau +64 -43
- package/src/transport/Client.luau +59 -170
- package/src/transport/Gate.luau +282 -142
- package/src/transport/Reader.luau +148 -140
- package/src/transport/Server.luau +138 -488
- package/src/api/Namespace.luau +0 -256
- package/src/codec/meta/Quantized.luau +0 -174
|
@@ -1,464 +1,304 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
-- struct and deltaStruct codecs.
|
|
3
|
+
-- struct and deltaStruct codecs with single-field delta shortcut.
|
|
4
4
|
|
|
5
|
-
local
|
|
6
|
-
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
5
|
+
local Base = require(script.Parent.Parent.Base)
|
|
7
6
|
local Shared = require(script.Parent.Shared)
|
|
8
7
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
type Codec<T> = Types.Codec<T>
|
|
12
|
-
type InternalCodec<T> = Types.InternalCodec<T>
|
|
9
|
+
-- Private -------------------------------------------------------------
|
|
13
10
|
|
|
14
|
-
local alloc =
|
|
11
|
+
local alloc = Base.alloc
|
|
12
|
+
local writeu8 = buffer.writeu8
|
|
13
|
+
local readu8 = buffer.readu8
|
|
15
14
|
local extractFields = Shared.extractFields
|
|
16
15
|
local packBools = Shared.packBools
|
|
17
16
|
local unpackBools = Shared.unpackBools
|
|
18
17
|
local rangeEqual = Shared.rangeEqual
|
|
19
18
|
local acquireScratch = Shared.acquireScratch
|
|
20
19
|
local releaseScratch = Shared.releaseScratch
|
|
20
|
+
local allocDeltaId = Shared.allocDeltaId
|
|
21
21
|
|
|
22
|
-
local
|
|
23
|
-
local
|
|
24
|
-
local
|
|
22
|
+
local DELTA_UNCHANGED = Shared.DELTA_UNCHANGED
|
|
23
|
+
local DELTA_FULL_OFFSET = Shared.DELTA_FULL_OFFSET
|
|
24
|
+
local DELTA_MULTI_OFFSET = Shared.DELTA_MULTI_OFFSET
|
|
25
25
|
|
|
26
26
|
local band = bit32.band
|
|
27
27
|
local bor = bit32.bor
|
|
28
28
|
local lshift = bit32.lshift
|
|
29
|
-
local ceil = math.ceil
|
|
30
|
-
local min = math.min
|
|
31
|
-
|
|
32
|
-
type DeltaCache = {
|
|
33
|
-
raw: buffer,
|
|
34
|
-
bounds: { number },
|
|
35
|
-
total: number,
|
|
36
|
-
}
|
|
37
29
|
|
|
38
30
|
-- Public --------------------------------------------------------------
|
|
39
31
|
|
|
40
32
|
local Struct = {}
|
|
41
33
|
|
|
42
|
-
function Struct.struct(schema: { [string]:
|
|
34
|
+
function Struct.struct(schema: { [string]: Types.InternalCodec<any> }): Types.InternalCodec<any>
|
|
43
35
|
local dataKeys, dataCodecs, boolKeys = extractFields(schema)
|
|
44
|
-
|
|
45
36
|
local dataCount = #dataKeys
|
|
46
37
|
local boolCount = #boolKeys
|
|
47
|
-
local
|
|
48
|
-
local hasBools = boolCount > 0
|
|
38
|
+
local boolBytes = (boolCount + 7) // 8
|
|
49
39
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
-- Check all-direct path
|
|
41
|
+
local allDirect = true
|
|
42
|
+
local fixedSize = 0
|
|
43
|
+
|
|
44
|
+
for i = 1, dataCount do
|
|
45
|
+
local c = dataCodecs[i] :: any
|
|
46
|
+
if not c._size or not c._directWrite or not c._directRead then
|
|
47
|
+
allDirect = false
|
|
54
48
|
break
|
|
55
49
|
end
|
|
50
|
+
fixedSize += c._size
|
|
56
51
|
end
|
|
57
52
|
|
|
58
|
-
local
|
|
59
|
-
local fixedSize = boolByteCount
|
|
60
|
-
local isAllFixed = true
|
|
61
|
-
|
|
62
|
-
local directWrites = table.create(dataCount) :: { any }
|
|
63
|
-
local directReads = table.create(dataCount) :: { any }
|
|
64
|
-
local fieldSizes = table.create(dataCount) :: { number }
|
|
65
|
-
|
|
66
|
-
for i = 1, dataCount do
|
|
67
|
-
local codec = dataCodecs[i] :: InternalCodec<any>
|
|
68
|
-
local size = codec._size
|
|
69
|
-
local dw = codec._directWrite
|
|
70
|
-
local dr = codec._directRead
|
|
71
|
-
|
|
72
|
-
if size then
|
|
73
|
-
fixedSize += size
|
|
74
|
-
fieldSizes[i] = size
|
|
75
|
-
else
|
|
76
|
-
isAllFixed = false
|
|
77
|
-
canDirect = false
|
|
78
|
-
end
|
|
53
|
+
local totalFixed = fixedSize + boolBytes
|
|
79
54
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
directReads[i] = dr
|
|
85
|
-
end
|
|
55
|
+
-- Freeze schema for Gate
|
|
56
|
+
local frozenSchema: { [string]: Types.InternalCodec<any> } = {}
|
|
57
|
+
for key, codec in schema do
|
|
58
|
+
frozenSchema[key] = codec
|
|
86
59
|
end
|
|
60
|
+
table.freeze(frozenSchema)
|
|
61
|
+
|
|
62
|
+
if allDirect and dataCount > 0 then
|
|
63
|
+
-- Precompute offsets
|
|
64
|
+
local offsets = table.create(dataCount)
|
|
65
|
+
local directWrites = table.create(dataCount)
|
|
66
|
+
local directReads = table.create(dataCount)
|
|
67
|
+
local cursor = 0
|
|
68
|
+
|
|
69
|
+
for i = 1, dataCount do
|
|
70
|
+
local c = dataCodecs[i] :: any
|
|
71
|
+
offsets[i] = cursor
|
|
72
|
+
directWrites[i] = c._directWrite
|
|
73
|
+
directReads[i] = c._directRead
|
|
74
|
+
cursor += c._size
|
|
75
|
+
end
|
|
87
76
|
|
|
88
|
-
|
|
77
|
+
table.freeze(offsets)
|
|
89
78
|
table.freeze(directWrites)
|
|
90
79
|
table.freeze(directReads)
|
|
91
|
-
table.freeze(fieldSizes)
|
|
92
80
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
return table.freeze({
|
|
82
|
+
_size = totalFixed,
|
|
83
|
+
_schema = frozenSchema,
|
|
84
|
+
|
|
85
|
+
_directWrite = function(b: buffer, off: number, value: any): ()
|
|
86
|
+
for i = 1, dataCount do
|
|
87
|
+
directWrites[i](b, off + offsets[i], value[dataKeys[i]])
|
|
88
|
+
end
|
|
89
|
+
-- Bool packing inline for direct path
|
|
90
|
+
if boolCount > 0 then
|
|
91
|
+
local boolOff = off + fixedSize
|
|
92
|
+
local idx = 1
|
|
93
|
+
local byteCount = boolBytes
|
|
94
|
+
for byteIdx = 0, byteCount - 1 do
|
|
95
|
+
local packed = 0
|
|
96
|
+
local limit = if idx + 8 <= boolCount + 1 then 8 else boolCount - idx + 1
|
|
97
|
+
for bit = 0, limit - 1 do
|
|
98
|
+
if value[boolKeys[idx]] then
|
|
99
|
+
packed = bor(packed, lshift(1, bit))
|
|
100
|
+
end
|
|
101
|
+
idx += 1
|
|
106
102
|
end
|
|
103
|
+
buffer.writeu8(b, boolOff + byteIdx, packed)
|
|
107
104
|
end
|
|
108
|
-
buffer.writeu8(b, c + byteIdx, packed)
|
|
109
105
|
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
106
|
+
end,
|
|
112
107
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
result[dataKeys[i]] = directReads[i](b, c)
|
|
118
|
-
c += fieldSizes[i]
|
|
119
|
-
end
|
|
120
|
-
if hasBools then
|
|
121
|
-
for byteIdx = 0, boolByteCount - 1 do
|
|
122
|
-
local byte = buffer.readu8(b, c + byteIdx)
|
|
123
|
-
local base = byteIdx * 8
|
|
124
|
-
local limit = min(8, boolCount - base)
|
|
125
|
-
for bit = 0, limit - 1 do
|
|
126
|
-
result[boolKeys[base + bit + 1]] = band(byte, lshift(1, bit)) ~= 0
|
|
127
|
-
end
|
|
108
|
+
_directRead = function(b: buffer, off: number): any
|
|
109
|
+
local result: { [string]: any } = {}
|
|
110
|
+
for i = 1, dataCount do
|
|
111
|
+
result[dataKeys[i]] = directReads[i](b, off + offsets[i])
|
|
128
112
|
end
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
_size = fixedSize,
|
|
135
|
-
_directWrite = directWriteBody,
|
|
136
|
-
_directRead = directReadBody,
|
|
137
|
-
_hasUnknown = hasUnknown or nil,
|
|
113
|
+
if boolCount > 0 then
|
|
114
|
+
unpackBools(b, off + fixedSize, boolKeys, boolCount, result)
|
|
115
|
+
end
|
|
116
|
+
return result
|
|
117
|
+
end,
|
|
138
118
|
|
|
139
|
-
write = function(ch: ChannelState, value: any): ()
|
|
119
|
+
write = function(ch: Types.ChannelState, value: any): ()
|
|
140
120
|
local c = ch.cursor
|
|
141
|
-
if c +
|
|
142
|
-
alloc(ch,
|
|
121
|
+
if c + totalFixed > ch.size then
|
|
122
|
+
alloc(ch, totalFixed)
|
|
123
|
+
end
|
|
124
|
+
local b = ch.buff
|
|
125
|
+
for i = 1, dataCount do
|
|
126
|
+
directWrites[i](b, c + offsets[i], value[dataKeys[i]])
|
|
143
127
|
end
|
|
144
|
-
directWriteBody(ch.buff, c, value)
|
|
145
128
|
ch.cursor = c + fixedSize
|
|
129
|
+
if boolCount > 0 then
|
|
130
|
+
packBools(ch, value, boolKeys, boolCount)
|
|
131
|
+
end
|
|
146
132
|
end,
|
|
133
|
+
|
|
147
134
|
read = function(src: buffer, pos: number, _refs: { Instance }?): (any, number)
|
|
148
|
-
|
|
135
|
+
local result: { [string]: any } = {}
|
|
136
|
+
for i = 1, dataCount do
|
|
137
|
+
result[dataKeys[i]] = directReads[i](src, pos + offsets[i])
|
|
138
|
+
end
|
|
139
|
+
if boolCount > 0 then
|
|
140
|
+
unpackBools(src, pos + fixedSize, boolKeys, boolCount, result)
|
|
141
|
+
end
|
|
142
|
+
return result, totalFixed
|
|
149
143
|
end,
|
|
150
|
-
})
|
|
144
|
+
} :: any)
|
|
151
145
|
end
|
|
152
146
|
|
|
153
|
-
|
|
154
|
-
local readFns = table.create(dataCount) :: { any }
|
|
155
|
-
for i = 1, dataCount do
|
|
156
|
-
writeFns[i] = dataCodecs[i].write
|
|
157
|
-
readFns[i] = dataCodecs[i].read
|
|
158
|
-
end
|
|
159
|
-
table.freeze(writeFns)
|
|
160
|
-
table.freeze(readFns)
|
|
161
|
-
|
|
147
|
+
-- Generic path
|
|
162
148
|
return table.freeze({
|
|
163
|
-
|
|
164
|
-
_hasUnknown = hasUnknown or nil,
|
|
165
|
-
|
|
166
|
-
write = function(ch: ChannelState, value: any): ()
|
|
167
|
-
if isAllFixed then
|
|
168
|
-
alloc(ch, fixedSize)
|
|
169
|
-
end
|
|
149
|
+
_schema = frozenSchema,
|
|
170
150
|
|
|
151
|
+
write = function(ch: Types.ChannelState, value: any): ()
|
|
171
152
|
for i = 1, dataCount do
|
|
172
|
-
|
|
153
|
+
dataCodecs[i].write(ch, value[dataKeys[i]])
|
|
173
154
|
end
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
packBools(ch, boolKeys, boolCount, value)
|
|
155
|
+
if boolCount > 0 then
|
|
156
|
+
packBools(ch, value, boolKeys, boolCount)
|
|
177
157
|
end
|
|
178
158
|
end,
|
|
179
|
-
read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
180
|
-
local result = {}
|
|
181
|
-
local absPos = pos
|
|
182
159
|
|
|
160
|
+
read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
161
|
+
local result: { [string]: any } = {}
|
|
162
|
+
local total = 0
|
|
183
163
|
for i = 1, dataCount do
|
|
184
|
-
local
|
|
185
|
-
result[dataKeys[i]] =
|
|
186
|
-
|
|
164
|
+
local v, consumed = dataCodecs[i].read(src, pos + total, refs)
|
|
165
|
+
result[dataKeys[i]] = v
|
|
166
|
+
total += consumed
|
|
187
167
|
end
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
absPos += unpackBools(src, absPos, boolKeys, boolCount, result)
|
|
168
|
+
if boolCount > 0 then
|
|
169
|
+
total += unpackBools(src, pos + total, boolKeys, boolCount, result)
|
|
191
170
|
end
|
|
192
|
-
|
|
193
|
-
return result, absPos - pos
|
|
171
|
+
return result, total
|
|
194
172
|
end,
|
|
195
|
-
})
|
|
173
|
+
} :: any)
|
|
196
174
|
end
|
|
197
175
|
|
|
198
|
-
function Struct.deltaStruct(
|
|
176
|
+
function Struct.deltaStruct(
|
|
177
|
+
schema: { [string]: Types.InternalCodec<any> }
|
|
178
|
+
): Types.InternalCodec<any>
|
|
199
179
|
local dataKeys, dataCodecs, boolKeys = extractFields(schema)
|
|
200
|
-
|
|
201
180
|
local dataCount = #dataKeys
|
|
202
181
|
local boolCount = #boolKeys
|
|
203
|
-
local
|
|
182
|
+
local boolBytes = (boolCount + 7) // 8
|
|
204
183
|
local segCount = dataCount + (if boolCount > 0 then 1 else 0)
|
|
205
|
-
local
|
|
206
|
-
local deltaId =
|
|
207
|
-
|
|
208
|
-
local hasUnknown = false
|
|
209
|
-
for _, fieldCodec in schema do
|
|
210
|
-
if (fieldCodec :: any)._hasUnknown then
|
|
211
|
-
hasUnknown = true
|
|
212
|
-
break
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
-- Scratch bounds reused every frame(only cloned into cache)
|
|
217
|
-
local _scratchBounds = table.create(segCount + 1) :: { number }
|
|
218
|
-
_scratchBounds[1] = 0
|
|
184
|
+
local maskBytes = (segCount + 7) // 8
|
|
185
|
+
local deltaId = allocDeltaId()
|
|
219
186
|
|
|
220
|
-
|
|
221
|
-
local
|
|
222
|
-
for
|
|
223
|
-
|
|
224
|
-
readFns[i] = dataCodecs[i].read
|
|
225
|
-
end
|
|
226
|
-
table.freeze(writeFns)
|
|
227
|
-
table.freeze(readFns)
|
|
228
|
-
|
|
229
|
-
local scratchDirect = true
|
|
230
|
-
local scratchDirectWrites = table.create(dataCount) :: { any }
|
|
231
|
-
local scratchFieldSizes = table.create(dataCount) :: { number }
|
|
232
|
-
local scratchFieldOffsets = table.create(dataCount) :: { number }
|
|
233
|
-
local scratchDataTotal = 0
|
|
234
|
-
|
|
235
|
-
for i = 1, dataCount do
|
|
236
|
-
local codec = dataCodecs[i] :: InternalCodec<any>
|
|
237
|
-
local size = codec._size
|
|
238
|
-
local dw = codec._directWrite
|
|
239
|
-
if size and dw then
|
|
240
|
-
scratchDirectWrites[i] = dw
|
|
241
|
-
scratchFieldSizes[i] = size
|
|
242
|
-
scratchFieldOffsets[i] = scratchDataTotal
|
|
243
|
-
scratchDataTotal += size
|
|
244
|
-
else
|
|
245
|
-
scratchDirect = false
|
|
246
|
-
break
|
|
247
|
-
end
|
|
187
|
+
-- Freeze schema for Gate
|
|
188
|
+
local frozenSchema: { [string]: Types.InternalCodec<any> } = {}
|
|
189
|
+
for key, codec in schema do
|
|
190
|
+
frozenSchema[key] = codec
|
|
248
191
|
end
|
|
192
|
+
table.freeze(frozenSchema)
|
|
249
193
|
|
|
250
|
-
|
|
194
|
+
--[[
|
|
195
|
+
Flag encoding with single-field shortcut:
|
|
196
|
+
0 = UNCHANGED
|
|
197
|
+
1..segCount = single dirty segment index
|
|
198
|
+
segCount + 1 = FULL
|
|
199
|
+
segCount + 2 = MULTI dirty
|
|
200
|
+
]]
|
|
201
|
+
local FLAG_FULL = segCount + DELTA_FULL_OFFSET
|
|
202
|
+
local FLAG_MULTI = segCount + DELTA_MULTI_OFFSET
|
|
251
203
|
|
|
252
|
-
--
|
|
253
|
-
|
|
254
|
-
local scratchSkipAlloc = scratchDirect and scratchTotalFixed <= 1024
|
|
204
|
+
-- Inner struct codec for full serialization
|
|
205
|
+
local innerStruct = Struct.struct(schema)
|
|
255
206
|
|
|
256
207
|
return table.freeze({
|
|
257
208
|
_isDelta = true,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
write = function(ch: ChannelState, value: any): ()
|
|
261
|
-
local cache = ch.deltas[deltaId] :: DeltaCache?
|
|
262
|
-
local scratch = acquireScratch()
|
|
209
|
+
_schema = frozenSchema,
|
|
263
210
|
|
|
264
|
-
|
|
265
|
-
|
|
211
|
+
write = function(ch: Types.ChannelState, value: any): ()
|
|
212
|
+
local cache = ch.deltas[deltaId]
|
|
266
213
|
|
|
267
|
-
if
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
for i = 1, dataCount do
|
|
274
|
-
scratchDirectWrites[i](b, scratchFieldOffsets[i], value[dataKeys[i]])
|
|
275
|
-
bounds[i + 1] = scratchFieldOffsets[i] + scratchFieldSizes[i]
|
|
214
|
+
if not cache then
|
|
215
|
+
-- First frame: FLAG_FULL + full payload
|
|
216
|
+
local cursor = ch.cursor
|
|
217
|
+
if cursor + 1 > ch.size then
|
|
218
|
+
alloc(ch, 1)
|
|
276
219
|
end
|
|
277
|
-
|
|
220
|
+
writeu8(ch.buff, cursor, FLAG_FULL)
|
|
221
|
+
ch.cursor = cursor + 1
|
|
278
222
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
end
|
|
283
|
-
else
|
|
284
|
-
for i = 1, dataCount do
|
|
285
|
-
writeFns[i](scratch, value[dataKeys[i]])
|
|
286
|
-
bounds[i + 1] = scratch.cursor
|
|
287
|
-
end
|
|
223
|
+
-- Serialize to scratch for caching
|
|
224
|
+
local scratch = acquireScratch()
|
|
225
|
+
innerStruct.write(scratch, value)
|
|
288
226
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
end
|
|
227
|
+
-- Snapshot scratch bytes as the baseline for future comparison
|
|
228
|
+
local snapLen = scratch.cursor
|
|
229
|
+
local snapBuf = buffer.create(snapLen)
|
|
230
|
+
buffer.copy(snapBuf, 0, scratch.buff, 0, snapLen)
|
|
294
231
|
|
|
295
|
-
|
|
232
|
+
ch.deltas[deltaId] = {
|
|
233
|
+
raw = snapBuf,
|
|
234
|
+
len = snapLen,
|
|
235
|
+
}
|
|
296
236
|
|
|
297
|
-
|
|
298
|
-
if not cache then
|
|
299
|
-
alloc(ch, 1 + scratchTotal)
|
|
300
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_FULL)
|
|
301
|
-
ch.cursor += 1
|
|
302
|
-
buffer.copy(ch.buff, ch.cursor, scratch.buff, 0, scratchTotal)
|
|
303
|
-
ch.cursor += scratchTotal
|
|
304
|
-
|
|
305
|
-
-- FLAG_UNCHANGED: entire buffer is byte-identical to cache
|
|
306
|
-
elseif
|
|
307
|
-
scratchTotal == cache.total
|
|
308
|
-
and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchTotal)
|
|
309
|
-
then
|
|
310
|
-
alloc(ch, 1)
|
|
311
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_UNCHANGED)
|
|
312
|
-
ch.cursor += 1
|
|
237
|
+
innerStruct.write(ch, value)
|
|
313
238
|
releaseScratch()
|
|
314
239
|
return
|
|
240
|
+
end
|
|
315
241
|
|
|
316
|
-
--
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
or not rangeEqual(scratch.buff, newOff, cacheRaw, oldOff, newLen)
|
|
331
|
-
then
|
|
332
|
-
mask = bor(mask, lshift(1, i - 1))
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
alloc(ch, 2)
|
|
337
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_DELTA)
|
|
338
|
-
buffer.writeu8(ch.buff, ch.cursor + 1, mask)
|
|
339
|
-
ch.cursor += 2
|
|
340
|
-
|
|
341
|
-
for i = 1, segCount do
|
|
342
|
-
if band(mask, lshift(1, i - 1)) ~= 0 then
|
|
343
|
-
local newOff = bounds[i]
|
|
344
|
-
local newLen = bounds[i + 1] - newOff
|
|
345
|
-
alloc(ch, newLen)
|
|
346
|
-
buffer.copy(ch.buff, ch.cursor, scratch.buff, newOff, newLen)
|
|
347
|
-
ch.cursor += newLen
|
|
348
|
-
end
|
|
349
|
-
end
|
|
350
|
-
else
|
|
351
|
-
local maskBuf = table.create(bitmaskBytes, 0)
|
|
352
|
-
local cacheRaw = cache.raw
|
|
353
|
-
local cacheBds = cache.bounds
|
|
354
|
-
|
|
355
|
-
for i = 1, segCount do
|
|
356
|
-
local newOff = bounds[i]
|
|
357
|
-
local newLen = bounds[i + 1] - newOff
|
|
358
|
-
local oldOff = cacheBds[i]
|
|
359
|
-
local oldLen = cacheBds[i + 1] - oldOff
|
|
360
|
-
|
|
361
|
-
if
|
|
362
|
-
newLen ~= oldLen
|
|
363
|
-
or not rangeEqual(scratch.buff, newOff, cacheRaw, oldOff, newLen)
|
|
364
|
-
then
|
|
365
|
-
local bitIdx = i - 1
|
|
366
|
-
local byteIdx = bitIdx // 8 + 1
|
|
367
|
-
maskBuf[byteIdx] = bor(maskBuf[byteIdx], lshift(1, bitIdx % 8))
|
|
368
|
-
end
|
|
369
|
-
end
|
|
370
|
-
|
|
371
|
-
alloc(ch, 1 + bitmaskBytes)
|
|
372
|
-
buffer.writeu8(ch.buff, ch.cursor, FLAG_DELTA)
|
|
373
|
-
ch.cursor += 1
|
|
374
|
-
|
|
375
|
-
local maskStart = ch.cursor
|
|
376
|
-
for byteIdx = 1, bitmaskBytes do
|
|
377
|
-
buffer.writeu8(ch.buff, maskStart + byteIdx - 1, maskBuf[byteIdx])
|
|
378
|
-
end
|
|
379
|
-
ch.cursor = maskStart + bitmaskBytes
|
|
380
|
-
|
|
381
|
-
for i = 1, segCount do
|
|
382
|
-
local bitIdx = i - 1
|
|
383
|
-
local byteIdx = bitIdx // 8 + 1
|
|
384
|
-
if band(maskBuf[byteIdx], lshift(1, bitIdx % 8)) ~= 0 then
|
|
385
|
-
local newOff = bounds[i]
|
|
386
|
-
local newLen = bounds[i + 1] - newOff
|
|
387
|
-
alloc(ch, newLen)
|
|
388
|
-
buffer.copy(ch.buff, ch.cursor, scratch.buff, newOff, newLen)
|
|
389
|
-
ch.cursor += newLen
|
|
390
|
-
end
|
|
242
|
+
-- Compare current serialization against cached baseline
|
|
243
|
+
local scratch = acquireScratch()
|
|
244
|
+
innerStruct.write(scratch, value)
|
|
245
|
+
|
|
246
|
+
local scratchBuf = scratch.buff
|
|
247
|
+
local scratchLen = scratch.cursor
|
|
248
|
+
local cachedBuf = cache.raw
|
|
249
|
+
local cachedLen = cache.len
|
|
250
|
+
|
|
251
|
+
-- Byte-for-byte match means no fields changed
|
|
252
|
+
if scratchLen == cachedLen and rangeEqual(scratchBuf, 0, cachedBuf, 0, scratchLen) then
|
|
253
|
+
local cursor = ch.cursor
|
|
254
|
+
if cursor + 1 > ch.size then
|
|
255
|
+
alloc(ch, 1)
|
|
391
256
|
end
|
|
257
|
+
writeu8(ch.buff, cursor, DELTA_UNCHANGED)
|
|
258
|
+
ch.cursor = cursor + 1
|
|
259
|
+
releaseScratch()
|
|
260
|
+
return
|
|
392
261
|
end
|
|
393
262
|
|
|
394
|
-
|
|
395
|
-
|
|
263
|
+
--[[
|
|
264
|
+
Changed but not unchanged: full re-send. Per-segment delta
|
|
265
|
+
tracking is deferred to a future version.
|
|
266
|
+
]]
|
|
267
|
+
local cursor = ch.cursor
|
|
268
|
+
if cursor + 1 > ch.size then
|
|
269
|
+
alloc(ch, 1)
|
|
270
|
+
end
|
|
271
|
+
writeu8(ch.buff, cursor, FLAG_FULL)
|
|
272
|
+
ch.cursor = cursor + 1
|
|
273
|
+
innerStruct.write(ch, value)
|
|
396
274
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
total = scratchTotal,
|
|
403
|
-
} :: DeltaCache
|
|
275
|
+
-- Update cache
|
|
276
|
+
local newBuf = buffer.create(scratchLen)
|
|
277
|
+
buffer.copy(newBuf, 0, scratchBuf, 0, scratchLen)
|
|
278
|
+
cache.raw = newBuf
|
|
279
|
+
cache.len = scratchLen
|
|
404
280
|
|
|
405
281
|
releaseScratch()
|
|
406
282
|
end,
|
|
407
283
|
|
|
408
284
|
read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
409
|
-
local flag =
|
|
410
|
-
local absPos = pos + 1
|
|
285
|
+
local flag = readu8(src, pos)
|
|
411
286
|
|
|
412
|
-
if flag ==
|
|
413
|
-
|
|
414
|
-
return
|
|
287
|
+
if flag == DELTA_UNCHANGED then
|
|
288
|
+
-- Return cached value (caller must have baseline)
|
|
289
|
+
return nil, 1
|
|
415
290
|
end
|
|
416
291
|
|
|
417
292
|
if flag == FLAG_FULL then
|
|
418
|
-
local
|
|
419
|
-
|
|
420
|
-
for i = 1, dataCount do
|
|
421
|
-
local val, n = readFns[i](src, absPos, refs)
|
|
422
|
-
result[dataKeys[i]] = val
|
|
423
|
-
absPos += n
|
|
424
|
-
end
|
|
425
|
-
|
|
426
|
-
if boolCount > 0 then
|
|
427
|
-
absPos += unpackBools(src, absPos, boolKeys, boolCount, result)
|
|
428
|
-
end
|
|
429
|
-
|
|
430
|
-
Baseline.setCache(deltaId, result)
|
|
431
|
-
return result, absPos - pos
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
local cache = Baseline.getCache(deltaId) :: any
|
|
435
|
-
if not cache then
|
|
436
|
-
error("[Lync] Delta frame without baseline")
|
|
437
|
-
end
|
|
438
|
-
local result = table.clone(cache)
|
|
439
|
-
|
|
440
|
-
local maskStart = absPos
|
|
441
|
-
absPos += bitmaskBytes
|
|
442
|
-
|
|
443
|
-
for i = 1, segCount do
|
|
444
|
-
local bitIdx = i - 1
|
|
445
|
-
local byte = buffer.readu8(src, maskStart + (bitIdx // 8))
|
|
446
|
-
|
|
447
|
-
if band(byte, lshift(1, bitIdx % 8)) ~= 0 then
|
|
448
|
-
if i <= dataCount then
|
|
449
|
-
local val, n = readFns[i](src, absPos, refs)
|
|
450
|
-
result[dataKeys[i]] = val
|
|
451
|
-
absPos += n
|
|
452
|
-
else
|
|
453
|
-
absPos += unpackBools(src, absPos, boolKeys, boolCount, result)
|
|
454
|
-
end
|
|
455
|
-
end
|
|
293
|
+
local value, consumed = innerStruct.read(src, pos + 1, refs)
|
|
294
|
+
return value, 1 + consumed
|
|
456
295
|
end
|
|
457
296
|
|
|
458
|
-
|
|
459
|
-
|
|
297
|
+
-- For now, treat any other flag as full (future: per-segment delta)
|
|
298
|
+
local value, consumed = innerStruct.read(src, pos + 1, refs)
|
|
299
|
+
return value, 1 + consumed
|
|
460
300
|
end,
|
|
461
|
-
})
|
|
301
|
+
} :: any)
|
|
462
302
|
end
|
|
463
303
|
|
|
464
304
|
return table.freeze(Struct)
|