namespace DeepDrftContent.Processors;
///
/// Bounded-buffer streaming primitives shared by the audio processors on the store path. None of
/// these hold the whole file in memory: copies move a fixed window at a time, and the header read
/// caps its allocation regardless of file size.
///
internal static class AudioStoreStream
{
private const int CopyBufferSize = 81920; // 80 KB — matches the controller staging copy.
///
/// Bounded disk-to-disk copy of into .
/// Used for passthrough formats whose stored bytes equal the source bytes. Hand-rolled rather than
/// because FileStream's override writes in 128 KB
/// blocks; this keeps every write at or below , so peak managed memory
/// is provably O(buffer), never O(filesize).
///
public static async Task CopyFileAsync(string sourcePath, Stream destination, CancellationToken ct)
{
await using var src = new FileStream(
sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: CopyBufferSize, useAsync: true);
var buffer = new byte[CopyBufferSize];
int read;
while ((read = await src.ReadAsync(buffer, ct)) > 0)
{
await destination.WriteAsync(buffer.AsMemory(0, read), ct);
}
}
///
/// Reads at most bytes from the start of — enough
/// for header/metadata parsing without loading the (potentially ~GB) body. Bounds the allocation
/// at min(cap, fileLength). Size-based metadata (e.g. average bitrate) must use the true
/// file length, supplied separately, not the prefix length.
///
public static async Task ReadPrefixAsync(string path, long cap, CancellationToken ct)
{
await using var fs = new FileStream(
path, FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: CopyBufferSize, useAsync: true);
var length = (int)Math.Min(cap, fs.Length);
var buffer = new byte[length];
var total = 0;
while (total < length)
{
var read = await fs.ReadAsync(buffer.AsMemory(total, length - total), ct);
if (read == 0)
break;
total += read;
}
return total == length ? buffer : buffer[..total];
}
}