File: WorkloadManifestReader.SystemTextJson.cs
Web Access
Project: ..\..\..\src\Tasks\Microsoft.NET.Build.Tasks\Microsoft.NET.Build.Tasks.csproj (Microsoft.NET.Build.Tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers;
using System.Text.Json;
 
namespace Microsoft.NET.Sdk.WorkloadManifestReader
{
    public partial class WorkloadManifestReader
    {
        public static WorkloadManifest ReadWorkloadManifest(string manifestId, Stream manifestStream, Stream? localizationStream, string manifestPath)
        {
            var readerOptions = new JsonReaderOptions
            {
                AllowTrailingCommas = true,
                CommentHandling = JsonCommentHandling.Skip
            };
 
            var localizationCatalog = ReadLocalizationCatalog(localizationStream, readerOptions);
            var manifestReader = new Utf8JsonStreamReader(manifestStream, readerOptions);
 
            return ReadWorkloadManifest(manifestId, manifestPath, localizationCatalog, ref manifestReader);
        }
 
        private static LocalizationCatalog? ReadLocalizationCatalog(Stream? localizationStream, JsonReaderOptions readerOptions)
        {
            if (localizationStream == null)
            {
                return null;
            }
 
            var localizationReader = new Utf8JsonStreamReader(localizationStream, readerOptions);
            return ReadLocalizationCatalog(ref localizationReader);
        }
 
        internal ref struct Utf8JsonStreamReader
        {
            static ReadOnlySpan<byte> utf8Bom => new byte[] { 0xEF, 0xBB, 0xBF };
 
            const int segmentSize = 4096;
 
            Utf8JsonReader reader;
            readonly Stream stream;
 
            byte[]? buffer;
 
            Span<byte> span;
 
            public Utf8JsonStreamReader(Stream stream, JsonReaderOptions readerOptions)
            {
                this.stream = stream;
 
                buffer = ArrayPool<byte>.Shared.Rent(segmentSize);
                var readCount = stream.Read(buffer, 0, buffer.Length);
                span = buffer.AsSpan().Slice(0, readCount);
 
                if (span.StartsWith(utf8Bom))
                {
                    span = span.Slice(utf8Bom.Length, span.Length - utf8Bom.Length);
                }
 
                reader = new Utf8JsonReader(span, readCount >= stream.Length, new JsonReaderState(readerOptions));
            }
 
            public bool Read()
            {
                while (!reader.Read())
                {
                    if (reader.IsFinalBlock)
                    {
                        if (buffer != null)
                        {
                            ArrayPool<byte>.Shared.Return(buffer);
                            buffer = null;
                        }
                        return false;
                    }
 
                    var newSegmentSize = segmentSize;
 
                    // if the value was too big to fit in the buffer, get a bigger buffer
                    if (reader.BytesConsumed == span.Length)
                    {
                        newSegmentSize = span.Length * 2;
                    }
 
                    int remaining = (int)(span.Length - reader.BytesConsumed);
 
                    var newBuffer = ArrayPool<byte>.Shared.Rent(newSegmentSize);
 
                    if (remaining > 0)
                    {
                        span.Slice((int)reader.BytesConsumed).CopyTo(newBuffer);
                    }
 
                    var readCount = stream.Read(newBuffer, remaining, newBuffer.Length - remaining);
 
                    if (buffer != null)
                    {
                        ArrayPool<byte>.Shared.Return(buffer);
                    }
                    buffer = newBuffer;
                    span = newBuffer.AsSpan().Slice(0, remaining + readCount);
 
                    reader = new Utf8JsonReader(span, stream.Position >= stream.Length, reader.CurrentState);
                }
 
                return true;
            }
 
            public long TokenStartIndex => reader.TokenStartIndex;
 
            public JsonTokenType TokenType => reader.TokenType;
 
            public int CurrentDepth => reader.CurrentDepth;
 
            public string? GetString() => reader.GetString();
            public bool TryGetInt64(out long value) => reader.TryGetInt64(out value);
            public bool GetBool() => reader.GetBoolean();
        }
    }
 
    internal static class JsonTokenTypeExtensions
    {
        public static bool IsBool(this JsonTokenType tokenType) => tokenType == JsonTokenType.True || tokenType == JsonTokenType.False;
        public static bool IsInt(this JsonTokenType tokenType) => tokenType == JsonTokenType.Number;
    }
}