< Summary

Information
Class: System.Net.Http.Headers.ContentRangeHeaderValue
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\System.Net.Http\src\System\Net\Http\Headers\ContentRangeHeaderValue.cs
Line coverage
58%
Covered lines: 133
Uncovered lines: 94
Coverable lines: 227
Total lines: 373
Line coverage: 58.5%
Branch coverage
65%
Covered branches: 60
Total branches: 92
Branch coverage: 65.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%117.69%
.ctor(...)100%110%
.ctor(...)100%110%
.ctor()100%11100%
.ctor(...)100%110%
Equals(...)0%880%
GetHashCode()100%110%
ToString()75%4478.26%
Parse(...)100%110%
TryParse(...)0%220%
GetContentRangeLength(...)86.36%222290.47%
TryGetLengthLength(...)100%66100%
TryGetRangeLength(...)100%1616100%
TryCreateContentRange(...)57.14%282858.82%
System.ICloneable.Clone()100%110%

File(s)

D:\runner\runtime\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    {
 4812        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
 3933        public bool HasLength => _length >= 0; // e.g. "Content-Range: bytes 12-34/*"
 34
 3935        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
 4878        private ContentRangeHeaderValue()
 4879        {
 4880            _from = -1;
 4881            _length = -1;
 4882        }
 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()
 39109        {
 39110            var sb = new ValueStringBuilder(stackalloc char[256]);
 39111            sb.Append(_unit);
 39112            sb.Append(' ');
 113
 39114            if (HasRange)
 0115            {
 0116                sb.AppendSpanFormattable(_from);
 0117                sb.Append('-');
 0118                sb.AppendSpanFormattable(_to);
 0119            }
 120            else
 39121            {
 39122                sb.Append('*');
 39123            }
 124
 39125            sb.Append('/');
 39126            if (HasLength)
 6127            {
 6128                sb.AppendSpanFormattable(_length);
 6129            }
 130            else
 33131            {
 33132                sb.Append('*');
 33133            }
 134
 39135            return sb.ToString();
 39136        }
 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)
 174158        {
 174159            Debug.Assert(startIndex >= 0);
 160
 174161            parsedValue = null;
 162
 174163            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
 0164            {
 0165                return 0;
 166            }
 167
 168            // Parse the unit string: <unit> in '<unit> <from>-<to>/<length>'
 174169            int unitLength = HttpRuleParser.GetTokenLength(input, startIndex);
 170
 174171            if (unitLength == 0)
 4172            {
 4173                return 0;
 174            }
 175
 170176            string unit = input.Substring(startIndex, unitLength);
 170177            int current = startIndex + unitLength;
 170178            int separatorLength = HttpRuleParser.GetWhitespaceLength(input, current);
 179
 170180            if (separatorLength == 0)
 4181            {
 4182                return 0;
 183            }
 184
 166185            current += separatorLength;
 186
 166187            if (current == input.Length)
 4188            {
 4189                return 0;
 190            }
 191
 192            // Read range values <from> and <to> in '<unit> <from>-<to>/<length>'
 162193            int fromStartIndex = current;
 194            int fromLength;
 195            int toStartIndex;
 196            int toLength;
 162197            if (!TryGetRangeLength(input, ref current, out fromLength, out toStartIndex, out toLength))
 58198            {
 58199                return 0;
 200            }
 201
 202            // After the range is read we expect the length separator '/'
 104203            if ((current == input.Length) || (input[current] != '/'))
 40204            {
 40205                return 0;
 206            }
 207
 64208            current++; // Skip '/' separator
 64209            current += HttpRuleParser.GetWhitespaceLength(input, current);
 210
 64211            if (current == input.Length)
 2212            {
 2213                return 0;
 214            }
 215
 216            // We may not have a length (e.g. 'bytes 1-2/*'). But if we do, parse the length now.
 62217            int lengthStartIndex = current;
 218            int lengthLength;
 62219            if (!TryGetLengthLength(input, ref current, out lengthLength))
 14220            {
 14221                return 0;
 222            }
 223
 48224            if (!TryCreateContentRange(input, unit, fromStartIndex, fromLength, toStartIndex, toLength,
 48225                lengthStartIndex, lengthLength, out parsedValue))
 0226            {
 0227                return 0;
 228            }
 229
 48230            return current - startIndex;
 174231        }
 232
 233        private static bool TryGetLengthLength(string input, ref int current, out int lengthLength)
 62234        {
 62235            lengthLength = 0;
 236
 62237            if (input[current] == '*')
 36238            {
 36239                current++;
 36240            }
 241            else
 26242            {
 243                // Parse length value: <length> in '<unit> <from>-<to>/<length>'
 26244                lengthLength = HttpRuleParser.GetNumberLength(input, current, false);
 245
 26246                if ((lengthLength == 0) || (lengthLength > HttpRuleParser.MaxInt64Digits))
 14247                {
 14248                    return false;
 249                }
 250
 12251                current += lengthLength;
 12252            }
 253
 48254            current += HttpRuleParser.GetWhitespaceLength(input, current);
 48255            return true;
 62256        }
 257
 258        private static bool TryGetRangeLength(string input, ref int current, out int fromLength, out int toStartIndex,
 259            out int toLength)
 162260        {
 162261            fromLength = 0;
 162262            toStartIndex = 0;
 162263            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 '/'.
 162267            if (input[current] == '*')
 102268            {
 102269                current++;
 102270            }
 271            else
 60272            {
 273                // Parse first range value: <from> in '<unit> <from>-<to>/<length>'
 60274                fromLength = HttpRuleParser.GetNumberLength(input, current, false);
 275
 60276                if ((fromLength == 0) || (fromLength > HttpRuleParser.MaxInt64Digits))
 16277                {
 16278                    return false;
 279                }
 280
 44281                current += fromLength;
 44282                current += HttpRuleParser.GetWhitespaceLength(input, current);
 283
 284                // After the first value, the '-' character must follow.
 44285                if ((current == input.Length) || (input[current] != '-'))
 22286                {
 287                    // We need a '-' character otherwise this can't be a valid range.
 22288                    return false;
 289                }
 290
 22291                current++; // skip the '-' character
 22292                current += HttpRuleParser.GetWhitespaceLength(input, current);
 293
 22294                if (current == input.Length)
 2295                {
 2296                    return false;
 297                }
 298
 299                // Parse second range value: <to> in '<unit> <from>-<to>/<length>'
 20300                toStartIndex = current;
 20301                toLength = HttpRuleParser.GetNumberLength(input, current, false);
 302
 20303                if ((toLength == 0) || (toLength > HttpRuleParser.MaxInt64Digits))
 18304                {
 18305                    return false;
 306                }
 307
 2308                current += toLength;
 2309            }
 310
 104311            current += HttpRuleParser.GetWhitespaceLength(input, current);
 104312            return true;
 162313        }
 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
 48317        {
 48318            parsedValue = null;
 319
 48320            long from = 0;
 48321            if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input, fromStartIndex, fromLength, out from))
 0322            {
 0323                return false;
 324            }
 325
 48326            long to = 0;
 48327            if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input, toStartIndex, toLength, out to))
 0328            {
 0329                return false;
 330            }
 331
 332            // 'from' must not be greater than 'to'
 48333            if ((fromLength > 0) && (toLength > 0) && (from > to))
 0334            {
 0335                return false;
 336            }
 337
 48338            long length = 0;
 48339            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'
 48345            if ((toLength > 0) && (lengthLength > 0) && (to >= length))
 0346            {
 0347                return false;
 348            }
 349
 48350            ContentRangeHeaderValue result = new ContentRangeHeaderValue();
 48351            result._unit = unit;
 352
 48353            if (fromLength > 0)
 0354            {
 0355                result._from = from;
 0356                result._to = to;
 0357            }
 358
 48359            if (lengthLength > 0)
 12360            {
 12361                result._length = length;
 12362            }
 363
 48364            parsedValue = result;
 48365            return true;
 48366        }
 367
 368        object ICloneable.Clone()
 0369        {
 0370            return new ContentRangeHeaderValue(this);
 0371        }
 372    }
 373}