using System; using System.Buffers; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace CodeWalker.Core.Utils { public static class StreamingExtensions { public static Task ReadAsync(this BinaryReader br, byte[] buffer, int index, int count) { return br.BaseStream.ReadAsync(buffer, index, count); } public static void CopyToFast(this Stream stream, Stream destination) { var buffer = ArrayPool.Shared.Rent(81920); try { int read; while ((read = stream.Read(buffer, 0, buffer.Length)) != 0) destination.Write(buffer, 0, read); } finally { ArrayPool.Shared.Return(buffer); } } public static async Task CopyToFastAsync(this Stream stream, Stream destination, int bufferSize = 131072, CancellationToken cancellationToken = default) { var buffer = ArrayPool.Shared.Rent(bufferSize); try { int bytesRead; while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0) { await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false); } } catch(Exception ex) { Console.WriteLine(ex.ToString()); throw; } finally { ArrayPool.Shared.Return(buffer); } } private static async Task FinishWriteAsync(Task writeTask, byte[] localBuffer) { try { await writeTask.ConfigureAwait(false); } finally { ArrayPool.Shared.Return(localBuffer); } } public static ValueTask WriteAsync(this Stream stream, Memory buffer, CancellationToken cancellationToken = default) { if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) { return new ValueTask(stream.WriteAsync(array.Array!, array.Offset, array.Count, cancellationToken)); } byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); buffer.Span.CopyTo(sharedBuffer); return new ValueTask(FinishWriteAsync(stream.WriteAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer)); } public static void Write(this Stream stream, ReadOnlySpan buffer) { byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { buffer.CopyTo(sharedBuffer); stream.Write(sharedBuffer, 0, buffer.Length); } finally { ArrayPool.Shared.Return(sharedBuffer); } } public static int Read(this Stream stream, Span buffer) { byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { int numRead = stream.Read(sharedBuffer, 0, buffer.Length); if ((uint)numRead > (uint)buffer.Length) { throw new IOException("Stream too long!"); } new ReadOnlySpan(sharedBuffer, 0, numRead).CopyTo(buffer); return numRead; } finally { ArrayPool.Shared.Return(sharedBuffer); } } public static int Read(this Stream stream, Memory buffer) { if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) { return stream.Read(array.Array!, array.Offset, array.Count); } byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); try { int numRead = stream.Read(sharedBuffer, 0, buffer.Length); if ((uint)numRead > (uint)buffer.Length) { throw new IOException("Stream too long!"); } new ReadOnlySpan(sharedBuffer, 0, numRead).CopyTo(buffer.Span); return numRead; } finally { ArrayPool.Shared.Return(sharedBuffer); } } public static ValueTask ReadAsync(this Stream stream, Memory buffer, CancellationToken cancellationToken = default) { if (MemoryMarshal.TryGetArray(buffer, out ArraySegment array)) { return new ValueTask(stream.ReadAsync(array.Array!, array.Offset, array.Count, cancellationToken)); } byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length); return FinishReadAsync(stream.ReadAsync(sharedBuffer, 0, buffer.Length, cancellationToken), sharedBuffer, buffer); static async ValueTask FinishReadAsync(Task readTask, byte[] localBuffer, Memory localDestination) { try { int result = await readTask.ConfigureAwait(false); new ReadOnlySpan(localBuffer, 0, result).CopyTo(localDestination.Span); return result; } finally { ArrayPool.Shared.Return(localBuffer); } } } } }