< Summary

Line coverage
20%
Covered lines: 47
Uncovered lines: 180
Coverable lines: 227
Total lines: 374
Line coverage: 20.7%
Branch coverage
23%
Covered branches: 22
Total branches: 92
Branch coverage: 23.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor()100%110%
.ctor(...)100%110%
Equals(...)0%880%
GetHashCode()100%110%
ToString()0%440%
Parse(...)100%110%
TryParse(...)0%220%
GetContentRangeLength(...)59.09%222261.9%
TryGetLengthLength(...)0%660%
TryGetRangeLength(...)56.25%161663.63%
TryCreateContentRange(...)0%28280%
System.ICloneable.Clone()100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Http/src/System/Net/Http/Headers/ContentRangeHeaderValue.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Text;
 7
 8namespace System.Net.Http.Headers
 9{
 10    public class ContentRangeHeaderValue : ICloneable
 11    {
 012        private string _unit = null!;
 13        private long _from;
 14        private long _to;
 15        private long _length;
 16
 17        public string Unit
 18        {
 019            get { return _unit; }
 20            set
 021            {
 022                HeaderUtilities.CheckValidToken(value);
 023                _unit = value;
 024            }
 25        }
 26
 027        public long? From => HasRange ? _from : null;
 28
 029        public long? To => HasRange ? _to : null;
 30
 031        public long? Length => HasLength ? _length : null;
 32
 033        public bool HasLength => _length >= 0; // e.g. "Content-Range: bytes 12-34/*"
 34
 035        public bool HasRange => _from >= 0; // e.g. "Content-Range: bytes */1234"
 36
 037        public ContentRangeHeaderValue(long from, long to, long length)
 038        {
 39            // Scenario: "Content-Range: bytes 12-34/5678"
 40
 041            ArgumentOutOfRangeException.ThrowIfNegative(length);
 042            ArgumentOutOfRangeException.ThrowIfNegative(to);
 043            ArgumentOutOfRangeException.ThrowIfGreaterThan(to, length);
 044            ArgumentOutOfRangeException.ThrowIfNegative(from);
 045            ArgumentOutOfRangeException.ThrowIfGreaterThan(from, to);
 46
 047            _from = from;
 048            _to = to;
 049            _length = length;
 050            _unit = HeaderUtilities.BytesUnit;
 051        }
 52
 053        public ContentRangeHeaderValue(long length)
 054        {
 55            // Scenario: "Content-Range: bytes */1234"
 56
 057            ArgumentOutOfRangeException.ThrowIfNegative(length);
 58
 059            _length = length;
 060            _unit = HeaderUtilities.BytesUnit;
 061            _from = -1;
 062        }
 63
 064        public ContentRangeHeaderValue(long from, long to)
 065        {
 66            // Scenario: "Content-Range: bytes 12-34/*"
 67
 068            ArgumentOutOfRangeException.ThrowIfNegative(to);
 069            ArgumentOutOfRangeException.ThrowIfNegative(from);
 070            ArgumentOutOfRangeException.ThrowIfGreaterThan(from, to);
 71
 072            _from = from;
 073            _to = to;
 074            _unit = HeaderUtilities.BytesUnit;
 075            _length = -1;
 076        }
 77
 078        private ContentRangeHeaderValue()
 079        {
 080            _from = -1;
 081            _length = -1;
 082        }
 83
 084        private ContentRangeHeaderValue(ContentRangeHeaderValue source)
 085        {
 086            Debug.Assert(source != null);
 87
 088            _from = source._from;
 089            _to = source._to;
 090            _length = source._length;
 091            _unit = source._unit;
 092        }
 93
 94        public override bool Equals([NotNullWhen(true)] object? obj) =>
 095            obj is ContentRangeHeaderValue other &&
 096            _from == other._from &&
 097            _to == other._to &&
 098            _length == other._length &&
 099            string.Equals(_unit, other._unit, StringComparison.OrdinalIgnoreCase);
 100
 101        public override int GetHashCode() =>
 0102            HashCode.Combine(
 0103                StringComparer.OrdinalIgnoreCase.GetHashCode(_unit),
 0104                _from,
 0105                _to,
 0106                _length);
 107
 108        public override string ToString()
 0109        {
 0110            var sb = new ValueStringBuilder(stackalloc char[256]);
 0111            sb.Append(_unit);
 0112            sb.Append(' ');
 113
 0114            if (HasRange)
 0115            {
 0116                sb.AppendSpanFormattable(_from);
 0117                sb.Append('-');
 0118                sb.AppendSpanFormattable(_to);
 0119            }
 120            else
 0121            {
 0122                sb.Append('*');
 0123            }
 124
 0125            sb.Append('/');
 0126            if (HasLength)
 0127            {
 0128                sb.AppendSpanFormattable(_length);
 0129            }
 130            else
 0131            {
 0132                sb.Append('*');
 0133            }
 134
 0135            return sb.ToString();
 0136        }
 137
 138        public static ContentRangeHeaderValue Parse(string input)
 0139        {
 0140            int index = 0;
 0141            return (ContentRangeHeaderValue)GenericHeaderParser.ContentRangeParser.ParseValue(input, null, ref index);
 0142        }
 143
 144        public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out ContentRangeHeaderValue? 
 0145        {
 0146            int index = 0;
 0147            parsedValue = null;
 148
 0149            if (GenericHeaderParser.ContentRangeParser.TryParseValue(input, null, ref index, out object? output))
 0150            {
 0151                parsedValue = (ContentRangeHeaderValue)output!;
 0152                return true;
 153            }
 0154            return false;
 0155        }
 156
 157        internal static int GetContentRangeLength(string? input, int startIndex, out object? parsedValue)
 64158        {
 64159            Debug.Assert(startIndex >= 0);
 160
 64161            parsedValue = null;
 162
 64163            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
 0164            {
 0165                return 0;
 166            }
 167
 168            // Parse the unit string: <unit> in '<unit> <from>-<to>/<length>'
 64169            int unitLength = HttpRuleParser.GetTokenLength(input, startIndex);
 170
 64171            if (unitLength == 0)
 6172            {
 6173                return 0;
 174            }
 175
 58176            string unit = input.Substring(startIndex, unitLength);
 58177            int current = startIndex + unitLength;
 58178            int separatorLength = HttpRuleParser.GetWhitespaceLength(input, current);
 179
 58180            if (separatorLength == 0)
 4181            {
 4182                return 0;
 183            }
 184
 54185            current += separatorLength;
 186
 54187            if (current == input.Length)
 2188            {
 2189                return 0;
 190            }
 191
 192            // Read range values <from> and <to> in '<unit> <from>-<to>/<length>'
 52193            int fromStartIndex = current;
 194            int fromLength;
 195            int toStartIndex;
 196            int toLength;
 52197            if (!TryGetRangeLength(input, ref current, out fromLength, out toStartIndex, out toLength))
 10198            {
 10199                return 0;
 200            }
 201
 202            // After the range is read we expect the length separator '/'
 42203            if ((current == input.Length) || (input[current] != '/'))
 42204            {
 42205                return 0;
 206            }
 207
 0208            current++; // Skip '/' separator
 0209            current += HttpRuleParser.GetWhitespaceLength(input, current);
 210
 0211            if (current == input.Length)
 0212            {
 0213                return 0;
 214            }
 215
 216            // We may not have a length (e.g. 'bytes 1-2/*'). But if we do, parse the length now.
 0217            int lengthStartIndex = current;
 218            int lengthLength;
 0219            if (!TryGetLengthLength(input, ref current, out lengthLength))
 0220            {
 0221                return 0;
 222            }
 223
 0224            if (!TryCreateContentRange(input, unit, fromStartIndex, fromLength, toStartIndex, toLength,
 0225                lengthStartIndex, lengthLength, out parsedValue))
 0226            {
 0227                return 0;
 228            }
 229
 0230            return current - startIndex;
 64231        }
 232
 233        private static bool TryGetLengthLength(string input, ref int current, out int lengthLength)
 0234        {
 0235            lengthLength = 0;
 236
 0237            if (input[current] == '*')
 0238            {
 0239                current++;
 0240            }
 241            else
 0242            {
 243                // Parse length value: <length> in '<unit> <from>-<to>/<length>'
 0244                lengthLength = HttpRuleParser.GetNumberLength(input, current, false);
 245
 0246                if ((lengthLength == 0) || (lengthLength > HttpRuleParser.MaxInt64Digits))
 0247                {
 0248                    return false;
 249                }
 250
 0251                current += lengthLength;
 0252            }
 253
 0254            current += HttpRuleParser.GetWhitespaceLength(input, current);
 0255            return true;
 0256        }
 257
 258        private static bool TryGetRangeLength(string input, ref int current, out int fromLength, out int toStartIndex,
 259            out int toLength)
 52260        {
 52261            fromLength = 0;
 52262            toStartIndex = 0;
 52263            toLength = 0;
 264
 265            // Check if we have a value like 'bytes */133'. If yes, skip the range part and continue parsing the
 266            // length separator '/'.
 52267            if (input[current] == '*')
 42268            {
 42269                current++;
 42270            }
 271            else
 10272            {
 273                // Parse first range value: <from> in '<unit> <from>-<to>/<length>'
 10274                fromLength = HttpRuleParser.GetNumberLength(input, current, false);
 275
 10276                if ((fromLength == 0) || (fromLength > HttpRuleParser.MaxInt64Digits))
 2277                {
 2278                    return false;
 279                }
 280
 8281                current += fromLength;
 8282                current += HttpRuleParser.GetWhitespaceLength(input, current);
 283
 284                // After the first value, the '-' character must follow.
 8285                if ((current == input.Length) || (input[current] != '-'))
 8286                {
 287                    // We need a '-' character otherwise this can't be a valid range.
 8288                    return false;
 289                }
 290
 0291                current++; // skip the '-' character
 0292                current += HttpRuleParser.GetWhitespaceLength(input, current);
 293
 0294                if (current == input.Length)
 0295                {
 0296                    return false;
 297                }
 298
 299                // Parse second range value: <to> in '<unit> <from>-<to>/<length>'
 0300                toStartIndex = current;
 0301                toLength = HttpRuleParser.GetNumberLength(input, current, false);
 302
 0303                if ((toLength == 0) || (toLength > HttpRuleParser.MaxInt64Digits))
 0304                {
 0305                    return false;
 306                }
 307
 0308                current += toLength;
 0309            }
 310
 42311            current += HttpRuleParser.GetWhitespaceLength(input, current);
 42312            return true;
 52313        }
 314
 315        private static bool TryCreateContentRange(string input, string unit, int fromStartIndex, int fromLength,
 316            int toStartIndex, int toLength, int lengthStartIndex, int lengthLength, [NotNullWhen(true)] out object? pars
 0317        {
 0318            parsedValue = null;
 319
 0320            long from = 0;
 0321            if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input, fromStartIndex, fromLength, out from))
 0322            {
 0323                return false;
 324            }
 325
 0326            long to = 0;
 0327            if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input, toStartIndex, toLength, out to))
 0328            {
 0329                return false;
 330            }
 331
 332            // 'from' must not be greater than 'to'
 0333            if ((fromLength > 0) && (toLength > 0) && (from > to))
 0334            {
 0335                return false;
 336            }
 337
 0338            long length = 0;
 0339            if ((lengthLength > 0) && !HeaderUtilities.TryParseInt64(input, lengthStartIndex, lengthLength, out length))
 0340            {
 0341                return false;
 342            }
 343
 344            // 'from' and 'to' must be less than 'length'
 0345            if ((toLength > 0) && (lengthLength > 0) && (to >= length))
 0346            {
 0347                return false;
 348            }
 349
 0350            ContentRangeHeaderValue result = new ContentRangeHeaderValue();
 0351            result._unit = unit;
 352
 0353            if (fromLength > 0)
 0354            {
 0355                result._from = from;
 0356                result._to = to;
 0357            }
 358
 0359            if (lengthLength > 0)
 0360            {
 0361                result._length = length;
 0362            }
 363
 0364            parsedValue = result;
 0365            return true;
 0366        }
 367
 368        object ICloneable.Clone()
 0369        {
 0370            return new ContentRangeHeaderValue(this);
 0371        }
 372    }
 373}
 374