Created
December 16, 2021 04:00
-
-
Save neuecc/f958641693686bea4a474bd414498c41 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class LargeBuffer | |
{ | |
const int MaxArrayLength = 0x7FFFFFC7; | |
readonly byte[][] chunk; | |
readonly long length; | |
int writtenInCurrent; | |
int chunkIndex; | |
public long Length => length; | |
public LargeBuffer(long length) | |
: this(length, MaxArrayLength) | |
{ | |
} | |
public LargeBuffer(long length, int chunkLength) | |
{ | |
var chunkCount = (int)(length / chunkLength) + 1; | |
this.chunk = new byte[chunkCount][]; | |
long used = 0; | |
for (int i = 0; i < chunkCount; i++) | |
{ | |
chunk[i] = new byte[Math.Min(chunkLength, length - used)]; | |
used += chunk[i].Length; | |
} | |
this.length = length; | |
this.writtenInCurrent = 0; | |
this.chunkIndex = 0; | |
} | |
// for writer | |
public void Advance(int count) | |
{ | |
writtenInCurrent += count; | |
if (writtenInCurrent == chunk[chunkIndex].Length) | |
{ | |
chunkIndex++; | |
writtenInCurrent = 0; | |
} | |
} | |
public Span<byte> GetSpan() | |
{ | |
if (chunkIndex >= chunk.Length) return Array.Empty<byte>(); | |
return new Span<byte>(chunk[chunkIndex], writtenInCurrent, chunk[chunkIndex].Length - writtenInCurrent); | |
} | |
public Memory<byte> GetMemory() | |
{ | |
if (chunkIndex >= chunk.Length) return Array.Empty<byte>(); | |
return new Memory<byte>(chunk[chunkIndex], writtenInCurrent, chunk[chunkIndex].Length - writtenInCurrent); | |
} | |
public void Reset() | |
{ | |
this.writtenInCurrent = 0; | |
this.chunkIndex = 0; | |
} | |
public async Task CopyFromAsync(Stream stream, IProgress<int>? progress = null, CancellationToken cancellationToken = default) | |
{ | |
var read = 0; | |
while ((read = await stream.ReadAsync(GetMemory(), cancellationToken)) != 0) | |
{ | |
progress?.Report(read); | |
Advance(read); | |
} | |
} | |
// for reader | |
public ReadOnlySequence<byte> AsReadOnlySequence() | |
{ | |
Segment? lastSegment = null; | |
Segment? nextSegment = null; | |
for (int i = chunk.Length - 1; i >= 0; i--) | |
{ | |
nextSegment = new Segment(chunk[i], nextSegment); | |
if (lastSegment == null) | |
{ | |
lastSegment = nextSegment; | |
} | |
} | |
var firstSegment = nextSegment; | |
var segment = firstSegment; | |
var index = 0; | |
while (segment != null) | |
{ | |
segment.SetRunningIndex(index); | |
index += segment.Memory.Length; | |
segment = segment.Next as Segment; | |
} | |
return new ReadOnlySequence<byte>(nextSegment!, 0, lastSegment!, lastSegment!.Memory.Length); | |
} | |
class Segment : ReadOnlySequenceSegment<byte> | |
{ | |
public Segment(byte[] buffer, Segment? nextSegment) | |
{ | |
this.Memory = buffer; | |
this.Next = nextSegment; | |
} | |
internal void SetRunningIndex(long runningIndex) | |
{ | |
this.RunningIndex = runningIndex; | |
} | |
} | |
public async Task WriteToFileAsync(string path, IProgress<int>? progress = null, CancellationToken cancellationToken = default) | |
{ | |
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, 1, useAsync: true)) | |
{ | |
await WriteToAsync(fs, progress, cancellationToken); | |
} | |
} | |
public async Task WriteToAsync(Stream stream, IProgress<int>? progress = null, CancellationToken cancellationToken = default) | |
{ | |
for (int i = 0; i < chunk.Length; i++) | |
{ | |
await stream.WriteAsync(chunk[i], 0, chunk[i].Length, cancellationToken); | |
progress?.Report(chunk[i].Length); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment