< Summary

Line coverage
70%
Covered lines: 84
Uncovered lines: 35
Coverable lines: 119
Total lines: 217
Line coverage: 70.5%
Branch coverage
78%
Covered branches: 58
Total branches: 74
Branch coverage: 78.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)91.66%121290%
.ctor(...)100%110%
ToString()0%440%
Equals(...)0%440%
GetHashCode()100%110%
GetRangeItemListLength(...)92.85%1414100%
GetRangeItemLength(...)83.33%363678.72%
System.ICloneable.Clone()100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.Http/src/System/Net/Http/Headers/RangeItemHeaderValue.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.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Globalization;
 8
 9namespace System.Net.Http.Headers
 10{
 11    public class RangeItemHeaderValue : ICloneable
 12    {
 13        // Set to -1 if not set.
 14        private readonly long _from;
 15        private readonly long _to;
 16
 4217        public long? From => _from >= 0 ? _from : null;
 18
 6019        public long? To => _to >= 0 ? _to : null;
 20
 15621        public RangeItemHeaderValue(long? from, long? to)
 15622        {
 15623            if (!from.HasValue && !to.HasValue)
 024            {
 025                throw new ArgumentException(SR.net_http_headers_invalid_range);
 26            }
 15627            if (from.HasValue)
 1828            {
 1829                ArgumentOutOfRangeException.ThrowIfNegative(from.GetValueOrDefault(), nameof(from));
 1830            }
 15631            if (to.HasValue)
 15032            {
 15033                ArgumentOutOfRangeException.ThrowIfNegative(to.GetValueOrDefault(), nameof(to));
 15034            }
 15635            if (from.HasValue && to.HasValue)
 1236            {
 1237                ArgumentOutOfRangeException.ThrowIfGreaterThan(from.GetValueOrDefault(), to.GetValueOrDefault(), nameof(
 1238            }
 39
 15640            _from = from ?? -1;
 15641            _to = to ?? -1;
 15642        }
 43
 044        internal RangeItemHeaderValue(RangeItemHeaderValue source)
 045        {
 046            Debug.Assert(source != null);
 47
 048            _from = source._from;
 049            _to = source._to;
 050        }
 51
 52        public override unsafe string ToString()
 053        {
 054            Span<char> stackBuffer = stackalloc char[128];
 55
 056            if (_from < 0)
 057            {
 058                return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"-{_to}");
 59            }
 60
 061            if (_to < 0)
 062            {
 063                return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"{_from}-"); ;
 64            }
 65
 066            return string.Create(CultureInfo.InvariantCulture, stackBuffer, $"{_from}-{_to}");
 067        }
 68
 69        public override bool Equals([NotNullWhen(true)] object? obj) =>
 070            obj is RangeItemHeaderValue other &&
 071            _from == other._from &&
 072            _to == other._to;
 73
 74        public override int GetHashCode() =>
 075            HashCode.Combine(_from, _to);
 76
 77        // Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
 78        // list segments are allowed, e.g. ",1-2, , 3-4,,".
 79        internal static int GetRangeItemListLength(string? input, int startIndex,
 80            ICollection<RangeItemHeaderValue> rangeCollection)
 14881        {
 14882            Debug.Assert(rangeCollection != null);
 14883            Debug.Assert(startIndex >= 0);
 84
 14885            if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
 686            {
 687                return 0;
 88            }
 89
 90            // Empty segments are allowed, so skip all delimiter-only segments (e.g. ", ,").
 14291            int current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, startIndex, true, out _);
 92            // It's OK if we didn't find leading separator characters. Ignore 'separatorFound'.
 93
 14294            if (current == input.Length)
 295            {
 296                return 0;
 97            }
 98
 25099            while (true)
 250100            {
 250101                int rangeLength = GetRangeItemLength(input, current, out RangeItemHeaderValue? range);
 102
 250103                if (rangeLength == 0)
 94104                {
 94105                    return 0;
 106                }
 107
 156108                rangeCollection.Add(range!);
 109
 156110                current += rangeLength;
 156111                current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out bool separatorFound
 112
 113                // If the string is not consumed, we must have a delimiter, otherwise the string is not a valid
 114                // range list.
 156115                if ((current < input.Length) && !separatorFound)
 30116                {
 30117                    return 0;
 118                }
 119
 126120                if (current == input.Length)
 16121                {
 16122                    return current - startIndex;
 123                }
 110124            }
 148125        }
 126
 127        internal static int GetRangeItemLength(string? input, int startIndex, out RangeItemHeaderValue? parsedValue)
 250128        {
 250129            Debug.Assert(startIndex >= 0);
 130
 131            // This parser parses number ranges: e.g. '1-2', '1-', '-2'.
 132
 250133            parsedValue = null;
 134
 250135            if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
 0136            {
 0137                return 0;
 138            }
 139
 140            // Caller must remove leading whitespace. If not, we'll return 0.
 250141            int current = startIndex;
 142
 143            // Try parse the first value of a value pair.
 250144            int fromStartIndex = current;
 250145            int fromLength = HttpRuleParser.GetNumberLength(input, current, false);
 146
 250147            if (fromLength > HttpRuleParser.MaxInt64Digits)
 0148            {
 0149                return 0;
 150            }
 151
 250152            current += fromLength;
 250153            current += HttpRuleParser.GetWhitespaceLength(input, current);
 154
 155            // After the first value, the '-' character must follow.
 250156            if ((current == input.Length) || (input[current] != '-'))
 42157            {
 158                // We need a '-' character otherwise this can't be a valid range.
 42159                return 0;
 160            }
 161
 208162            current++; // skip the '-' character
 208163            current += HttpRuleParser.GetWhitespaceLength(input, current);
 164
 208165            int toStartIndex = current;
 208166            int toLength = 0;
 167
 168            // If we didn't reach the end of the string, try parse the second value of the range.
 208169            if (current < input.Length)
 204170            {
 204171                toLength = HttpRuleParser.GetNumberLength(input, current, false);
 172
 204173                if (toLength > HttpRuleParser.MaxInt64Digits)
 0174                {
 0175                    return 0;
 176                }
 177
 204178                current += toLength;
 204179                current += HttpRuleParser.GetWhitespaceLength(input, current);
 204180            }
 181
 208182            if ((fromLength == 0) && (toLength == 0))
 38183            {
 38184                return 0; // At least one value must be provided in order to be a valid range.
 185            }
 186
 187            // Try convert first value to int64
 170188            long from = 0;
 170189            if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input, fromStartIndex, fromLength, out from))
 0190            {
 0191                return 0;
 192            }
 193
 194            // Try convert second value to int64
 170195            long to = 0;
 170196            if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input, toStartIndex, toLength, out to))
 0197            {
 0198                return 0;
 199            }
 200
 201            // 'from' must not be greater than 'to'
 170202            if ((fromLength > 0) && (toLength > 0) && (from > to))
 14203            {
 14204                return 0;
 205            }
 206
 156207            parsedValue = new RangeItemHeaderValue((fromLength == 0 ? null : from), (toLength == 0 ? null : to));
 156208            return current - startIndex;
 250209        }
 210
 211        object ICloneable.Clone()
 0212        {
 0213            return new RangeItemHeaderValue(this);
 0214        }
 215    }
 216}
 217