Skip to content

Commit dd42509

Browse files
Clean up
1 parent a8adf78 commit dd42509

File tree

5 files changed

+78
-310
lines changed

5 files changed

+78
-310
lines changed

src/Components/Components/src/Rendering/MultipleAttributesDictionary.cs

Lines changed: 0 additions & 152 deletions
This file was deleted.

src/Components/Components/src/Rendering/RenderTreeBuilder.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7-
using System.Runtime.CompilerServices;
87
using Microsoft.AspNetCore.Components.RenderTree;
98

109
namespace Microsoft.AspNetCore.Components.Rendering
@@ -27,7 +26,7 @@ public sealed class RenderTreeBuilder : IDisposable
2726
private readonly Stack<int> _openElementIndices = new Stack<int>();
2827
private RenderTreeFrameType? _lastNonAttributeFrameType;
2928
private bool _hasSeenAddMultipleAttributes;
30-
private MultipleAttributesDictionary? _seenAttributeNames;
29+
private Dictionary<string, int>? _seenAttributeNames;
3130

3231
/// <summary>
3332
/// The reserved parameter name used for supplying child content.
@@ -707,21 +706,22 @@ internal void ProcessDuplicateAttributes(int first)
707706
}
708707

709708
// Now that we've found the last attribute, we can iterate backwards and process duplicates.
710-
var seenAttributeNames = (_seenAttributeNames ??= new MultipleAttributesDictionary());
709+
var seenAttributeNames = (_seenAttributeNames ??= new Dictionary<string, int>(SimplifiedStringHashComparer.Instance));
711710
for (var i = last; i >= first; i--)
712711
{
713712
ref var frame = ref buffer[i];
714713
Debug.Assert(frame.FrameTypeField == RenderTreeFrameType.Attribute, $"Frame type is {frame.FrameTypeField} at {i}");
715714

716-
if (!seenAttributeNames.TryAdd(frame.AttributeNameField, i, out var index))
715+
if (!seenAttributeNames.TryAdd(frame.AttributeNameField, i))
717716
{
717+
var index = seenAttributeNames[frame.AttributeNameField];
718718
if (index < i)
719719
{
720720
// This attribute is overriding a "silent frame" where we didn't create a frame for an AddAttribute call.
721721
// This is the case for a null event handler, or bool false value.
722722
//
723723
// We need to update our tracking, in case the attribute appeared 3 or more times.
724-
seenAttributeNames.Replace(frame.AttributeNameField, i);
724+
seenAttributeNames[frame.AttributeNameField] = i;
725725
}
726726
else if (index > i)
727727
{
@@ -778,8 +778,8 @@ internal void TrackAttributeName(string name)
778778
return;
779779
}
780780

781-
var seenAttributeNames = (_seenAttributeNames ??= new MultipleAttributesDictionary());
782-
seenAttributeNames.TryAdd(name, _entries.Count, out _); // See comment in ProcessAttributes for why this is OK.
781+
var seenAttributeNames = (_seenAttributeNames ??= new Dictionary<string, int>(SimplifiedStringHashComparer.Instance));
782+
seenAttributeNames[name] = _entries.Count; // See comment in ProcessAttributes for why this is OK.
783783
}
784784

785785
void IDisposable.Dispose()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.AspNetCore.Components.Rendering
8+
{
9+
/// <summary>
10+
/// This comparer is optimized for use with dictionaries where the great majority of insertions/lookups
11+
/// don't match existing entries. For example, when building a dictionary of almost entirely unique keys.
12+
/// It's faster than the normal string comparer in this case because it doesn't use string.GetHashCode,
13+
/// and hence doesn't have to consider every character in the string.
14+
///
15+
/// This primary scenario is <see cref="RenderTreeBuilder.ProcessDuplicateAttributes(int)"/>, which needs
16+
/// to detect when one attribute is overriding another, but in the vast majority of cases attributes don't
17+
/// actually override each other.
18+
/// </summary>
19+
internal class SimplifiedStringHashComparer : IEqualityComparer<string>
20+
{
21+
public readonly static SimplifiedStringHashComparer Instance = new SimplifiedStringHashComparer();
22+
23+
public bool Equals(string? x, string? y)
24+
{
25+
return string.Equals(x, y, StringComparison.OrdinalIgnoreCase);
26+
}
27+
28+
public int GetHashCode(string key)
29+
{
30+
var keyLength = key.Length;
31+
if (keyLength > 0)
32+
{
33+
// Consider just the length and middle and last characters.
34+
// This will produce a distinct result for a sufficiently large
35+
// proportion of attribute names.
36+
return unchecked(
37+
char.ToLowerInvariant(key[keyLength - 1])
38+
+ 31 * char.ToLowerInvariant(key[keyLength / 2])
39+
+ 961 * keyLength);
40+
}
41+
else
42+
{
43+
return default;
44+
}
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)