@axpecter/lync 1.5.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +499 -357
- package/package.json +2 -2
- package/src/Types.luau +69 -27
- package/src/api/Group.luau +101 -106
- package/src/api/Packet.luau +191 -157
- package/src/api/Query.luau +223 -282
- 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 +189 -201
- package/src/codec/composite/Map.luau +97 -341
- package/src/codec/composite/Optional.luau +27 -23
- package/src/codec/composite/Shared.luau +96 -65
- package/src/codec/composite/Struct.luau +201 -339
- package/src/codec/composite/Tagged.luau +64 -178
- package/src/codec/composite/Tuple.luau +81 -95
- 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 +29 -19
- 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 -35
- 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 +249 -306
- package/src/init.luau +332 -173
- package/src/internal/Baseline.luau +29 -24
- package/src/internal/Channel.luau +346 -161
- package/src/internal/Middleware.luau +60 -85
- package/src/internal/Pool.luau +19 -30
- package/src/internal/Registry.luau +61 -101
- package/src/transport/Bridge.luau +64 -43
- package/src/transport/Client.luau +60 -117
- package/src/transport/Gate.luau +282 -133
- package/src/transport/Reader.luau +159 -100
- package/src/transport/Server.luau +147 -292
- package/src/api/Namespace.luau +0 -251
- package/src/codec/meta/Quantized.luau +0 -174
|
@@ -1,227 +1,301 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
-- Per-channel buffer state with
|
|
3
|
+
-- Per-channel buffer state with MSB batch framing, XOR, timestamps.
|
|
4
4
|
|
|
5
5
|
local Types = require(script.Parent.Parent.Types)
|
|
6
6
|
|
|
7
|
-
type ChannelState = Types.ChannelState
|
|
8
|
-
|
|
9
7
|
-- Constants -----------------------------------------------------------
|
|
10
8
|
|
|
11
|
-
local
|
|
12
|
-
local
|
|
13
|
-
local
|
|
9
|
+
local TS_NONE = 0
|
|
10
|
+
local TS_FRAME = 1
|
|
11
|
+
local TS_OFFSET = 2
|
|
12
|
+
local TS_FULL = 3
|
|
14
13
|
|
|
15
|
-
local
|
|
16
|
-
local
|
|
17
|
-
local band32 = bit32.band
|
|
18
|
-
local countlz = bit32.countlz
|
|
19
|
-
local lshift = bit32.lshift
|
|
14
|
+
local DEFAULT_MAX = 262144
|
|
15
|
+
local EMPTY_REFS = table.freeze({})
|
|
20
16
|
|
|
21
17
|
-- State ---------------------------------------------------------------
|
|
22
18
|
|
|
23
|
-
local _maxSize =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
local
|
|
28
|
-
|
|
29
|
-
-- Public --------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
local Channel = {}
|
|
32
|
-
|
|
33
|
-
-- Override the maximum buffer size. Call before start(). Range: 4096–1048576.
|
|
34
|
-
function Channel.setMaxSize(bytes: number): ()
|
|
35
|
-
if bytes < 4096 or bytes > 1048576 then
|
|
36
|
-
error(`[Lync] Channel max size must be 4096–1048576, got {bytes}`)
|
|
37
|
-
end
|
|
38
|
-
_maxSize = bytes
|
|
39
|
-
end
|
|
19
|
+
local _maxSize = DEFAULT_MAX
|
|
20
|
+
local _anyTimestamp = false
|
|
21
|
+
local _statsEnabled = false
|
|
22
|
+
local _frameCounter = 0
|
|
23
|
+
local _offsetMs = 0
|
|
24
|
+
local _clock = 0.0
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
return _maxSize
|
|
43
|
-
end
|
|
26
|
+
-- Private -------------------------------------------------------------
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
end
|
|
28
|
+
local countlz = bit32.countlz
|
|
29
|
+
local lshift = bit32.lshift
|
|
30
|
+
local bxor = bit32.bxor
|
|
31
|
+
local bor = bit32.bor
|
|
32
|
+
local band = bit32.band
|
|
33
|
+
local writeu8 = buffer.writeu8
|
|
34
|
+
local writeu16 = buffer.writeu16
|
|
35
|
+
local writef64 = buffer.writef64
|
|
36
|
+
local readu8 = buffer.readu8
|
|
37
|
+
local readu32 = buffer.readu32
|
|
38
|
+
local round = math.round
|
|
39
|
+
local min = math.min
|
|
58
40
|
|
|
59
|
-
|
|
60
|
-
function Channel.alloc(ch: ChannelState, bytes: number): ()
|
|
41
|
+
local function alloc(ch: Types.ChannelState, bytes: number): ()
|
|
61
42
|
local needed = ch.cursor + bytes
|
|
62
43
|
if needed <= ch.size then
|
|
63
44
|
return
|
|
64
45
|
end
|
|
65
46
|
if needed > _maxSize then
|
|
66
47
|
error(
|
|
67
|
-
|
|
48
|
+
if ch.currentPacket
|
|
49
|
+
then `[Lync] Channel: buffer overflow ({needed}B, max {_maxSize}B) in "{ch.currentPacket}"`
|
|
50
|
+
else `[Lync] Channel: buffer overflow ({needed}B, max {_maxSize}B)`
|
|
68
51
|
)
|
|
69
52
|
end
|
|
70
|
-
|
|
71
53
|
local newSize = lshift(1, 32 - countlz(needed - 1))
|
|
72
|
-
|
|
73
54
|
local newBuff = buffer.create(newSize)
|
|
74
55
|
buffer.copy(newBuff, 0, ch.buff, 0, ch.cursor)
|
|
75
56
|
ch.buff = newBuff
|
|
76
57
|
ch.size = newSize
|
|
77
58
|
end
|
|
78
59
|
|
|
79
|
-
|
|
60
|
+
-- Public --------------------------------------------------------------
|
|
80
61
|
|
|
81
|
-
|
|
82
|
-
function Channel.sealAndDump(ch: ChannelState): (buffer, { Instance })
|
|
83
|
-
if ch.lastId >= 0 then
|
|
84
|
-
buffer.writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
85
|
-
ch.lastId = -1
|
|
86
|
-
ch.countPos = 0
|
|
87
|
-
ch.itemCount = 0
|
|
88
|
-
end
|
|
62
|
+
local Channel = {}
|
|
89
63
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
64
|
+
Channel.TS_NONE = TS_NONE
|
|
65
|
+
Channel.TS_FRAME = TS_FRAME
|
|
66
|
+
Channel.TS_OFFSET = TS_OFFSET
|
|
67
|
+
Channel.TS_FULL = TS_FULL
|
|
93
68
|
|
|
94
|
-
|
|
95
|
-
return
|
|
69
|
+
function Channel.create(): Types.ChannelState
|
|
70
|
+
return {
|
|
71
|
+
buff = buffer.create(1024),
|
|
72
|
+
cursor = 0,
|
|
73
|
+
size = 1024,
|
|
74
|
+
refs = {},
|
|
75
|
+
refCount = 0,
|
|
76
|
+
lastId = -1,
|
|
77
|
+
countPos = -1,
|
|
78
|
+
itemCount = 0,
|
|
79
|
+
singleMode = false,
|
|
80
|
+
singlePos = 0,
|
|
81
|
+
deltas = {},
|
|
82
|
+
prevDump = nil,
|
|
83
|
+
prevDumpLen = 0,
|
|
84
|
+
currentPacket = nil,
|
|
85
|
+
}
|
|
96
86
|
end
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
_currentPacket = name
|
|
88
|
+
function Channel.alloc(ch: Types.ChannelState, bytes: number): ()
|
|
89
|
+
alloc(ch, bytes)
|
|
101
90
|
end
|
|
102
91
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
ch.countPos = c + 1
|
|
115
|
-
ch.cursor = c + 3
|
|
116
|
-
ch.lastId = id
|
|
117
|
-
ch.itemCount = 0
|
|
92
|
+
function Channel.reset(ch: Types.ChannelState): ()
|
|
93
|
+
ch.cursor = 0
|
|
94
|
+
ch.lastId = -1
|
|
95
|
+
ch.countPos = -1
|
|
96
|
+
ch.itemCount = 0
|
|
97
|
+
ch.singleMode = false
|
|
98
|
+
ch.singlePos = 0
|
|
99
|
+
ch.currentPacket = nil
|
|
100
|
+
if ch.refCount > 0 then
|
|
101
|
+
table.clear(ch.refs)
|
|
102
|
+
ch.refCount = 0
|
|
118
103
|
end
|
|
119
|
-
|
|
120
|
-
ch.itemCount += 1
|
|
121
|
-
codec.write(ch, data)
|
|
122
104
|
end
|
|
123
105
|
|
|
124
|
-
--
|
|
125
|
-
function
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
106
|
+
--[[
|
|
107
|
+
Resolves the batch-open function for a given timestamp mode.
|
|
108
|
+
The returned function handles MSB single-item framing:
|
|
109
|
+
First item: writes [0x80 | id] (single mode, no count).
|
|
110
|
+
Second item: upgrades to multi mode with u16 count.
|
|
111
|
+
]]
|
|
112
|
+
function Channel.resolveOpenFn(
|
|
113
|
+
timestampMode: number,
|
|
114
|
+
name: string
|
|
115
|
+
): (ch: Types.ChannelState, id: number) -> ()
|
|
116
|
+
if timestampMode == TS_NONE then
|
|
117
|
+
return function(ch: Types.ChannelState, id: number): ()
|
|
118
|
+
-- Seal previous batch
|
|
119
|
+
if ch.lastId >= 0 and not ch.singleMode then
|
|
120
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
121
|
+
end
|
|
122
|
+
ch.currentPacket = name
|
|
123
|
+
ch.lastId = id
|
|
124
|
+
ch.itemCount = 0
|
|
125
|
+
|
|
126
|
+
-- Write single-mode header: [1IIIIIII]
|
|
127
|
+
local cursor = ch.cursor
|
|
128
|
+
if cursor + 1 > ch.size then
|
|
129
|
+
alloc(ch, 1)
|
|
130
|
+
end
|
|
131
|
+
writeu8(ch.buff, cursor, bor(0x80, id))
|
|
132
|
+
ch.singleMode = true
|
|
133
|
+
ch.singlePos = cursor
|
|
134
|
+
ch.cursor = cursor + 1
|
|
135
|
+
end
|
|
136
|
+
elseif timestampMode == TS_FRAME then
|
|
137
|
+
return function(ch: Types.ChannelState, id: number): ()
|
|
138
|
+
if ch.lastId >= 0 and not ch.singleMode then
|
|
139
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
140
|
+
end
|
|
141
|
+
ch.currentPacket = name
|
|
142
|
+
ch.lastId = id
|
|
143
|
+
ch.itemCount = 0
|
|
144
|
+
|
|
145
|
+
local cursor = ch.cursor
|
|
146
|
+
if cursor + 2 > ch.size then
|
|
147
|
+
alloc(ch, 2)
|
|
148
|
+
end
|
|
149
|
+
local b = ch.buff
|
|
150
|
+
writeu8(b, cursor, bor(0x80, id))
|
|
151
|
+
writeu8(b, cursor + 1, _frameCounter)
|
|
152
|
+
ch.singleMode = true
|
|
153
|
+
ch.singlePos = cursor
|
|
154
|
+
ch.cursor = cursor + 2
|
|
155
|
+
end
|
|
156
|
+
elseif timestampMode == TS_OFFSET then
|
|
157
|
+
return function(ch: Types.ChannelState, id: number): ()
|
|
158
|
+
if ch.lastId >= 0 and not ch.singleMode then
|
|
159
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
160
|
+
end
|
|
161
|
+
ch.currentPacket = name
|
|
162
|
+
ch.lastId = id
|
|
163
|
+
ch.itemCount = 0
|
|
164
|
+
|
|
165
|
+
local cursor = ch.cursor
|
|
166
|
+
if cursor + 3 > ch.size then
|
|
167
|
+
alloc(ch, 3)
|
|
168
|
+
end
|
|
169
|
+
local b = ch.buff
|
|
170
|
+
writeu8(b, cursor, bor(0x80, id))
|
|
171
|
+
writeu16(b, cursor + 1, _offsetMs)
|
|
172
|
+
ch.singleMode = true
|
|
173
|
+
ch.singlePos = cursor
|
|
174
|
+
ch.cursor = cursor + 3
|
|
175
|
+
end
|
|
176
|
+
else -- TS_FULL
|
|
177
|
+
return function(ch: Types.ChannelState, id: number): ()
|
|
178
|
+
if ch.lastId >= 0 and not ch.singleMode then
|
|
179
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
180
|
+
end
|
|
181
|
+
ch.currentPacket = name
|
|
182
|
+
ch.lastId = id
|
|
183
|
+
ch.itemCount = 0
|
|
184
|
+
|
|
185
|
+
local cursor = ch.cursor
|
|
186
|
+
if cursor + 9 > ch.size then
|
|
187
|
+
alloc(ch, 9)
|
|
188
|
+
end
|
|
189
|
+
local b = ch.buff
|
|
190
|
+
writeu8(b, cursor, bor(0x80, id))
|
|
191
|
+
writef64(b, cursor + 1, _clock)
|
|
192
|
+
ch.singleMode = true
|
|
193
|
+
ch.singlePos = cursor
|
|
194
|
+
ch.cursor = cursor + 9
|
|
135
195
|
end
|
|
136
|
-
|
|
137
|
-
alloc(ch, 3)
|
|
138
|
-
local c = ch.cursor
|
|
139
|
-
buffer.writeu8(ch.buff, c, id)
|
|
140
|
-
ch.countPos = c + 1
|
|
141
|
-
ch.cursor = c + 3
|
|
142
|
-
ch.lastId = id
|
|
143
|
-
ch.itemCount = 0
|
|
144
196
|
end
|
|
197
|
+
end
|
|
145
198
|
|
|
146
|
-
|
|
199
|
+
--[[
|
|
200
|
+
Writes one item into a batch. Handles MSB single→multi upgrade.
|
|
201
|
+
]]
|
|
202
|
+
function Channel.writeBatch(ch: Types.ChannelState, reg: Types.Registration, data: any): ()
|
|
203
|
+
if reg.id ~= ch.lastId or ch.itemCount >= 0xFFFF then
|
|
204
|
+
reg._openFn(ch, reg.id)
|
|
205
|
+
elseif ch.singleMode then
|
|
206
|
+
--[[
|
|
207
|
+
Second item for same packet: upgrade to multi mode.
|
|
208
|
+
Rewrite header byte to clear MSB, insert u16 count after.
|
|
209
|
+
]]
|
|
210
|
+
local b = ch.buff
|
|
211
|
+
local headerPos = ch.singlePos
|
|
212
|
+
writeu8(b, headerPos, reg.id) -- MSB cleared = multi-item mode
|
|
213
|
+
|
|
214
|
+
local tsBytes = reg.headerSize - 1
|
|
215
|
+
local afterTs = headerPos + 1 + tsBytes
|
|
216
|
+
|
|
217
|
+
--[[
|
|
218
|
+
Upgrading from single→multi requires inserting 2 bytes (u16 count)
|
|
219
|
+
between the timestamp and the first item's payload, shifting
|
|
220
|
+
existing data right by 2.
|
|
221
|
+
]]
|
|
222
|
+
local cursor = ch.cursor
|
|
223
|
+
local payloadLen = cursor - afterTs
|
|
224
|
+
if cursor + 2 > ch.size then
|
|
225
|
+
alloc(ch, 2)
|
|
226
|
+
end
|
|
227
|
+
local b2 = ch.buff
|
|
147
228
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
ch.cursor += srcLen
|
|
152
|
-
end
|
|
229
|
+
if payloadLen > 0 then
|
|
230
|
+
buffer.copy(b2, afterTs + 2, b2, afterTs, payloadLen)
|
|
231
|
+
end
|
|
153
232
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
233
|
+
ch.countPos = afterTs
|
|
234
|
+
writeu16(b2, afterTs, 0) -- placeholder u16; sealAndDump writes final count
|
|
235
|
+
ch.cursor = cursor + 2
|
|
236
|
+
ch.singleMode = false
|
|
157
237
|
end
|
|
158
|
-
end
|
|
159
238
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
buffer.writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
163
|
-
ch.lastId = -1
|
|
164
|
-
ch.countPos = 0
|
|
165
|
-
ch.itemCount = 0
|
|
166
|
-
end
|
|
239
|
+
ch.itemCount += 1
|
|
240
|
+
reg.codec.write(ch, data)
|
|
167
241
|
end
|
|
168
242
|
|
|
169
243
|
--[[
|
|
170
|
-
|
|
171
|
-
id(u8) + correlationId(u16) + status(u8)
|
|
172
|
-
Status 0 = payload follows. Status 1 = nil response.
|
|
244
|
+
Seals the current batch and returns a snapshot for transmission.
|
|
173
245
|
]]
|
|
174
|
-
function Channel.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
codec: any?,
|
|
180
|
-
data: any?
|
|
181
|
-
): ()
|
|
182
|
-
_currentPacket = name
|
|
183
|
-
sealOpen(ch)
|
|
246
|
+
function Channel.sealAndDump(ch: Types.ChannelState): (buffer, { Instance }, number, number)
|
|
247
|
+
-- Multi-item batches need their count finalized before transmission
|
|
248
|
+
if ch.lastId >= 0 and not ch.singleMode and ch.countPos >= 0 then
|
|
249
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
250
|
+
end
|
|
184
251
|
|
|
185
|
-
|
|
186
|
-
local
|
|
187
|
-
buffer.
|
|
188
|
-
buffer.writeu16(ch.buff, c + 1, correlationId)
|
|
252
|
+
local cursor = ch.cursor
|
|
253
|
+
local snapshot = buffer.create(cursor)
|
|
254
|
+
buffer.copy(snapshot, 0, ch.buff, 0, cursor)
|
|
189
255
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
256
|
+
local refs: { Instance }
|
|
257
|
+
local refCount = ch.refCount
|
|
258
|
+
if refCount > 0 then
|
|
259
|
+
refs = table.clone(ch.refs)
|
|
194
260
|
else
|
|
195
|
-
|
|
196
|
-
ch.cursor = c + 4
|
|
261
|
+
refs = EMPTY_REFS :: any
|
|
197
262
|
end
|
|
263
|
+
|
|
264
|
+
return snapshot, refs, cursor, refCount
|
|
198
265
|
end
|
|
199
266
|
|
|
200
|
-
--
|
|
201
|
-
|
|
202
|
-
|
|
267
|
+
--[[
|
|
268
|
+
XOR current frame against previous for deflate-friendly output.
|
|
269
|
+
]]
|
|
270
|
+
function Channel.xorApply(
|
|
271
|
+
current: buffer,
|
|
272
|
+
curLen: number,
|
|
273
|
+
previous: buffer?,
|
|
274
|
+
prevLen: number
|
|
275
|
+
): buffer
|
|
203
276
|
if not previous then
|
|
204
277
|
return current
|
|
205
278
|
end
|
|
206
279
|
|
|
207
|
-
local prevLen = buffer.len(previous :: buffer)
|
|
208
|
-
if prevLen == 0 then
|
|
209
|
-
return current
|
|
210
|
-
end
|
|
211
|
-
|
|
212
280
|
local result = buffer.create(curLen)
|
|
213
281
|
local overlap = min(curLen, prevLen)
|
|
214
282
|
local prev = previous :: buffer
|
|
215
283
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
284
|
+
-- u32 chunks
|
|
285
|
+
local aligned = overlap - (overlap % 4)
|
|
286
|
+
local i = 0
|
|
287
|
+
while i < aligned do
|
|
288
|
+
buffer.writeu32(result, i, bxor(readu32(current, i), readu32(prev, i)))
|
|
289
|
+
i += 4
|
|
219
290
|
end
|
|
220
291
|
|
|
221
|
-
|
|
222
|
-
|
|
292
|
+
-- u8 remainder of overlap
|
|
293
|
+
while i < overlap do
|
|
294
|
+
writeu8(result, i, bxor(readu8(current, i), readu8(prev, i)))
|
|
295
|
+
i += 1
|
|
223
296
|
end
|
|
224
297
|
|
|
298
|
+
-- Excess bytes (current longer than previous)
|
|
225
299
|
if curLen > overlap then
|
|
226
300
|
buffer.copy(result, overlap, current, overlap, curLen - overlap)
|
|
227
301
|
end
|
|
@@ -229,12 +303,123 @@ function Channel.xorApply(current: buffer, previous: buffer?): buffer
|
|
|
229
303
|
return result
|
|
230
304
|
end
|
|
231
305
|
|
|
232
|
-
|
|
233
|
-
|
|
306
|
+
--[[
|
|
307
|
+
Writes a query frame with MSB encoding for nil-status.
|
|
308
|
+
MSB set = nil response, MSB clear = payload follows.
|
|
309
|
+
]]
|
|
310
|
+
function Channel.writeQuery(
|
|
311
|
+
ch: Types.ChannelState,
|
|
312
|
+
id: number,
|
|
313
|
+
correlationId: number,
|
|
314
|
+
codec: Types.InternalCodec<any>?,
|
|
315
|
+
data: any
|
|
316
|
+
): ()
|
|
317
|
+
-- Queries are standalone frames, so close any open packet batch first
|
|
318
|
+
if ch.lastId >= 0 and not ch.singleMode and ch.countPos >= 0 then
|
|
319
|
+
writeu16(ch.buff, ch.countPos, ch.itemCount)
|
|
320
|
+
end
|
|
234
321
|
ch.lastId = -1
|
|
235
|
-
ch.
|
|
236
|
-
|
|
237
|
-
|
|
322
|
+
ch.singleMode = false
|
|
323
|
+
|
|
324
|
+
if codec and data ~= nil then
|
|
325
|
+
local cursor = ch.cursor
|
|
326
|
+
if cursor + 1 > ch.size then
|
|
327
|
+
alloc(ch, 1)
|
|
328
|
+
end
|
|
329
|
+
writeu8(ch.buff, cursor, id)
|
|
330
|
+
ch.cursor = cursor + 1
|
|
331
|
+
|
|
332
|
+
-- Inlined varint avoids the function call overhead for correlation IDs
|
|
333
|
+
if correlationId <= 0xBF then
|
|
334
|
+
local c2 = ch.cursor
|
|
335
|
+
if c2 + 1 > ch.size then
|
|
336
|
+
alloc(ch, 1)
|
|
337
|
+
end
|
|
338
|
+
writeu8(ch.buff, c2, correlationId)
|
|
339
|
+
ch.cursor = c2 + 1
|
|
340
|
+
elseif correlationId <= 8383 then
|
|
341
|
+
local c2 = ch.cursor
|
|
342
|
+
if c2 + 2 > ch.size then
|
|
343
|
+
alloc(ch, 2)
|
|
344
|
+
end
|
|
345
|
+
local adjusted = correlationId - 192
|
|
346
|
+
writeu8(ch.buff, c2, bor(0xC0, band(adjusted // 256, 0x1F)))
|
|
347
|
+
writeu8(ch.buff, c2 + 1, band(adjusted, 0xFF))
|
|
348
|
+
ch.cursor = c2 + 2
|
|
349
|
+
else
|
|
350
|
+
local c2 = ch.cursor
|
|
351
|
+
if c2 + 5 > ch.size then
|
|
352
|
+
alloc(ch, 5)
|
|
353
|
+
end
|
|
354
|
+
writeu8(ch.buff, c2, 0xF0)
|
|
355
|
+
buffer.writeu32(ch.buff, c2 + 1, correlationId)
|
|
356
|
+
ch.cursor = c2 + 5
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
codec.write(ch, data)
|
|
360
|
+
else
|
|
361
|
+
-- MSB set signals nil response to the reader
|
|
362
|
+
local cursor = ch.cursor
|
|
363
|
+
if cursor + 1 > ch.size then
|
|
364
|
+
alloc(ch, 1)
|
|
365
|
+
end
|
|
366
|
+
writeu8(ch.buff, cursor, bor(0x80, id))
|
|
367
|
+
ch.cursor = cursor + 1
|
|
368
|
+
|
|
369
|
+
if correlationId <= 0xBF then
|
|
370
|
+
local c2 = ch.cursor
|
|
371
|
+
if c2 + 1 > ch.size then
|
|
372
|
+
alloc(ch, 1)
|
|
373
|
+
end
|
|
374
|
+
writeu8(ch.buff, c2, correlationId)
|
|
375
|
+
ch.cursor = c2 + 1
|
|
376
|
+
elseif correlationId <= 8383 then
|
|
377
|
+
local c2 = ch.cursor
|
|
378
|
+
if c2 + 2 > ch.size then
|
|
379
|
+
alloc(ch, 2)
|
|
380
|
+
end
|
|
381
|
+
local adjusted = correlationId - 192
|
|
382
|
+
writeu8(ch.buff, c2, bor(0xC0, band(adjusted // 256, 0x1F)))
|
|
383
|
+
writeu8(ch.buff, c2 + 1, band(adjusted, 0xFF))
|
|
384
|
+
ch.cursor = c2 + 2
|
|
385
|
+
else
|
|
386
|
+
local c2 = ch.cursor
|
|
387
|
+
if c2 + 5 > ch.size then
|
|
388
|
+
alloc(ch, 5)
|
|
389
|
+
end
|
|
390
|
+
writeu8(ch.buff, c2, 0xF0)
|
|
391
|
+
buffer.writeu32(ch.buff, c2 + 1, correlationId)
|
|
392
|
+
ch.cursor = c2 + 5
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
-- Configuration -------------------------------------------------------
|
|
398
|
+
|
|
399
|
+
function Channel.setMaxSize(bytes: number): ()
|
|
400
|
+
_maxSize = bytes
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
function Channel.updateTimestamp(): ()
|
|
404
|
+
_frameCounter = (_frameCounter + 1) % 256
|
|
405
|
+
_offsetMs = round((os.clock() % 65.536) * 1000)
|
|
406
|
+
_clock = os.clock()
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
function Channel.enableTimestamps(): ()
|
|
410
|
+
_anyTimestamp = true
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
function Channel.hasTimestamps(): boolean
|
|
414
|
+
return _anyTimestamp
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
function Channel.enableStats(): ()
|
|
418
|
+
_statsEnabled = true
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
function Channel.statsEnabled(): boolean
|
|
422
|
+
return _statsEnabled
|
|
238
423
|
end
|
|
239
424
|
|
|
240
425
|
return table.freeze(Channel)
|