Make custom JSON converters safe for multi-segment readers - #8923
Merged
Merged
Conversation
The hand-written DateTime, TimeSpan, Stringified, and fractional numeric converters read reader.ValueSpan directly to parse stringified numbers with Utf8Parser. ValueSpan is unsafe when Utf8JsonReader is constructed over a multi-segment ReadOnlySequence (it throws, or returns a non-contiguous span), so these converters only worked on the single-segment reader that JsonSerializer uses for stream deserialization. Any sequence-based deserializer, such as a streaming reader over a PipeReader, failed on the first stringified epoch date or duration. Add JsonReaderExtensions.GetValueBytes(ref reader, scoped Span<byte>) which copies the current token's value into a caller-provided contiguous buffer (single-segment via ValueSpan, multi-segment by iterating ValueSequence segments), returns its length, and throws JsonException when the value exceeds the buffer. The 18 numeric/date converters now parse from buffer[..length], with the scratch buffer sized per type (bool/int 16, long/float 32, double and ISO date 64). The IndexSettingsTimeSeries extended-year peek only needs the first byte and now reads reader.ValueSequence.First.Span / reader.ValueSpan directly, avoiding a GetString allocation. The scoped modifier on the buffer parameter is required: without it the by-value Span would be escape-able through the unscoped ref Utf8JsonReader, so passing a stackalloc would be rejected (CS8350). Generated converters were already sequence-safe via the HasValueSequence-guarded ValueTextEquals, so no generator changes are needed.
This was referenced Jun 24, 2026
Contributor
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation and see the Github Action logs for details |
flobernd
added a commit
that referenced
this pull request
Jun 24, 2026
) (#8924) Co-authored-by: Florian Bernd <git@flobernd.de>
flobernd
added a commit
that referenced
this pull request
Jun 24, 2026
…) (#8925) Co-authored-by: Florian Bernd <git@flobernd.de>
This was referenced Aug 4, 2026
Closed
This was referenced Aug 12, 2026
This was referenced Aug 21, 2026
This was referenced Aug 28, 2026
Closed
This was referenced Sep 6, 2026
This was referenced Sep 9, 2026
This was referenced Sep 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The hand-written DateTime, TimeSpan, Stringified, and fractional numeric converters read
reader.ValueSpandirectly to parse stringified numbers withUtf8Parser.ValueSpanis unsafe whenUtf8JsonReaderis constructed over a multi-segmentReadOnlySequence<byte>(it throws, or returns a non-contiguous span), so these converters only worked on the single-segment reader thatJsonSerializeruses for stream deserialization. Any sequence-based deserializer, such as a streaming reader over aPipeReader, failed on the first stringified epoch date or duration. Surfaced while prototyping streaming deserialization for #8919.Changes
JsonReaderExtensions.GetValueBytes(ref reader, scoped Span<byte>): copies the current token's value into a caller-provided contiguous buffer (single-segment viaValueSpan, multi-segment by iteratingValueSequencesegments), returns the length, and throwsJsonExceptionwhen the value exceeds the buffer.buffer[..length], with the scratch buffer sized per type (bool/int 16, long/float 32, double and ISO date 64).IndexSettingsTimeSeriesConverterextended-year first-byte peek now readsreader.ValueSequence.First.Span/reader.ValueSpandirectly, avoiding aGetStringallocation.scopedmodifier on the buffer parameter is required so a stackalloc can be passed (otherwise the by-valueSpanwould be escape-able through the unscopedref Utf8JsonReader, CS8350).Generated converters were already sequence-safe via the
HasValueSequence-guardedValueTextEquals, so no generator changes are needed. Builds clean across the target frameworks (verifiednet10.0andnetstandard2.0); the existing single-segment typed deserialize path is unchanged.