diff --git a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.Read.cs b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.Read.cs index 529f924..3348370 100644 --- a/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.Read.cs +++ b/AyCode.Core/Serializers/Binaries/AcBinaryDeserializer.BinaryDeserializationContext.Read.cs @@ -651,9 +651,29 @@ public static partial class AcBinaryDeserializer #endregion + /// + /// Ensures bytes are readable from the current _buffer[_position] + /// position, advancing to the next segment via when + /// the current buffer is exhausted. + /// JIT specialization fast-path: when TInput.IsTrustedSingleSegment is the + /// constant true (e.g. ), the entire method body is eliminated + /// at JIT time — bounds-check + segment-advance both vanish. Per-read overhead drops to ~0 ns. + /// Trade-off: corrupt-wire detection downgrades from + /// to a generic ; acceptable for trusted byte[] inputs + /// where the buffer is already validated by the caller. + /// For non-trusted inputs (, AsyncPipeReaderInputAdapter), the + /// guard's if (false) return; form is dead-code-eliminated — the bounds-check and segment-advance + /// keep their original behaviour with zero added overhead. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private void EnsureAvailable(int length) { + // Trusted-single-segment fast path — JIT folds the constant per TInput specialization: + // ArrayBinaryInput → if (true) return; → method body entirely eliminated + // SequenceBinaryInput → if (false) return; → guard eliminated, bounds-check kept + // AsyncPipeReaderInput → if (false) return; → guard eliminated, bounds-check kept + if (TInput.IsTrustedSingleSegment) return; + if (_position > _bufferLength - length) { if (!Input.TryAdvanceSegment(ref _buffer, ref _position, ref _bufferLength, length)) diff --git a/AyCode.Core/Serializers/Binaries/ArrayBinaryInput.cs b/AyCode.Core/Serializers/Binaries/ArrayBinaryInput.cs index 2217061..7d1a602 100644 --- a/AyCode.Core/Serializers/Binaries/ArrayBinaryInput.cs +++ b/AyCode.Core/Serializers/Binaries/ArrayBinaryInput.cs @@ -12,6 +12,13 @@ namespace AyCode.Core.Serializers.Binaries; /// public struct ArrayBinaryInput : IBinaryInputBase { + /// + /// Single-buffer input — entire payload loaded upfront, no cross-segment advance needed. + /// JIT-constant trueBinaryDeserializationContext.EnsureAvailable body is + /// dead-code-eliminated for this specialization (per-read bounds-check overhead drops to ~0 ns). + /// + public static bool IsTrustedSingleSegment => true; + private readonly byte[] _data; private readonly int _offset; private readonly int _length; diff --git a/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInput.cs b/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInput.cs index 3845f36..20c6fbc 100644 --- a/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInput.cs +++ b/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInput.cs @@ -91,6 +91,13 @@ namespace AyCode.Core.Serializers.Binaries; /// public sealed class AsyncPipeReaderInput : IBinaryInputBase, IDisposable { + /// + /// Streaming input — chunks arrive over time via ; the read path must block / advance + /// on chunk-arrival via . JIT-constant false → context's + /// EnsureAvailable bounds-check + segment-advance kept (essential for streaming-handoff). + /// + public static bool IsTrustedSingleSegment => false; + private byte[] _buffer; private int _writePos; private int _readPos; // consumer reports consumed position here diff --git a/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInputAdapter.cs b/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInputAdapter.cs index 3a4296f..02e4b99 100644 --- a/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInputAdapter.cs +++ b/AyCode.Core/Serializers/Binaries/AsyncPipeReaderInputAdapter.cs @@ -35,6 +35,13 @@ namespace AyCode.Core.Serializers.Binaries; /// internal readonly struct AsyncPipeReaderInputAdapter : IBinaryInputBase { + /// + /// Streaming input — chunks arrive over time via ; the read path + /// must block / advance on chunk-arrival. JIT-constant falseEnsureAvailable bounds-check + /// + kept (essential for streaming-handoff). + /// + public static bool IsTrustedSingleSegment => false; + private readonly AsyncPipeReaderInput _input; [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/AyCode.Core/Serializers/Binaries/IBinaryInputBase.cs b/AyCode.Core/Serializers/Binaries/IBinaryInputBase.cs index da05cf4..4ceac58 100644 --- a/AyCode.Core/Serializers/Binaries/IBinaryInputBase.cs +++ b/AyCode.Core/Serializers/Binaries/IBinaryInputBase.cs @@ -15,6 +15,22 @@ namespace AyCode.Core.Serializers.Binaries; /// public interface IBinaryInputBase { + /// + /// Compile-time JIT-constant flag — true when the input is guaranteed to be a single contiguous buffer + /// loaded entirely upfront (no cross-segment advance needed, no streaming-handoff needed). Used by + /// BinaryDeserializationContext.EnsureAvailable to skip the bounds-check-and-segment-advance + /// path on trusted inputs: if (TInput.IsTrustedSingleSegment) return; folds to a no-op via JIT + /// generic-struct specialization, eliminating the per-read overhead. + /// Set true when: (single byte[] buffer, fully loaded). + /// Set false when: (multi-segment ROS — cross-segment advance required), + /// AsyncPipeReaderInputAdapter (streaming — chunk-arrival handoff required), or any other input that may + /// dynamically advance buffers during deserialization. + /// Trade-off in trusted mode: corrupt-wire detection downgrades from a domain-specific + /// AcBinaryDeserializationException to a generic IndexOutOfRangeException. Acceptable + /// for byte[]-API consumers — the buffer is already validated by the caller. + /// + static abstract bool IsTrustedSingleSegment { get; } + /// /// Provides the initial buffer, starting position, and buffer length. /// Called once before deserialization begins. diff --git a/AyCode.Core/Serializers/Binaries/SequenceBinaryInput.cs b/AyCode.Core/Serializers/Binaries/SequenceBinaryInput.cs index 5b79e70..b1b5388 100644 --- a/AyCode.Core/Serializers/Binaries/SequenceBinaryInput.cs +++ b/AyCode.Core/Serializers/Binaries/SequenceBinaryInput.cs @@ -18,6 +18,16 @@ namespace AyCode.Core.Serializers.Binaries; /// public struct SequenceBinaryInput : IBinaryInputBase { + /// + /// Multi-segment input — cross-segment advance via is required even on + /// nominally single-segment sequences (the wire shape isn't known statically here; multi-segment is the + /// safe assumption). JIT-constant falseEnsureAvailable bounds-check + advance kept. + /// Note: callers that have a single-segment array-backed sequence are redirected to the byte[] overload + /// at the entry point (see AcBinaryDeserializer.Deserialize(ReadOnlySequence<byte>)), which + /// uses and thus benefits from the trusted-single-segment fast path. + /// + public static bool IsTrustedSingleSegment => false; + private ReadOnlySequence _sequence; private SequencePosition _nextPosition;