Skip to content

Commit ac4aac0

Browse files
authored
Optimize alphanumeric encoder and galois field constants (#684)
* Enhance alphanumeric and numeric data segment encoding with new bit length calculations and writing methods * Refactor WriteTo method parameters for clarity and consistency in AlphanumericDataSegment * Refactor GetBitLength methods for parameter clarity and consistency in AlphanumericEncoder * Refactor GetBitLength and WriteToBitArray methods for parameter consistency and clarity * Refactor WriteToBitArray method parameters for clarity and consistency * Refactor PlainTextToBinaryNumeric method to handle remaining digits more efficiently * Refactor AlphanumericEncoder to simplify character encoding logic and improve performance * update * Apply suggestion from @Shane32 * Eliminate reflection in tests * Update * Update GaloisField too * update * Update * update * Add strong naming support for QRCoder and update test project configuration * Update
1 parent 359ad78 commit ac4aac0

File tree

11 files changed

+96
-84
lines changed

11 files changed

+96
-84
lines changed

Directory.Build.targets

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</PropertyGroup>
88

99
<!-- Strong name signing for Release builds of packable projects -->
10-
<PropertyGroup Condition="'$(Configuration)' == 'Release' AND '$(IsPackable)' == 'true'">
10+
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
1111
<SignAssembly>true</SignAssembly>
1212
<DelaySign>false</DelaySign>
1313
</PropertyGroup>

QRCoder/PayloadGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public static partial class PayloadGenerator
1414
/// </summary>
1515
/// <param name="iban">The IBAN to validate.</param>
1616
/// <returns>True if the IBAN is valid; otherwise, false.</returns>
17-
private static bool IsValidIban(string iban)
17+
internal static bool IsValidIban(string iban)
1818
{
1919
//Clean IBAN
2020
var ibanCleared = iban.ToUpperInvariant().Replace(" ", "").Replace("-", "");
@@ -48,7 +48,7 @@ private static bool IsValidIban(string iban)
4848
/// </summary>
4949
/// <param name="iban">The QR IBAN to validate.</param>
5050
/// <returns>True if the QR IBAN is valid; otherwise, false.</returns>
51-
private static bool IsValidQRIban(string iban)
51+
internal static bool IsValidQRIban(string iban)
5252
{
5353
var foundQrIid = false;
5454
try

QRCoder/QRCodeGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ private static void ConvertToDecNotationInPlace(Polynom poly)
750750
/// Determines the most efficient encoding mode for the given plain text based on its character content
751751
/// and a flag indicating whether to force UTF-8 encoding.
752752
/// </summary>
753-
private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8)
753+
internal static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8)
754754
{
755755
if (forceUtf8)
756756
return EncodingMode.Byte;
@@ -760,7 +760,7 @@ private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forc
760760
if (IsInRange(c, '0', '9'))
761761
continue; // numeric - char.IsDigit() for Latin1
762762
result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric
763-
if (AlphanumericEncoder.CanEncodeNonDigit(c))
763+
if (AlphanumericEncoder.CanEncode(c))
764764
continue; // alphanumeric
765765
return EncodingMode.Byte; // not numeric or alphanumeric, assume byte
766766
}

QRCoder/QRCodeGenerator/AlphanumericEncoder.cs

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,36 @@ public partial class QRCodeGenerator
55
/// <summary>
66
/// Encodes alphanumeric characters (<c>0–9</c>, <c>A–Z</c> (uppercase), space, <c>$</c>, <c>%</c>, <c>*</c>, <c>+</c>, <c>-</c>, period, <c>/</c>, colon) into a binary format suitable for QR codes.
77
/// </summary>
8-
private static class AlphanumericEncoder
8+
internal static class AlphanumericEncoder
99
{
10-
private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' };
10+
#if HAS_SPAN
11+
// With C# 7.3 and later, this byte array is inlined into the assembly's read-only data section, improving performance and reducing memory usage.
12+
// See: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/
13+
internal static ReadOnlySpan<byte> _map =>
14+
#else
15+
internal static readonly byte[] _map =
16+
#endif
17+
[
18+
// 0..31
19+
255, 255, 255, 255, 255, 255, 255, 255,
20+
255, 255, 255, 255, 255, 255, 255, 255,
21+
255, 255, 255, 255, 255, 255, 255, 255,
22+
255, 255, 255, 255, 255, 255, 255, 255,
23+
// 32..47 (space, ! " # $ % & ' ( ) * + , - . /)
24+
36, 255, 255, 255, 37, 38, 255, 255, 255, 255, 39, 40, 255, 41, 42, 43,
25+
// 48..57 (0..9)
26+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
27+
// 58..64 (: ; < = > ? @)
28+
44, 255, 255, 255, 255, 255, 255,
29+
// 65..90 (A..Z)
30+
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
31+
// (we don't index > 90)
32+
];
1133

1234
/// <summary>
13-
/// A dictionary mapping alphanumeric characters to their respective positions used in QR code encoding.
14-
/// This includes digits 0-9, uppercase letters A-Z, and some special characters.
35+
/// Checks if a character is present in the alphanumeric encoding table.
1536
/// </summary>
16-
private static readonly Dictionary<char, int> _alphanumEncDict = CreateAlphanumEncDict(_alphanumEncTable);
17-
18-
/// <summary>
19-
/// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding.
20-
/// This includes digits 0-9, uppercase letters A-Z, and some special characters.
21-
/// </summary>
22-
/// <returns>A dictionary mapping each supported alphanumeric character to its corresponding value.</returns>
23-
private static Dictionary<char, int> CreateAlphanumEncDict(char[] alphanumEncTable)
24-
{
25-
var localAlphanumEncDict = new Dictionary<char, int>(45);
26-
// Add 0-9
27-
for (char c = '0'; c <= '9'; c++)
28-
localAlphanumEncDict.Add(c, c - '0');
29-
// Add uppercase alphabetic characters.
30-
for (char c = 'A'; c <= 'Z'; c++)
31-
localAlphanumEncDict.Add(c, localAlphanumEncDict.Count);
32-
// Add special characters from a predefined table.
33-
for (int i = 0; i < _alphanumEncTable.Length; i++)
34-
localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count);
35-
return localAlphanumEncDict;
36-
}
37-
38-
/// <summary>
39-
/// Checks if a non-digit character is present in the alphanumeric encoding table.
40-
/// </summary>
41-
public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0;
37+
public static bool CanEncode(char c) => c <= 90 && _map[c] != 255;
4238

4339
/// <summary>
4440
/// Calculates the bit length required to encode alphanumeric text of a given length.
@@ -81,7 +77,7 @@ public static int WriteToBitArray(string plainText, int index, int count, BitArr
8177
while (count >= 2)
8278
{
8379
// Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating.
84-
var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]];
80+
var dec = _map[plainText[index++]] * 45 + _map[plainText[index++]];
8581
// Convert the number to binary and store it in the BitArray.
8682
codeIndex = DecToBin(dec, 11, codeText, codeIndex);
8783
count -= 2;
@@ -90,7 +86,7 @@ public static int WriteToBitArray(string plainText, int index, int count, BitArr
9086
// Handle the last character if the length is odd.
9187
if (count > 0)
9288
{
93-
codeIndex = DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex);
89+
codeIndex = DecToBin(_map[plainText[index]], 6, codeText, codeIndex);
9490
}
9591

9692
return codeIndex;

QRCoder/QRCodeGenerator/EncodingMode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public partial class QRCodeGenerator
55
/// <summary>
66
/// Specifies the encoding modes for the characters in a QR code.
77
/// </summary>
8-
private enum EncodingMode
8+
internal enum EncodingMode
99
{
1010
/// <summary>
1111
/// Numeric encoding mode, which is used to encode numeric data (digits 0-9).

QRCoder/QRCodeGenerator/GaloisField.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,21 @@ public partial class QRCodeGenerator
1313
/// polynomial and used for efficient encoding and decoding operations.
1414
/// </para>
1515
/// </summary>
16-
private static class GaloisField
16+
internal static class GaloisField
1717
{
18-
private static readonly int[] _galoisFieldByExponentAlpha = { 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1 };
19-
private static readonly int[] _galoisFieldByIntegerValue = { 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175 };
18+
#if HAS_SPAN
19+
internal static ReadOnlySpan<byte> _galoisFieldByExponentAlpha =>
20+
#else
21+
internal static readonly byte[] _galoisFieldByExponentAlpha =
22+
#endif
23+
[1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142, 1];
24+
25+
#if HAS_SPAN
26+
internal static ReadOnlySpan<byte> _galoisFieldByIntegerValue =>
27+
#else
28+
internal static readonly byte[] _galoisFieldByIntegerValue =
29+
#endif
30+
[0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175];
2031

2132
/// <summary>
2233
/// Retrieves the integer value from the Galois field that corresponds to a given exponent.

QRCoder/QRCodeGenerator/OptimizedDataSegment.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ private static EncodingMode SelectInitialMode(string text, int startPos, int ver
175175
if (numericCount < threshold)
176176
{
177177
var nextPos = startPos + numericCount;
178-
if (nextPos < text.Length && !IsAlphanumericNonDigit(text[nextPos]))
178+
if (nextPos < text.Length && !IsAlphanumeric(text[nextPos]))
179179
return EncodingMode.Byte;
180180
}
181181
// ELSE IF there are less than [7-9] characters followed by data from the exclusive subset of the Alphanumeric character set
@@ -184,7 +184,7 @@ private static EncodingMode SelectInitialMode(string text, int startPos, int ver
184184
if (numericCount < threshold)
185185
{
186186
var nextPos = startPos + numericCount;
187-
if (nextPos < text.Length && IsAlphanumericNonDigit(text[nextPos]))
187+
if (nextPos < text.Length && IsAlphanumeric(text[nextPos]))
188188
return EncodingMode.Alphanumeric;
189189
}
190190
return EncodingMode.Numeric;
@@ -329,9 +329,6 @@ private static int CountConsecutive(string text, int startPos, Func<char, bool>
329329
private static bool IsNumeric(char c) => IsInRange(c, '0', '9');
330330

331331
// Checks if a character is alphanumeric (can be encoded in alphanumeric mode).
332-
private static bool IsAlphanumeric(char c) => IsNumeric(c) || IsAlphanumericNonDigit(c);
333-
334-
// Checks if a non-digit character can be encoded in alphanumeric mode.
335-
private static bool IsAlphanumericNonDigit(char c) => AlphanumericEncoder.CanEncodeNonDigit(c);
332+
private static bool IsAlphanumeric(char c) => AlphanumericEncoder.CanEncode(c);
336333
}
337334
}

QRCoder/QRCoder.csproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
<Guid>e668b98b-83bb-4e60-b33c-4fd5ed9c0156</Guid>
1717
</PropertyGroup>
1818

19+
<ItemGroup Condition="'$(Configuration)' == 'Release'">
20+
<InternalsVisibleTo Include="QRCoderTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010071e74d3f6dcbba7726fcbb7ad05a2dfa3b8e13f9bf2938c05de266a83a7f3c95201b7cdc9e1f88842093ece868c3ec7874e3b8907008763c85711bf0e2e9757a3068385380a757bd52fa77248f227f602b0785e040756ef42dabc7de7f8376847c71b3f20356a9176cc88067583ee3501e61f8aa07bdd70f41418986fb79a1a3" />
21+
</ItemGroup>
22+
23+
<ItemGroup Condition="'$(Configuration)' != 'Release'">
24+
<InternalsVisibleTo Include="QRCoderTests" />
25+
</ItemGroup>
26+
1927
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' OR '$(TargetFramework)' == 'net40' ">
2028
<Reference Include="PresentationCore" />
2129
<Reference Include="PresentationFramework" />

QRCoderTests/PayloadGeneratorTests/IbanTests.cs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System.Reflection;
2-
31
namespace QRCoderTests.PayloadGeneratorTests;
42

53
public class IbanTests
@@ -9,8 +7,7 @@ public void iban_validator_validate_german_iban()
97
{
108
var iban = "DE15268500010154131577";
119

12-
var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static);
13-
var result = (bool)method!.Invoke(null, new object[] { iban })!;
10+
var result = PayloadGenerator.IsValidIban(iban);
1411

1512
result.ShouldBe<bool>(true);
1613
}
@@ -20,8 +17,7 @@ public void iban_validator_validate_swiss_iban()
2017
{
2118
var iban = "CH1900767000U00121977";
2219

23-
var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static);
24-
var result = (bool)method!.Invoke(null, new object[] { iban })!;
20+
var result = PayloadGenerator.IsValidIban(iban);
2521

2622
result.ShouldBe<bool>(true);
2723
}
@@ -31,8 +27,7 @@ public void iban_validator_invalidates_iban()
3127
{
3228
var iban = "DE29268500010154131577";
3329

34-
var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static);
35-
var result = (bool)method!.Invoke(null, new object[] { iban })!;
30+
var result = PayloadGenerator.IsValidIban(iban);
3631

3732
result.ShouldBe<bool>(false);
3833
}
@@ -42,8 +37,7 @@ public void qriban_validator_validates_iban()
4237
{
4338
var iban = "CH2430043000000789012";
4439

45-
var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static);
46-
var result = (bool)method!.Invoke(null, new object[] { iban })!;
40+
var result = PayloadGenerator.IsValidQRIban(iban);
4741

4842
result.ShouldBe<bool>(true);
4943
}
@@ -53,8 +47,7 @@ public void qriban_validator_invalidates_iban()
5347
{
5448
var iban = "CH3908704016075473007";
5549

56-
var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static);
57-
var result = (bool)method!.Invoke(null, new object[] { iban })!;
50+
var result = PayloadGenerator.IsValidQRIban(iban);
5851

5952
result.ShouldBe<bool>(false);
6053
}

QRCoderTests/QRCoderTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<SuppressTfmSupportBuildErrors>true</SuppressTfmSupportBuildErrors>
99
<RuntimeFrameworkVersion Condition="'$(TargetFramework)' == 'netcoreapp2.1'">2.1.30</RuntimeFrameworkVersion>
1010
<NoWarn>$(NoWarn);CA1707;CA1416;CA1850</NoWarn>
11+
<AssemblyOriginatorKeyFile>..\QRCoder\QRCoderStrongName.snk</AssemblyOriginatorKeyFile>
1112
</PropertyGroup>
1213

1314
<ItemGroup>

0 commit comments

Comments
 (0)