100 lines
4.6 KiB
C#
100 lines
4.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Elwig.Helpers {
|
|
static partial class Extensions {
|
|
|
|
[LibraryImport("msvcrt.dll")]
|
|
[UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
private static unsafe partial int memcmp(void* b1, void* b2, long count);
|
|
|
|
public static unsafe int CompareBuffers(char[] buffer1, int offset1, char[] buffer2, int offset2, int count) {
|
|
fixed (char* b1 = buffer1, b2 = buffer2) {
|
|
return memcmp(b1 + offset1, b2 + offset2, count);
|
|
}
|
|
}
|
|
|
|
public static string? ReadUntil(this StreamReader reader, string delimiter) {
|
|
return ReadUntil(reader, delimiter.ToCharArray());
|
|
}
|
|
|
|
public static string? ReadUntil(this StreamReader reader, char delimiter) {
|
|
return ReadUntil(reader, new char[] { delimiter });
|
|
}
|
|
|
|
public static string? ReadUntil(this StreamReader reader, char[] delimiter) {
|
|
var buf = new char[512];
|
|
int bufSize = 0, ret;
|
|
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
|
if ((ret = reader.Read()) == -1)
|
|
return null;
|
|
buf[bufSize++] = (char)ret;
|
|
|
|
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
|
return new string(buf, 0, bufSize);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static Task<string?> ReadUntilAsync(this StreamReader reader, string delimiter) {
|
|
return ReadUntilAsync(reader, delimiter.ToCharArray());
|
|
}
|
|
|
|
public static Task<string?> ReadUntilAsync(this StreamReader reader, char delimiter) {
|
|
return ReadUntilAsync(reader, new char[] { delimiter });
|
|
}
|
|
|
|
public static async Task<string?> ReadUntilAsync(this StreamReader reader, char[] delimiter) {
|
|
var buf = new char[512];
|
|
int bufSize = 0;
|
|
var tmpBuf = new char[1];
|
|
while (!reader.EndOfStream && bufSize < buf.Length - 1) {
|
|
if ((await reader.ReadAsync(tmpBuf, 0, 1)) != 1)
|
|
return null;
|
|
buf[bufSize++] = tmpBuf[0];
|
|
|
|
if (bufSize >= delimiter.Length && CompareBuffers(buf, bufSize - delimiter.Length, delimiter, 0, delimiter.Length) == 0)
|
|
return new string(buf, 0, bufSize);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static async Task CopyToAsync(this Stream source, Stream destination, int bufferSize, IProgress<long>? progress = null, CancellationToken cancellationToken = default) {
|
|
ArgumentNullException.ThrowIfNull(source);
|
|
if (!source.CanRead) throw new ArgumentException("Has to be readable", nameof(source));
|
|
ArgumentNullException.ThrowIfNull(destination);
|
|
if (!destination.CanWrite) throw new ArgumentException("Has to be writable", nameof(destination));
|
|
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
|
|
|
|
var buffer = new byte[bufferSize];
|
|
long totalBytesRead = 0;
|
|
int bytesRead;
|
|
while ((bytesRead = await source.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) {
|
|
await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
|
|
totalBytesRead += bytesRead;
|
|
progress?.Report(totalBytesRead);
|
|
}
|
|
}
|
|
|
|
public static async Task DownloadAsync(this HttpClient client, string requestUri, Stream destination, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
|
using var response = await client.GetAsync(requestUri, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
|
response.EnsureSuccessStatusCode();
|
|
var contentLength = response.Content.Headers.ContentLength;
|
|
|
|
using var download = await response.Content.ReadAsStreamAsync(cancellationToken);
|
|
if (progress == null || !contentLength.HasValue) {
|
|
await download.CopyToAsync(destination, cancellationToken);
|
|
return;
|
|
}
|
|
|
|
var relativeProgress = new Progress<long>(totalBytes => progress.Report((double)totalBytes / contentLength.Value));
|
|
await download.CopyToAsync(destination, 81920, relativeProgress, cancellationToken);
|
|
progress.Report(100.0);
|
|
}
|
|
}
|
|
}
|