@axpecter/lync 2.1.2 → 2.2.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 +241 -349
- package/package.json +1 -1
- package/src/Types.luau +53 -13
- package/src/api/Group.luau +41 -51
- package/src/api/Packet.luau +145 -143
- package/src/api/Query.luau +204 -202
- package/src/api/Scope.luau +36 -38
- package/src/api/Signal.luau +68 -94
- package/src/codec/Base.luau +43 -23
- package/src/codec/composite/Array.luau +161 -152
- package/src/codec/composite/Map.luau +37 -53
- package/src/codec/composite/Optional.luau +15 -21
- package/src/codec/composite/Shared.luau +78 -41
- package/src/codec/composite/Struct.luau +64 -125
- package/src/codec/composite/Tagged.luau +15 -25
- package/src/codec/composite/Tuple.luau +15 -20
- package/src/codec/datatype/Buffer.luau +44 -51
- package/src/codec/datatype/CFrame.luau +72 -104
- package/src/codec/datatype/Color.luau +14 -10
- package/src/codec/datatype/Instance.luau +32 -42
- package/src/codec/datatype/IntVector.luau +24 -22
- package/src/codec/datatype/NumberRange.luau +21 -12
- package/src/codec/datatype/Ray.luau +13 -7
- package/src/codec/datatype/Rect.luau +13 -7
- package/src/codec/datatype/Region.luau +25 -22
- package/src/codec/datatype/Sequence.luau +144 -118
- package/src/codec/datatype/String.luau +29 -59
- package/src/codec/datatype/UDim.luau +29 -30
- package/src/codec/datatype/Vector.luau +89 -105
- package/src/codec/meta/Auto.luau +194 -246
- package/src/codec/meta/Bitfield.luau +37 -36
- package/src/codec/meta/Custom.luau +10 -10
- package/src/codec/meta/Enum.luau +8 -11
- package/src/codec/meta/Float.luau +16 -20
- package/src/codec/meta/Nothing.luau +2 -2
- package/src/codec/meta/Unknown.luau +22 -32
- package/src/codec/primitive/Bool.luau +9 -5
- package/src/codec/primitive/Float16.luau +13 -23
- package/src/codec/primitive/Int.luau +20 -28
- package/src/codec/primitive/Number.luau +6 -10
- package/src/codec/primitive/Signed.luau +32 -0
- package/src/codec/primitive/Varint.luau +59 -28
- package/src/index.d.ts +8 -2
- package/src/init.luau +118 -143
- package/src/internal/Baseline.luau +17 -18
- package/src/internal/Channel.luau +143 -212
- package/src/internal/Middleware.luau +37 -47
- package/src/internal/Pool.luau +10 -10
- package/src/internal/Registry.luau +38 -34
- package/src/internal/Transport.luau +28 -0
- package/src/internal/Util.luau +26 -0
- package/src/transport/Bridge.luau +32 -24
- package/src/transport/Client.luau +51 -53
- package/src/transport/Gate.luau +183 -113
- package/src/transport/Reader.luau +95 -66
- package/src/transport/Server.luau +130 -125
|
@@ -3,29 +3,29 @@
|
|
|
3
3
|
-- User-defined fixed-size codec.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Types = require(script.Parent.Parent.Parent.Types)
|
|
6
7
|
|
|
7
|
-
-- Private
|
|
8
|
+
-- Private ----------------------------------------------------------------
|
|
8
9
|
|
|
9
10
|
local floor = math.floor
|
|
10
11
|
|
|
11
|
-
-- Public
|
|
12
|
+
-- Public -----------------------------------------------------------------
|
|
12
13
|
|
|
13
14
|
local Custom = {}
|
|
14
15
|
|
|
15
|
-
function Custom.define(
|
|
16
|
+
function Custom.define<T>(
|
|
16
17
|
size: number,
|
|
17
|
-
writeFn: (b: buffer, offset: number, value:
|
|
18
|
-
readFn: (b: buffer, offset: number) ->
|
|
19
|
-
|
|
18
|
+
writeFn: (b: buffer, offset: number, value: T) -> (),
|
|
19
|
+
readFn: (b: buffer, offset: number) -> T,
|
|
20
|
+
typeCheck: string?
|
|
21
|
+
): Types.InternalCodec<T>
|
|
20
22
|
if type(size) ~= "number" or size < 0 or floor(size) ~= size then
|
|
21
23
|
error("[Lync] Lync.custom: size must be a non-negative integer")
|
|
22
24
|
end
|
|
23
|
-
|
|
24
25
|
if size == 0 then
|
|
25
|
-
return Base.
|
|
26
|
+
return Base.constant(nil :: any) :: Types.InternalCodec<T>
|
|
26
27
|
end
|
|
27
|
-
|
|
28
|
-
return Base.define(size, writeFn, readFn)
|
|
28
|
+
return Base.define(size, writeFn, readFn, if typeCheck then { _typeCheck = typeCheck } else nil)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
return table.freeze(Custom)
|
package/src/codec/meta/Enum.luau
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
-- String enum codec. Maps values to u8 indices at
|
|
3
|
+
-- String-keyed enum codec. Maps values to u8 indices at definition time.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Types = require(script.Parent.Parent.Parent.Types)
|
|
6
7
|
|
|
7
|
-
-- Public
|
|
8
|
+
-- Public -----------------------------------------------------------------
|
|
8
9
|
|
|
9
10
|
local EnumCodec = {}
|
|
10
11
|
|
|
11
|
-
function EnumCodec.define(...: string):
|
|
12
|
+
function EnumCodec.define(...: string): Types.InternalCodec<string>
|
|
12
13
|
local values = { ... }
|
|
13
14
|
local count = #values
|
|
14
15
|
if count == 0 then
|
|
@@ -18,10 +19,8 @@ function EnumCodec.define(...: string): any
|
|
|
18
19
|
error("[Lync] Lync.enum: exceeds 256 variants")
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
-- Build lookup tables
|
|
22
22
|
local toIndex: { [string]: number } = {}
|
|
23
23
|
local toValue = table.create(count)
|
|
24
|
-
|
|
25
24
|
for i = 1, count do
|
|
26
25
|
local val = values[i]
|
|
27
26
|
if toIndex[val] then
|
|
@@ -30,24 +29,22 @@ function EnumCodec.define(...: string): any
|
|
|
30
29
|
toIndex[val] = i - 1
|
|
31
30
|
toValue[i] = val
|
|
32
31
|
end
|
|
33
|
-
|
|
34
32
|
table.freeze(toIndex)
|
|
35
33
|
table.freeze(toValue)
|
|
36
34
|
|
|
37
35
|
return Base.define(1, function(b: buffer, off: number, value: string): ()
|
|
38
36
|
local idx = toIndex[value]
|
|
39
37
|
if idx == nil then
|
|
40
|
-
error(`[Lync] Enum: invalid value "{value}"`)
|
|
38
|
+
error(`[Lync] Enum.write: invalid value "{value}"`)
|
|
41
39
|
end
|
|
42
40
|
buffer.writeu8(b, off, idx)
|
|
43
41
|
end, function(b: buffer, off: number): string
|
|
44
|
-
local
|
|
45
|
-
local val = toValue[idx + 1]
|
|
42
|
+
local val = toValue[buffer.readu8(b, off) + 1]
|
|
46
43
|
if val == nil then
|
|
47
|
-
error(`[Lync] Enum: invalid index
|
|
44
|
+
error(`[Lync] Enum.read: invalid index`)
|
|
48
45
|
end
|
|
49
46
|
return val
|
|
50
|
-
end, { _typeCheck = "string" })
|
|
47
|
+
end, { _typeCheck = "string", _isEnum = true, _enumValues = toIndex })
|
|
51
48
|
end
|
|
52
49
|
|
|
53
50
|
return table.freeze(EnumCodec)
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
-- Fixed-point quantization codec. Maps float
|
|
3
|
+
-- Fixed-point quantization codec. Maps a float range to u8/u16/u32.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Types = require(script.Parent.Parent.Parent.Types)
|
|
6
7
|
|
|
7
|
-
-- Private
|
|
8
|
+
-- Private ----------------------------------------------------------------
|
|
8
9
|
|
|
9
10
|
local ceil = math.ceil
|
|
10
11
|
local clamp = math.clamp
|
|
11
12
|
local round = math.round
|
|
12
|
-
local
|
|
13
|
-
local
|
|
13
|
+
local maxN = math.max
|
|
14
|
+
local minN = math.min
|
|
14
15
|
|
|
15
|
-
-- Public
|
|
16
|
+
-- Public -----------------------------------------------------------------
|
|
16
17
|
|
|
17
18
|
local Float = {}
|
|
18
19
|
|
|
19
|
-
function Float.float(
|
|
20
|
+
function Float.float(
|
|
21
|
+
rangeMin: number,
|
|
22
|
+
rangeMax: number,
|
|
23
|
+
precision: number
|
|
24
|
+
): Types.InternalCodec<number>
|
|
20
25
|
if rangeMin > rangeMax then
|
|
21
26
|
error(`[Lync] Lync.float: min ({rangeMin}) must be <= max ({rangeMax})`)
|
|
22
27
|
end
|
|
@@ -25,30 +30,21 @@ function Float.float(rangeMin: number, rangeMax: number, precision: number): any
|
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
local delta = rangeMax - rangeMin
|
|
28
|
-
|
|
29
|
-
-- Constant value: zero bytes
|
|
30
33
|
if delta == 0 then
|
|
31
|
-
return Base.
|
|
34
|
+
return Base.constant(rangeMin)
|
|
32
35
|
end
|
|
33
36
|
|
|
34
|
-
local maxInt =
|
|
37
|
+
local maxInt = minN(0xFFFFFFFF, maxN(1, ceil(delta / precision)))
|
|
35
38
|
|
|
36
39
|
local compBytes: number
|
|
37
40
|
local wfn: (buffer, number, number) -> ()
|
|
38
41
|
local rfn: (buffer, number) -> number
|
|
39
|
-
|
|
40
42
|
if maxInt <= 0xFF then
|
|
41
|
-
compBytes = 1
|
|
42
|
-
wfn = buffer.writeu8
|
|
43
|
-
rfn = buffer.readu8
|
|
43
|
+
compBytes, wfn, rfn = 1, buffer.writeu8, buffer.readu8
|
|
44
44
|
elseif maxInt <= 0xFFFF then
|
|
45
|
-
compBytes = 2
|
|
46
|
-
wfn = buffer.writeu16
|
|
47
|
-
rfn = buffer.readu16
|
|
45
|
+
compBytes, wfn, rfn = 2, buffer.writeu16, buffer.readu16
|
|
48
46
|
else
|
|
49
|
-
compBytes = 4
|
|
50
|
-
wfn = buffer.writeu32
|
|
51
|
-
rfn = buffer.readu32
|
|
47
|
+
compBytes, wfn, rfn = 4, buffer.writeu32, buffer.readu32
|
|
52
48
|
end
|
|
53
49
|
|
|
54
50
|
local scale = maxInt / delta
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
6
|
|
|
7
|
-
-- Public
|
|
7
|
+
-- Public -----------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local Nothing = {}
|
|
10
10
|
|
|
11
|
-
Nothing.nothing = Base.
|
|
11
|
+
Nothing.nothing = Base.constant(nil :: nil)
|
|
12
12
|
|
|
13
13
|
return table.freeze(Nothing)
|
|
@@ -2,47 +2,37 @@
|
|
|
2
2
|
--!optimize 2
|
|
3
3
|
-- Bypasses buffer serialization. Uses Roblox remote sidecar refs.
|
|
4
4
|
|
|
5
|
-
local
|
|
5
|
+
local Channel = require(script.Parent.Parent.Parent.internal.Channel)
|
|
6
6
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
7
7
|
|
|
8
|
-
-- Private
|
|
8
|
+
-- Private ----------------------------------------------------------------
|
|
9
9
|
|
|
10
|
-
local
|
|
11
|
-
local writeu16 = buffer.writeu16
|
|
10
|
+
local pushRef = Channel.pushRef
|
|
12
11
|
local readu16 = buffer.readu16
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
local function writeUnknown(ch: Types.ChannelState, value: any): ()
|
|
14
|
+
pushRef(ch, value)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
local function readUnknown(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
18
|
+
if not refs then
|
|
19
|
+
error("[Lync] Unknown.read: refs array is required")
|
|
20
|
+
end
|
|
21
|
+
local idx = readu16(src, pos)
|
|
22
|
+
if idx < 1 or idx > #refs then
|
|
23
|
+
error(`[Lync] Unknown.read: ref index {idx} out of bounds`)
|
|
24
|
+
end
|
|
25
|
+
return refs[idx], 2
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
-- Public -----------------------------------------------------------------
|
|
15
29
|
|
|
16
30
|
local Unknown = {}
|
|
17
31
|
|
|
18
32
|
Unknown.unknown = table.freeze({
|
|
19
33
|
_hasUnknown = true,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if idx > 0xFFFF then
|
|
24
|
-
error("[Lync] Unknown: ref overflow (>65535)")
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
ch.refs[idx] = value :: any
|
|
28
|
-
ch.refCount = idx
|
|
29
|
-
|
|
30
|
-
local cursor = ch.cursor
|
|
31
|
-
if cursor + 2 > ch.size then
|
|
32
|
-
alloc(ch, 2)
|
|
33
|
-
end
|
|
34
|
-
writeu16(ch.buff, cursor, idx)
|
|
35
|
-
ch.cursor = cursor + 2
|
|
36
|
-
end,
|
|
37
|
-
|
|
38
|
-
read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
|
|
39
|
-
if not refs then
|
|
40
|
-
error("[Lync] Unknown: read requires refs array")
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
local idx = readu16(src, pos)
|
|
44
|
-
return (refs :: any)[idx], 2
|
|
45
|
-
end,
|
|
46
|
-
} :: any)
|
|
34
|
+
write = writeUnknown,
|
|
35
|
+
read = readUnknown,
|
|
36
|
+
})
|
|
47
37
|
|
|
48
38
|
return table.freeze(Unknown)
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
--
|
|
3
|
+
-- Single-byte boolean codec. _isBool flag enables struct/array bit packing.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
6
|
|
|
7
|
-
-- Public
|
|
7
|
+
-- Public -----------------------------------------------------------------
|
|
8
8
|
|
|
9
9
|
local Bool = {}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
local function writeBool(b: buffer, off: number, value: boolean): ()
|
|
12
12
|
buffer.writeu8(b, off, if value then 1 else 0)
|
|
13
|
-
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
local function readBool(b: buffer, off: number): boolean
|
|
14
16
|
return buffer.readu8(b, off) ~= 0
|
|
15
|
-
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
Bool.bool = Base.define(1, writeBool, readBool, {
|
|
16
20
|
_isBool = true,
|
|
17
21
|
_typeCheck = "boolean",
|
|
18
22
|
})
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
--
|
|
3
|
+
-- IEEE 754 binary16 codec. 2 bytes, +/- 65504, ~3 decimal digits of precision.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
6
|
|
|
7
|
-
--
|
|
7
|
+
-- Constants --------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
local MAX_FINITE = 65504
|
|
10
|
+
local NAN_BITS = 0x7E00
|
|
11
|
+
local INF_BITS = 0x7C00
|
|
12
|
+
|
|
13
|
+
-- Private ----------------------------------------------------------------
|
|
8
14
|
|
|
9
15
|
local band = bit32.band
|
|
10
16
|
local bor = bit32.bor
|
|
@@ -16,37 +22,29 @@ local round = math.round
|
|
|
16
22
|
local huge = math.huge
|
|
17
23
|
|
|
18
24
|
local function encode(value: number): number
|
|
19
|
-
-- NaN
|
|
20
25
|
if value ~= value then
|
|
21
|
-
return
|
|
26
|
+
return NAN_BITS
|
|
22
27
|
end
|
|
23
|
-
|
|
24
|
-
-- Zero (both +0 and -0 map to positive zero)
|
|
25
28
|
if value == 0 then
|
|
26
29
|
return 0
|
|
27
30
|
end
|
|
28
31
|
|
|
29
|
-
-- Sign extraction
|
|
30
32
|
local sign = 0
|
|
31
33
|
if value < 0 then
|
|
32
34
|
sign = 0x8000
|
|
33
35
|
value = -value
|
|
34
36
|
end
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return bor(sign, 0x7C00)
|
|
38
|
+
if value > MAX_FINITE then
|
|
39
|
+
return bor(sign, INF_BITS)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
|
-
-- Decompose via frexp: value = mantissa * 2^exponent, mantissa in [0.5, 1)
|
|
42
42
|
local mantissa, exponent = frexp(value)
|
|
43
43
|
local biasedExp = exponent + 14
|
|
44
44
|
|
|
45
45
|
if biasedExp <= 0 then
|
|
46
|
-
-- Subnormal: frac = round(mantissa * 2^(biasedExp + 10))
|
|
47
46
|
local frac = round(ldexp(mantissa, biasedExp + 10))
|
|
48
47
|
if frac >= 1024 then
|
|
49
|
-
-- Rounds to minimum normal
|
|
50
48
|
return bor(sign, 0x0400)
|
|
51
49
|
end
|
|
52
50
|
if frac <= 0 then
|
|
@@ -55,18 +53,14 @@ local function encode(value: number): number
|
|
|
55
53
|
return bor(sign, frac)
|
|
56
54
|
end
|
|
57
55
|
|
|
58
|
-
-- Normal: frac = round((mantissa * 2 - 1) * 1024)
|
|
59
56
|
local frac = round((mantissa * 2 - 1) * 1024)
|
|
60
|
-
|
|
61
|
-
-- Carry: mantissa rounding overflows into exponent
|
|
62
57
|
if frac >= 1024 then
|
|
63
58
|
frac = 0
|
|
64
59
|
biasedExp += 1
|
|
65
60
|
end
|
|
66
61
|
|
|
67
|
-
-- Overflow after carry
|
|
68
62
|
if biasedExp >= 31 then
|
|
69
|
-
return bor(sign,
|
|
63
|
+
return bor(sign, INF_BITS)
|
|
70
64
|
end
|
|
71
65
|
|
|
72
66
|
return bor(sign, lshift(biasedExp, 10), frac)
|
|
@@ -78,22 +72,18 @@ local function decode(raw: number): number
|
|
|
78
72
|
local frac = band(raw, 0x03FF)
|
|
79
73
|
|
|
80
74
|
local value: number
|
|
81
|
-
|
|
82
75
|
if exp == 0 then
|
|
83
|
-
-- Zero or subnormal
|
|
84
76
|
value = if frac == 0 then 0 else ldexp(frac, -24)
|
|
85
77
|
elseif exp == 31 then
|
|
86
|
-
-- Infinity or NaN
|
|
87
78
|
value = if frac == 0 then huge else 0 / 0
|
|
88
79
|
else
|
|
89
|
-
-- Normal: (frac + 1024) * 2^(exp - 25)
|
|
90
80
|
value = ldexp(frac + 1024, exp - 25)
|
|
91
81
|
end
|
|
92
82
|
|
|
93
83
|
return if sign ~= 0 then -value else value
|
|
94
84
|
end
|
|
95
85
|
|
|
96
|
-
-- Public
|
|
86
|
+
-- Public -----------------------------------------------------------------
|
|
97
87
|
|
|
98
88
|
local Float16 = {}
|
|
99
89
|
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!optimize 2
|
|
3
|
-
--
|
|
3
|
+
-- Range-typed integer codec. Picks the narrowest u8/u16/u32/i8/i16/i32 wire form.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
|
+
local Signed = require(script.Parent.Signed)
|
|
7
|
+
local Types = require(script.Parent.Parent.Parent.Types)
|
|
6
8
|
|
|
7
|
-
-- Private
|
|
9
|
+
-- Private ----------------------------------------------------------------
|
|
8
10
|
|
|
9
11
|
local floor = math.floor
|
|
12
|
+
local writeI8 = Signed.writeI8
|
|
13
|
+
local writeI16 = Signed.writeI16
|
|
14
|
+
local writeI32 = Signed.writeI32
|
|
10
15
|
|
|
11
|
-
-- Public
|
|
16
|
+
-- Public -----------------------------------------------------------------
|
|
12
17
|
|
|
13
18
|
local Int = {}
|
|
14
19
|
|
|
15
|
-
function Int.int(min: number, max: number):
|
|
20
|
+
function Int.int(min: number, max: number): Types.InternalCodec<number>
|
|
16
21
|
if min > max then
|
|
17
22
|
error(`[Lync] Lync.int: min ({min}) must be <= max ({max})`)
|
|
18
23
|
end
|
|
@@ -28,37 +33,24 @@ function Int.int(min: number, max: number): any
|
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
if min >= 0 then
|
|
31
|
-
-- Unsigned path
|
|
32
36
|
if max <= 0xFF then
|
|
33
37
|
return Base.define(1, buffer.writeu8, buffer.readu8, meta)
|
|
34
|
-
|
|
38
|
+
end
|
|
39
|
+
if max <= 0xFFFF then
|
|
35
40
|
return Base.define(2, buffer.writeu16, buffer.readu16, meta)
|
|
36
|
-
|
|
41
|
+
end
|
|
42
|
+
if max <= 0xFFFFFFFF then
|
|
37
43
|
return Base.define(4, buffer.writeu32, buffer.readu32, meta)
|
|
38
44
|
end
|
|
39
45
|
else
|
|
40
|
-
-- Signed path: use unsigned write/read, interpret as signed
|
|
41
46
|
if min >= -0x80 and max <= 0x7F then
|
|
42
|
-
return Base.define(1,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return Base.define(2, function(b: buffer, off: number, v: number): ()
|
|
50
|
-
buffer.writeu16(b, off, if v < 0 then v + 0x10000 else v)
|
|
51
|
-
end, function(b: buffer, off: number): number
|
|
52
|
-
local raw = buffer.readu16(b, off)
|
|
53
|
-
return if raw >= 0x8000 then raw - 0x10000 else raw
|
|
54
|
-
end, meta)
|
|
55
|
-
elseif min >= -0x80000000 and max <= 0x7FFFFFFF then
|
|
56
|
-
return Base.define(4, function(b: buffer, off: number, v: number): ()
|
|
57
|
-
buffer.writeu32(b, off, if v < 0 then v + 0x100000000 else v)
|
|
58
|
-
end, function(b: buffer, off: number): number
|
|
59
|
-
local raw = buffer.readu32(b, off)
|
|
60
|
-
return if raw >= 0x80000000 then raw - 0x100000000 else raw
|
|
61
|
-
end, meta)
|
|
47
|
+
return Base.define(1, writeI8, buffer.readi8, meta)
|
|
48
|
+
end
|
|
49
|
+
if min >= -0x8000 and max <= 0x7FFF then
|
|
50
|
+
return Base.define(2, writeI16, buffer.readi16, meta)
|
|
51
|
+
end
|
|
52
|
+
if min >= -0x80000000 and max <= 0x7FFFFFFF then
|
|
53
|
+
return Base.define(4, writeI32, buffer.readi32, meta)
|
|
62
54
|
end
|
|
63
55
|
end
|
|
64
56
|
|
|
@@ -4,23 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
6
|
|
|
7
|
-
-- Constants
|
|
7
|
+
-- Constants --------------------------------------------------------------
|
|
8
8
|
|
|
9
|
+
-- Shared scratch for Auto codec's f32 round-trip test. Roblox is
|
|
10
|
+
-- single-threaded so a module-level scratch cannot be aliased.
|
|
9
11
|
local F32_SCRATCH = buffer.create(4)
|
|
10
12
|
|
|
11
|
-
-- Public
|
|
13
|
+
-- Public -----------------------------------------------------------------
|
|
12
14
|
|
|
13
15
|
local Number = {}
|
|
14
16
|
|
|
15
|
-
Number.f32 = Base.define(4, buffer.writef32, buffer.readf32, {
|
|
16
|
-
|
|
17
|
-
})
|
|
17
|
+
Number.f32 = Base.define(4, buffer.writef32, buffer.readf32, { _typeCheck = "number" })
|
|
18
|
+
Number.f64 = Base.define(8, buffer.writef64, buffer.readf64, { _typeCheck = "number" })
|
|
18
19
|
|
|
19
|
-
Number.f64 = Base.define(8, buffer.writef64, buffer.readf64, {
|
|
20
|
-
_typeCheck = "number",
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
-- Shared scratch buffer for Auto codec f32 round-trip test
|
|
24
20
|
Number._f32Scratch = F32_SCRATCH
|
|
25
21
|
|
|
26
22
|
return table.freeze(Number)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
--!strict
|
|
2
|
+
--!native
|
|
3
|
+
-- Signed integer buffer helpers. Reads use FASTCALL2 readi*; writes
|
|
4
|
+
-- emulate via writeu* + two's complement (writei* is not FASTCALL).
|
|
5
|
+
|
|
6
|
+
-- Private ----------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
local writeu8 = buffer.writeu8
|
|
9
|
+
local writeu16 = buffer.writeu16
|
|
10
|
+
local writeu32 = buffer.writeu32
|
|
11
|
+
|
|
12
|
+
-- Public -----------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
local Signed = {}
|
|
15
|
+
|
|
16
|
+
function Signed.writeI8(b: buffer, off: number, value: number): ()
|
|
17
|
+
writeu8(b, off, if value < 0 then value + 0x100 else value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
function Signed.writeI16(b: buffer, off: number, value: number): ()
|
|
21
|
+
writeu16(b, off, if value < 0 then value + 0x10000 else value)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
function Signed.writeI32(b: buffer, off: number, value: number): ()
|
|
25
|
+
writeu32(b, off, if value < 0 then value + 0x100000000 else value)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Signed.readI8 = buffer.readi8
|
|
29
|
+
Signed.readI16 = buffer.readi16
|
|
30
|
+
Signed.readI32 = buffer.readi32
|
|
31
|
+
|
|
32
|
+
return table.freeze(Signed)
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
--!strict
|
|
2
2
|
--!native
|
|
3
|
-
-- Dense prefix
|
|
3
|
+
-- Dense prefix varint. Single-branch decode, range up to 2^32 - 1.
|
|
4
4
|
|
|
5
5
|
local Base = require(script.Parent.Parent.Base)
|
|
6
6
|
local Types = require(script.Parent.Parent.Parent.Types)
|
|
7
7
|
|
|
8
|
-
-- Constants
|
|
8
|
+
-- Constants --------------------------------------------------------------
|
|
9
9
|
|
|
10
|
+
--[[
|
|
11
|
+
Wire layout (first byte determines length):
|
|
12
|
+
0x00..0xBF 1 byte value = b0 [0..191]
|
|
13
|
+
0xC0..0xDF 2 bytes value = (b0 & 0x1F)<<8 | b1 + 192 [192..8383]
|
|
14
|
+
0xE0..0xEF 3 bytes value = (b0 & 0x0F)<<16 | u16 + 8384 [8384..1056959]
|
|
15
|
+
0xF0..0xFF 5 bytes value = u32 [..0xFFFFFFFF]
|
|
16
|
+
]]
|
|
10
17
|
local INLINE_MAX = 0xBF
|
|
18
|
+
local TWO_MAX = 8383
|
|
19
|
+
local THREE_MAX = 1056959
|
|
20
|
+
local TWO_BIAS = 192
|
|
21
|
+
local THREE_BIAS = 8384
|
|
11
22
|
|
|
12
|
-
-- Private
|
|
23
|
+
-- Private ----------------------------------------------------------------
|
|
13
24
|
|
|
14
25
|
local alloc = Base.alloc
|
|
15
26
|
local band = bit32.band
|
|
@@ -22,7 +33,7 @@ local readu8 = buffer.readu8
|
|
|
22
33
|
local readu16 = buffer.readu16
|
|
23
34
|
local readu32 = buffer.readu32
|
|
24
35
|
|
|
25
|
-
-- Public
|
|
36
|
+
-- Public -----------------------------------------------------------------
|
|
26
37
|
|
|
27
38
|
local Varint = {}
|
|
28
39
|
|
|
@@ -30,61 +41,68 @@ Varint.INLINE_MAX = INLINE_MAX
|
|
|
30
41
|
|
|
31
42
|
function Varint.write(ch: Types.ChannelState, value: number): ()
|
|
32
43
|
if value < 0 or value > 0xFFFFFFFF or value % 1 ~= 0 then
|
|
33
|
-
error(`[Lync] Varint: value must be an integer in [0, 4294967295], got {value}`)
|
|
44
|
+
error(`[Lync] Varint.write: value must be an integer in [0, 4294967295], got {value}`)
|
|
34
45
|
end
|
|
35
46
|
|
|
36
47
|
local cursor = ch.cursor
|
|
37
|
-
local b = ch.buff
|
|
38
48
|
|
|
39
49
|
if value <= INLINE_MAX then
|
|
40
50
|
if cursor + 1 > ch.size then
|
|
41
51
|
alloc(ch, 1)
|
|
42
52
|
end
|
|
43
|
-
writeu8(
|
|
53
|
+
writeu8(ch.buff, cursor, value)
|
|
44
54
|
ch.cursor = cursor + 1
|
|
45
|
-
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
if value <= TWO_MAX then
|
|
46
59
|
if cursor + 2 > ch.size then
|
|
47
60
|
alloc(ch, 2)
|
|
48
61
|
end
|
|
49
|
-
local adjusted = value -
|
|
50
|
-
b = ch.buff
|
|
62
|
+
local adjusted = value - TWO_BIAS
|
|
63
|
+
local b = ch.buff
|
|
51
64
|
writeu8(b, cursor, bor(0xC0, rshift(adjusted, 8)))
|
|
52
65
|
writeu8(b, cursor + 1, band(adjusted, 0xFF))
|
|
53
66
|
ch.cursor = cursor + 2
|
|
54
|
-
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
if value <= THREE_MAX then
|
|
55
71
|
if cursor + 3 > ch.size then
|
|
56
72
|
alloc(ch, 3)
|
|
57
73
|
end
|
|
58
|
-
local adjusted = value -
|
|
59
|
-
b = ch.buff
|
|
74
|
+
local adjusted = value - THREE_BIAS
|
|
75
|
+
local b = ch.buff
|
|
60
76
|
writeu8(b, cursor, bor(0xE0, rshift(adjusted, 16)))
|
|
61
77
|
writeu16(b, cursor + 1, band(adjusted, 0xFFFF))
|
|
62
78
|
ch.cursor = cursor + 3
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
writeu8(b, cursor, 0xF0)
|
|
69
|
-
writeu32(b, cursor + 1, value)
|
|
70
|
-
ch.cursor = cursor + 5
|
|
79
|
+
return
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if cursor + 5 > ch.size then
|
|
83
|
+
alloc(ch, 5)
|
|
71
84
|
end
|
|
85
|
+
local b = ch.buff
|
|
86
|
+
writeu8(b, cursor, 0xF0)
|
|
87
|
+
writeu32(b, cursor + 1, value)
|
|
88
|
+
ch.cursor = cursor + 5
|
|
72
89
|
end
|
|
73
90
|
|
|
74
91
|
function Varint.read(src: buffer, pos: number): (number, number)
|
|
75
92
|
local b0 = readu8(src, pos)
|
|
76
|
-
|
|
77
93
|
if b0 <= INLINE_MAX then
|
|
78
94
|
return b0, 1
|
|
79
|
-
elseif b0 <= 0xDF then
|
|
80
|
-
return band(b0, 0x1F) * 256 + readu8(src, pos + 1) + 192, 2
|
|
81
|
-
elseif b0 <= 0xEF then
|
|
82
|
-
return band(b0, 0x0F) * 65536 + readu16(src, pos + 1) + 8384, 3
|
|
83
|
-
else
|
|
84
|
-
return readu32(src, pos + 1), 5
|
|
85
95
|
end
|
|
96
|
+
if b0 <= 0xDF then
|
|
97
|
+
return band(b0, 0x1F) * 256 + readu8(src, pos + 1) + TWO_BIAS, 2
|
|
98
|
+
end
|
|
99
|
+
if b0 <= 0xEF then
|
|
100
|
+
return band(b0, 0x0F) * 65536 + readu16(src, pos + 1) + THREE_BIAS, 3
|
|
101
|
+
end
|
|
102
|
+
return readu32(src, pos + 1), 5
|
|
86
103
|
end
|
|
87
104
|
|
|
105
|
+
-- Encode a value already known to fit in INLINE_MAX (single byte).
|
|
88
106
|
function Varint.writeInline(ch: Types.ChannelState, value: number): ()
|
|
89
107
|
local cursor = ch.cursor
|
|
90
108
|
if cursor + 1 > ch.size then
|
|
@@ -94,4 +112,17 @@ function Varint.writeInline(ch: Types.ChannelState, value: number): ()
|
|
|
94
112
|
ch.cursor = cursor + 1
|
|
95
113
|
end
|
|
96
114
|
|
|
115
|
+
--[[
|
|
116
|
+
Decode a length prefix that uses the single-byte fast path for
|
|
117
|
+
lengths in [0, INLINE_MAX] and the full varint encoding above that.
|
|
118
|
+
Used by Buffer, String, and Auto's string branch.
|
|
119
|
+
]]
|
|
120
|
+
function Varint.readLengthPrefix(src: buffer, pos: number): (number, number)
|
|
121
|
+
local b0 = readu8(src, pos)
|
|
122
|
+
if b0 <= INLINE_MAX then
|
|
123
|
+
return b0, 1
|
|
124
|
+
end
|
|
125
|
+
return Varint.read(src, pos)
|
|
126
|
+
end
|
|
127
|
+
|
|
97
128
|
return table.freeze(Varint)
|