Skip to content

Commit

Permalink
Merge pull request #4112 from arcadiogarcia/user/arcadiog/shorterExpr…
Browse files Browse the repository at this point in the history
…essionStrings

Generate shorter expression animation strings from ExpressionNode
  • Loading branch information
michael-hawker committed Aug 10, 2021
2 parents ee93385 + 1918c0d commit 18b86b7
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 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,9 +284,10 @@ internal void EnsureReferenceInfo()

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

_compObjToParamNameMap.Add(compObj, paramName);
}
Expand All @@ -312,6 +314,37 @@ internal void EnsureReferenceInfo()
refNode.ParamName = paramName;
}
}

// 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)
{
const int alphabetLength = 'Z' - 'A' + 1;

// 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--;

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

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

/// <summary>
Expand Down Expand Up @@ -670,4 +703,4 @@ public ReferenceInfo(string paramName, CompositionObject compObj)
/// <value>The subchannels.</value>
protected internal string[] Subchannels { get; set; }
}
}
}
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 18b86b7

Please sign in to comment.