@axpecter/lync 1.3.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 +300 -0
- package/package.json +38 -0
- package/src/Types.luau +63 -0
- package/src/api/Group.luau +126 -0
- package/src/api/Namespace.luau +226 -0
- package/src/api/Packet.luau +147 -0
- package/src/api/Query.luau +295 -0
- package/src/api/Signal.luau +224 -0
- package/src/codec/Base.luau +49 -0
- package/src/codec/composite/Array.luau +275 -0
- package/src/codec/composite/Map.luau +395 -0
- package/src/codec/composite/Optional.luau +47 -0
- package/src/codec/composite/Shared.luau +151 -0
- package/src/codec/composite/Struct.luau +440 -0
- package/src/codec/composite/Tagged.luau +222 -0
- package/src/codec/composite/Tuple.luau +143 -0
- package/src/codec/datatype/Buffer.luau +44 -0
- package/src/codec/datatype/CFrame.luau +51 -0
- package/src/codec/datatype/Color.luau +22 -0
- package/src/codec/datatype/Instance.luau +48 -0
- package/src/codec/datatype/IntVector.luau +25 -0
- package/src/codec/datatype/NumberRange.luau +14 -0
- package/src/codec/datatype/Ray.luau +27 -0
- package/src/codec/datatype/Rect.luau +21 -0
- package/src/codec/datatype/Region.luau +58 -0
- package/src/codec/datatype/Sequence.luau +129 -0
- package/src/codec/datatype/String.luau +87 -0
- package/src/codec/datatype/UDim.luau +27 -0
- package/src/codec/datatype/Vector.luau +25 -0
- package/src/codec/meta/Auto.luau +353 -0
- package/src/codec/meta/Bitfield.luau +191 -0
- package/src/codec/meta/Custom.luau +27 -0
- package/src/codec/meta/Enum.luau +80 -0
- package/src/codec/meta/Nothing.luau +9 -0
- package/src/codec/meta/Quantized.luau +170 -0
- package/src/codec/meta/Unknown.luau +35 -0
- package/src/codec/primitive/Bool.luau +30 -0
- package/src/codec/primitive/Float16.luau +111 -0
- package/src/codec/primitive/Number.luau +48 -0
- package/src/codec/primitive/Varint.luau +76 -0
- package/src/index.d.ts +279 -0
- package/src/init.luau +161 -0
- package/src/internal/Baseline.luau +41 -0
- package/src/internal/Channel.luau +235 -0
- package/src/internal/Middleware.luau +109 -0
- package/src/internal/Pool.luau +68 -0
- package/src/internal/Registry.luau +146 -0
- package/src/transport/Bridge.luau +66 -0
- package/src/transport/Client.luau +151 -0
- package/src/transport/Gate.luau +222 -0
- package/src/transport/Reader.luau +175 -0
- package/src/transport/Server.luau +364 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Fixed-point compression. Maps float ranges to integer ranges.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
8
|
+
|
|
9
|
+
type ChannelState = Types.ChannelState
|
|
10
|
+
type Codec<T> = Types.Codec<T>
|
|
11
|
+
|
|
12
|
+
-- Constants --------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local clamp = math.clamp
|
|
15
|
+
local round = math.round
|
|
16
|
+
local ceil = math.ceil
|
|
17
|
+
local min = math.min
|
|
18
|
+
local max = math.max
|
|
19
|
+
|
|
20
|
+
local U32_MAX = 4294967295
|
|
21
|
+
|
|
22
|
+
-- Private ----------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
local function selectIO (bytes: number): ((b: buffer, offset: number, v: number) -> (), (
|
|
25
|
+
b: buffer,
|
|
26
|
+
offset: number
|
|
27
|
+
) -> number)
|
|
28
|
+
if bytes == 1 then
|
|
29
|
+
return buffer.writeu8, buffer.readu8
|
|
30
|
+
end
|
|
31
|
+
if bytes == 2 then
|
|
32
|
+
return buffer.writeu16, buffer.readu16
|
|
33
|
+
end
|
|
34
|
+
return buffer.writeu32, buffer.readu32
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
local function selectWidth (maxInt: number): number
|
|
38
|
+
if maxInt <= 255 then
|
|
39
|
+
return 1
|
|
40
|
+
end
|
|
41
|
+
if maxInt <= 65535 then
|
|
42
|
+
return 2
|
|
43
|
+
end
|
|
44
|
+
return 4
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
-- Public -----------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
local Quantized = {}
|
|
50
|
+
|
|
51
|
+
-- Shared validation and precomputation for both float and vec3 quantizers.
|
|
52
|
+
local function setup (minVal: number, maxVal: number, precision: number)
|
|
53
|
+
if minVal > maxVal then
|
|
54
|
+
error (`[Lync] Quantized min > max: {minVal} > {maxVal}`)
|
|
55
|
+
end
|
|
56
|
+
if precision <= 0 then
|
|
57
|
+
error (`[Lync] Quantized precision must be positive: {precision}`)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
local delta = maxVal - minVal
|
|
61
|
+
if delta == 0 then
|
|
62
|
+
return nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
local maxInt = min (U32_MAX, max (1, ceil (delta / precision)))
|
|
66
|
+
local bytes = selectWidth (maxInt)
|
|
67
|
+
local wfn, rfn = selectIO (bytes)
|
|
68
|
+
local invDelta = 1 / delta
|
|
69
|
+
local invMaxInt = delta / maxInt
|
|
70
|
+
|
|
71
|
+
return delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
function Quantized.float (minVal: number, maxVal: number, precision: number): Codec<number>
|
|
75
|
+
local delta, maxInt, bytes, wfn, rfn, invDelta, invMaxInt = setup (minVal, maxVal, precision)
|
|
76
|
+
if not delta then
|
|
77
|
+
return table.freeze ({
|
|
78
|
+
_size = 0,
|
|
79
|
+
_directWrite = function (_b: buffer, _offset: number, _value: number): () end,
|
|
80
|
+
_directRead = function (_b: buffer, _offset: number): number
|
|
81
|
+
return minVal
|
|
82
|
+
end,
|
|
83
|
+
write = function (_ch: ChannelState, _value: number): () end,
|
|
84
|
+
read = function (_src: buffer, _pos: number, _refs: { Instance }?): (number, number)
|
|
85
|
+
return minVal, 0
|
|
86
|
+
end,
|
|
87
|
+
})
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
return table.freeze ({
|
|
91
|
+
_size = bytes,
|
|
92
|
+
_directWrite = function (b: buffer, offset: number, value: number): ()
|
|
93
|
+
wfn (b, offset, round (clamp ((value - minVal) * invDelta, 0, 1) * maxInt))
|
|
94
|
+
end,
|
|
95
|
+
_directRead = function (b: buffer, offset: number): number
|
|
96
|
+
return rfn (b, offset) * invMaxInt + minVal
|
|
97
|
+
end,
|
|
98
|
+
write = function (ch: ChannelState, value: number): ()
|
|
99
|
+
local c = ch.cursor
|
|
100
|
+
if c + bytes > ch.size then
|
|
101
|
+
alloc (ch, bytes)
|
|
102
|
+
end
|
|
103
|
+
wfn (ch.buff, c, round (clamp ((value - minVal) * invDelta, 0, 1) * maxInt))
|
|
104
|
+
ch.cursor = c + bytes
|
|
105
|
+
end,
|
|
106
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (number, number)
|
|
107
|
+
return rfn (src, pos) * invMaxInt + minVal, bytes
|
|
108
|
+
end,
|
|
109
|
+
})
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
function Quantized.vec3 (minVal: number, maxVal: number, precision: number): Codec<Vector3>
|
|
113
|
+
local delta, maxInt, compBytes, wfn, rfn, invDelta, invMaxInt =
|
|
114
|
+
setup (minVal, maxVal, precision)
|
|
115
|
+
if not delta then
|
|
116
|
+
local val = Vector3.new (minVal, minVal, minVal)
|
|
117
|
+
return table.freeze ({
|
|
118
|
+
_size = 0,
|
|
119
|
+
_directWrite = function (_b: buffer, _offset: number, _value: Vector3): () end,
|
|
120
|
+
_directRead = function (_b: buffer, _offset: number): Vector3
|
|
121
|
+
return val
|
|
122
|
+
end,
|
|
123
|
+
write = function (_ch: ChannelState, _value: Vector3): () end,
|
|
124
|
+
read = function (_src: buffer, _pos: number, _refs: { Instance }?): (Vector3, number)
|
|
125
|
+
return val, 0
|
|
126
|
+
end,
|
|
127
|
+
})
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
local totalBytes = compBytes * 3
|
|
131
|
+
local stride = compBytes
|
|
132
|
+
local stride2 = compBytes * 2
|
|
133
|
+
|
|
134
|
+
return table.freeze ({
|
|
135
|
+
_size = totalBytes,
|
|
136
|
+
_directWrite = function (b: buffer, offset: number, value: Vector3): ()
|
|
137
|
+
wfn (b, offset, round (clamp ((value.X - minVal) * invDelta, 0, 1) * maxInt))
|
|
138
|
+
wfn (b, offset + stride, round (clamp ((value.Y - minVal) * invDelta, 0, 1) * maxInt))
|
|
139
|
+
wfn (b, offset + stride2, round (clamp ((value.Z - minVal) * invDelta, 0, 1) * maxInt))
|
|
140
|
+
end,
|
|
141
|
+
_directRead = function (b: buffer, offset: number): Vector3
|
|
142
|
+
return Vector3.new (
|
|
143
|
+
rfn (b, offset) * invMaxInt + minVal,
|
|
144
|
+
rfn (b, offset + stride) * invMaxInt + minVal,
|
|
145
|
+
rfn (b, offset + stride2) * invMaxInt + minVal
|
|
146
|
+
)
|
|
147
|
+
end,
|
|
148
|
+
write = function (ch: ChannelState, value: Vector3): ()
|
|
149
|
+
local c = ch.cursor
|
|
150
|
+
if c + totalBytes > ch.size then
|
|
151
|
+
alloc (ch, totalBytes)
|
|
152
|
+
end
|
|
153
|
+
local b = ch.buff
|
|
154
|
+
wfn (b, c, round (clamp ((value.X - minVal) * invDelta, 0, 1) * maxInt))
|
|
155
|
+
wfn (b, c + stride, round (clamp ((value.Y - minVal) * invDelta, 0, 1) * maxInt))
|
|
156
|
+
wfn (b, c + stride2, round (clamp ((value.Z - minVal) * invDelta, 0, 1) * maxInt))
|
|
157
|
+
ch.cursor = c + totalBytes
|
|
158
|
+
end,
|
|
159
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (Vector3, number)
|
|
160
|
+
return Vector3.new (
|
|
161
|
+
rfn (src, pos) * invMaxInt + minVal,
|
|
162
|
+
rfn (src, pos + stride) * invMaxInt + minVal,
|
|
163
|
+
rfn (src, pos + stride2) * invMaxInt + minVal
|
|
164
|
+
),
|
|
165
|
+
totalBytes
|
|
166
|
+
end,
|
|
167
|
+
})
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
return table.freeze (Quantized)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Sidecar codec. Bypasses buffer serialization, uses Roblox remote refs.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
|
|
8
|
+
return table.freeze ({
|
|
9
|
+
unknown = table.freeze ({
|
|
10
|
+
write = function (ch: any, value: any): ()
|
|
11
|
+
local refs = ch.refs
|
|
12
|
+
local idx = #refs + 1
|
|
13
|
+
if idx > 65535 then
|
|
14
|
+
error (`[Lync] Unknown ref overflow: {idx} exceeds u16 max`)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
refs[idx] = value
|
|
18
|
+
|
|
19
|
+
local c = ch.cursor
|
|
20
|
+
if c + 2 > ch.size then
|
|
21
|
+
alloc (ch, 2)
|
|
22
|
+
end
|
|
23
|
+
buffer.writeu16 (ch.buff, c, idx)
|
|
24
|
+
ch.cursor = c + 2
|
|
25
|
+
end,
|
|
26
|
+
read = function (src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
27
|
+
if not refs then
|
|
28
|
+
error ("[Lync] Unknown read requires refs array")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
local idx = buffer.readu16 (src, pos)
|
|
32
|
+
return refs[idx], 2
|
|
33
|
+
end,
|
|
34
|
+
}),
|
|
35
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Boolean codec. _isBool tells struct to pack into bitfields.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
|
|
8
|
+
return table.freeze ({
|
|
9
|
+
bool = table.freeze ({
|
|
10
|
+
_size = 1,
|
|
11
|
+
_isBool = true,
|
|
12
|
+
_directWrite = function (b: buffer, offset: number, value: boolean): ()
|
|
13
|
+
buffer.writeu8 (b, offset, if value then 1 else 0)
|
|
14
|
+
end,
|
|
15
|
+
_directRead = function (b: buffer, offset: number): boolean
|
|
16
|
+
return buffer.readu8 (b, offset) ~= 0
|
|
17
|
+
end,
|
|
18
|
+
write = function (ch: any, value: boolean): ()
|
|
19
|
+
local c = ch.cursor
|
|
20
|
+
if c + 1 > ch.size then
|
|
21
|
+
alloc (ch, 1)
|
|
22
|
+
end
|
|
23
|
+
buffer.writeu8 (ch.buff, c, if value then 1 else 0)
|
|
24
|
+
ch.cursor = c + 1
|
|
25
|
+
end,
|
|
26
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (boolean, number)
|
|
27
|
+
return buffer.readu8 (src, pos) ~= 0, 1
|
|
28
|
+
end,
|
|
29
|
+
}),
|
|
30
|
+
})
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Half-precision float codec. 2 bytes, ±65504, ~3 decimal digits.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local alloc = Channel.alloc
|
|
7
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
8
|
+
|
|
9
|
+
type ChannelState = Types.ChannelState
|
|
10
|
+
|
|
11
|
+
-- Constants --------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
local MAX_HALF = 65504
|
|
14
|
+
local BIAS = 15
|
|
15
|
+
local EXP_MAX = 31
|
|
16
|
+
local MANT_BITS = 10
|
|
17
|
+
local MANT_SCALE = 1024
|
|
18
|
+
local SIGN_BIT = 0x8000
|
|
19
|
+
local INF_BITS = 0x7C00
|
|
20
|
+
local NAN_BITS = 0x7E00
|
|
21
|
+
|
|
22
|
+
local band = bit32.band
|
|
23
|
+
local bor = bit32.bor
|
|
24
|
+
local lshift = bit32.lshift
|
|
25
|
+
local rshift = bit32.rshift
|
|
26
|
+
local frexp = math.frexp
|
|
27
|
+
local ldexp = math.ldexp
|
|
28
|
+
local round = math.round
|
|
29
|
+
local huge = math.huge
|
|
30
|
+
|
|
31
|
+
-- Private ----------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
local function encode (value: number): number
|
|
34
|
+
if value ~= value then
|
|
35
|
+
return NAN_BITS
|
|
36
|
+
end
|
|
37
|
+
if value == 0 then
|
|
38
|
+
return 0
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
local sign = 0
|
|
42
|
+
if value < 0 then
|
|
43
|
+
sign = SIGN_BIT
|
|
44
|
+
value = -value
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if value > MAX_HALF then
|
|
48
|
+
return bor (sign, INF_BITS)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
local mantissa, exponent = frexp (value)
|
|
52
|
+
exponent += 14
|
|
53
|
+
|
|
54
|
+
local bits: number
|
|
55
|
+
if exponent <= 0 then
|
|
56
|
+
local shift = exponent + MANT_BITS - 1
|
|
57
|
+
if shift < 0 then
|
|
58
|
+
return sign
|
|
59
|
+
end
|
|
60
|
+
bits = round (mantissa * lshift (1, shift))
|
|
61
|
+
elseif exponent >= EXP_MAX then
|
|
62
|
+
bits = INF_BITS
|
|
63
|
+
else
|
|
64
|
+
bits = bor (lshift (exponent, MANT_BITS), round ((mantissa * 2 - 1) * MANT_SCALE))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
return bor (sign, bits)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
local function decode (packed: number): number
|
|
71
|
+
local exponent = band (rshift (packed, MANT_BITS), 0x1F)
|
|
72
|
+
local mantissa = band (packed, 0x03FF)
|
|
73
|
+
|
|
74
|
+
local result: number
|
|
75
|
+
if exponent == 0 then
|
|
76
|
+
result = if mantissa == 0 then 0 else ldexp (mantissa / MANT_SCALE, 1 - BIAS)
|
|
77
|
+
elseif exponent == EXP_MAX then
|
|
78
|
+
result = if mantissa == 0 then huge else 0 / 0
|
|
79
|
+
else
|
|
80
|
+
result = ldexp (mantissa / MANT_SCALE + 1, exponent - BIAS)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return if band (packed, SIGN_BIT) ~= 0 then -result else result
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
-- Public -----------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
local Float16 = {}
|
|
89
|
+
|
|
90
|
+
Float16.f16 = table.freeze ({
|
|
91
|
+
_size = 2,
|
|
92
|
+
_directWrite = function (b: buffer, offset: number, value: number): ()
|
|
93
|
+
buffer.writeu16 (b, offset, encode (value))
|
|
94
|
+
end,
|
|
95
|
+
_directRead = function (b: buffer, offset: number): number
|
|
96
|
+
return decode (buffer.readu16 (b, offset))
|
|
97
|
+
end,
|
|
98
|
+
write = function (ch: ChannelState, value: number): ()
|
|
99
|
+
local c = ch.cursor
|
|
100
|
+
if c + 2 > ch.size then
|
|
101
|
+
alloc (ch, 2)
|
|
102
|
+
end
|
|
103
|
+
buffer.writeu16 (ch.buff, c, encode (value))
|
|
104
|
+
ch.cursor = c + 2
|
|
105
|
+
end,
|
|
106
|
+
read = function (src: buffer, pos: number, _refs: { Instance }?): (number, number)
|
|
107
|
+
return decode (buffer.readu16 (src, pos)), 2
|
|
108
|
+
end,
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
return table.freeze (Float16)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- u8, u16, u32, i8, i16, i32, f32, f64 codecs.
|
|
4
|
+
|
|
5
|
+
local Base = require (script.Parent.Parent.Base)
|
|
6
|
+
|
|
7
|
+
return table.freeze ({
|
|
8
|
+
u8 = Base.define (1, function (b: buffer, o: number, v: number): ()
|
|
9
|
+
buffer.writeu8 (b, o, v)
|
|
10
|
+
end, function (b: buffer, o: number): number
|
|
11
|
+
return buffer.readu8 (b, o)
|
|
12
|
+
end),
|
|
13
|
+
u16 = Base.define (2, function (b: buffer, o: number, v: number): ()
|
|
14
|
+
buffer.writeu16 (b, o, v)
|
|
15
|
+
end, function (b: buffer, o: number): number
|
|
16
|
+
return buffer.readu16 (b, o)
|
|
17
|
+
end),
|
|
18
|
+
u32 = Base.define (4, function (b: buffer, o: number, v: number): ()
|
|
19
|
+
buffer.writeu32 (b, o, v)
|
|
20
|
+
end, function (b: buffer, o: number): number
|
|
21
|
+
return buffer.readu32 (b, o)
|
|
22
|
+
end),
|
|
23
|
+
i8 = Base.define (1, function (b: buffer, o: number, v: number): ()
|
|
24
|
+
buffer.writei8 (b, o, v)
|
|
25
|
+
end, function (b: buffer, o: number): number
|
|
26
|
+
return buffer.readi8 (b, o)
|
|
27
|
+
end),
|
|
28
|
+
i16 = Base.define (2, function (b: buffer, o: number, v: number): ()
|
|
29
|
+
buffer.writei16 (b, o, v)
|
|
30
|
+
end, function (b: buffer, o: number): number
|
|
31
|
+
return buffer.readi16 (b, o)
|
|
32
|
+
end),
|
|
33
|
+
i32 = Base.define (4, function (b: buffer, o: number, v: number): ()
|
|
34
|
+
buffer.writei32 (b, o, v)
|
|
35
|
+
end, function (b: buffer, o: number): number
|
|
36
|
+
return buffer.readi32 (b, o)
|
|
37
|
+
end),
|
|
38
|
+
f32 = Base.define (4, function (b: buffer, o: number, v: number): ()
|
|
39
|
+
buffer.writef32 (b, o, v)
|
|
40
|
+
end, function (b: buffer, o: number): number
|
|
41
|
+
return buffer.readf32 (b, o)
|
|
42
|
+
end),
|
|
43
|
+
f64 = Base.define (8, function (b: buffer, o: number, v: number): ()
|
|
44
|
+
buffer.writef64 (b, o, v)
|
|
45
|
+
end, function (b: buffer, o: number): number
|
|
46
|
+
return buffer.readf64 (b, o)
|
|
47
|
+
end),
|
|
48
|
+
})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- LEB128 variable-length integer encoding and decoding.
|
|
4
|
+
|
|
5
|
+
local Channel = require (script.Parent.Parent.Parent.internal.Channel)
|
|
6
|
+
local Types = require (script.Parent.Parent.Parent.Types)
|
|
7
|
+
|
|
8
|
+
type ChannelState = Types.ChannelState
|
|
9
|
+
|
|
10
|
+
local alloc = Channel.alloc
|
|
11
|
+
|
|
12
|
+
-- Constants --------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local CONT = 0x80
|
|
15
|
+
local MASK = 0x7F
|
|
16
|
+
local MAX_BYTES = 5
|
|
17
|
+
local MAX_VALUE = 4294967295
|
|
18
|
+
|
|
19
|
+
local band = bit32.band
|
|
20
|
+
local bor = bit32.bor
|
|
21
|
+
local rshift = bit32.rshift
|
|
22
|
+
local lshift = bit32.lshift
|
|
23
|
+
|
|
24
|
+
-- Public -----------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
local Varint = {}
|
|
27
|
+
|
|
28
|
+
function Varint.write (ch: ChannelState, value: number): ()
|
|
29
|
+
if value < 0 or value > MAX_VALUE then
|
|
30
|
+
error (`[Lync] Varint value out of range: {value}`)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
-- Fast path: ~80%+ of real-world varints are single-byte (lengths, counts)
|
|
34
|
+
if value < CONT then
|
|
35
|
+
local c = ch.cursor
|
|
36
|
+
if c + 1 > ch.size then
|
|
37
|
+
alloc (ch, 1)
|
|
38
|
+
end
|
|
39
|
+
buffer.writeu8 (ch.buff, c, value)
|
|
40
|
+
ch.cursor = c + 1
|
|
41
|
+
return
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
local c = ch.cursor
|
|
45
|
+
if c + MAX_BYTES > ch.size then
|
|
46
|
+
alloc (ch, MAX_BYTES)
|
|
47
|
+
end
|
|
48
|
+
local b = ch.buff
|
|
49
|
+
|
|
50
|
+
while value >= CONT do
|
|
51
|
+
buffer.writeu8 (b, c, bor (band (value, MASK), CONT))
|
|
52
|
+
value = rshift (value, 7)
|
|
53
|
+
c += 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
buffer.writeu8 (b, c, value)
|
|
57
|
+
ch.cursor = c + 1
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
-- Reads rely on buffer.readu8 throwing on OOB. MAX_BYTES rejects malformed loops.
|
|
61
|
+
function Varint.read (src: buffer, pos: number): (number, number)
|
|
62
|
+
local result = 0
|
|
63
|
+
|
|
64
|
+
for offset = 0, MAX_BYTES - 1 do
|
|
65
|
+
local byte = buffer.readu8 (src, pos + offset)
|
|
66
|
+
result = bor (result, lshift (band (byte, MASK), offset * 7))
|
|
67
|
+
|
|
68
|
+
if band (byte, CONT) == 0 then
|
|
69
|
+
return result, offset + 1
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
error (`[Lync] Varint exceeds {MAX_BYTES} bytes at position {pos}`)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
return table.freeze (Varint)
|