@axpecter/lync 2.1.1 → 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.
Files changed (56) hide show
  1. package/README.md +241 -349
  2. package/package.json +1 -1
  3. package/src/Types.luau +53 -13
  4. package/src/api/Group.luau +41 -51
  5. package/src/api/Packet.luau +145 -143
  6. package/src/api/Query.luau +204 -202
  7. package/src/api/Scope.luau +36 -38
  8. package/src/api/Signal.luau +68 -94
  9. package/src/codec/Base.luau +43 -23
  10. package/src/codec/composite/Array.luau +161 -152
  11. package/src/codec/composite/Map.luau +37 -53
  12. package/src/codec/composite/Optional.luau +15 -21
  13. package/src/codec/composite/Shared.luau +78 -41
  14. package/src/codec/composite/Struct.luau +64 -125
  15. package/src/codec/composite/Tagged.luau +15 -25
  16. package/src/codec/composite/Tuple.luau +15 -20
  17. package/src/codec/datatype/Buffer.luau +44 -51
  18. package/src/codec/datatype/CFrame.luau +72 -104
  19. package/src/codec/datatype/Color.luau +14 -10
  20. package/src/codec/datatype/Instance.luau +32 -42
  21. package/src/codec/datatype/IntVector.luau +24 -22
  22. package/src/codec/datatype/NumberRange.luau +21 -12
  23. package/src/codec/datatype/Ray.luau +13 -7
  24. package/src/codec/datatype/Rect.luau +13 -7
  25. package/src/codec/datatype/Region.luau +25 -22
  26. package/src/codec/datatype/Sequence.luau +144 -118
  27. package/src/codec/datatype/String.luau +29 -59
  28. package/src/codec/datatype/UDim.luau +29 -30
  29. package/src/codec/datatype/Vector.luau +89 -105
  30. package/src/codec/meta/Auto.luau +194 -246
  31. package/src/codec/meta/Bitfield.luau +37 -36
  32. package/src/codec/meta/Custom.luau +10 -10
  33. package/src/codec/meta/Enum.luau +8 -11
  34. package/src/codec/meta/Float.luau +16 -20
  35. package/src/codec/meta/Nothing.luau +2 -2
  36. package/src/codec/meta/Unknown.luau +22 -32
  37. package/src/codec/primitive/Bool.luau +9 -5
  38. package/src/codec/primitive/Float16.luau +13 -23
  39. package/src/codec/primitive/Int.luau +20 -28
  40. package/src/codec/primitive/Number.luau +6 -10
  41. package/src/codec/primitive/Signed.luau +32 -0
  42. package/src/codec/primitive/Varint.luau +59 -28
  43. package/src/index.d.ts +9 -3
  44. package/src/init.luau +118 -143
  45. package/src/internal/Baseline.luau +17 -18
  46. package/src/internal/Channel.luau +143 -212
  47. package/src/internal/Middleware.luau +37 -47
  48. package/src/internal/Pool.luau +10 -10
  49. package/src/internal/Registry.luau +38 -34
  50. package/src/internal/Transport.luau +28 -0
  51. package/src/internal/Util.luau +26 -0
  52. package/src/transport/Bridge.luau +32 -24
  53. package/src/transport/Client.luau +51 -53
  54. package/src/transport/Gate.luau +183 -113
  55. package/src/transport/Reader.luau +95 -66
  56. package/src/transport/Server.luau +130 -125
@@ -1,15 +1,17 @@
1
1
  --!strict
2
2
  --!native
3
- -- array and deltaArray codecs.
3
+ -- Array and deltaArray codecs. Bool elements are bit-packed.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
+ local Channel = require(script.Parent.Parent.Parent.internal.Channel)
6
7
  local Shared = require(script.Parent.Shared)
7
8
  local Types = require(script.Parent.Parent.Parent.Types)
8
9
  local Varint = require(script.Parent.Parent.primitive.Varint)
9
10
 
10
- -- Private -------------------------------------------------------------
11
+ -- Private ----------------------------------------------------------------
11
12
 
12
13
  local alloc = Base.alloc
14
+ local writeByte = Channel.writeByte
13
15
  local writeu8 = buffer.writeu8
14
16
  local readu8 = buffer.readu8
15
17
  local band = bit32.band
@@ -19,137 +21,149 @@ local lshift = bit32.lshift
19
21
  local acquireScratch = Shared.acquireScratch
20
22
  local releaseScratch = Shared.releaseScratch
21
23
  local rangeEqual = Shared.rangeEqual
24
+ local snapshot = Shared.snapshot
22
25
  local allocDeltaId = Shared.allocDeltaId
23
-
24
26
  local DELTA_UNCHANGED = Shared.DELTA_UNCHANGED
25
- local DELTA_FULL_OFFSET = Shared.DELTA_FULL_OFFSET
26
- local DELTA_MULTI_OFFSET = Shared.DELTA_MULTI_OFFSET
27
-
28
- -- Public --------------------------------------------------------------
29
-
30
- local Array = {}
31
-
32
- function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Types.InternalCodec<any>
33
- local elemAny = element :: any
34
-
35
- -- Bool array: bitpacked
36
- if elemAny._isBool then
37
- return table.freeze({
38
- write = function(ch: Types.ChannelState, value: { any }): ()
39
- local len = #value
40
- Varint.write(ch, len)
41
- if len == 0 then
42
- return
43
- end
27
+ local DELTA_FULL = Shared.DELTA_FULL
44
28
 
45
- local byteCount = (len + 7) // 8
46
- local cursor = ch.cursor
47
- if cursor + byteCount > ch.size then
48
- alloc(ch, byteCount)
49
- end
50
- local b = ch.buff
51
-
52
- local idx = 1
53
- for byteIdx = 0, byteCount - 1 do
54
- local packed = 0
55
- local limit = if idx + 8 <= len + 1 then 8 else len - idx + 1
56
- for bit = 0, limit - 1 do
57
- if value[idx] then
58
- packed = bor(packed, lshift(1, bit))
59
- end
60
- idx += 1
61
- end
62
- writeu8(b, cursor + byteIdx, packed)
63
- end
64
- ch.cursor = cursor + byteCount
65
- end,
29
+ local function makeBoolArray(
30
+ element: Types.InternalCodec<any>,
31
+ maxCount: number?
32
+ ): Types.InternalCodec<any>
33
+ return table.freeze({
34
+ _isArray = true,
35
+ _element = element,
36
+ _maxCount = maxCount,
66
37
 
67
- read = function(src: buffer, pos: number, _refs: { Instance }?): ({ boolean }, number)
68
- local len, lenBytes = Varint.read(src, pos)
69
- if maxCount and len > maxCount then
70
- error(`[Lync] Array: count {len} exceeds max {maxCount}`)
71
- end
72
- if len == 0 then
73
- return {}, lenBytes
74
- end
38
+ write = function(ch: Types.ChannelState, value: { boolean }): ()
39
+ local len = #value
40
+ Varint.write(ch, len)
41
+ if len == 0 then
42
+ return
43
+ end
75
44
 
76
- local byteCount = (len + 7) // 8
77
- local dataStart = pos + lenBytes
78
- local result = table.create(len)
79
- local idx = 1
80
-
81
- for byteIdx = 0, byteCount - 1 do
82
- local byte = readu8(src, dataStart + byteIdx)
83
- local limit = if idx + 8 <= len + 1 then 8 else len - idx + 1
84
- for bit = 0, limit - 1 do
85
- result[idx] = band(byte, lshift(1, bit)) ~= 0
86
- idx += 1
45
+ local byteCount = (len + 7) // 8
46
+ local cursor = ch.cursor
47
+ if cursor + byteCount > ch.size then
48
+ alloc(ch, byteCount)
49
+ end
50
+ local b = ch.buff
51
+
52
+ local idx = 1
53
+ for byteIdx = 0, byteCount - 1 do
54
+ local packed = 0
55
+ local remaining = len - idx + 1
56
+ local limit = if remaining >= 8 then 8 else remaining
57
+ for bit = 0, limit - 1 do
58
+ if value[idx] then
59
+ packed = bor(packed, lshift(1, bit))
87
60
  end
61
+ idx += 1
88
62
  end
63
+ writeu8(b, cursor + byteIdx, packed)
64
+ end
65
+ ch.cursor = cursor + byteCount
66
+ end,
89
67
 
90
- return result, lenBytes + byteCount
91
- end,
92
- } :: any)
93
- end
68
+ read = function(src: buffer, pos: number, _refs: { Instance }?): ({ boolean }, number)
69
+ local len, lenBytes = Varint.read(src, pos)
70
+ if maxCount and len > maxCount then
71
+ error(`[Lync] Array.read: count {len} exceeds max {maxCount}`)
72
+ end
73
+ if len == 0 then
74
+ return {}, lenBytes
75
+ end
94
76
 
95
- -- Direct array: element has _size + _directWrite + _directRead
96
- local elemSize = elemAny._size
97
- local elemDW = elemAny._directWrite
98
- local elemDR = elemAny._directRead
77
+ local byteCount = (len + 7) // 8
78
+ local dataStart = pos + lenBytes
79
+ local result = table.create(len)
99
80
 
100
- if elemSize and elemDW and elemDR then
101
- return table.freeze({
102
- write = function(ch: Types.ChannelState, value: { any }): ()
103
- local len = #value
104
- Varint.write(ch, len)
105
- if len == 0 then
106
- return
81
+ local idx = 1
82
+ for byteIdx = 0, byteCount - 1 do
83
+ local byte = readu8(src, dataStart + byteIdx)
84
+ local remaining = len - idx + 1
85
+ local limit = if remaining >= 8 then 8 else remaining
86
+ for bit = 0, limit - 1 do
87
+ result[idx] = band(byte, lshift(1, bit)) ~= 0
88
+ idx += 1
107
89
  end
90
+ end
91
+ return result, lenBytes + byteCount
92
+ end,
93
+ })
94
+ end
108
95
 
109
- local payloadBytes = len * elemSize
110
- local cursor = ch.cursor
111
- if cursor + payloadBytes > ch.size then
112
- alloc(ch, payloadBytes)
113
- end
114
- local b = ch.buff
96
+ local function makeDirectArray(
97
+ element: Types.InternalCodec<any>,
98
+ elemSize: number,
99
+ elemDW: (b: buffer, off: number, value: any) -> (),
100
+ elemDR: (b: buffer, off: number) -> any,
101
+ maxCount: number?
102
+ ): Types.InternalCodec<any>
103
+ return table.freeze({
104
+ _isArray = true,
105
+ _element = element,
106
+ _maxCount = maxCount,
115
107
 
116
- for i = 1, len do
117
- elemDW(b, cursor + (i - 1) * elemSize, value[i])
118
- end
119
- ch.cursor = cursor + payloadBytes
120
- end,
108
+ write = function(ch: Types.ChannelState, value: { any }): ()
109
+ local len = #value
110
+ Varint.write(ch, len)
111
+ if len == 0 then
112
+ return
113
+ end
121
114
 
122
- read = function(src: buffer, pos: number, _refs: { Instance }?): ({ any }, number)
123
- local len, lenBytes = Varint.read(src, pos)
124
- if maxCount and len > maxCount then
125
- error(`[Lync] Array: count {len} exceeds max {maxCount}`)
126
- end
127
- if len == 0 then
128
- return {}, lenBytes
129
- end
115
+ local payloadBytes = len * elemSize
116
+ local cursor = ch.cursor
117
+ if cursor + payloadBytes > ch.size then
118
+ alloc(ch, payloadBytes)
119
+ end
120
+ local b = ch.buff
130
121
 
131
- local payloadBytes = len * elemSize
132
- local dataStart = pos + lenBytes
122
+ local off = cursor
123
+ for i = 1, len do
124
+ elemDW(b, off, value[i])
125
+ off += elemSize
126
+ end
127
+ ch.cursor = cursor + payloadBytes
128
+ end,
133
129
 
134
- -- Safety cap
135
- local remaining = buffer.len(src) - dataStart
136
- if payloadBytes > remaining then
137
- len = remaining // elemSize
138
- payloadBytes = len * elemSize
139
- end
130
+ read = function(src: buffer, pos: number, _refs: { Instance }?): ({ any }, number)
131
+ local len, lenBytes = Varint.read(src, pos)
132
+ if maxCount and len > maxCount then
133
+ error(`[Lync] Array.read: count {len} exceeds max {maxCount}`)
134
+ end
135
+ if len == 0 then
136
+ return {}, lenBytes
137
+ end
140
138
 
141
- local result = table.create(len)
142
- for i = 1, len do
143
- result[i] = elemDR(src, dataStart + (i - 1) * elemSize)
144
- end
139
+ local payloadBytes = len * elemSize
140
+ local dataStart = pos + lenBytes
141
+ local remaining = buffer.len(src) - dataStart
142
+ if payloadBytes > remaining then
143
+ len = remaining // elemSize
144
+ payloadBytes = len * elemSize
145
+ end
145
146
 
146
- return result, lenBytes + payloadBytes
147
- end,
148
- } :: any)
149
- end
147
+ local result = table.create(len)
148
+ local off = dataStart
149
+ for i = 1, len do
150
+ result[i] = elemDR(src, off)
151
+ off += elemSize
152
+ end
153
+ return result, lenBytes + payloadBytes
154
+ end,
155
+ })
156
+ end
150
157
 
151
- -- Generic array
158
+ local function makeGenericArray(
159
+ element: Types.InternalCodec<any>,
160
+ maxCount: number?
161
+ ): Types.InternalCodec<any>
152
162
  return table.freeze({
163
+ _isArray = true,
164
+ _element = element,
165
+ _maxCount = maxCount,
166
+
153
167
  write = function(ch: Types.ChannelState, value: { any }): ()
154
168
  local len = #value
155
169
  Varint.write(ch, len)
@@ -161,7 +175,7 @@ function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Type
161
175
  read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
162
176
  local len, lenBytes = Varint.read(src, pos)
163
177
  if maxCount and len > maxCount then
164
- error(`[Lync] Array: count {len} exceeds max {maxCount}`)
178
+ error(`[Lync] Array.read: count {len} exceeds max {maxCount}`)
165
179
  end
166
180
  if len == 0 then
167
181
  return {}, lenBytes
@@ -171,13 +185,34 @@ function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Type
171
185
  local total = lenBytes
172
186
  for i = 1, len do
173
187
  local v, consumed = element.read(src, pos + total, refs)
188
+ if consumed == 0 then
189
+ error(`[Lync] Array.read: element codec consumed 0 bytes at index {i}`)
190
+ end
174
191
  result[i] = v
175
192
  total += consumed
176
193
  end
177
-
178
194
  return result, total
179
195
  end,
180
- } :: any)
196
+ })
197
+ end
198
+
199
+ -- Public -----------------------------------------------------------------
200
+
201
+ local Array = {}
202
+
203
+ function Array.array(element: Types.InternalCodec<any>, maxCount: number?): Types.InternalCodec<any>
204
+ if element._isBool then
205
+ return makeBoolArray(element, maxCount)
206
+ end
207
+
208
+ local elemSize = element._size
209
+ local elemDW = element._directWrite
210
+ local elemDR = element._directRead
211
+ if elemSize and elemDW and elemDR then
212
+ return makeDirectArray(element, elemSize, elemDW, elemDR, maxCount)
213
+ end
214
+
215
+ return makeGenericArray(element, maxCount)
181
216
  end
182
217
 
183
218
  function Array.deltaArray(
@@ -194,73 +229,47 @@ function Array.deltaArray(
194
229
  local cache = ch.deltas[deltaId]
195
230
 
196
231
  if not cache then
197
- -- First frame: full
198
- local cursor = ch.cursor
199
- if cursor + 1 > ch.size then
200
- alloc(ch, 1)
201
- end
202
- writeu8(ch.buff, cursor, 1) -- FLAG_FULL (len+1 where len=0 for first)
203
- ch.cursor = cursor + 1
232
+ writeByte(ch, DELTA_FULL)
204
233
 
205
234
  local scratch = acquireScratch()
206
235
  innerArray.write(scratch, value)
207
236
  local snapLen = scratch.cursor
208
- local snapBuf = buffer.create(snapLen)
209
- buffer.copy(snapBuf, 0, scratch.buff, 0, snapLen)
210
237
 
211
- ch.deltas[deltaId] = { raw = snapBuf, len = snapLen }
238
+ ch.deltas[deltaId] = { raw = snapshot(scratch.buff, snapLen), len = snapLen }
212
239
  innerArray.write(ch, value)
213
240
  releaseScratch()
214
241
  return
215
242
  end
216
243
 
217
- -- Compare against cache
218
244
  local scratch = acquireScratch()
219
245
  innerArray.write(scratch, value)
220
246
  local scratchLen = scratch.cursor
221
- local cachedLen = cache.len
222
247
 
223
248
  if
224
- scratchLen == cachedLen and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
249
+ scratchLen == cache.len
250
+ and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
225
251
  then
226
- local cursor = ch.cursor
227
- if cursor + 1 > ch.size then
228
- alloc(ch, 1)
229
- end
230
- writeu8(ch.buff, cursor, DELTA_UNCHANGED)
231
- ch.cursor = cursor + 1
252
+ writeByte(ch, DELTA_UNCHANGED)
232
253
  releaseScratch()
233
254
  return
234
255
  end
235
256
 
236
- -- Changed: full re-send
237
- local cursor = ch.cursor
238
- if cursor + 1 > ch.size then
239
- alloc(ch, 1)
240
- end
241
- writeu8(ch.buff, cursor, 1)
242
- ch.cursor = cursor + 1
257
+ writeByte(ch, DELTA_FULL)
243
258
  innerArray.write(ch, value)
244
259
 
245
- local newBuf = buffer.create(scratchLen)
246
- buffer.copy(newBuf, 0, scratch.buff, 0, scratchLen)
247
- cache.raw = newBuf
260
+ cache.raw = snapshot(scratch.buff, scratchLen)
248
261
  cache.len = scratchLen
249
-
250
262
  releaseScratch()
251
263
  end,
252
264
 
253
265
  read = function(src: buffer, pos: number, refs: { Instance }?): ({ any }, number)
254
- local flag = readu8(src, pos)
255
-
256
- if flag == DELTA_UNCHANGED then
257
- return {} :: any, 1
266
+ if readu8(src, pos) == DELTA_UNCHANGED then
267
+ return {}, 1
258
268
  end
259
-
260
269
  local value, consumed = innerArray.read(src, pos + 1, refs)
261
270
  return value, 1 + consumed
262
271
  end,
263
- } :: any)
272
+ })
264
273
  end
265
274
 
266
275
  return table.freeze(Array)
@@ -1,25 +1,28 @@
1
1
  --!strict
2
2
  --!native
3
- -- map and deltaMap codecs.
3
+ -- Map and deltaMap codecs.
4
4
 
5
5
  local Base = require(script.Parent.Parent.Base)
6
+ local Channel = require(script.Parent.Parent.Parent.internal.Channel)
6
7
  local Shared = require(script.Parent.Shared)
7
8
  local Types = require(script.Parent.Parent.Parent.Types)
8
9
  local Varint = require(script.Parent.Parent.primitive.Varint)
9
10
 
10
- -- Private -------------------------------------------------------------
11
+ -- Private ----------------------------------------------------------------
11
12
 
12
13
  local alloc = Base.alloc
13
- local writeu8 = buffer.writeu8
14
+ local writeByte = Channel.writeByte
14
15
  local readu8 = buffer.readu8
15
16
 
16
17
  local acquireScratch = Shared.acquireScratch
17
18
  local releaseScratch = Shared.releaseScratch
18
19
  local rangeEqual = Shared.rangeEqual
20
+ local snapshot = Shared.snapshot
19
21
  local allocDeltaId = Shared.allocDeltaId
20
22
  local DELTA_UNCHANGED = Shared.DELTA_UNCHANGED
23
+ local DELTA_FULL = Shared.DELTA_FULL
21
24
 
22
- -- Public --------------------------------------------------------------
25
+ -- Public -----------------------------------------------------------------
23
26
 
24
27
  local Map = {}
25
28
 
@@ -28,9 +31,16 @@ function Map.map(
28
31
  valueCodec: Types.InternalCodec<any>,
29
32
  maxCount: number?
30
33
  ): Types.InternalCodec<any>
34
+ local fixedKey = keyCodec._size
35
+ local fixedValue = valueCodec._size
36
+
31
37
  return table.freeze({
32
- write = function(ch: Types.ChannelState, value: any): ()
33
- -- Count entries
38
+ _isMap = true,
39
+ _keyCodec = keyCodec,
40
+ _valueCodec = valueCodec,
41
+ _maxCount = maxCount,
42
+
43
+ write = function(ch: Types.ChannelState, value: { [any]: any }): ()
34
44
  local count = 0
35
45
  for _ in value do
36
46
  count += 1
@@ -41,11 +51,8 @@ function Map.map(
41
51
  return
42
52
  end
43
53
 
44
- -- Pre-alloc if both fixed size
45
- local kAny = keyCodec :: any
46
- local vAny = valueCodec :: any
47
- if kAny._size and vAny._size then
48
- local entrySize = kAny._size + vAny._size
54
+ if fixedKey and fixedValue then
55
+ local entrySize = fixedKey + fixedValue
49
56
  local cursor = ch.cursor
50
57
  if cursor + count * entrySize > ch.size then
51
58
  alloc(ch, count * entrySize)
@@ -58,26 +65,27 @@ function Map.map(
58
65
  end
59
66
  end,
60
67
 
61
- read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
68
+ read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
62
69
  local count, lenBytes = Varint.read(src, pos)
63
70
  if maxCount and count > maxCount then
64
- error(`[Lync] Map: count {count} exceeds max {maxCount}`)
71
+ error(`[Lync] Map.read: count {count} exceeds max {maxCount}`)
65
72
  end
66
73
 
67
74
  local result: { [any]: any } = {}
68
75
  local total = lenBytes
69
-
70
- for _ = 1, count do
76
+ for i = 1, count do
71
77
  local k, kc = keyCodec.read(src, pos + total, refs)
72
78
  total += kc
73
79
  local v, vc = valueCodec.read(src, pos + total, refs)
74
80
  total += vc
81
+ if kc == 0 and vc == 0 then
82
+ error(`[Lync] Map.read: zero-byte entry at index {i}`)
83
+ end
75
84
  result[k] = v
76
85
  end
77
-
78
86
  return result, total
79
87
  end,
80
- } :: any)
88
+ })
81
89
  end
82
90
 
83
91
  function Map.deltaMap(
@@ -91,24 +99,17 @@ function Map.deltaMap(
91
99
  return table.freeze({
92
100
  _isDelta = true,
93
101
 
94
- write = function(ch: Types.ChannelState, value: any): ()
102
+ write = function(ch: Types.ChannelState, value: { [any]: any }): ()
95
103
  local cache = ch.deltas[deltaId]
96
104
 
97
105
  if not cache then
98
- local cursor = ch.cursor
99
- if cursor + 1 > ch.size then
100
- alloc(ch, 1)
101
- end
102
- writeu8(ch.buff, cursor, 3) -- FLAG_FULL
103
- ch.cursor = cursor + 1
106
+ writeByte(ch, DELTA_FULL)
104
107
 
105
108
  local scratch = acquireScratch()
106
109
  innerMap.write(scratch, value)
107
110
  local snapLen = scratch.cursor
108
- local snapBuf = buffer.create(snapLen)
109
- buffer.copy(snapBuf, 0, scratch.buff, 0, snapLen)
110
111
 
111
- ch.deltas[deltaId] = { raw = snapBuf, len = snapLen }
112
+ ch.deltas[deltaId] = { raw = snapshot(scratch.buff, snapLen), len = snapLen }
112
113
  innerMap.write(ch, value)
113
114
  releaseScratch()
114
115
  return
@@ -117,49 +118,32 @@ function Map.deltaMap(
117
118
  local scratch = acquireScratch()
118
119
  innerMap.write(scratch, value)
119
120
  local scratchLen = scratch.cursor
120
- local cachedLen = cache.len
121
121
 
122
122
  if
123
- scratchLen == cachedLen and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
123
+ scratchLen == cache.len
124
+ and rangeEqual(scratch.buff, 0, cache.raw, 0, scratchLen)
124
125
  then
125
- local cursor = ch.cursor
126
- if cursor + 1 > ch.size then
127
- alloc(ch, 1)
128
- end
129
- writeu8(ch.buff, cursor, DELTA_UNCHANGED)
130
- ch.cursor = cursor + 1
126
+ writeByte(ch, DELTA_UNCHANGED)
131
127
  releaseScratch()
132
128
  return
133
129
  end
134
130
 
135
- -- Changed: full re-send
136
- local cursor = ch.cursor
137
- if cursor + 1 > ch.size then
138
- alloc(ch, 1)
139
- end
140
- writeu8(ch.buff, cursor, 3) -- FLAG_FULL
141
- ch.cursor = cursor + 1
131
+ writeByte(ch, DELTA_FULL)
142
132
  innerMap.write(ch, value)
143
133
 
144
- local newBuf = buffer.create(scratchLen)
145
- buffer.copy(newBuf, 0, scratch.buff, 0, scratchLen)
146
- cache.raw = newBuf
134
+ cache.raw = snapshot(scratch.buff, scratchLen)
147
135
  cache.len = scratchLen
148
-
149
136
  releaseScratch()
150
137
  end,
151
138
 
152
- read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
153
- local flag = readu8(src, pos)
154
-
155
- if flag == DELTA_UNCHANGED then
156
- return {} :: any, 1
139
+ read = function(src: buffer, pos: number, refs: { Instance }?): ({ [any]: any }, number)
140
+ if readu8(src, pos) == DELTA_UNCHANGED then
141
+ return {}, 1
157
142
  end
158
-
159
143
  local value, consumed = innerMap.read(src, pos + 1, refs)
160
144
  return value, 1 + consumed
161
145
  end,
162
- } :: any)
146
+ })
163
147
  end
164
148
 
165
149
  return table.freeze(Map)
@@ -1,40 +1,35 @@
1
1
  --!strict
2
2
  --!optimize 2
3
- -- Optional codec. 1 byte flag, value only if present.
3
+ -- Optional codec. 1-byte present/absent flag, value only if present.
4
4
 
5
- local Base = require(script.Parent.Parent.Base)
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 alloc = Base.alloc
11
- local writeu8 = buffer.writeu8
10
+ local writeByte = Channel.writeByte
12
11
  local readu8 = buffer.readu8
13
12
 
14
- -- Public --------------------------------------------------------------
13
+ -- Public -----------------------------------------------------------------
15
14
 
16
15
  local Optional = {}
17
16
 
18
17
  function Optional.optional(inner: Types.InternalCodec<any>): Types.InternalCodec<any>
19
- local codec: any = {
18
+ local codec: Types.InternalCodec<any> = {
19
+ _isOptional = true,
20
+ _inner = inner,
21
+
20
22
  write = function(ch: Types.ChannelState, value: any): ()
21
- local cursor = ch.cursor
22
- if cursor + 1 > ch.size then
23
- alloc(ch, 1)
24
- end
25
23
  if value == nil then
26
- writeu8(ch.buff, cursor, 0)
27
- ch.cursor = cursor + 1
28
- else
29
- writeu8(ch.buff, cursor, 1)
30
- ch.cursor = cursor + 1
31
- inner.write(ch, value)
24
+ writeByte(ch, 0)
25
+ return
32
26
  end
27
+ writeByte(ch, 1)
28
+ inner.write(ch, value)
33
29
  end,
34
30
 
35
31
  read = function(src: buffer, pos: number, refs: { Instance }?): (any, number)
36
- local flag = readu8(src, pos)
37
- if flag == 0 then
32
+ if readu8(src, pos) == 0 then
38
33
  return nil, 1
39
34
  end
40
35
  local value, consumed = inner.read(src, pos + 1, refs)
@@ -42,10 +37,9 @@ function Optional.optional(inner: Types.InternalCodec<any>): Types.InternalCodec
42
37
  end,
43
38
  }
44
39
 
45
- if (inner :: any)._hasUnknown then
40
+ if inner._hasUnknown then
46
41
  codec._hasUnknown = true
47
42
  end
48
-
49
43
  return table.freeze(codec)
50
44
  end
51
45