Skip to content

Instantly share code, notes, and snippets.

@neuecc
Created December 16, 2021 04:00
Show Gist options
  • Save neuecc/f958641693686bea4a474bd414498c41 to your computer and use it in GitHub Desktop.
Save neuecc/f958641693686bea4a474bd414498c41 to your computer and use it in GitHub Desktop.
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