Skip to content

Commit

Permalink
Optimized CreateUniqueParamNameFromIndex method
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Jul 22, 2021
1 parent 0800b41 commit c5fc961
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Windows.UI;
using Windows.UI.Composition;

Expand Down Expand Up @@ -283,10 +284,10 @@ internal void EnsureReferenceInfo()

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

_compObjToParamNameMap.Add(compObj, paramName);
}
Expand Down Expand Up @@ -314,20 +315,35 @@ internal void EnsureReferenceInfo()
}
}

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

while (i / alphabetLength > 0)
// The total length of the resulting sequence is guaranteed to always
// be less than 8, given that log26(4294967295) ≈ 6.8. In this case we
// are just allocating the immediate next power of two following that.
// Note: this is using a char* buffer instead of Span<char> as the latter
// is not referenced here, and we don't want to pull in an extra package.
char* characters = stackalloc char[8];

characters[0] = (char)('A' + (i % alphabetLength));

int totalCharacters = 1;

while ((i /= alphabetLength) > 0)
{
i = (i / alphabetLength) - 1;
var nextCharacter = (char)('A' + (i % alphabetLength));
paramName = nextCharacter + paramName;
i--;

characters[totalCharacters++] = (char)('A' + (i % alphabetLength));
}

return paramName;
return new string(characters, 0, totalCharacters);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFrameworks>uap10.0.17763</TargetFrameworks>
<Title>Windows Community Toolkit Animations</Title>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>
This library provides helpers and extensions on top of Windows Composition and XAML storyboards. It is part of the Windows Community Toolkit.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Runtime.CompilerServices
{
/// <summary>
/// Used to indicate to the compiler that the <c>.locals init</c> flag should not be set in method headers.
/// </summary>
/// <remarks>Internal copy from the BCL attribute.</remarks>
[AttributeUsage(
AttributeTargets.Module |
AttributeTargets.Class |
AttributeTargets.Struct |
AttributeTargets.Interface |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Property |
AttributeTargets.Event,
Inherited = false)]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}

0 comments on commit c5fc961

Please sign in to comment.