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 9433781 commit 50008c0
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 @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFrameworks>uap10.0.17763</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<PropertyGroup>
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 50008c0

Please sign in to comment.