File: Logging\BinaryLogger\Postprocessing\StreamExtensions.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Buffers;
using System.Diagnostics;
using System.IO;
using System.Text;
using Microsoft.Build.Shared;
 
namespace Microsoft.Build.Logging
{
    internal static class StreamExtensions
    {
        private static bool CheckIsSkipNeeded(long bytesCount)
        {
            if (bytesCount is < 0 or > int.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(bytesCount), ResourceUtilities.FormatResourceStringStripCodeAndKeyword("Binlog_StreamUtils_UnsupportedSkipOffset",
                    bytesCount));
            }
 
            return bytesCount > 0;
        }
 
        public static long SkipBytes(this Stream stream, long bytesCount)
        {
            if (!CheckIsSkipNeeded(bytesCount))
            {
                return 0;
            }
 
            byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
            using var _ = new CleanupScope(() => ArrayPool<byte>.Shared.Return(buffer));
            return SkipBytes(stream, bytesCount, buffer);
        }
 
        public static long SkipBytes(this Stream stream, long bytesCount, byte[] buffer)
        {
            if (!CheckIsSkipNeeded(bytesCount))
            {
                return 0;
            }
 
            long totalRead = 0;
            while (totalRead < bytesCount)
            {
                int read = stream.Read(buffer, 0, (int)Math.Min(bytesCount - totalRead, buffer.Length));
                if (read == 0)
                {
                    throw new InvalidDataException("Unexpected end of stream.");
                }
 
                totalRead += read;
            }
 
            return totalRead;
        }
 
        public static byte[] ReadToEnd(this Stream stream)
        {
            MemoryStream ms = stream.TryGetLength(out long length) && length <= int.MaxValue ? new((int)length) : new();
            stream.CopyTo(ms);
            byte[] buffer = ms.GetBuffer();
            return buffer.Length == ms.Length ? buffer : ms.ToArray();
        }
 
        public static bool TryGetLength(this Stream stream, out long length)
        {
            try
            {
                if (stream.CanSeek)
                {
                    length = stream.Length;
                    return true;
                }
            }
            catch (NotSupportedException)
            {
            }
 
            length = 0;
            return false;
        }
 
        public static Stream ToReadableSeekableStream(this Stream stream)
        {
            return TransparentReadStream.EnsureSeekableStream(stream);
        }
 
        /// <summary>
        /// Creates bounded read-only, forward-only view over an underlying stream.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public static Stream Slice(this Stream stream, long length)
        {
            return new SubStream(stream, length);
        }
    }
}