@woosh/meep-engine 3.46.0 → 3.47.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/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +10 -2
- package/editor/ecs/component/FieldDescriptor.js +15 -0
- package/editor/ecs/component/createObjectEditor.js +41 -12
- package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Transform64Editor.js +77 -70
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +4 -0
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +7 -0
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.js +4 -2
- package/package.json +1 -1
- package/src/core/collection/array/array_insert_at.d.ts +16 -0
- package/src/core/collection/array/array_insert_at.d.ts.map +1 -0
- package/src/core/collection/array/array_insert_at.js +32 -0
- package/src/core/collection/array/array_remove_at.d.ts +15 -0
- package/src/core/collection/array/array_remove_at.d.ts.map +1 -0
- package/src/core/collection/array/array_remove_at.js +30 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts +37 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.js +172 -0
- package/src/core/geom/Quaternion.d.ts +1 -0
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +12 -48
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +9 -4
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +10 -0
- package/src/engine/ecs/transform/t64_look_rotation.d.ts +4 -4
- package/src/engine/ecs/transform/t64_look_rotation.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_look_rotation.js +7 -18
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +30 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +72 -29
- package/src/engine/input/devices/KeyboardDevice.d.ts +1 -0
- package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
- package/src/engine/input/devices/KeyboardDevice.js +7 -6
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +15 -2
- package/src/engine/network/CONGESTION_CONTROL.md +9 -9
- package/src/engine/network/MESSAGE_TRANSPORT.md +364 -0
- package/src/engine/network/NetworkSession.d.ts +31 -11
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +453 -286
- package/src/engine/network/README.md +77 -27
- package/src/engine/network/SMOOTHNESS_DESIGN.md +13 -5
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
- package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
- package/src/engine/network/orchestrator/HostJoin.d.ts +111 -0
- package/src/engine/network/orchestrator/HostJoin.d.ts.map +1 -0
- package/src/engine/network/orchestrator/HostJoin.js +271 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +151 -112
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +450 -499
- package/src/engine/network/orchestrator/PendingSlices.d.ts +30 -0
- package/src/engine/network/orchestrator/PendingSlices.d.ts.map +1 -0
- package/src/engine/network/orchestrator/PendingSlices.js +83 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts +34 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts.map +1 -0
- package/src/engine/network/orchestrator/ReliableFrames.js +96 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +77 -10
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +236 -61
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +20 -6
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +33 -8
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +7 -1
- package/src/engine/network/sim/ActionLog.d.ts +32 -5
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +47 -5
- package/src/engine/network/sim/SimActionExecutor.d.ts +42 -2
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +100 -4
- package/src/engine/network/sim/Snapshotter.d.ts +12 -0
- package/src/engine/network/sim/Snapshotter.d.ts.map +1 -1
- package/src/engine/network/sim/Snapshotter.js +43 -0
- package/src/engine/network/state/Baseline.d.ts +1 -1
- package/src/engine/network/state/Baseline.js +1 -1
- package/src/engine/network/state/FrameBufferRing.d.ts +25 -6
- package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
- package/src/engine/network/state/FrameBufferRing.js +51 -6
- package/src/engine/network/state/InputRing.d.ts +4 -0
- package/src/engine/network/state/InputRing.d.ts.map +1 -1
- package/src/engine/network/state/InputRing.js +7 -0
- package/src/engine/network/transport/DeferredTransport.d.ts.map +1 -1
- package/src/engine/network/transport/DeferredTransport.js +1 -0
- package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
- package/src/engine/network/transport/LoopbackTransport.js +4 -2
- package/src/engine/network/transport/Transport.d.ts +7 -0
- package/src/engine/network/transport/Transport.d.ts.map +1 -1
- package/src/engine/network/transport/Transport.js +8 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +20 -5
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +8 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebTransportTransport.js +6 -0
- package/src/engine/network/transport/connection/Connection.d.ts +191 -0
- package/src/engine/network/transport/connection/Connection.d.ts.map +1 -0
- package/src/engine/network/transport/connection/Connection.js +1262 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts +94 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RangeSet.js +260 -0
- package/src/engine/network/transport/connection/RxStream.d.ts +48 -0
- package/src/engine/network/transport/connection/RxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RxStream.js +211 -0
- package/src/engine/network/transport/connection/TxStream.d.ts +90 -0
- package/src/engine/network/transport/connection/TxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/TxStream.js +243 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts +17 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts.map +1 -0
- package/src/engine/network/transport/connection/bytes_copy.js +34 -0
- package/src/engine/network/transport/connection/varint.d.ts +46 -0
- package/src/engine/network/transport/connection/varint.d.ts.map +1 -0
- package/src/engine/network/transport/connection/varint.js +107 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +38 -7
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitter.js +35 -7
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.js +48 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +12 -2
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +173 -9
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +9 -3
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/LiveEmitterSet.js +28 -8
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +8 -0
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.js +51 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +15 -1
- package/src/engine/sound/sopra/README.md +768 -644
- package/src/engine/sound/sopra/SopraEngine.d.ts +104 -8
- package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
- package/src/engine/sound/sopra/SopraEngine.js +327 -12
- package/src/engine/sound/sopra/definition/EventDescription.d.ts +28 -2
- package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescription.js +55 -5
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +10 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +77 -55
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts +31 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.js +75 -0
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +38 -9
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +34 -5
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +7 -2
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +137 -122
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +3 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.js +146 -131
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +1 -0
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +6 -1
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts +44 -2
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/BusGraph.js +83 -43
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts +96 -0
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectChain.js +286 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts +123 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.js +320 -0
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts +55 -5
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +201 -26
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +2 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +6 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +15 -0
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +32 -0
- package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/MockAudioContext.js +3 -0
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +13 -3
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/fadeOutAndStop.js +25 -13
- package/src/shade/RENDERER_CONTRACT.md +6 -3
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts +24 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.js +34 -0
- package/src/shade/device/ShadeDeviceFailure.d.ts +12 -5
- package/src/shade/device/ShadeDeviceFailure.d.ts.map +1 -1
- package/src/shade/device/ShadeDeviceFailure.js +17 -8
- package/src/shade/device/ShadeDeviceFailureReason.d.ts +1 -1
- package/src/shade/device/ShadeDeviceFailureReason.js +2 -2
- package/src/shade/device/shade_device_floor_unmet.d.ts +13 -0
- package/src/shade/device/shade_device_floor_unmet.d.ts.map +1 -0
- package/src/shade/device/shade_device_floor_unmet.js +32 -0
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +29 -55
- package/src/shade/renderer/buffer/GPUBufferAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferAllocator.js +9 -0
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts +21 -12
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts.map +1 -1
- package/src/shade/renderer/buffer/buffer_pool_ops.js +18 -19
- package/src/shade/renderer/gpu_primitive/test/gpu_test.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/test/gpu_test.js +8 -11
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/pool/TransientResourcePool.d.ts +24 -0
- package/src/shade/renderer/pool/TransientResourcePool.d.ts.map +1 -1
- package/src/shade/renderer/pool/TransientResourcePool.js +41 -10
- package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ComputeShader.js +18 -2
- package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ImageShader.js +230 -218
- package/src/view/graphics/GraphicsContextFailureView.d.ts +1 -2
- package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
- package/src/view/graphics/GraphicsContextFailureView.js +1 -2
- package/src/engine/network/transport/Channel.js +0 -336
- package/src/engine/network/transport/ReliableCommandPipeline.js +0 -335
- package/src/engine/network/transport/fragments/FragmentAssembler.js +0 -350
- package/src/engine/network/transport/fragments/FragmentRetention.js +0 -191
- package/src/engine/network/transport/fragments/fragment_send.js +0 -157
- package/src/engine/network/transport/fragments/packet_size.js +0 -107
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A set of non-negative integers held as sorted, disjoint, non-adjacent half-open ranges
|
|
3
|
+
* `[start, end)`. Packet numbers a receiver has seen, stream bytes it holds, bytes a sender must
|
|
4
|
+
* re-send: each is a few long runs, so a range list is both the compact and the cheap form.
|
|
5
|
+
*
|
|
6
|
+
* Values are doubles, exact to 2^53, so neither a packet number nor a stream offset ever wraps.
|
|
7
|
+
* Storage is one `Float64Array` of `[start, end]` pairs that grows to a high-watermark and stays:
|
|
8
|
+
* nothing is allocated once it is big enough.
|
|
9
|
+
*
|
|
10
|
+
* Operations are linear in the number of ranges. Every user bounds that number (an ACK reports at
|
|
11
|
+
* most a few dozen; a stream's holes are bounded by its window), so linear is the fast form too.
|
|
12
|
+
*
|
|
13
|
+
* @author Alex Goldring
|
|
14
|
+
* @copyright Company Named Limited (c) 2026
|
|
15
|
+
*/
|
|
16
|
+
export class RangeSet {
|
|
17
|
+
/**
|
|
18
|
+
* @param {number} [initial_capacity] ranges to make room for up front
|
|
19
|
+
*/
|
|
20
|
+
constructor(initial_capacity?: number);
|
|
21
|
+
/**
|
|
22
|
+
* Number of disjoint ranges.
|
|
23
|
+
* @returns {number}
|
|
24
|
+
*/
|
|
25
|
+
get count(): number;
|
|
26
|
+
/**
|
|
27
|
+
* @param {number} i range index, `[0, count)`
|
|
28
|
+
* @returns {number} inclusive start
|
|
29
|
+
*/
|
|
30
|
+
start(i: number): number;
|
|
31
|
+
/**
|
|
32
|
+
* @param {number} i range index, `[0, count)`
|
|
33
|
+
* @returns {number} exclusive end
|
|
34
|
+
*/
|
|
35
|
+
end(i: number): number;
|
|
36
|
+
/**
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
is_empty(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Smallest value held. Only meaningful when not empty.
|
|
42
|
+
* @returns {number}
|
|
43
|
+
*/
|
|
44
|
+
min(): number;
|
|
45
|
+
/**
|
|
46
|
+
* One past the largest value held. Only meaningful when not empty.
|
|
47
|
+
* @returns {number}
|
|
48
|
+
*/
|
|
49
|
+
max_end(): number;
|
|
50
|
+
clear(): void;
|
|
51
|
+
/**
|
|
52
|
+
* @param {number} value
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
contains(value: number): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Whether every value of `[start, end)` is held.
|
|
58
|
+
* @param {number} start
|
|
59
|
+
* @param {number} end
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
contains_range(start: number, end: number): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* End of the run that starts at `value`: the largest `e` with `[value, e)` held, or `value`
|
|
65
|
+
* itself when `value` is not held.
|
|
66
|
+
* @param {number} value
|
|
67
|
+
* @returns {number}
|
|
68
|
+
*/
|
|
69
|
+
run_end(value: number): number;
|
|
70
|
+
/**
|
|
71
|
+
* Add `[start, end)`, merging with whatever it touches.
|
|
72
|
+
* @param {number} start
|
|
73
|
+
* @param {number} end
|
|
74
|
+
*/
|
|
75
|
+
add(start: number, end: number): void;
|
|
76
|
+
/**
|
|
77
|
+
* Remove `[start, end)`, splitting a range it lands inside.
|
|
78
|
+
* @param {number} start
|
|
79
|
+
* @param {number} end
|
|
80
|
+
*/
|
|
81
|
+
remove(start: number, end: number): void;
|
|
82
|
+
/**
|
|
83
|
+
* Remove everything below `value`.
|
|
84
|
+
* @param {number} value
|
|
85
|
+
*/
|
|
86
|
+
remove_below(value: number): void;
|
|
87
|
+
/**
|
|
88
|
+
* Drop whole ranges from the bottom until at most `max_count` remain.
|
|
89
|
+
* @param {number} max_count
|
|
90
|
+
*/
|
|
91
|
+
keep_highest(max_count: number): void;
|
|
92
|
+
#private;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=RangeSet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RangeSet.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/network/transport/connection/RangeSet.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH;IAaI;;OAEG;IACH,+BAFW,MAAM,EAKhB;IAED;;;OAGG;IACH,oBAEC;IAED;;;OAGG;IACH,SAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;OAGG;IACH,OAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;OAEG;IACH,YAFa,OAAO,CAInB;IAED;;;OAGG;IACH,OAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,WAFa,MAAM,CAIlB;IAED,cAEC;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAUnB;IAED;;;;;OAKG;IACH,sBAJW,MAAM,OACN,MAAM,GACJ,OAAO,CAWnB;IAED;;;;;OAKG;IACH,eAHW,MAAM,GACJ,MAAM,CAWlB;IAED;;;;OAIG;IACH,WAHW,MAAM,OACN,MAAM,QA0ChB;IAED;;;;OAIG;IACH,cAHW,MAAM,OACN,MAAM,QAwChB;IAED;;;OAGG;IACH,oBAFW,MAAM,QAKhB;IAED;;;OAGG;IACH,wBAFW,MAAM,QAQhB;;CAWJ"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { assert } from "../../../../core/assert.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A set of non-negative integers held as sorted, disjoint, non-adjacent half-open ranges
|
|
5
|
+
* `[start, end)`. Packet numbers a receiver has seen, stream bytes it holds, bytes a sender must
|
|
6
|
+
* re-send: each is a few long runs, so a range list is both the compact and the cheap form.
|
|
7
|
+
*
|
|
8
|
+
* Values are doubles, exact to 2^53, so neither a packet number nor a stream offset ever wraps.
|
|
9
|
+
* Storage is one `Float64Array` of `[start, end]` pairs that grows to a high-watermark and stays:
|
|
10
|
+
* nothing is allocated once it is big enough.
|
|
11
|
+
*
|
|
12
|
+
* Operations are linear in the number of ranges. Every user bounds that number (an ACK reports at
|
|
13
|
+
* most a few dozen; a stream's holes are bounded by its window), so linear is the fast form too.
|
|
14
|
+
*
|
|
15
|
+
* @author Alex Goldring
|
|
16
|
+
* @copyright Company Named Limited (c) 2026
|
|
17
|
+
*/
|
|
18
|
+
export class RangeSet {
|
|
19
|
+
/**
|
|
20
|
+
* `[start0, end0, start1, end1, ...]`, ascending, disjoint and non-adjacent.
|
|
21
|
+
* @type {Float64Array}
|
|
22
|
+
*/
|
|
23
|
+
#data;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Number of ranges held.
|
|
27
|
+
* @type {number}
|
|
28
|
+
*/
|
|
29
|
+
#count = 0;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {number} [initial_capacity] ranges to make room for up front
|
|
33
|
+
*/
|
|
34
|
+
constructor(initial_capacity = 8) {
|
|
35
|
+
assert.isPositiveInteger(initial_capacity, 'initial_capacity');
|
|
36
|
+
this.#data = new Float64Array(initial_capacity * 2);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Number of disjoint ranges.
|
|
41
|
+
* @returns {number}
|
|
42
|
+
*/
|
|
43
|
+
get count() {
|
|
44
|
+
return this.#count;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {number} i range index, `[0, count)`
|
|
49
|
+
* @returns {number} inclusive start
|
|
50
|
+
*/
|
|
51
|
+
start(i) {
|
|
52
|
+
return this.#data[i * 2];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {number} i range index, `[0, count)`
|
|
57
|
+
* @returns {number} exclusive end
|
|
58
|
+
*/
|
|
59
|
+
end(i) {
|
|
60
|
+
return this.#data[i * 2 + 1];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @returns {boolean}
|
|
65
|
+
*/
|
|
66
|
+
is_empty() {
|
|
67
|
+
return this.#count === 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Smallest value held. Only meaningful when not empty.
|
|
72
|
+
* @returns {number}
|
|
73
|
+
*/
|
|
74
|
+
min() {
|
|
75
|
+
return this.#data[0];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* One past the largest value held. Only meaningful when not empty.
|
|
80
|
+
* @returns {number}
|
|
81
|
+
*/
|
|
82
|
+
max_end() {
|
|
83
|
+
return this.#data[this.#count * 2 - 1];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
clear() {
|
|
87
|
+
this.#count = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {number} value
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
contains(value) {
|
|
95
|
+
const data = this.#data;
|
|
96
|
+
for (let i = 0; i < this.#count; i++) {
|
|
97
|
+
const s = data[i * 2];
|
|
98
|
+
if (value < s) return false;
|
|
99
|
+
if (value < data[i * 2 + 1]) return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Whether every value of `[start, end)` is held.
|
|
106
|
+
* @param {number} start
|
|
107
|
+
* @param {number} end
|
|
108
|
+
* @returns {boolean}
|
|
109
|
+
*/
|
|
110
|
+
contains_range(start, end) {
|
|
111
|
+
if (end <= start) return true;
|
|
112
|
+
const data = this.#data;
|
|
113
|
+
for (let i = 0; i < this.#count; i++) {
|
|
114
|
+
const s = data[i * 2];
|
|
115
|
+
if (start < s) return false;
|
|
116
|
+
if (start < data[i * 2 + 1]) return end <= data[i * 2 + 1];
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* End of the run that starts at `value`: the largest `e` with `[value, e)` held, or `value`
|
|
123
|
+
* itself when `value` is not held.
|
|
124
|
+
* @param {number} value
|
|
125
|
+
* @returns {number}
|
|
126
|
+
*/
|
|
127
|
+
run_end(value) {
|
|
128
|
+
const data = this.#data;
|
|
129
|
+
for (let i = 0; i < this.#count; i++) {
|
|
130
|
+
const s = data[i * 2];
|
|
131
|
+
if (value < s) return value;
|
|
132
|
+
const e = data[i * 2 + 1];
|
|
133
|
+
if (value < e) return e;
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Add `[start, end)`, merging with whatever it touches.
|
|
140
|
+
* @param {number} start
|
|
141
|
+
* @param {number} end
|
|
142
|
+
*/
|
|
143
|
+
add(start, end) {
|
|
144
|
+
if (end <= start) return;
|
|
145
|
+
const data = this.#data;
|
|
146
|
+
const n = this.#count;
|
|
147
|
+
|
|
148
|
+
// first range whose end reaches `start` (touching counts: they merge); a binary search, so
|
|
149
|
+
// the common add — at or past the top — costs nothing for the ranges below it
|
|
150
|
+
let lo = 0;
|
|
151
|
+
let top = n;
|
|
152
|
+
while (lo < top) {
|
|
153
|
+
const mid = (lo + top) >>> 1;
|
|
154
|
+
if (data[mid * 2 + 1] < start) lo = mid + 1;
|
|
155
|
+
else top = mid;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// first range that starts strictly past `end`
|
|
159
|
+
let hi = lo;
|
|
160
|
+
while (hi < n && data[hi * 2] <= end) hi++;
|
|
161
|
+
|
|
162
|
+
if (lo === hi) {
|
|
163
|
+
// touches nothing: insert at lo
|
|
164
|
+
this.#ensure_capacity(n + 1);
|
|
165
|
+
const d = this.#data;
|
|
166
|
+
d.copyWithin((lo + 1) * 2, lo * 2, n * 2);
|
|
167
|
+
d[lo * 2] = start;
|
|
168
|
+
d[lo * 2 + 1] = end;
|
|
169
|
+
this.#count = n + 1;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// merge ranges [lo, hi) with [start, end) into slot lo
|
|
174
|
+
const merged_start = Math.min(start, data[lo * 2]);
|
|
175
|
+
const merged_end = Math.max(end, data[(hi - 1) * 2 + 1]);
|
|
176
|
+
data[lo * 2] = merged_start;
|
|
177
|
+
data[lo * 2 + 1] = merged_end;
|
|
178
|
+
const removed = hi - lo - 1;
|
|
179
|
+
if (removed > 0) {
|
|
180
|
+
data.copyWithin((lo + 1) * 2, hi * 2, n * 2);
|
|
181
|
+
this.#count = n - removed;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Remove `[start, end)`, splitting a range it lands inside.
|
|
187
|
+
* @param {number} start
|
|
188
|
+
* @param {number} end
|
|
189
|
+
*/
|
|
190
|
+
remove(start, end) {
|
|
191
|
+
if (end <= start) return;
|
|
192
|
+
let i = 0;
|
|
193
|
+
while (i < this.#count) {
|
|
194
|
+
const data = this.#data;
|
|
195
|
+
const s = data[i * 2];
|
|
196
|
+
const e = data[i * 2 + 1];
|
|
197
|
+
if (e <= start) {
|
|
198
|
+
i++;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (s >= end) return;
|
|
202
|
+
|
|
203
|
+
if (s < start && e > end) {
|
|
204
|
+
// split in two
|
|
205
|
+
const n = this.#count;
|
|
206
|
+
this.#ensure_capacity(n + 1);
|
|
207
|
+
const d = this.#data;
|
|
208
|
+
d.copyWithin((i + 2) * 2, (i + 1) * 2, n * 2);
|
|
209
|
+
d[i * 2 + 1] = start;
|
|
210
|
+
d[(i + 1) * 2] = end;
|
|
211
|
+
d[(i + 1) * 2 + 1] = e;
|
|
212
|
+
this.#count = n + 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (s < start) {
|
|
216
|
+
data[i * 2 + 1] = start;
|
|
217
|
+
i++;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (e > end) {
|
|
221
|
+
data[i * 2] = end;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
// fully covered: delete range i
|
|
225
|
+
data.copyWithin(i * 2, (i + 1) * 2, this.#count * 2);
|
|
226
|
+
this.#count--;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Remove everything below `value`.
|
|
232
|
+
* @param {number} value
|
|
233
|
+
*/
|
|
234
|
+
remove_below(value) {
|
|
235
|
+
if (this.#count === 0) return;
|
|
236
|
+
this.remove(this.#data[0], value);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Drop whole ranges from the bottom until at most `max_count` remain.
|
|
241
|
+
* @param {number} max_count
|
|
242
|
+
*/
|
|
243
|
+
keep_highest(max_count) {
|
|
244
|
+
const n = this.#count;
|
|
245
|
+
if (n <= max_count) return;
|
|
246
|
+
const drop = n - max_count;
|
|
247
|
+
this.#data.copyWithin(0, drop * 2, n * 2);
|
|
248
|
+
this.#count = max_count;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {number} ranges
|
|
253
|
+
*/
|
|
254
|
+
#ensure_capacity(ranges) {
|
|
255
|
+
if (ranges * 2 <= this.#data.length) return;
|
|
256
|
+
const next = new Float64Array(Math.max(ranges * 2, this.#data.length * 2));
|
|
257
|
+
next.set(this.#data.subarray(0, this.#count * 2));
|
|
258
|
+
this.#data = next;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** {@link RxStream#next_message}: nothing whole yet. */
|
|
2
|
+
export const RX_NONE: -1;
|
|
3
|
+
/** {@link RxStream#next_message}: the stream broke the protocol; stop reading it. */
|
|
4
|
+
export const RX_ERROR: -2;
|
|
5
|
+
export class RxStream {
|
|
6
|
+
/**
|
|
7
|
+
* @param {number} max_message_bytes largest message this lane accepts
|
|
8
|
+
* @param {number} window bytes past the delivered point the sender may have in flight
|
|
9
|
+
*/
|
|
10
|
+
constructor(max_message_bytes: number, window: number);
|
|
11
|
+
/** @type {number} @readonly */
|
|
12
|
+
readonly max_message_bytes: number;
|
|
13
|
+
/** @type {number} @readonly */
|
|
14
|
+
readonly window: number;
|
|
15
|
+
/**
|
|
16
|
+
* Most disjoint ranges held out of order: one per 256 bytes of window, and never fewer
|
|
17
|
+
* than 1,024.
|
|
18
|
+
* @type {number} @readonly
|
|
19
|
+
*/
|
|
20
|
+
readonly max_ranges: number;
|
|
21
|
+
/** Whether the stream has broken the protocol. */
|
|
22
|
+
get failed(): boolean;
|
|
23
|
+
/** How the stream broke the protocol; empty while it has not. */
|
|
24
|
+
get failure(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Take one segment.
|
|
27
|
+
*
|
|
28
|
+
* @param {number} offset stream offset of the segment's first byte
|
|
29
|
+
* @param {Uint8Array} bytes
|
|
30
|
+
* @param {number} from index of the first byte in `bytes`
|
|
31
|
+
* @param {number} length
|
|
32
|
+
* @returns {boolean} false when the segment breaks the protocol
|
|
33
|
+
*/
|
|
34
|
+
receive(offset: number, bytes: Uint8Array, from: number, length: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* The next whole message, consumed. Writes its position in {@link bytes} into `out[0]`; valid until
|
|
37
|
+
* the next {@link receive}.
|
|
38
|
+
*
|
|
39
|
+
* @param {Float64Array|number[]} out
|
|
40
|
+
* @returns {number} message length, {@link RX_NONE} or {@link RX_ERROR}
|
|
41
|
+
*/
|
|
42
|
+
next_message(out: Float64Array | number[]): number;
|
|
43
|
+
/** The buffer {@link next_message} positions are in. */
|
|
44
|
+
get bytes(): Uint8Array;
|
|
45
|
+
clear(): void;
|
|
46
|
+
#private;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=RxStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RxStream.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/network/transport/connection/RxStream.js"],"names":[],"mappings":"AAyBA,wDAAwD;AACxD,yBAA0B;AAE1B,qFAAqF;AACrF,0BAA2B;AAE3B;IAuCI;;;OAGG;IACH,+BAHW,MAAM,UACN,MAAM,EAchB;IAXG,+BAA+B;IAC/B,4BADW,MAAM,CACyB;IAC1C,+BAA+B;IAC/B,iBADW,MAAM,CACG;IACpB;;;;OAIG;IACH,qBAFU,MAAM,CAEyC;IAI7D,kDAAkD;IAClD,sBAEC;IAED,iEAAiE;IACjE,sBAEC;IAED;;;;;;;;OAQG;IACH,gBANW,MAAM,SACN,UAAU,QACV,MAAM,UACN,MAAM,GACJ,OAAO,CAqBnB;IAYD;;;;;;OAMG;IACH,kBAHW,YAAY,GAAC,MAAM,EAAE,GACnB,MAAM,CA+BlB;IAED,wDAAwD;IACxD,wBAEC;IAED,cAKC;;CAwBJ"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { bytes_copy } from "./bytes_copy.js";
|
|
2
|
+
import { RangeSet } from "./RangeSet.js";
|
|
3
|
+
import { varint_read } from "./varint.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Receive half of one reliable lane: puts stream segments back in order and hands out whole
|
|
7
|
+
* `[varint length][bytes]` messages as soon as their bytes are contiguous.
|
|
8
|
+
*
|
|
9
|
+
* Memory is bounded by configuration, never by what the peer claims. A segment reaching past
|
|
10
|
+
* `consumed + max_message_bytes + header + window` cannot come from a sender keeping to its window
|
|
11
|
+
* (see {@link TxStream}), and a message declaring more than `max_message_bytes` is refused: both
|
|
12
|
+
* are protocol errors, and the lane stops.
|
|
13
|
+
*
|
|
14
|
+
* So is time. What a segment costs to take in grows with the holes held, and a peer sending
|
|
15
|
+
* one-byte segments two bytes apart would have the receiver hold half a window of them. An honest
|
|
16
|
+
* sender's holes are packets it lost and has not re-sent yet: a few hundred at the most in a
|
|
17
|
+
* 256 KiB window, even over small datagrams. More than {@link max_ranges} is a protocol error.
|
|
18
|
+
*
|
|
19
|
+
* @author Alex Goldring
|
|
20
|
+
* @copyright Company Named Limited (c) 2026
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/** Largest header a message length can have. */
|
|
24
|
+
const MAX_HEADER_BYTES = 8;
|
|
25
|
+
|
|
26
|
+
/** {@link RxStream#next_message}: nothing whole yet. */
|
|
27
|
+
export const RX_NONE = -1;
|
|
28
|
+
|
|
29
|
+
/** {@link RxStream#next_message}: the stream broke the protocol; stop reading it. */
|
|
30
|
+
export const RX_ERROR = -2;
|
|
31
|
+
|
|
32
|
+
export class RxStream {
|
|
33
|
+
/**
|
|
34
|
+
* Stream bytes from {@link #buffer_base} on.
|
|
35
|
+
* @type {Uint8Array}
|
|
36
|
+
*/
|
|
37
|
+
#buffer;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Stream offset of `#buffer[0]`.
|
|
41
|
+
* @type {number}
|
|
42
|
+
*/
|
|
43
|
+
#buffer_base = 0;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Every byte below this offset has been handed out.
|
|
47
|
+
* @type {number}
|
|
48
|
+
*/
|
|
49
|
+
#consumed = 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Ranges held at or above {@link #consumed}.
|
|
53
|
+
* @type {RangeSet}
|
|
54
|
+
*/
|
|
55
|
+
#received = new RangeSet();
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Set once the stream has broken the protocol.
|
|
59
|
+
* @type {boolean}
|
|
60
|
+
*/
|
|
61
|
+
#failed = false;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* How it broke the protocol, once {@link #failed}.
|
|
65
|
+
* @type {string}
|
|
66
|
+
*/
|
|
67
|
+
#failure = '';
|
|
68
|
+
|
|
69
|
+
#cursor = { position: 0 };
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {number} max_message_bytes largest message this lane accepts
|
|
73
|
+
* @param {number} window bytes past the delivered point the sender may have in flight
|
|
74
|
+
*/
|
|
75
|
+
constructor(max_message_bytes, window) {
|
|
76
|
+
/** @type {number} @readonly */
|
|
77
|
+
this.max_message_bytes = max_message_bytes;
|
|
78
|
+
/** @type {number} @readonly */
|
|
79
|
+
this.window = window;
|
|
80
|
+
/**
|
|
81
|
+
* Most disjoint ranges held out of order: one per 256 bytes of window, and never fewer
|
|
82
|
+
* than 1,024.
|
|
83
|
+
* @type {number} @readonly
|
|
84
|
+
*/
|
|
85
|
+
this.max_ranges = Math.max(1024, Math.ceil(window / 256));
|
|
86
|
+
this.#buffer = new Uint8Array(1024);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Whether the stream has broken the protocol. */
|
|
90
|
+
get failed() {
|
|
91
|
+
return this.#failed;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** How the stream broke the protocol; empty while it has not. */
|
|
95
|
+
get failure() {
|
|
96
|
+
return this.#failure;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Take one segment.
|
|
101
|
+
*
|
|
102
|
+
* @param {number} offset stream offset of the segment's first byte
|
|
103
|
+
* @param {Uint8Array} bytes
|
|
104
|
+
* @param {number} from index of the first byte in `bytes`
|
|
105
|
+
* @param {number} length
|
|
106
|
+
* @returns {boolean} false when the segment breaks the protocol
|
|
107
|
+
*/
|
|
108
|
+
receive(offset, bytes, from, length) {
|
|
109
|
+
if (this.#failed) return false;
|
|
110
|
+
let start = offset;
|
|
111
|
+
const end = offset + length;
|
|
112
|
+
if (end <= this.#consumed) return true; // all of it handed out already
|
|
113
|
+
if (start < this.#consumed) {
|
|
114
|
+
from += this.#consumed - start;
|
|
115
|
+
start = this.#consumed;
|
|
116
|
+
}
|
|
117
|
+
if (end > this.#consumed + this.max_message_bytes + MAX_HEADER_BYTES + this.window) {
|
|
118
|
+
return this.#fail('segment past the receive window');
|
|
119
|
+
}
|
|
120
|
+
this.#reserve(end);
|
|
121
|
+
bytes_copy(bytes, from, this.#buffer, start - this.#buffer_base, end - start);
|
|
122
|
+
this.#received.add(start, end);
|
|
123
|
+
if (this.#received.count > this.max_ranges) {
|
|
124
|
+
return this.#fail(`more than ${this.max_ranges} holes held out of order`);
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {string} reason
|
|
131
|
+
* @returns {boolean} false
|
|
132
|
+
*/
|
|
133
|
+
#fail(reason) {
|
|
134
|
+
this.#failed = true;
|
|
135
|
+
this.#failure = reason;
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The next whole message, consumed. Writes its position in {@link bytes} into `out[0]`; valid until
|
|
141
|
+
* the next {@link receive}.
|
|
142
|
+
*
|
|
143
|
+
* @param {Float64Array|number[]} out
|
|
144
|
+
* @returns {number} message length, {@link RX_NONE} or {@link RX_ERROR}
|
|
145
|
+
*/
|
|
146
|
+
next_message(out) {
|
|
147
|
+
if (this.#failed) return RX_ERROR;
|
|
148
|
+
const received = this.#received;
|
|
149
|
+
if (received.is_empty() || received.min() > this.#consumed) return RX_NONE;
|
|
150
|
+
const contiguous = received.end(0);
|
|
151
|
+
|
|
152
|
+
const read = this.#consumed - this.#buffer_base;
|
|
153
|
+
const cursor = this.#cursor;
|
|
154
|
+
cursor.position = read;
|
|
155
|
+
const length = varint_read(this.#buffer, cursor, contiguous - this.#buffer_base);
|
|
156
|
+
if (length < 0) {
|
|
157
|
+
// header not all here yet - unless it is already as long as a header can be
|
|
158
|
+
if (contiguous - this.#consumed >= MAX_HEADER_BYTES) {
|
|
159
|
+
this.#fail('message length is not a varint');
|
|
160
|
+
return RX_ERROR;
|
|
161
|
+
}
|
|
162
|
+
return RX_NONE;
|
|
163
|
+
}
|
|
164
|
+
if (length > this.max_message_bytes) {
|
|
165
|
+
this.#fail(`message larger than max_message_bytes ${this.max_message_bytes}`);
|
|
166
|
+
return RX_ERROR;
|
|
167
|
+
}
|
|
168
|
+
const body = cursor.position;
|
|
169
|
+
if (body + length > contiguous - this.#buffer_base) return RX_NONE;
|
|
170
|
+
|
|
171
|
+
out[0] = body;
|
|
172
|
+
this.#consumed = this.#buffer_base + body + length;
|
|
173
|
+
received.remove_below(this.#consumed);
|
|
174
|
+
return length;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** The buffer {@link next_message} positions are in. */
|
|
178
|
+
get bytes() {
|
|
179
|
+
return this.#buffer;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
clear() {
|
|
183
|
+
this.#buffer_base = this.#consumed = 0;
|
|
184
|
+
this.#received.clear();
|
|
185
|
+
this.#failed = false;
|
|
186
|
+
this.#failure = '';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Make the buffer reach stream offset `end`: first by dropping what has been handed out, then
|
|
191
|
+
* by growing. Bounded by the caller's check against the window.
|
|
192
|
+
* @param {number} end
|
|
193
|
+
*/
|
|
194
|
+
#reserve(end) {
|
|
195
|
+
if (end - this.#buffer_base <= this.#buffer.length) return;
|
|
196
|
+
const drop = this.#consumed - this.#buffer_base;
|
|
197
|
+
if (drop > 0) {
|
|
198
|
+
const held_end = this.#received.is_empty() ? this.#consumed : this.#received.max_end();
|
|
199
|
+
const kept = held_end - this.#consumed;
|
|
200
|
+
if (kept > 0) this.#buffer.copyWithin(0, drop, drop + kept);
|
|
201
|
+
this.#buffer_base = this.#consumed;
|
|
202
|
+
}
|
|
203
|
+
const needed = end - this.#buffer_base;
|
|
204
|
+
if (needed <= this.#buffer.length) return;
|
|
205
|
+
let size = this.#buffer.length * 2;
|
|
206
|
+
while (size < needed) size *= 2;
|
|
207
|
+
const next = new Uint8Array(size);
|
|
208
|
+
next.set(this.#buffer);
|
|
209
|
+
this.#buffer = next;
|
|
210
|
+
}
|
|
211
|
+
}
|