Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions src/Compilers/Core/Portable/InternalUtilities/Hash.cs
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,7 @@ internal static int GetFNVHashCode(string text, int start, int length)
=> GetFNVHashCode(text.AsSpan(start, length));

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

internal static int GetCaseInsensitiveFNVHashCode(ReadOnlySpan<char> data)
{
Expand Down Expand Up @@ -375,14 +373,7 @@ internal static int GetFNVHashCode(char ch)
/// <param name="text">The string to combine</param>
/// <returns>The result of combining <paramref name="hashCode"/> with <paramref name="text"/> using the FNV-1a algorithm</returns>
internal static int CombineFNVHash(int hashCode, string text)
{
foreach (char ch in text)
{
hashCode = unchecked((hashCode ^ ch) * Hash.FnvPrime);
}

return hashCode;
}
=> CombineFNVHash(hashCode, text.AsSpan());

/// <summary>
/// Combine a char with an existing FNV-1a hash code
Expand Down
216 changes: 11 additions & 205 deletions src/Compilers/Core/Portable/InternalUtilities/StringTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
using System;
using System.Text;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Collections;

#if DEBUG
using System.Diagnostics;
#endif
using Microsoft.CodeAnalysis.PooledObjects;

namespace Roslyn.Utilities
{
Expand Down Expand Up @@ -158,72 +154,10 @@ internal string Add(ReadOnlySpan<char> chars)
}

internal string Add(string chars, int start, int len)
{
var hashCode = Hash.GetFNVHashCode(chars, start, len);

// capture array to avoid extra range checks
var arr = _localTable;
var idx = LocalIdxFromHash(hashCode);

var text = arr[idx].Text;

if (text != null && arr[idx].HashCode == hashCode)
{
var result = arr[idx].Text;
if (StringTable.TextEquals(result, chars, start, len))
{
return result;
}
}

string? shared = FindSharedEntry(chars, start, len, hashCode);
if (shared != null)
{
// PERF: the following code does element-wise assignment of a struct
// because current JIT produces better code compared to
// arr[idx] = new Entry(...)
arr[idx].HashCode = hashCode;
arr[idx].Text = shared;

return shared;
}

return AddItem(chars, start, len, hashCode);
}
=> Add(chars.AsSpan(start, len));

internal string Add(char chars)
{
var hashCode = Hash.GetFNVHashCode(chars);

// capture array to avoid extra range checks
var arr = _localTable;
var idx = LocalIdxFromHash(hashCode);

var text = arr[idx].Text;

if (text != null)
{
var result = arr[idx].Text;
if (text.Length == 1 && text[0] == chars)
{
return result;
}
}

string? shared = FindSharedEntry(chars, hashCode);
if (shared != null)
{
// PERF: the following code does element-wise assignment of a struct
// because current JIT produces better code compared to
// arr[idx] = new Entry(...)
arr[idx].HashCode = hashCode;
arr[idx].Text = shared;

return shared;
}

return AddItem(chars, hashCode);
}
=> Add([chars]);

internal string Add(StringBuilder chars)
{
Expand Down Expand Up @@ -260,39 +194,7 @@ internal string Add(StringBuilder chars)
}

internal string Add(string chars)
{
var hashCode = Hash.GetFNVHashCode(chars);

// capture array to avoid extra range checks
var arr = _localTable;
var idx = LocalIdxFromHash(hashCode);

var text = arr[idx].Text;

if (text != null && arr[idx].HashCode == hashCode)
{
var result = arr[idx].Text;
if (result == chars)
{
return result;
}
}

string? shared = FindSharedEntry(chars, hashCode);
if (shared != null)
{
// PERF: the following code does element-wise assignment of a struct
// because current JIT produces better code compared to
// arr[idx] = new Entry(...)
arr[idx].HashCode = hashCode;
arr[idx].Text = shared;

return shared;
}

AddCore(chars, hashCode);
return chars;
}
=> Add(chars.AsSpan());

private static string? FindSharedEntry(ReadOnlySpan<char> chars, int hashCode)
{
Expand Down Expand Up @@ -330,39 +232,7 @@ internal string Add(string chars)
}

private static string? FindSharedEntry(string chars, int start, int len, int hashCode)
{
var arr = s_sharedTable;
int idx = SharedIdxFromHash(hashCode);

string? e = null;
// we use quadratic probing here
// bucket positions are (n^2 + n)/2 relative to the masked hashcode
for (int i = 1; i < SharedBucketSize + 1; i++)
{
e = arr[idx].Text;
int hash = arr[idx].HashCode;

if (e != null)
{
if (hash == hashCode && TextEquals(e, chars, start, len))
{
break;
}

// this is not e we are looking for
e = null;
}
else
{
// once we see unfilled entry, the rest of the bucket will be empty
break;
}

idx = (idx + i) & SharedSizeMask;
}

return e;
}
=> FindSharedEntry(chars.AsSpan(start, len), hashCode);

private static string? FindSharedEntryASCII(int hashCode, ReadOnlySpan<byte> asciiChars)
{
Expand Down Expand Up @@ -400,38 +270,7 @@ internal string Add(string chars)
}

private static string? FindSharedEntry(char chars, int hashCode)
{
var arr = s_sharedTable;
int idx = SharedIdxFromHash(hashCode);

string? e = null;
// we use quadratic probing here
// bucket positions are (n^2 + n)/2 relative to the masked hashcode
for (int i = 1; i < SharedBucketSize + 1; i++)
{
e = arr[idx].Text;

if (e != null)
{
if (e.Length == 1 && e[0] == chars)
{
break;
}

// this is not e we are looking for
e = null;
}
else
{
// once we see unfilled entry, the rest of the bucket will be empty
break;
}

idx = (idx + i) & SharedSizeMask;
}

return e;
}
=> FindSharedEntry([chars], hashCode);

private static string? FindSharedEntry(StringBuilder chars, int hashCode)
{
Expand Down Expand Up @@ -469,39 +308,7 @@ internal string Add(string chars)
}

private static string? FindSharedEntry(string chars, int hashCode)
{
var arr = s_sharedTable;
int idx = SharedIdxFromHash(hashCode);

string? e = null;
// we use quadratic probing here
// bucket positions are (n^2 + n)/2 relative to the masked hashcode
for (int i = 1; i < SharedBucketSize + 1; i++)
{
e = arr[idx].Text;
int hash = arr[idx].HashCode;

if (e != null)
{
if (hash == hashCode && e == chars)
{
break;
}

// this is not e we are looking for
e = null;
}
else
{
// once we see unfilled entry, the rest of the bucket will be empty
break;
}

idx = (idx + i) & SharedSizeMask;
}

return e;
}
=> FindSharedEntry(chars.AsSpan(), hashCode);

private string AddItem(ReadOnlySpan<char> chars, int hashCode)
{
Expand All @@ -512,17 +319,16 @@ private string AddItem(ReadOnlySpan<char> chars, int hashCode)

private string AddItem(string chars, int start, int len, int hashCode)
{
// Don't defer to ReadOnlySpan<char> here, as it would cause an extra allocation
// in the case where start/len exactly match the full span of chars.

var text = chars.Substring(start, len);
AddCore(text, hashCode);
return text;
}

private string AddItem(char chars, int hashCode)
{
var text = new String(chars, 1);
AddCore(text, hashCode);
return text;
}
=> AddItem([chars], hashCode);

private string AddItem(StringBuilder chars, int hashCode)
{
Expand Down