Skip to content

Commit 50008c0

Browse files
committed
Optimized CreateUniqueParamNameFromIndex method
1 parent 9433781 commit 50008c0

File tree

3 files changed

+52
-11
lines changed

3 files changed

+52
-11
lines changed

Microsoft.Toolkit.Uwp.UI.Animations/Expressions/ExpressionNodes/ExpressionNode.cs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Numerics;
8+
using System.Runtime.CompilerServices;
89
using Windows.UI;
910
using Windows.UI.Composition;
1011

@@ -283,10 +284,10 @@ internal void EnsureReferenceInfo()
283284

284285
// Create a map to store the generated paramNames for each CompObj
285286
_compObjToParamNameMap = new Dictionary<CompositionObject, string>();
286-
var paramCount = 0;
287+
var paramCount = 0u;
287288
foreach (var compObj in compObjects)
288289
{
289-
string paramName = UniqueParamNameFromIndex(paramCount++); // Guid.NewGuid().ToUppercaseAsciiLetters();
290+
string paramName = CreateUniqueParamNameFromIndex(paramCount++);
290291

291292
_compObjToParamNameMap.Add(compObj, paramName);
292293
}
@@ -314,20 +315,35 @@ internal void EnsureReferenceInfo()
314315
}
315316
}
316317

317-
// Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, AB...
318-
string UniqueParamNameFromIndex(int i)
318+
// Generates Excel-column-like identifiers, e.g. A, B, ..., Z, AA, BA...
319+
// This implementation aggregates characters in reverse order to avoid having to
320+
// precompute the exact number of characters in the resulting string. This is not
321+
// important in this context as the only critical property to maintain is to have
322+
// a unique mapping to each input value to the resulting sequence of letters.
323+
[SkipLocalsInit]
324+
static unsafe string CreateUniqueParamNameFromIndex(uint i)
319325
{
320-
var alphabetLength = 'Z' - 'A' + 1;
321-
var paramName = ((char)('A' + (i % alphabetLength))).ToString();
326+
const int alphabetLength = 'Z' - 'A' + 1;
322327

323-
while (i / alphabetLength > 0)
328+
// The total length of the resulting sequence is guaranteed to always
329+
// be less than 8, given that log26(4294967295) ≈ 6.8. In this case we
330+
// are just allocating the immediate next power of two following that.
331+
// Note: this is using a char* buffer instead of Span<char> as the latter
332+
// is not referenced here, and we don't want to pull in an extra package.
333+
char* characters = stackalloc char[8];
334+
335+
characters[0] = (char)('A' + (i % alphabetLength));
336+
337+
int totalCharacters = 1;
338+
339+
while ((i /= alphabetLength) > 0)
324340
{
325-
i = (i / alphabetLength) - 1;
326-
var nextCharacter = (char)('A' + (i % alphabetLength));
327-
paramName = nextCharacter + paramName;
341+
i--;
342+
343+
characters[totalCharacters++] = (char)('A' + (i % alphabetLength));
328344
}
329345

330-
return paramName;
346+
return new string(characters, 0, totalCharacters);
331347
}
332348
}
333349

Microsoft.Toolkit.Uwp.UI.Animations/Microsoft.Toolkit.Uwp.UI.Animations.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>uap10.0.17763</TargetFrameworks>
5+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
56
</PropertyGroup>
67

78
<PropertyGroup>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace System.Runtime.CompilerServices
6+
{
7+
/// <summary>
8+
/// Used to indicate to the compiler that the <c>.locals init</c> flag should not be set in method headers.
9+
/// </summary>
10+
/// <remarks>Internal copy from the BCL attribute.</remarks>
11+
[AttributeUsage(
12+
AttributeTargets.Module |
13+
AttributeTargets.Class |
14+
AttributeTargets.Struct |
15+
AttributeTargets.Interface |
16+
AttributeTargets.Constructor |
17+
AttributeTargets.Method |
18+
AttributeTargets.Property |
19+
AttributeTargets.Event,
20+
Inherited = false)]
21+
internal sealed class SkipLocalsInitAttribute : Attribute
22+
{
23+
}
24+
}

0 commit comments

Comments
 (0)