Need a ThrottledStream
for a project, created the following class (still not tested), wouldn't mind some opinions on the code and suggestions on how it can be improved:
public class ThrottledStream : Stream
{
private const int OneSecond = 1000; // 1000 milliseconds
private readonly Stream _baseStream;
private static readonly Stopwatch Stopwatch = new();
public ThrottledStream(Stream stream) => _baseStream = stream;
public int MaxBytesPerSecond { get; set; }
public override bool CanRead => _baseStream.CanRead;
public override bool CanSeek => _baseStream.CanSeek;
public override bool CanWrite => _baseStream.CanWrite;
public override long Length => _baseStream.Length;
public override long Position
{
get => _baseStream.Position;
set => _baseStream.Position = value;
}
public override void Flush() => _baseStream.Flush();
public override int Read(byte[] buffer, int offset, int count)
{
var readBytes = 0;
while (readBytes != count)
{
var newOffset = readBytes + offset;
var bytesToRead = count - readBytes > MaxBytesPerSecond ? MaxBytesPerSecond : count - readBytes;
var throttledReadBytes = Throttle(() => _baseStream.Read(buffer, newOffset, bytesToRead));
if (throttledReadBytes == 0) // Reached end of stream
return readBytes;
readBytes += throttledReadBytes;
}
return readBytes;
}
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
public override void SetLength(long value) => _baseStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count)
{
var writtenBytes = 0;
while (writtenBytes != count)
{
var newOffset = writtenBytes + offset;
var bytesToWrite = count - writtenBytes > MaxBytesPerSecond ? MaxBytesPerSecond : count - writtenBytes;
Throttle(() =>
{
_baseStream.Write(buffer, newOffset, bytesToWrite);
return true;
});
writtenBytes += bytesToWrite;
}
}
private static T Throttle<T>(Func<T> func)
{
var start = GetElapsedMilliseconds();
var result = func.Invoke();
var finish = GetElapsedMilliseconds();
var timeToSleep = OneSecond - (finish - start);
if (timeToSleep > 0)
Thread.Sleep((int) timeToSleep);
return result;
}
private static long GetElapsedMilliseconds()
{
if (!Stopwatch.IsRunning)
Stopwatch.Start();
return Stopwatch.ElapsedMilliseconds;
}
}
Thread.Sleep
is inefficient. I would suggestTask.Delay
. Accordingly, the code should be asynchronous. \$\endgroup\$Stream
which only contains abstract Read/Write/Seek methods ... \$\endgroup\$