1
0
mirror of https://github.com/chylex/.NET-Community-Toolkit.git synced 2025-04-10 11:15:45 +02:00

Switched Bucket type to a struct

This commit is contained in:
Sergio Pedri 2020-07-08 00:37:04 +02:00
parent 5f858d4d74
commit aabd6d2fd6

View File

@ -102,12 +102,9 @@ public string GetOrAdd(ReadOnlySpan<char> span)
int bucketIndex = span.Length % NumberOfBuckets;
Bucket bucket = this.buckets.DangerousGetReferenceAt(bucketIndex);
ref Bucket bucket = ref this.buckets.DangerousGetReferenceAt(bucketIndex);
lock (bucket)
{
return bucket.GetOrAdd(span);
}
return bucket.GetOrAdd(span);
}
/// <summary>
@ -127,12 +124,9 @@ public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? valu
int bucketIndex = span.Length % NumberOfBuckets;
Bucket bucket = this.buckets.DangerousGetReferenceAt(bucketIndex);
ref Bucket bucket = ref this.buckets.DangerousGetReferenceAt(bucketIndex);
lock (bucket)
{
return bucket.TryGet(span, out value);
}
return bucket.TryGet(span, out value);
}
/// <summary>
@ -140,19 +134,16 @@ public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? valu
/// </summary>
public void Reset()
{
foreach (Bucket bucket in this.buckets)
foreach (ref Bucket bucket in this.buckets.AsSpan())
{
lock (bucket)
{
bucket.Reset();
}
bucket.Reset();
}
}
/// <summary>
/// A configurable bucket containing a group of cached <see cref="string"/> instances.
/// </summary>
private sealed class Bucket
private struct Bucket
{
/// <summary>
/// A bitmask used to avoid branches when computing the absolute value of an <see cref="int"/>.
@ -165,18 +156,25 @@ private sealed class Bucket
/// </summary>
private readonly int entriesPerBucket;
/// <summary>
/// A dummy <see cref="object"/> used for synchronization.
/// </summary>
private readonly object dummy;
/// <summary>
/// The array of entries currently in use.
/// </summary>
private string?[]? entries;
/// <summary>
/// Initializes a new instance of the <see cref="Bucket"/> class.
/// Initializes a new instance of the <see cref="Bucket"/> struct.
/// </summary>
/// <param name="entriesPerBucket">The number of entries being used in the current instance.</param>
public Bucket(int entriesPerBucket)
{
this.entriesPerBucket = entriesPerBucket;
this.dummy = new object();
this.entries = null;
}
/// <summary>
@ -186,50 +184,12 @@ public Bucket(int entriesPerBucket)
/// <returns>A <see cref="string"/> instance with the contents of <paramref name="span"/>, cached if possible.</returns>
public string GetOrAdd(ReadOnlySpan<char> span)
{
ref string?[]? entries = ref this.entries;
entries ??= new string[entriesPerBucket];
int entryIndex =
#if NETSTANDARD1_4
(span.GetDjb2HashCode() & SignMask) % entriesPerBucket;
#else
(HashCode<char>.Combine(span) & SignMask) % entriesPerBucket;
#endif
ref string? entry = ref entries.DangerousGetReferenceAt(entryIndex);
if (!(entry is null) &&
entry.AsSpan().SequenceEqual(span))
lock (this.dummy)
{
return entry;
}
ref string?[]? entries = ref this.entries;
#if SPAN_RUNTIME_SUPPORT
return entry = new string(span);
#else
unsafe
{
fixed (char* c = span)
{
return entry = new string(c, 0, span.Length);
}
}
#endif
}
entries ??= new string[entriesPerBucket];
/// <summary>
/// Implements <see cref="StringPool.TryGet"/> for the current <see cref="Bucket"/> instance.
/// </summary>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> with the contents to use.</param>
/// <param name="value">The resulting cached <see cref="string"/> instance, if present</param>
/// <returns>Whether or not the target <see cref="string"/> instance was found.</returns>
public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? value)
{
ref string?[]? entries = ref this.entries;
if (!(entries is null))
{
int entryIndex =
#if NETSTANDARD1_4
(span.GetDjb2HashCode() & SignMask) % entriesPerBucket;
@ -242,15 +202,59 @@ public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? valu
if (!(entry is null) &&
entry.AsSpan().SequenceEqual(span))
{
value = entry;
return true;
return entry;
}
#if SPAN_RUNTIME_SUPPORT
return entry = new string(span);
#else
unsafe
{
fixed (char* c = span)
{
return entry = new string(c, 0, span.Length);
}
}
#endif
}
}
value = null;
/// <summary>
/// Implements <see cref="StringPool.TryGet"/> for the current <see cref="Bucket"/> instance.
/// </summary>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> with the contents to use.</param>
/// <param name="value">The resulting cached <see cref="string"/> instance, if present</param>
/// <returns>Whether or not the target <see cref="string"/> instance was found.</returns>
public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? value)
{
lock (this.dummy)
{
ref string?[]? entries = ref this.entries;
return false;
if (!(entries is null))
{
int entryIndex =
#if NETSTANDARD1_4
(span.GetDjb2HashCode() & SignMask) % entriesPerBucket;
#else
(HashCode<char>.Combine(span) & SignMask) % entriesPerBucket;
#endif
ref string? entry = ref entries.DangerousGetReferenceAt(entryIndex);
if (!(entry is null) &&
entry.AsSpan().SequenceEqual(span))
{
value = entry;
return true;
}
}
value = null;
return false;
}
}
/// <summary>
@ -258,7 +262,10 @@ public bool TryGet(ReadOnlySpan<char> span, [NotNullWhen(true)] out string? valu
/// </summary>
public void Reset()
{
this.entries = null;
lock (this.dummy)
{
this.entries = null;
}
}
}