< Summary

Information
Class: NanoRoute.AwsLambda.Base64BodyReaderStream
Assembly: NanoRoute.AwsLambda.dll
File(s): /home/runner/work/nanoroute/nanoroute/Src/NanoRoute.AwsLambda/Private/Base64BodyReaderStream.cs
Line coverage
97%
Covered lines: 68
Uncovered lines: 2
Coverable lines: 70
Total lines: 150
Line coverage: 97.1%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/nanoroute/nanoroute/Src/NanoRoute.AwsLambda/Private/Base64BodyReaderStream.cs

#LineLine coverage
 1/********************************************************************************
 2* Base64BodyReaderStream.cs                                                     *
 3*                                                                               *
 4* Author: Denes Solti                                                           *
 5********************************************************************************/
 6using System;
 7using System.IO;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace NanoRoute.AwsLambda
 12{
 113    internal sealed class Base64BodyReaderStream(string body) : Stream
 14    {
 15        #region Private
 116        private readonly byte[] _stagedBytes = new byte[3];
 117        private ReadOnlyMemory<char> _remainingBody = body.AsMemory();
 18        private Memory<byte> _remainingStagedBytes;
 19
 20        private bool _disposed;
 21
 22        private int CopyStagedBytes(ref Span<byte> buffer)
 123        {
 124            int bytesToCopy = Math.Min(_remainingStagedBytes.Length, buffer.Length);
 125            if (bytesToCopy is 0)
 126                return 0;
 27
 128            _remainingStagedBytes.Span.Slice(0, bytesToCopy).CopyTo(buffer.Slice(0, bytesToCopy));
 129            _remainingStagedBytes = _remainingStagedBytes.Slice(bytesToCopy);
 130            buffer = buffer.Slice(bytesToCopy);
 31
 132            return bytesToCopy;
 133        }
 34
 35        private int DecodeStep(Span<byte> buffer)
 136        {
 137            ReadOnlySpan<char> bodySpan = _remainingBody.Span;
 138            int inputLength = Math.Min(bodySpan.Length / 4, buffer.Length / 3) * 4;
 39
 140            if (inputLength is 0)
 141            {
 142                if (!bodySpan.IsEmpty)
 143                    throw new FormatException();
 44
 045                return 0;
 46            }
 47
 148            if (!Convert.TryFromBase64Chars(bodySpan.Slice(0, inputLength), buffer, out int written))
 149                throw new FormatException();
 50
 151            _remainingBody = _remainingBody.Slice(inputLength);
 52
 153            return written;
 154        }
 55
 56        private int ReadCore(Span<byte> buffer, CancellationToken cancellation)
 157        {
 158            ObjectDisposedException.ThrowIf(_disposed, this);
 59
 160            if (buffer.IsEmpty)
 161                return 0;
 62
 163            int read = CopyStagedBytes(ref buffer);
 64
 165            while (buffer.Length >= 3 && !_remainingBody.IsEmpty)
 166            {
 167                cancellation.ThrowIfCancellationRequested();
 68
 169                int decoded = DecodeStep(buffer);
 170                if (decoded is 0)
 071                    break;
 72
 173                buffer = buffer.Slice(decoded);
 174                read += decoded;
 175            }
 76
 177            if (!buffer.IsEmpty && !_remainingBody.IsEmpty)
 178            {
 179                cancellation.ThrowIfCancellationRequested();
 80
 181                int stagedByteCount = DecodeStep(_stagedBytes);
 182                if (stagedByteCount is not 0)
 183                {
 184                    _remainingStagedBytes = _stagedBytes.AsMemory(0, stagedByteCount);
 85
 186                    read += CopyStagedBytes(ref buffer);
 187                }
 188            }
 89
 190            return read;
 191        }
 92
 93        protected override void Dispose(bool disposing)
 194        {
 195            _disposed = true;
 96
 197            base.Dispose(disposing);
 198        }
 99        #endregion
 100
 1101        public override bool CanRead => !_disposed;
 102
 1103        public override bool CanSeek => false;
 104
 1105        public override bool CanWrite => false;
 106
 107        public override int Read(byte[] buffer, int offset, int count)
 1108        {
 1109            ValidateBufferArguments(buffer, offset, count);
 110
 1111            return ReadCore(buffer.AsSpan(offset, count), CancellationToken.None);
 1112        }
 113
 1114        public override int Read(Span<byte> buffer) => ReadCore(buffer, CancellationToken.None);
 115
 116        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellation)
 117        {
 118            ValidateBufferArguments(buffer, offset, count);
 119
 120            await Task.Yield();
 121
 122            return ReadCore(buffer.AsSpan(offset, count), cancellation);
 123        }
 124
 125        public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellation)
 126        {
 127            await Task.Yield();
 128
 129            return ReadCore(buffer.Span, cancellation);
 130        }
 131
 132        #region Not Supported members
 1133        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 134
 1135        public override void SetLength(long value) => throw new NotSupportedException();
 136
 1137        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
 138
 1139        public override long Length => throw new NotSupportedException();
 140
 141        public override long Position
 142        {
 1143            get => throw new NotSupportedException();
 1144            set => throw new NotSupportedException();
 145        }
 146
 1147        public override void Flush() { }
 148        #endregion
 149    }
 150}