PageRenderTime 56ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/WCFWebApi/src/System.Net.Http/System/Net/Http/Headers/RangeItemHeaderValue.cs

#
C# | 235 lines | 179 code | 41 blank | 15 comment | 57 complexity | 432100f21814e857065b720715f2ac5f MD5 | raw file
Possible License(s): CC-BY-SA-3.0, Apache-2.0
  1. using System.Collections.Generic;
  2. using System.Diagnostics.Contracts;
  3. using System.Globalization;
  4. namespace System.Net.Http.Headers
  5. {
  6. public class RangeItemHeaderValue : ICloneable
  7. {
  8. private long? from;
  9. private long? to;
  10. public long? From
  11. {
  12. get { return from; }
  13. }
  14. public long? To
  15. {
  16. get { return to; }
  17. }
  18. public RangeItemHeaderValue(long? from, long? to)
  19. {
  20. if (!from.HasValue && !to.HasValue)
  21. {
  22. throw new ArgumentException(SR.net_http_headers_invalid_range);
  23. }
  24. if (from.HasValue && (from.Value < 0))
  25. {
  26. throw new ArgumentOutOfRangeException("from");
  27. }
  28. if (to.HasValue && (to.Value < 0))
  29. {
  30. throw new ArgumentOutOfRangeException("to");
  31. }
  32. if (from.HasValue && to.HasValue && (from.Value > to.Value))
  33. {
  34. throw new ArgumentOutOfRangeException("from");
  35. }
  36. this.from = from;
  37. this.to = to;
  38. }
  39. private RangeItemHeaderValue(RangeItemHeaderValue source)
  40. {
  41. Contract.Requires(source != null);
  42. this.from = source.from;
  43. this.to = source.to;
  44. }
  45. public override string ToString()
  46. {
  47. if (!from.HasValue)
  48. {
  49. return "-" + to.Value.ToString(NumberFormatInfo.InvariantInfo);
  50. }
  51. else if (!to.HasValue)
  52. {
  53. return from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-";
  54. }
  55. return from.Value.ToString(NumberFormatInfo.InvariantInfo) + "-" +
  56. to.Value.ToString(NumberFormatInfo.InvariantInfo);
  57. }
  58. public override bool Equals(object obj)
  59. {
  60. RangeItemHeaderValue other = obj as RangeItemHeaderValue;
  61. if (other == null)
  62. {
  63. return false;
  64. }
  65. return ((from == other.from) && (to == other.to));
  66. }
  67. public override int GetHashCode()
  68. {
  69. if (!from.HasValue)
  70. {
  71. return to.GetHashCode();
  72. }
  73. else if (!to.HasValue)
  74. {
  75. return from.GetHashCode();
  76. }
  77. return from.GetHashCode() ^ to.GetHashCode();
  78. }
  79. // Returns the length of a range list. E.g. "1-2, 3-4, 5-6" adds 3 ranges to 'rangeCollection'. Note that empty
  80. // list segments are allowed, e.g. ",1-2, , 3-4,,".
  81. internal static int GetRangeItemListLength(string input, int startIndex,
  82. ICollection<RangeItemHeaderValue> rangeCollection)
  83. {
  84. Contract.Requires(rangeCollection != null);
  85. Contract.Requires(startIndex >= 0);
  86. Contract.Ensures((Contract.Result<int>() == 0) || (rangeCollection.Count > 0),
  87. "If we can parse the string, then we expect to have at least one range item.");
  88. if ((string.IsNullOrEmpty(input)) || (startIndex >= input.Length))
  89. {
  90. return 0;
  91. }
  92. // Empty segments are allowed, so skip all delimiter-only segments (e.g. ", ,").
  93. bool separatorFound = false;
  94. int current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, startIndex, true, out separatorFound);
  95. // It's OK if we didn't find leading separator characters. Ignore 'separatorFound'.
  96. if (current == input.Length)
  97. {
  98. return 0;
  99. }
  100. RangeItemHeaderValue range = null;
  101. while (true)
  102. {
  103. int rangeLength = GetRangeItemLength(input, current, out range);
  104. if (rangeLength == 0)
  105. {
  106. return 0;
  107. }
  108. rangeCollection.Add(range);
  109. current = current + rangeLength;
  110. current = HeaderUtilities.GetNextNonEmptyOrWhitespaceIndex(input, current, true, out separatorFound);
  111. // If the string is not consumed, we must have a delimiter, otherwise the string is not a valid
  112. // range list.
  113. if ((current < input.Length) && !separatorFound)
  114. {
  115. return 0;
  116. }
  117. if (current == input.Length)
  118. {
  119. return current - startIndex;
  120. }
  121. }
  122. }
  123. internal static int GetRangeItemLength(string input, int startIndex, out RangeItemHeaderValue parsedValue)
  124. {
  125. Contract.Requires(startIndex >= 0);
  126. // This parser parses number ranges: e.g. '1-2', '1-', '-2'.
  127. parsedValue = null;
  128. if (string.IsNullOrEmpty(input) || (startIndex >= input.Length))
  129. {
  130. return 0;
  131. }
  132. // Caller must remove leading whitespaces. If not, we'll return 0.
  133. int current = startIndex;
  134. // Try parse the first value of a value pair.
  135. int fromStartIndex = current;
  136. int fromLength = HttpRuleParser.GetNumberLength(input, current, false);
  137. if (fromLength > HttpRuleParser.MaxInt64Digits)
  138. {
  139. return 0;
  140. }
  141. current = current + fromLength;
  142. current = current + HttpRuleParser.GetWhitespaceLength(input, current);
  143. // Afer the first value, the '-' character must follow.
  144. if ((current == input.Length) || (input[current] != '-'))
  145. {
  146. // We need a '-' character otherwise this can't be a valid range.
  147. return 0;
  148. }
  149. current++; // skip the '-' character
  150. current = current + HttpRuleParser.GetWhitespaceLength(input, current);
  151. int toStartIndex = current;
  152. int toLength = 0;
  153. // If we didn't reach the end of the string, try parse the second value of the range.
  154. if (current < input.Length)
  155. {
  156. toLength = HttpRuleParser.GetNumberLength(input, current, false);
  157. if (toLength > HttpRuleParser.MaxInt64Digits)
  158. {
  159. return 0;
  160. }
  161. current = current + toLength;
  162. current = current + HttpRuleParser.GetWhitespaceLength(input, current);
  163. }
  164. if ((fromLength == 0) && (toLength == 0))
  165. {
  166. return 0; // At least one value must be provided in order to be a valid range.
  167. }
  168. // Try convert first value to int64
  169. long from = 0;
  170. if ((fromLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(fromStartIndex, fromLength), out from))
  171. {
  172. return 0;
  173. }
  174. // Try convert second value to int64
  175. long to = 0;
  176. if ((toLength > 0) && !HeaderUtilities.TryParseInt64(input.Substring(toStartIndex, toLength), out to))
  177. {
  178. return 0;
  179. }
  180. // 'from' must not be greater than 'to'
  181. if ((fromLength > 0) && (toLength > 0) && (from > to))
  182. {
  183. return 0;
  184. }
  185. parsedValue = new RangeItemHeaderValue((fromLength == 0 ? (long?)null : (long?)from),
  186. (toLength == 0 ? (long?)null : (long?)to));
  187. return current - startIndex;
  188. }
  189. object ICloneable.Clone()
  190. {
  191. return new RangeItemHeaderValue(this);
  192. }
  193. }
  194. }