Skip to content

Commit

Permalink
Remove StringSlice in favor of ReadOnlyMemory<char>
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Oct 28, 2020
1 parent 4d8ba56 commit 7e6b174
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 183 deletions.
66 changes: 66 additions & 0 deletions src/Compilers/Core/Portable/CaseInsensitiveComparison.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,24 @@ public override int Compare(string? str1, string? str2)
return str1.Length - str2.Length;
}

#if !NET20 && !NETSTANDARD1_3
public int Compare(ReadOnlySpan<char> str1, ReadOnlySpan<char> str2)
{
int len = Math.Min(str1.Length, str2.Length);
for (int i = 0; i < len; i++)
{
int ordDiff = CompareLowerUnicode(str1[i], str2[i]);
if (ordDiff != 0)
{
return ordDiff;
}
}

// return the smaller string, or 0 if they are equal in length
return str1.Length - str2.Length;
}
#endif

private static bool AreEqualLowerUnicode(char c1, char c2)
{
return c1 == c2 || ToLower(c1) == ToLower(c2);
Expand Down Expand Up @@ -150,6 +168,26 @@ public override bool Equals(string? str1, string? str2)
return true;
}

#if !NET20 && !NETSTANDARD1_3
public bool Equals(ReadOnlySpan<char> str1, ReadOnlySpan<char> str2)
{
if (str1.Length != str2.Length)
{
return false;
}

for (int i = 0; i < str1.Length; i++)
{
if (!AreEqualLowerUnicode(str1[i], str2[i]))
{
return false;
}
}

return true;
}
#endif

public static bool EndsWith(string value, string possibleEnd)
{
if ((object)value == possibleEnd)
Expand Down Expand Up @@ -255,6 +293,20 @@ public override int GetHashCode(string str)
/// </remarks>
public static bool Equals(string left, string right) => s_comparer.Equals(left, right);

#if !NET20 && !NETSTANDARD1_3
/// <summary>
/// Determines if two strings are equal according to Unicode rules for case-insensitive
/// identifier comparison (lower-case mapping).
/// </summary>
/// <param name="left">First identifier to compare</param>
/// <param name="right">Second identifier to compare</param>
/// <returns>true if the identifiers should be considered the same.</returns>
/// <remarks>
/// These are also the rules used for VB identifier comparison.
/// </remarks>
public static bool Equals(ReadOnlySpan<char> left, ReadOnlySpan<char> right) => s_comparer.Equals(left, right);
#endif

/// <summary>
/// Determines if the string 'value' end with string 'possibleEnd'.
/// </summary>
Expand Down Expand Up @@ -283,6 +335,20 @@ public override int GetHashCode(string str)
/// </remarks>
public static int Compare(string left, string right) => s_comparer.Compare(left, right);

#if !NET20 && !NETSTANDARD1_3
/// <summary>
/// Compares two strings according to the Unicode rules for case-insensitive
/// identifier comparison (lower-case mapping).
/// </summary>
/// <param name="left">First identifier to compare</param>
/// <param name="right">Second identifier to compare</param>
/// <returns>-1 if <paramref name="left"/> &lt; <paramref name="right"/>, 1 if <paramref name="left"/> &gt; <paramref name="right"/>, 0 if they are equal.</returns>
/// <remarks>
/// These are also the rules used for VB identifier comparison.
/// </remarks>
public static int Compare(ReadOnlySpan<char> left, ReadOnlySpan<char> right) => s_comparer.Compare(left, right);
#endif

/// <summary>
/// Gets a case-insensitive hash code for Unicode identifiers.
/// </summary>
Expand Down
9 changes: 4 additions & 5 deletions src/Compilers/Core/Portable/InternalUtilities/Hash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,16 @@ internal static int GetFNVHashCode(string text, int start, int length)

internal static int GetCaseInsensitiveFNVHashCode(string text)
{
return GetCaseInsensitiveFNVHashCode(text, 0, text.Length);
return GetCaseInsensitiveFNVHashCode(text.AsSpan(0, text.Length));
}

internal static int GetCaseInsensitiveFNVHashCode(string text, int start, int length)
internal static int GetCaseInsensitiveFNVHashCode(ReadOnlySpan<char> data)
{
int hashCode = Hash.FnvOffsetBias;
int end = start + length;

for (int i = start; i < end; i++)
for (int i = 0; i < data.Length; i++)
{
hashCode = unchecked((hashCode ^ CaseInsensitiveComparison.ToLower(text[i])) * Hash.FnvPrime);
hashCode = unchecked((hashCode ^ CaseInsensitiveComparison.ToLower(data[i])) * Hash.FnvPrime);
}

return hashCode;
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
const Microsoft.CodeAnalysis.WellKnownDiagnosticTags.CompilationEnd = "CompilationEnd" -> string
static Microsoft.CodeAnalysis.CaseInsensitiveComparison.Compare(System.ReadOnlySpan<char> left, System.ReadOnlySpan<char> right) -> int
static Microsoft.CodeAnalysis.CaseInsensitiveComparison.Equals(System.ReadOnlySpan<char> left, System.ReadOnlySpan<char> right) -> bool
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ private static IEnumerable<int> FindNodeIndices(
{
// find any node that matches case-insensitively
var startingPosition = BinarySearch(nodes, name);
var nameSlice = new StringSlice(name);
var nameSlice = name.AsMemory();

if (startingPosition != -1)
{
Expand Down Expand Up @@ -281,10 +281,10 @@ private static IEnumerable<int> FindNodeIndices(
}
}

private static StringSlice GetNameSlice(
private static ReadOnlyMemory<char> GetNameSlice(
ImmutableArray<Node> nodes, int nodeIndex)
{
return new StringSlice(nodes[nodeIndex].Name);
return nodes[nodeIndex].Name.AsMemory();
}

private int BinarySearch(string name)
Expand All @@ -295,7 +295,7 @@ private int BinarySearch(string name)
/// </summary>
private static int BinarySearch(ImmutableArray<Node> nodes, string name)
{
var nameSlice = new StringSlice(name);
var nameSlice = name.AsMemory();
var max = nodes.Length - 1;
var min = 0;

Expand Down Expand Up @@ -357,7 +357,7 @@ private static Task<SpellChecker> CreateSpellCheckerAsync(
Checksum checksum, ImmutableArray<Node> sortedNodes)
{
return Task.FromResult(new SpellChecker(
checksum, sortedNodes.Select(n => new StringSlice(n.Name))));
checksum, sortedNodes.Select(n => n.Name.AsMemory())));
}

private static void SortNodes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ internal static SymbolTreeInfo ReadSymbolTreeInfo(
ObjectReader reader, Checksum checksum)
{
return TryReadSymbolTreeInfo(reader, checksum,
nodes => Task.FromResult(new SpellChecker(checksum, nodes.Select(n => new StringSlice(n.Name)))));
nodes => Task.FromResult(new SpellChecker(checksum, nodes.Select(n => n.Name.AsMemory()))));
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Workspaces/Core/Portable/Utilities/SpellChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public SpellChecker(Checksum checksum, BKTree bKTree)
_bkTree = bKTree;
}

public SpellChecker(Checksum checksum, IEnumerable<StringSlice> corpus)
public SpellChecker(Checksum checksum, IEnumerable<ReadOnlyMemory<char>> corpus)
: this(checksum, BKTree.Create(corpus))
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ private class Builder
private readonly Edge[] _compactEdges;
private readonly BuilderNode[] _builderNodes;

public Builder(IEnumerable<StringSlice> values)
public Builder(IEnumerable<ReadOnlyMemory<char>> values)
{
// TODO(cyrusn): Properly handle unicode normalization here.
var distinctValues = values.Where(v => v.Length > 0).Distinct(StringSliceComparer.OrdinalIgnoreCase).ToArray();
Expand All @@ -109,7 +109,7 @@ public Builder(IEnumerable<StringSlice> values)
var value = distinctValues[i];
_wordSpans[i] = new TextSpan(characterIndex, value.Length);

foreach (var ch in value)
foreach (var ch in value.Span)
{
_concatenatedLowerCaseWords[characterIndex] = CaseInsensitiveComparison.ToLower(ch);
characterIndex++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ private BKTree(char[] concatenatedLowerCaseWords, ImmutableArray<Node> nodes, Im
}

public static BKTree Create(params string[] values)
=> Create(values.Select(v => new StringSlice(v)));
=> Create(values.Select(v => v.AsMemory()));

public static BKTree Create(IEnumerable<StringSlice> values)
public static BKTree Create(IEnumerable<ReadOnlyMemory<char>> values)
=> new Builder(values).Create();

public IList<string> Find(string value, int? threshold = null)
Expand Down
Loading

0 comments on commit 7e6b174

Please sign in to comment.