| File: System\Net\Http\Headers\RangeHeaderValue.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Http\src\System.Net.Http.csproj (System.Net.Http) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; namespace System.Net.Http.Headers { public class RangeHeaderValue : ICloneable { private string _unit; private UnvalidatedObjectCollection<RangeItemHeaderValue>? _ranges; public string Unit { get { return _unit; } set { HeaderUtilities.CheckValidToken(value); _unit = value; } } public ICollection<RangeItemHeaderValue> Ranges => _ranges ??= new UnvalidatedObjectCollection<RangeItemHeaderValue>(); public RangeHeaderValue() { _unit = HeaderUtilities.BytesUnit; } public RangeHeaderValue(long? from, long? to) { // convenience ctor: "Range: bytes=from-to" _unit = HeaderUtilities.BytesUnit; Ranges.Add(new RangeItemHeaderValue(from, to)); } private RangeHeaderValue(RangeHeaderValue source) { Debug.Assert(source != null); _unit = source._unit; if (source._ranges != null) { foreach (RangeItemHeaderValue range in source._ranges) { this.Ranges.Add(new RangeItemHeaderValue(range)); } } } public override string ToString() { var sb = new ValueStringBuilder(stackalloc char[256]); sb.Append(_unit); sb.Append('='); if (_ranges != null) { bool first = true; foreach (RangeItemHeaderValue range in _ranges) { if (first) { first = false; } else { sb.Append(", "); } if (range.From.HasValue) sb.AppendSpanFormattable(range.From.GetValueOrDefault()); sb.Append('-'); if (range.To.HasValue) sb.AppendSpanFormattable(range.To.GetValueOrDefault()); } } return sb.ToString(); } public override bool Equals([NotNullWhen(true)] object? obj) { RangeHeaderValue? other = obj as RangeHeaderValue; if (other == null) { return false; } return string.Equals(_unit, other._unit, StringComparison.OrdinalIgnoreCase) && HeaderUtilities.AreEqualCollections(_ranges, other._ranges); } public override int GetHashCode() { int result = StringComparer.OrdinalIgnoreCase.GetHashCode(_unit); if (_ranges != null) { foreach (RangeItemHeaderValue range in _ranges) { result ^= range.GetHashCode(); } } return result; } public static RangeHeaderValue Parse(string input) { int index = 0; return (RangeHeaderValue)GenericHeaderParser.RangeParser.ParseValue(input, null, ref index); } public static bool TryParse([NotNullWhen(true)] string? input, [NotNullWhen(true)] out RangeHeaderValue? parsedValue) { int index = 0; parsedValue = null; if (GenericHeaderParser.RangeParser.TryParseValue(input, null, ref index, out object? output)) { parsedValue = (RangeHeaderValue)output!; return true; } return false; } internal static int GetRangeLength(string? input, int startIndex, out object? parsedValue) { Debug.Assert(startIndex >= 0); parsedValue = null; if (string.IsNullOrEmpty(input) || (startIndex >= input.Length)) { return 0; } // Parse the unit string: <unit> in '<unit>=<from1>-<to1>, <from2>-<to2>' int unitLength = HttpRuleParser.GetTokenLength(input, startIndex); if (unitLength == 0) { return 0; } RangeHeaderValue result = new RangeHeaderValue(); result._unit = input.Substring(startIndex, unitLength); int current = startIndex + unitLength; current += HttpRuleParser.GetWhitespaceLength(input, current); if ((current == input.Length) || (input[current] != '=')) { return 0; } current++; // skip '=' separator current += HttpRuleParser.GetWhitespaceLength(input, current); int rangesLength = RangeItemHeaderValue.GetRangeItemListLength(input, current, result.Ranges); if (rangesLength == 0) { return 0; } current += rangesLength; Debug.Assert(current == input.Length, "GetRangeItemListLength() should consume the whole string or fail."); parsedValue = result; return current - startIndex; } object ICloneable.Clone() { return new RangeHeaderValue(this); } } }