-
Notifications
You must be signed in to change notification settings - Fork 761
Use System.Text.Json for NativeAOT-critical NuGet JSON APIs #7601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
baronfel
wants to merge
29
commits into
NuGet:dev
Choose a base branch
from
baronfel:baronfel-migrate-runtime-graph-json
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
6bdca71
Forward feature switch attributes on modern .NET
baronfel e4372a1
Track buffered JSON token positions
baronfel 7a34ee1
Add System.Text.Json runtime graph reader
baronfel a48e9ae
Add System.Text.Json global.json reader
baronfel 1ef2127
Add System.Text.Json packages lock reader
baronfel b5d8652
Harden System.Text.Json reader compatibility
baronfel 23b05b9
Simplify runtime graph converter metadata
baronfel c1758b1
Explain JSON stream encoding fallback
baronfel a73db54
Migrate package lock writing to System.Text.Json
baronfel 54d0d9e
Add package lock writer fallback
baronfel 8a6226c
Remove feature switch type forwarding
baronfel cc257ad
Remove redundant nullable directives
baronfel c86f597
Preserve JSON text reader ownership
baronfel cac7098
Minimize System.Text.Json migration diff
baronfel 4adf45e
Make JSON feature-switch branches trim-safe
baronfel c458500
Stream runtime graph parsing with System.Text.Json
baronfel 06b6c35
Obsolete runtime graph TextReader parsing
baronfel a6b52eb
Stream package lock parsing with System.Text.Json
baronfel 74b7a15
Obsolete package lock TextReader parsing
baronfel 08ca20f
Use System.Text.Json for package lock writing
baronfel 3b3b727
Avoid number token array allocation
baronfel 75fc090
only use STJ for reading global.json files for simplicity
baronfel e487c5f
Move rid graph parsing entirely to STJ
baronfel aa93c50
final compilation bits and bobs
baronfel aecd60d
Finalize STJ-only JSON readers
baronfel ecc0fa5
Merge remote-tracking branch 'upstream/dev' into baronfel-migrate-run…
baronfel b17dce3
Address JSON parser review feedback
baronfel 6005af3
Optimize packages lock file rendering
baronfel 420de8b
Restore feature switch documentation
baronfel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ | |
| namespace NuGet.Shared | ||
| { | ||
| /// <summary> | ||
| /// This struct is used to read over a memeory stream in parts, in order to avoid reading the entire stream into memory. | ||
| /// This struct is used to read over a memory stream in parts, in order to avoid reading the entire stream into memory. | ||
| /// It functions as a wrapper around <see cref="Utf8JsonStreamReader"/>, while maintaining a stream and a buffer to read from. | ||
| /// </summary> | ||
| internal ref struct Utf8JsonStreamReader | ||
|
|
@@ -40,7 +40,18 @@ internal ref struct Utf8JsonStreamReader | |
| private bool _disposed; | ||
| private ArrayPool<byte> _bufferPool; | ||
| private int _bufferUsed = 0; | ||
| private int _bufferStartLineNumber; | ||
| private int _bufferStartBytePositionInLine; | ||
|
|
||
| /// <summary> | ||
| /// A buffered reader that reads from a stream in chunks, and uses a Utf8JsonReader to read the json content from the buffer. | ||
| /// The reader will advance the underlying stream, but will not dispose it. | ||
| /// </summary> | ||
| /// <param name="stream"></param> | ||
| /// <param name="bufferSize"></param> | ||
| /// <param name="arrayPool"></param> | ||
| /// <exception cref="ArgumentNullException"></exception> | ||
| /// <exception cref="ArgumentException"></exception> | ||
| internal Utf8JsonStreamReader(Stream stream, int bufferSize = BufferSizeDefault, ArrayPool<byte> arrayPool = null) | ||
| { | ||
| if (stream is null) | ||
|
|
@@ -57,6 +68,8 @@ internal Utf8JsonStreamReader(Stream stream, int bufferSize = BufferSizeDefault, | |
| _buffer = _bufferPool.Rent(bufferSize); | ||
| _disposed = false; | ||
| _stream = stream; | ||
| _bufferStartLineNumber = 0; | ||
| _bufferStartBytePositionInLine = 0; | ||
|
|
||
| if (_stream.Read(_buffer, offset: 0, count: 1) == 1 && | ||
| _stream.Read(_buffer, offset: ++_bufferUsed, count: 1) == 1 && | ||
|
|
@@ -82,6 +95,24 @@ internal Utf8JsonStreamReader(Stream stream, int bufferSize = BufferSizeDefault, | |
|
|
||
| internal JsonTokenType TokenType => _reader.TokenType; | ||
|
|
||
| internal int LineNumber | ||
| { | ||
| get | ||
| { | ||
| GetTokenStartPosition(out int lineNumber, out _); | ||
| return lineNumber + 1; | ||
| } | ||
| } | ||
|
|
||
| internal int ColumnNumber | ||
| { | ||
| get | ||
| { | ||
| GetTokenStartPosition(out _, out int bytePositionInLine); | ||
| return bytePositionInLine + 1; | ||
| } | ||
| } | ||
|
|
||
| internal bool ValueTextEquals(ReadOnlySpan<byte> utf8Text) => _reader.ValueTextEquals(utf8Text); | ||
|
|
||
| internal bool TryGetInt32(out int value) => _reader.TryGetInt32(out value); | ||
|
|
@@ -92,6 +123,8 @@ internal Utf8JsonStreamReader(Stream stream, int bufferSize = BufferSizeDefault, | |
|
|
||
| internal int GetInt32() => _reader.GetInt32(); | ||
|
|
||
| internal string ReadTokenAsString() => _reader.ReadTokenAsString(); | ||
|
|
||
| internal int CurrentDepth => _reader.CurrentDepth; | ||
|
|
||
| internal bool Read() | ||
|
|
@@ -334,11 +367,17 @@ internal IReadOnlyList<string> ReadStringArrayAsReadOnlyListFromArrayStart() | |
| // This function is called when Read() returns false and we're not already in the final block | ||
| private void GetMoreBytesFromStream() | ||
| { | ||
| if (_reader.BytesConsumed < _bufferUsed) | ||
| int bytesConsumed = checked((int)_reader.BytesConsumed); | ||
| AdvancePosition( | ||
| _buffer.AsSpan(start: 0, length: bytesConsumed), | ||
| ref _bufferStartLineNumber, | ||
| ref _bufferStartBytePositionInLine); | ||
|
|
||
| if (bytesConsumed < _bufferUsed) | ||
| { | ||
| // If the number of bytes consumed by the reader is less than the amount set in the buffer then we have leftover bytes | ||
| var oldBuffer = _buffer; | ||
| ReadOnlySpan<byte> leftover = oldBuffer.AsSpan((int)_reader.BytesConsumed); | ||
| ReadOnlySpan<byte> leftover = oldBuffer.AsSpan(bytesConsumed); | ||
| _bufferUsed = leftover.Length; | ||
|
|
||
| // If the leftover bytes are the same as the buffer size then we are at capacity and need to double the buffer size | ||
|
|
@@ -361,6 +400,33 @@ private void GetMoreBytesFromStream() | |
| ReadStreamIntoBuffer(_reader.CurrentState); | ||
| } | ||
|
|
||
| private void GetTokenStartPosition(out int lineNumber, out int bytePositionInLine) | ||
| { | ||
| lineNumber = _bufferStartLineNumber; | ||
| bytePositionInLine = _bufferStartBytePositionInLine; | ||
|
|
||
| AdvancePosition( | ||
| _buffer.AsSpan(start: 0, length: checked((int)_reader.TokenStartIndex)), | ||
| ref lineNumber, | ||
| ref bytePositionInLine); | ||
| } | ||
|
|
||
| private static void AdvancePosition( | ||
| ReadOnlySpan<byte> bytes, | ||
| ref int lineNumber, | ||
| ref int bytePositionInLine) | ||
| { | ||
| int newlineIndex; | ||
| while ((newlineIndex = bytes.IndexOf((byte)'\n')) >= 0) | ||
| { | ||
| lineNumber++; | ||
| bytePositionInLine = 0; | ||
| bytes = bytes.Slice(newlineIndex + 1); | ||
| } | ||
|
|
||
| bytePositionInLine += bytes.Length; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this logic still work correctly when this method is called from |
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Loops through the stream and reads it into the buffer until the buffer is full or the stream is empty, creates the Utf8JsonReader. | ||
| /// </summary> | ||
|
|
||
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
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this tracking to the STJ parser means that error messages that used Newtonsoft's Line Info interfaces can keep line info on STJ parsing paths - we use this here.