< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Forms.LimitedReadStream
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartDecompression.cs
Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589
Line coverage
56%
Covered lines: 17
Uncovered lines: 13
Coverable lines: 30
Total lines: 122
Line coverage: 56.6%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 02/05/2026 - 00:28:18 Line coverage: 56.6% (17/30) Branch coverage: 100% (4/4) Total lines: 122 Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589 02/05/2026 - 00:28:18 Line coverage: 56.6% (17/30) Branch coverage: 100% (4/4) Total lines: 122 Tag: Kestrun/Kestrun@d9261bd752e45afa789d10bc0c82b7d5724d9589

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_TotalRead()100%11100%
get_CanRead()100%210%
get_CanSeek()100%210%
get_CanWrite()100%210%
get_Length()100%210%
get_Position()100%210%
set_Position(...)100%210%
Flush()100%210%
Read(...)100%11100%
ReadAsync()100%11100%
Read(...)100%210%
Track(...)100%44100%
Seek(...)100%210%
SetLength(...)100%210%
Write(...)100%210%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Forms/KrPartDecompression.cs

#LineLine coverage
 1using System.IO.Compression;
 2
 3namespace Kestrun.Forms;
 4
 5/// <summary>
 6/// Provides per-part decompression helpers.
 7/// </summary>
 8public static class KrPartDecompression
 9{
 10    /// <summary>
 11    /// Wraps a stream in a decompression stream based on the content encoding.
 12    /// </summary>
 13    /// <param name="source">The source stream.</param>
 14    /// <param name="contentEncoding">The content encoding header value.</param>
 15    /// <returns>The decoded stream and normalized encoding.</returns>
 16    public static (Stream Stream, string Encoding) CreateDecodedStream(Stream source, string? contentEncoding)
 17    {
 18        if (string.IsNullOrWhiteSpace(contentEncoding))
 19        {
 20            return (source, "identity");
 21        }
 22
 23        var normalized = contentEncoding.Trim().ToLowerInvariant();
 24        return normalized switch
 25        {
 26            "identity" => (source, "identity"),
 27            "gzip" => (new GZipStream(source, CompressionMode.Decompress, leaveOpen: false), "gzip"),
 28            "deflate" => (new DeflateStream(source, CompressionMode.Decompress, leaveOpen: false), "deflate"),
 29            "br" => (new BrotliStream(source, CompressionMode.Decompress, leaveOpen: false), "br"),
 30            _ => (source, normalized)
 31        };
 32    }
 33}
 34
 35/// <summary>
 36/// Stream wrapper that enforces a maximum number of bytes read.
 37/// </summary>
 38/// <remarks>
 39/// Initializes a new instance of the <see cref="LimitedReadStream"/> class.
 40/// </remarks>
 41/// <param name="inner">The inner stream.</param>
 42/// <param name="maxBytes">The maximum number of bytes allowed.</param>
 643public sealed class LimitedReadStream(Stream inner, long maxBytes) : Stream
 44{
 645    private readonly Stream _inner = inner;
 646    private readonly long _maxBytes = maxBytes;
 47    private long _totalRead;
 48
 49    /// <summary>
 50    /// Gets the total number of bytes read from the stream.
 51    /// </summary>
 252    public long TotalRead => _totalRead;
 53
 54    /// <inheritdoc />
 055    public override bool CanRead => _inner.CanRead;
 56
 57    /// <inheritdoc />
 058    public override bool CanSeek => false;
 59
 60    /// <inheritdoc />
 061    public override bool CanWrite => false;
 62
 63    /// <inheritdoc />
 064    public override long Length => _inner.Length;
 65
 66    /// <inheritdoc />
 67    public override long Position
 68    {
 069        get => _inner.Position;
 070        set => throw new NotSupportedException();
 71    }
 72
 73    /// <inheritdoc />
 074    public override void Flush() => _inner.Flush();
 75
 76    /// <inheritdoc />
 77    public override int Read(byte[] buffer, int offset, int count)
 78    {
 279        var read = _inner.Read(buffer, offset, count);
 280        Track(read);
 181        return read;
 82    }
 83
 84    /// <inheritdoc />
 85    public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
 86    {
 1087        var read = await _inner.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 1088        Track(read);
 989        return read;
 990    }
 91
 92    /// <inheritdoc />
 93    public override int Read(Span<byte> buffer)
 94    {
 095        var read = _inner.Read(buffer);
 096        Track(read);
 097        return read;
 98    }
 99
 100    private void Track(int read)
 101    {
 12102        if (read <= 0)
 103        {
 4104            return;
 105        }
 106
 8107        _totalRead += read;
 8108        if (_totalRead > _maxBytes)
 109        {
 2110            throw new KrFormLimitExceededException($"Decompressed part size exceeded limit of {_maxBytes} bytes.");
 111        }
 6112    }
 113
 114    /// <inheritdoc />
 0115    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 116
 117    /// <inheritdoc />
 0118    public override void SetLength(long value) => throw new NotSupportedException();
 119
 120    /// <inheritdoc />
 0121    public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
 122}