File: System\Reflection\Internal\Utilities\StreamExtensions.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
 
namespace System.Reflection.Internal
{
    internal static partial class StreamExtensions
    {
        // From System.IO.Stream.CopyTo:
        // We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
        // The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
        // improvement in Copy performance.
        internal const int StreamCopyBufferSize = 81920;
 
        /// <summary>
        /// Copies specified amount of data from given stream to a target memory pointer.
        /// </summary>
        /// <exception cref="IOException">unexpected stream end.</exception>
        internal static unsafe void CopyTo(this Stream source, byte* destination, int size)
        {
            byte[] buffer = new byte[Math.Min(StreamCopyBufferSize, size)];
            while (size > 0)
            {
                int readSize = Math.Min(size, buffer.Length);
                int bytesRead = source.Read(buffer, 0, readSize);
 
                if (bytesRead <= 0 || bytesRead > readSize)
                {
                    throw new IOException(SR.UnexpectedStreamEnd);
                }
 
                Marshal.Copy(buffer, 0, (IntPtr)destination, bytesRead);
 
                destination += bytesRead;
                size -= bytesRead;
            }
        }
 
        /// <summary>
        /// Attempts to read all of the requested bytes from the stream into the buffer
        /// </summary>
        /// <returns>
        /// The number of bytes read. Less than <paramref name="count" /> will
        /// only be returned if the end of stream is reached before all bytes can be read.
        /// </returns>
        /// <remarks>
        /// Unlike <see cref="Stream.Read(byte[], int, int)"/> it is not guaranteed that
        /// the stream position or the output buffer will be unchanged if an exception is
        /// returned.
        /// </remarks>
        internal static int TryReadAll(this Stream stream, byte[] buffer, int offset, int count)
        {
            // The implementations for many streams, e.g. FileStream, allows 0 bytes to be
            // read and returns 0, but the documentation for Stream.Read states that 0 is
            // only returned when the end of the stream has been reached. Rather than deal
            // with this contradiction, let's just never pass a count of 0 bytes
            Debug.Assert(count > 0);
 
            int totalBytesRead;
            int bytesRead;
            for (totalBytesRead = 0; totalBytesRead < count; totalBytesRead += bytesRead)
            {
                // Note: Don't attempt to save state in-between calls to .Read as it would
                // require a possibly massive intermediate buffer array
                bytesRead = stream.Read(buffer,
                                        offset + totalBytesRead,
                                        count - totalBytesRead);
                if (bytesRead == 0)
                {
                    break;
                }
            }
            return totalBytesRead;
        }
 
#if NET
        internal static int TryReadAll(this Stream stream, Span<byte> buffer)
#if NET
            => stream.ReadAtLeast(buffer, buffer.Length, throwOnEndOfStream: false);
#else
        {
            int totalBytesRead = 0;
            while (totalBytesRead < buffer.Length)
            {
                int bytesRead = stream.Read(buffer.Slice(totalBytesRead));
                if (bytesRead == 0)
                {
                    break;
                }
 
                totalBytesRead += bytesRead;
            }
 
            return totalBytesRead;
        }
#endif
#endif
 
        /// <summary>
        /// Resolve image size as either the given user-specified size or distance from current position to end-of-stream.
        /// Also performs the relevant argument validation and publicly visible caller has same argument names.
        /// </summary>
        /// <exception cref="ArgumentException">size is 0 and distance from current position to end-of-stream can't fit in Int32.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Size is negative or extends past the end-of-stream from current position.</exception>
        internal static int GetAndValidateSize(Stream stream, int size, string streamParameterName)
        {
            long maxSize = stream.Length - stream.Position;
 
            if (size < 0 || size > maxSize)
            {
                throw new ArgumentOutOfRangeException(nameof(size));
            }
 
            if (size != 0)
            {
                return size;
            }
 
            if (maxSize > int.MaxValue)
            {
                throw new ArgumentException(SR.StreamTooLarge, streamParameterName);
            }
 
            return (int)maxSize;
        }
    }
}