Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Commit 8ecb147

Browse files
committed
Added overload to PathString.StartsWithSegments to allow specifying StringComparison:
- This allows us to have a fast-path (or just be more explicit) for the comparison by doing case-sensitive checks (which are cheaper)
1 parent 61466af commit 8ecb147

File tree

2 files changed

+124
-9
lines changed

2 files changed

+124
-9
lines changed

Diff for: src/Microsoft.AspNet.Http.Abstractions/PathString.cs

+47-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public struct PathString : IEquatable<PathString>
2626
/// <param name="value">The unescaped path to be assigned to the Value property.</param>
2727
public PathString(string value)
2828
{
29-
if (!String.IsNullOrEmpty(value) && value[0] != '/')
29+
if (!string.IsNullOrEmpty(value) && value[0] != '/')
3030
{
3131
throw new ArgumentException(""/*Resources.Exception_PathMustStartWithSlash*/, nameof(value));
3232
}
@@ -46,7 +46,7 @@ public string Value
4646
/// </summary>
4747
public bool HasValue
4848
{
49-
get { return !String.IsNullOrEmpty(_value); }
49+
get { return !string.IsNullOrEmpty(_value); }
5050
}
5151

5252
/// <summary>
@@ -98,23 +98,61 @@ public static PathString FromUriComponent(Uri uri)
9898
return new PathString("/" + uri.GetComponents(UriComponents.Path, UriFormat.Unescaped));
9999
}
100100

101+
/// <summary>
102+
/// Determines whether the beginning of this <see cref="PathString"/> instance matches the specified <see cref="PathString"/>.
103+
/// </summary>
104+
/// <param name="other">The <see cref="PathString"/> to compare.</param>
105+
/// <returns>true if value matches the beginning of this string; otherwise, false.</returns>
101106
public bool StartsWithSegments(PathString other)
102107
{
103-
string value1 = Value ?? String.Empty;
104-
string value2 = other.Value ?? String.Empty;
105-
if (value1.StartsWith(value2, StringComparison.OrdinalIgnoreCase))
108+
return StartsWithSegments(other, StringComparison.OrdinalIgnoreCase);
109+
}
110+
111+
/// <summary>
112+
/// Determines whether the beginning of this <see cref="PathString"/> instance matches the specified <see cref="PathString"/> when compared
113+
/// using the specified comparison option.
114+
/// </summary>
115+
/// <param name="other">The <see cref="PathString"/> to compare.</param>
116+
/// <param name="comparisonType">One of the enumeration values that determines how this <see cref="PathString"/> and value are compared.</param>
117+
/// <returns>true if value matches the beginning of this string; otherwise, false.</returns>
118+
public bool StartsWithSegments(PathString other, StringComparison comparisonType)
119+
{
120+
var value1 = Value ?? string.Empty;
121+
var value2 = other.Value ?? string.Empty;
122+
if (value1.StartsWith(value2, comparisonType))
106123
{
107124
return value1.Length == value2.Length || value1[value2.Length] == '/';
108125
}
109126
return false;
110127
}
111128

129+
/// <summary>
130+
/// Determines whether the beginning of this PathString instance matches the specified <see cref="PathString"/> when compared
131+
/// using the specified comparison option and returns the remaining segments.
132+
/// </summary>
133+
/// <param name="other">The <see cref="PathString"/> to compare.</param>
134+
/// <param name="remaining">The remaining segments after the match.</param>
135+
/// <returns>true if value matches the beginning of this string; otherwise, false.</returns>
112136
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Secondary information needed after boolean result obtained")]
113137
public bool StartsWithSegments(PathString other, out PathString remaining)
114138
{
115-
string value1 = Value ?? String.Empty;
116-
string value2 = other.Value ?? String.Empty;
117-
if (value1.StartsWith(value2, StringComparison.OrdinalIgnoreCase))
139+
return StartsWithSegments(other, StringComparison.OrdinalIgnoreCase, out remaining);
140+
}
141+
142+
/// <summary>
143+
/// Determines whether the beginning of this <see cref="PathString"/> instance matches the specified <see cref="PathString"/> and returns
144+
/// the remaining segments.
145+
/// </summary>
146+
/// <param name="other">The <see cref="PathString"/> to compare.</param>
147+
/// <param name="comparisonType">One of the enumeration values that determines how this <see cref="PathString"/> and value are compared.</param>
148+
/// <param name="remaining">The remaining segments after the match.</param>
149+
/// <returns>true if value matches the beginning of this string; otherwise, false.</returns>
150+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Secondary information needed after boolean result obtained")]
151+
public bool StartsWithSegments(PathString other, StringComparison comparisonType, out PathString remaining)
152+
{
153+
var value1 = Value ?? string.Empty;
154+
var value2 = other.Value ?? string.Empty;
155+
if (value1.StartsWith(value2, comparisonType))
118156
{
119157
if (value1.Length == value2.Length || value1[value2.Length] == '/')
120158
{
@@ -278,7 +316,7 @@ public static implicit operator PathString(string s)
278316
/// Implicitly calls ToString().
279317
/// </summary>
280318
/// <param name="path"></param>
281-
public static implicit operator string(PathString path)
319+
public static implicit operator string (PathString path)
282320
{
283321
return path.ToString();
284322
}

Diff for: test/Microsoft.AspNet.Http.Abstractions.Tests/PathStringTests.cs

+77
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using Microsoft.AspNet.Testing;
56
using Xunit;
67

@@ -70,5 +71,81 @@ public void ImplicitStringConverters_WorksWithAdd()
7071
result = path + "text";
7172
Assert.Equal("/pathtext", result);
7273
}
74+
75+
[Theory]
76+
[InlineData("/test/path", "/TEST", true)]
77+
[InlineData("/test/path", "/TEST/pa", false)]
78+
[InlineData("/TEST/PATH", "/test", true)]
79+
[InlineData("/TEST/path", "/test/pa", false)]
80+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", true)]
81+
public void StartsWithSegments_DoesACaseInsensitiveMatch(string sourcePath, string testPath, bool expectedResult)
82+
{
83+
var source = new PathString(sourcePath);
84+
var test = new PathString(testPath);
85+
86+
var result = source.StartsWithSegments(test);
87+
88+
Assert.Equal(expectedResult, result);
89+
}
90+
91+
[Theory]
92+
[InlineData("/test/path", "/TEST", true)]
93+
[InlineData("/test/path", "/TEST/pa", false)]
94+
[InlineData("/TEST/PATH", "/test", true)]
95+
[InlineData("/TEST/path", "/test/pa", false)]
96+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", true)]
97+
public void StartsWithSegmentsWithRemainder_DoesACaseInsensitiveMatch(string sourcePath, string testPath, bool expectedResult)
98+
{
99+
var source = new PathString(sourcePath);
100+
var test = new PathString(testPath);
101+
102+
PathString remaining;
103+
var result = source.StartsWithSegments(test, out remaining);
104+
105+
Assert.Equal(expectedResult, result);
106+
}
107+
108+
[Theory]
109+
[InlineData("/test/path", "/TEST", StringComparison.OrdinalIgnoreCase, true)]
110+
[InlineData("/test/path", "/TEST", StringComparison.Ordinal, false)]
111+
[InlineData("/test/path", "/TEST/pa", StringComparison.OrdinalIgnoreCase, false)]
112+
[InlineData("/test/path", "/TEST/pa", StringComparison.Ordinal, false)]
113+
[InlineData("/TEST/PATH", "/test", StringComparison.OrdinalIgnoreCase, true)]
114+
[InlineData("/TEST/PATH", "/test", StringComparison.Ordinal, false)]
115+
[InlineData("/TEST/path", "/test/pa", StringComparison.OrdinalIgnoreCase, false)]
116+
[InlineData("/TEST/path", "/test/pa", StringComparison.Ordinal, false)]
117+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", StringComparison.OrdinalIgnoreCase, true)]
118+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", StringComparison.Ordinal, false)]
119+
public void StartsWithSegments_DoesMatchUsingSpecifiedComparison(string sourcePath, string testPath, StringComparison comparison, bool expectedResult)
120+
{
121+
var source = new PathString(sourcePath);
122+
var test = new PathString(testPath);
123+
124+
var result = source.StartsWithSegments(test, comparison);
125+
126+
Assert.Equal(expectedResult, result);
127+
}
128+
129+
[Theory]
130+
[InlineData("/test/path", "/TEST", StringComparison.OrdinalIgnoreCase, true)]
131+
[InlineData("/test/path", "/TEST", StringComparison.Ordinal, false)]
132+
[InlineData("/test/path", "/TEST/pa", StringComparison.OrdinalIgnoreCase, false)]
133+
[InlineData("/test/path", "/TEST/pa", StringComparison.Ordinal, false)]
134+
[InlineData("/TEST/PATH", "/test", StringComparison.OrdinalIgnoreCase, true)]
135+
[InlineData("/TEST/PATH", "/test", StringComparison.Ordinal, false)]
136+
[InlineData("/TEST/path", "/test/pa", StringComparison.OrdinalIgnoreCase, false)]
137+
[InlineData("/TEST/path", "/test/pa", StringComparison.Ordinal, false)]
138+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", StringComparison.OrdinalIgnoreCase, true)]
139+
[InlineData("/test/PATH/path/TEST", "/TEST/path/PATH", StringComparison.Ordinal, false)]
140+
public void StartsWithSegmentsWithRemainder_DoesMatchUsingSpecifiedComparison(string sourcePath, string testPath, StringComparison comparison, bool expectedResult)
141+
{
142+
var source = new PathString(sourcePath);
143+
var test = new PathString(testPath);
144+
145+
PathString remaining;
146+
var result = source.StartsWithSegments(test, comparison, out remaining);
147+
148+
Assert.Equal(expectedResult, result);
149+
}
73150
}
74151
}

0 commit comments

Comments
 (0)