From 9a9d53966ae425f5c36552a02f6f4737270d5be4 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:30:02 -0400 Subject: [PATCH 01/17] Enhance alphanumeric and numeric data segment encoding with new bit length calculations and writing methods --- .../AlphanumericDataSegment.cs | 36 ++++++++- .../QRCodeGenerator/AlphanumericEncoder.cs | 33 +++++++- QRCoder/QRCodeGenerator/NumericDataSegment.cs | 80 ++++++++++++++----- 3 files changed, 124 insertions(+), 25 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs index 98fcd1c9..b5ee76c0 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs @@ -27,10 +27,22 @@ public AlphanumericDataSegment(string alphanumericText) /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The total number of bits required for this segment public override int GetBitLength(int version) + { + return GetBitLength(Text.Length, version); + } + + /// + /// Calculates the total bit length for encoding alphanumeric text of a given length for a specific QR code version. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The length of the alphanumeric text + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The total number of bits required + public static int GetBitLength(int textLength, int version) { int modeIndicatorLength = 4; int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - int dataLength = AlphanumericEncoder.GetBitLength(Text); + int dataLength = AlphanumericEncoder.GetBitLength(textLength); int length = modeIndicatorLength + countIndicatorLength + dataLength; return length; @@ -45,17 +57,33 @@ public override int GetBitLength(int version) /// The next index in the BitArray after the last bit written public override int WriteTo(BitArray bitArray, int startIndex, int version) { - var index = startIndex; + return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version); + } + + /// + /// Writes a portion of alphanumeric text to a BitArray at the specified index. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The full alphanumeric text + /// The starting index in the text to encode from + /// The number of characters to encode + /// The target BitArray to write to + /// The starting index in the BitArray + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The next index in the BitArray after the last bit written + public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + { + var index = bitIndex; // write mode indicator index = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, index); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - index = DecToBin(Text.Length, countIndicatorLength, bitArray, index); + index = DecToBin(length, countIndicatorLength, bitArray, index); // write data - encode alphanumeric text - index = AlphanumericEncoder.WriteToBitArray(Text, bitArray, index); + index = AlphanumericEncoder.WriteToBitArray(text, startIndex, length, bitArray, index); return index; } diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 11b3da70..cd62e4d7 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -40,6 +40,16 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; + /// + /// Calculates the bit length required to encode alphanumeric text of a given length. + /// + /// The length of the alphanumeric text to be encoded. + /// The number of bits required to encode the text. + public static int GetBitLength(int textLength) + { + return (textLength / 2) * 11 + (textLength & 1) * 6; + } + /// /// Calculates the bit length required to encode the given alphanumeric text. /// @@ -47,7 +57,7 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// The number of bits required to encode the text. public static int GetBitLength(string plainText) { - return (plainText.Length / 2) * 11 + (plainText.Length & 1) * 6; + return GetBitLength(plainText.Length); } /// @@ -75,8 +85,25 @@ public static BitArray GetBitArray(string plainText) /// The next index in the BitArray after the last bit written. public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex) { - var index = 0; - var count = plainText.Length; + return WriteToBitArray(plainText, 0, plainText.Length, codeText, codeIndex); + } + + /// + /// Writes a portion of alphanumeric plain text directly into an existing BitArray at the specified index. + /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, + /// and 6 bits for a single remaining character if the total count is odd. + /// + /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. + /// The starting index in the text to encode from. + /// The number of characters to encode. + /// The target BitArray to write to. + /// The starting index in the BitArray where writing should begin. + /// The next index in the BitArray after the last bit written. + public static int WriteToBitArray(string plainText, int startIndex, int length, BitArray codeText, int codeIndex) + { + var index = startIndex; + var count = length; + var endIndex = startIndex + length; // Process each pair of characters. while (count >= 2) diff --git a/QRCoder/QRCodeGenerator/NumericDataSegment.cs b/QRCoder/QRCodeGenerator/NumericDataSegment.cs index 61935736..68932885 100644 --- a/QRCoder/QRCodeGenerator/NumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/NumericDataSegment.cs @@ -27,10 +27,22 @@ public NumericDataSegment(string numericText) /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The total number of bits required for this segment public override int GetBitLength(int version) + { + return GetBitLength(Text.Length, version); + } + + /// + /// Calculates the total bit length for encoding numeric text of a given length for a specific QR code version. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The length of the numeric text + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The total number of bits required + public static int GetBitLength(int textLength, int version) { int modeIndicatorLength = 4; int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric); - int dataLength = Text.Length / 3 * 10 + (Text.Length % 3 == 1 ? 4 : Text.Length % 3 == 2 ? 7 : 0); + int dataLength = textLength / 3 * 10 + (textLength % 3 == 1 ? 4 : textLength % 3 == 2 ? 7 : 0); int length = modeIndicatorLength + countIndicatorLength + dataLength; return length; @@ -45,19 +57,33 @@ public override int GetBitLength(int version) /// The next index in the BitArray after the last bit written public override int WriteTo(BitArray bitArray, int startIndex, int version) { - var index = startIndex; + return WriteTo(Text, 0, Text.Length, bitArray, startIndex, version); + } + + /// + /// Writes a portion of numeric text to a BitArray at the specified index. + /// Includes mode indicator, count indicator, and data bits. + /// + /// The full numeric text + /// The starting index in the text to encode from + /// The number of characters to encode + /// The target BitArray to write to + /// The starting index in the BitArray + /// The QR code version (1-40, or -1 to -4 for Micro QR) + /// The next index in the BitArray after the last bit written + public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + { + var index = bitIndex; // write mode indicator index = DecToBin((int)EncodingMode.Numeric, 4, bitArray, index); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Numeric); - index = DecToBin(Text.Length, countIndicatorLength, bitArray, index); + index = DecToBin(length, countIndicatorLength, bitArray, index); // write data - encode numeric text - var data = PlainTextToBinaryNumeric(Text); - data.CopyTo(bitArray, 0, index, data.Length); - index += data.Length; + index = PlainTextToBinaryNumeric(text, startIndex, length, bitArray, index); return index; } @@ -74,10 +100,26 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) // Calculate the length of the BitArray needed to encode the text. // Groups of three digits are encoded in 10 bits, remaining groups of two or one digits take 7 or 4 bits respectively. var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); - var index = 0; + PlainTextToBinaryNumeric(plainText, 0, plainText.Length, bitArray, 0); + return bitArray; + } + + /// + /// Converts a portion of numeric plain text into a binary format specifically optimized for QR codes, writing directly to an existing BitArray. + /// Numeric compression groups up to 3 digits into 10 bits, less for remaining digits if they do not complete a group of three. + /// + /// The numeric text to be encoded, which should only contain digit characters. + /// The starting index in the text to encode from. + /// The number of characters to encode. + /// The target BitArray to write to. + /// The starting index in the BitArray where bits will be written. + /// The next index in the BitArray after the last bit written. + private static int PlainTextToBinaryNumeric(string plainText, int offset, int length, BitArray bitArray, int bitIndex) + { + var endIndex = offset + length; // Process each group of three digits. - for (int i = 0; i < plainText.Length - 2; i += 3) + for (int i = offset; i < endIndex - 2; i += 3) { // Parse the next three characters as a decimal integer. #if HAS_SPAN @@ -86,29 +128,31 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); #endif // Convert the decimal to binary and store it in the BitArray. - index = DecToBin(dec, 10, bitArray, index); + bitIndex = DecToBin(dec, 10, bitArray, bitIndex); + offset += 3; + length -= 3; } // Handle any remaining digits if the total number is not a multiple of three. - if (plainText.Length % 3 == 2) // Two remaining digits are encoded in 7 bits. + if (length == 2) // Two remaining digits are encoded in 7 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 7, bitArray, index); + bitIndex = DecToBin(dec, 7, bitArray, bitIndex); } - else if (plainText.Length % 3 == 1) // One remaining digit is encoded in 4 bits. + else if (length == 1) // One remaining digit is encoded in 4 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); #endif - index = DecToBin(dec, 4, bitArray, index); + bitIndex = DecToBin(dec, 4, bitArray, bitIndex); } - return bitArray; + return bitIndex; } } From 0ebc8e4ca9ecbe038e4aa36229adc73dfb4dbe92 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:36:55 -0400 Subject: [PATCH 02/17] Refactor WriteTo method parameters for clarity and consistency in AlphanumericDataSegment --- QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs index b5ee76c0..4025698c 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs @@ -65,27 +65,25 @@ public override int WriteTo(BitArray bitArray, int startIndex, int version) /// Includes mode indicator, count indicator, and data bits. /// /// The full alphanumeric text - /// The starting index in the text to encode from + /// The starting index in the text to encode from /// The number of characters to encode /// The target BitArray to write to /// The starting index in the BitArray /// The QR code version (1-40, or -1 to -4 for Micro QR) /// The next index in the BitArray after the last bit written - public static int WriteTo(string text, int startIndex, int length, BitArray bitArray, int bitIndex, int version) + public static int WriteTo(string text, int offset, int length, BitArray bitArray, int bitIndex, int version) { - var index = bitIndex; - // write mode indicator - index = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, index); + bitIndex = DecToBin((int)EncodingMode.Alphanumeric, 4, bitArray, bitIndex); // write count indicator int countIndicatorLength = GetCountIndicatorLength(version, EncodingMode.Alphanumeric); - index = DecToBin(length, countIndicatorLength, bitArray, index); + bitIndex = DecToBin(length, countIndicatorLength, bitArray, bitIndex); // write data - encode alphanumeric text - index = AlphanumericEncoder.WriteToBitArray(text, startIndex, length, bitArray, index); + bitIndex = AlphanumericEncoder.WriteToBitArray(text, offset, length, bitArray, bitIndex); - return index; + return bitIndex; } } } From 74eaa58a0bb9d57812230ef94b648ad93e02d259 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:37:57 -0400 Subject: [PATCH 03/17] Refactor GetBitLength methods for parameter clarity and consistency in AlphanumericEncoder --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index cd62e4d7..529420e0 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -41,23 +41,23 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; /// - /// Calculates the bit length required to encode alphanumeric text of a given length. + /// Calculates the bit length required to encode the given alphanumeric text. /// - /// The length of the alphanumeric text to be encoded. + /// The alphanumeric text to be encoded. /// The number of bits required to encode the text. - public static int GetBitLength(int textLength) + public static int GetBitLength(string plainText) { - return (textLength / 2) * 11 + (textLength & 1) * 6; + return GetBitLength(plainText.Length); } /// - /// Calculates the bit length required to encode the given alphanumeric text. + /// Calculates the bit length required to encode alphanumeric text of a given length. /// - /// The alphanumeric text to be encoded. + /// The length of the alphanumeric text to be encoded. /// The number of bits required to encode the text. - public static int GetBitLength(string plainText) + public static int GetBitLength(int textLength) { - return GetBitLength(plainText.Length); + return (textLength / 2) * 11 + (textLength & 1) * 6; } /// From ff57ed9b616d68914a733ca74160c54efe8923a0 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:39:37 -0400 Subject: [PATCH 04/17] Refactor GetBitLength and WriteToBitArray methods for parameter consistency and clarity --- .../QRCodeGenerator/AlphanumericEncoder.cs | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 529420e0..b1c3f39d 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -40,16 +40,6 @@ private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTab /// public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; - /// - /// Calculates the bit length required to encode the given alphanumeric text. - /// - /// The alphanumeric text to be encoded. - /// The number of bits required to encode the text. - public static int GetBitLength(string plainText) - { - return GetBitLength(plainText.Length); - } - /// /// Calculates the bit length required to encode alphanumeric text of a given length. /// @@ -69,25 +59,11 @@ public static int GetBitLength(int textLength) /// A BitArray representing the binary data of the encoded alphanumeric text. public static BitArray GetBitArray(string plainText) { - var codeText = new BitArray(GetBitLength(plainText)); - WriteToBitArray(plainText, codeText, 0); + var codeText = new BitArray(GetBitLength(plainText.Length)); + WriteToBitArray(plainText, 0, plainText.Length, codeText, 0); return codeText; } - /// - /// Writes alphanumeric plain text directly into an existing BitArray at the specified index. - /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, - /// and 6 bits for a single remaining character if the total count is odd. - /// - /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// The target BitArray to write to. - /// The starting index in the BitArray where writing should begin. - /// The next index in the BitArray after the last bit written. - public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex) - { - return WriteToBitArray(plainText, 0, plainText.Length, codeText, codeIndex); - } - /// /// Writes a portion of alphanumeric plain text directly into an existing BitArray at the specified index. /// Alphanumeric encoding packs characters into 11-bit groups for each pair of characters, From e1bf23d6f019b09c97d9a02dda0c4a8a9a1606db Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:40:49 -0400 Subject: [PATCH 05/17] Refactor WriteToBitArray method parameters for clarity and consistency --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index b1c3f39d..a2e8969f 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -70,17 +70,13 @@ public static BitArray GetBitArray(string plainText) /// and 6 bits for a single remaining character if the total count is odd. /// /// The alphanumeric text to be encoded, which should only contain characters valid in QR alphanumeric mode. - /// The starting index in the text to encode from. - /// The number of characters to encode. + /// The starting index in the text to encode from. + /// The number of characters to encode. /// The target BitArray to write to. /// The starting index in the BitArray where writing should begin. /// The next index in the BitArray after the last bit written. - public static int WriteToBitArray(string plainText, int startIndex, int length, BitArray codeText, int codeIndex) + public static int WriteToBitArray(string plainText, int index, int count, BitArray codeText, int codeIndex) { - var index = startIndex; - var count = length; - var endIndex = startIndex + length; - // Process each pair of characters. while (count >= 2) { From 3dabc79ed4048bbde7f5ae3fb0e3d49c054553da Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 15:44:36 -0400 Subject: [PATCH 06/17] Refactor PlainTextToBinaryNumeric method to handle remaining digits more efficiently --- QRCoder/QRCodeGenerator/NumericDataSegment.cs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/QRCoder/QRCodeGenerator/NumericDataSegment.cs b/QRCoder/QRCodeGenerator/NumericDataSegment.cs index 68932885..3337af62 100644 --- a/QRCoder/QRCodeGenerator/NumericDataSegment.cs +++ b/QRCoder/QRCodeGenerator/NumericDataSegment.cs @@ -134,23 +134,14 @@ private static int PlainTextToBinaryNumeric(string plainText, int offset, int le } // Handle any remaining digits if the total number is not a multiple of three. - if (length == 2) // Two remaining digits are encoded in 7 bits. + if (length > 0) // Two remaining digits are encoded in 7 bits; one remaining digit is encoded in 4 bits. { #if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.AsSpan(offset, length), NumberStyles.None, CultureInfo.InvariantCulture); #else - var dec = int.Parse(plainText.Substring(offset, 2), NumberStyles.None, CultureInfo.InvariantCulture); + var dec = int.Parse(plainText.Substring(offset, length), NumberStyles.None, CultureInfo.InvariantCulture); #endif - bitIndex = DecToBin(dec, 7, bitArray, bitIndex); - } - else if (length == 1) // One remaining digit is encoded in 4 bits. - { -#if HAS_SPAN - var dec = int.Parse(plainText.AsSpan(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); -#else - var dec = int.Parse(plainText.Substring(offset, 1), NumberStyles.None, CultureInfo.InvariantCulture); -#endif - bitIndex = DecToBin(dec, 4, bitArray, bitIndex); + bitIndex = DecToBin(dec, length == 2 ? 7 : 4, bitArray, bitIndex); } return bitIndex; From b6dfc37441ef4862aff9236af1804601e69170db Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 17:15:18 -0400 Subject: [PATCH 07/17] Refactor AlphanumericEncoder to simplify character encoding logic and improve performance --- QRCoder/QRCodeGenerator.cs | 2 +- .../QRCodeGenerator/AlphanumericEncoder.cs | 54 +++++++-------- QRCoderTests/QRGeneratorTests.cs | 67 +++++++++++++++++-- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index beb3d576..90abdea7 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -756,7 +756,7 @@ private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forc if (IsInRange(c, '0', '9')) continue; // numeric - char.IsDigit() for Latin1 result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric - if (AlphanumericEncoder.CanEncodeNonDigit(c)) + if (AlphanumericEncoder.CanEncode(c)) continue; // alphanumeric return EncodingMode.Byte; // not numeric or alphanumeric, assume byte } diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index a2e8969f..58e6ef3f 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -7,38 +7,32 @@ public partial class QRCodeGenerator /// private static class AlphanumericEncoder { - private static readonly char[] _alphanumEncTable = { ' ', '$', '%', '*', '+', '-', '.', '/', ':' }; - - /// - /// A dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. - /// This includes digits 0-9, uppercase letters A-Z, and some special characters. - /// - private static readonly Dictionary _alphanumEncDict = CreateAlphanumEncDict(_alphanumEncTable); - - /// - /// Creates a dictionary mapping alphanumeric characters to their respective positions used in QR code encoding. - /// This includes digits 0-9, uppercase letters A-Z, and some special characters. - /// - /// A dictionary mapping each supported alphanumeric character to its corresponding value. - private static Dictionary CreateAlphanumEncDict(char[] alphanumEncTable) - { - var localAlphanumEncDict = new Dictionary(45); - // Add 0-9 - for (char c = '0'; c <= '9'; c++) - localAlphanumEncDict.Add(c, c - '0'); - // Add uppercase alphabetic characters. - for (char c = 'A'; c <= 'Z'; c++) - localAlphanumEncDict.Add(c, localAlphanumEncDict.Count); - // Add special characters from a predefined table. - for (int i = 0; i < _alphanumEncTable.Length; i++) - localAlphanumEncDict.Add(alphanumEncTable[i], localAlphanumEncDict.Count); - return localAlphanumEncDict; - } +#if HAS_SPAN + private static ReadOnlySpan _map => +#else + private static readonly byte[] _map = +#endif + [ + // 0..31 + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + // 32..47 (space, ! " # $ % & ' ( ) * + , - . /) + 36, 255, 255, 255, 37, 38, 255, 255, 255, 255, 39, 40, 255, 41, 42, 43, + // 48..57 (0..9) + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + // 58..64 (: ; < = > ? @) + 44, 255, 255, 255, 255, 255, 255, + // 65..90 (A..Z) + 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 + // (we don't index > 90) + ]; /// /// Checks if a non-digit character is present in the alphanumeric encoding table. /// - public static bool CanEncodeNonDigit(char c) => IsInRange(c, 'A', 'Z') || Array.IndexOf(_alphanumEncTable, c) >= 0; + public static bool CanEncode(char c) => c <= 90 && _map[c] != 255; /// /// Calculates the bit length required to encode alphanumeric text of a given length. @@ -81,7 +75,7 @@ public static int WriteToBitArray(string plainText, int index, int count, BitArr while (count >= 2) { // Convert each pair of characters to a number by looking them up in the alphanumeric dictionary and calculating. - var dec = _alphanumEncDict[plainText[index++]] * 45 + _alphanumEncDict[plainText[index++]]; + var dec = _map[plainText[index++]] * 45 + _map[plainText[index++]]; // Convert the number to binary and store it in the BitArray. codeIndex = DecToBin(dec, 11, codeText, codeIndex); count -= 2; @@ -90,7 +84,7 @@ public static int WriteToBitArray(string plainText, int index, int count, BitArr // Handle the last character if the length is odd. if (count > 0) { - codeIndex = DecToBin(_alphanumEncDict[plainText[index]], 6, codeText, codeIndex); + codeIndex = DecToBin(_map[plainText[index]], 6, codeText, codeIndex); } return codeIndex; diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 1fd197b5..2fafe71c 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -1,5 +1,6 @@ #pragma warning disable CA5350 // Weak cryptography algorithm (SHA1). +using System.Linq.Expressions; using System.Reflection; using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel; @@ -379,14 +380,70 @@ public void can_encode_various_strings_various_ecc(int inputChars, ECCLevel eccL [Fact] public void validate_alphanumencdict() { - var gen = new QRCodeGenerator(); + var encoderType = Type.GetType("QRCoder.QRCodeGenerator+AlphanumericEncoder, QRCoder"); + encoderType.ShouldNotBeNull(); + + // Get the byte array from _map + byte[] mapBytes; +#if NETCOREAPP3_0_OR_GREATER // HAS_SPAN would be defined for .NET Core 2.1 but we don't target that + // When HAS_SPAN is defined in AlphanumericEncoder, _map returns ReadOnlySpan + // We need to compile a function that calls _map and converts it to byte[] (since a span cannot be boxed) + var mapProperty = encoderType.GetProperty("_map", BindingFlags.NonPublic | BindingFlags.Static); + mapProperty.ShouldNotBeNull(); + + // Get the ToArray method from ReadOnlySpan + var toArrayMethod = typeof(ReadOnlySpan).GetMethod("ToArray"); + toArrayMethod.ShouldNotBeNull(); + + // Build an expression tree: () => _map.ToArray() + // First, create an expression to call the _map property getter + var mapPropertyAccess = Expression.Property(null, mapProperty); + + // Then, create an expression to call ToArray() on the result + var toArrayCall = Expression.Call(mapPropertyAccess, toArrayMethod); + + // Compile the expression into a function + var lambda = Expression.Lambda>(toArrayCall); + var getMapFunc = lambda.Compile(); + + // Invoke the compiled function to get the byte array + mapBytes = getMapFunc(); +#else + // When HAS_SPAN is not defined in AlphanumericEncoder, _map returns byte[] directly + var mapField = encoderType.GetField("_map", BindingFlags.NonPublic | BindingFlags.Static); + mapField.ShouldNotBeNull(); + mapBytes = (byte[])mapField.GetValue(null)!; +#endif + + // Create a dictionary from the byte array (char -> int mapping, excluding 255) + var charToIntDict = new Dictionary(); + for (int i = 0; i < mapBytes.Length; i++) + { + if (mapBytes[i] != 255) + { + charToIntDict[(char)i] = mapBytes[i]; + } + } + + // Get the CanEncode method + var canEncodeMethod = encoderType.GetMethod("CanEncode", BindingFlags.Public | BindingFlags.Static); + canEncodeMethod.ShouldNotBeNull(); + + // Verify CanEncode for all possible char values (0-65535) + for (int i = 0; i < 65536; i++) + { + char c = (char)i; + bool canEncode = (bool)canEncodeMethod.Invoke(null, new object[] { c })!; + bool shouldEncode = charToIntDict.ContainsKey(c); + canEncode.ShouldBe(shouldEncode, $"CanEncode mismatch for char '{c}' (code {i})"); + } + + // Assemble the string as before and validate var checkString = string.Empty; - var encoderType = Type.GetType("QRCoder.QRCodeGenerator+AlphanumericEncoder, QRCoder"); - var gField = encoderType!.GetField("_alphanumEncDict", BindingFlags.NonPublic | BindingFlags.Static); - foreach (var listitem in (Dictionary)gField!.GetValue(gen)!) + foreach (var kvp in charToIntDict.OrderBy(x => x.Value)) { - checkString += $"{listitem.Key},{listitem.Value}:"; + checkString += $"{kvp.Key},{kvp.Value}:"; } checkString.ShouldBe("0,0:1,1:2,2:3,3:4,4:5,5:6,6:7,7:8,8:9,9:A,10:B,11:C,12:D,13:E,14:F,15:G,16:H,17:I,18:J,19:K,20:L,21:M,22:N,23:O,24:P,25:Q,26:R,27:S,28:T,29:U,30:V,31:W,32:X,33:Y,34:Z,35: ,36:$,37:%,38:*,39:+,40:-,41:.,42:/,43::,44:"); } From af8b4ccf9b113cfdeb4c597923a4e60a3b4a84f1 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 17:18:09 -0400 Subject: [PATCH 08/17] update --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 58e6ef3f..dce47f58 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -8,6 +8,8 @@ public partial class QRCodeGenerator private static class AlphanumericEncoder { #if HAS_SPAN + // 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. + // See: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/ private static ReadOnlySpan _map => #else private static readonly byte[] _map = From 5e32a4b4a8d050c06d9d9da0bbd4155af8ad61f7 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 22:28:27 -0400 Subject: [PATCH 09/17] Apply suggestion from @Shane32 --- QRCoder/QRCodeGenerator/AlphanumericEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index dce47f58..997857b7 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -32,7 +32,7 @@ private static class AlphanumericEncoder ]; /// - /// Checks if a non-digit character is present in the alphanumeric encoding table. + /// Checks if a character is present in the alphanumeric encoding table. /// public static bool CanEncode(char c) => c <= 90 && _map[c] != 255; From 8d7f12f31fd5deb845fdaa3f3e7cdcad84761519 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 22:52:29 -0400 Subject: [PATCH 10/17] Eliminate reflection in tests --- QRCoder/PayloadGenerator.cs | 4 +- QRCoder/QRCodeGenerator.cs | 2 +- .../QRCodeGenerator/AlphanumericEncoder.cs | 6 +- QRCoder/QRCodeGenerator/EncodingMode.cs | 2 +- QRCoder/QRCodeGenerator/GaloisField.cs | 6 +- QRCoder/QRCoder.csproj | 4 ++ .../PayloadGeneratorTests/IbanTests.cs | 17 ++--- QRCoderTests/QRGeneratorTests.cs | 66 +++---------------- 8 files changed, 28 insertions(+), 79 deletions(-) diff --git a/QRCoder/PayloadGenerator.cs b/QRCoder/PayloadGenerator.cs index fc4917c8..1bb15d81 100644 --- a/QRCoder/PayloadGenerator.cs +++ b/QRCoder/PayloadGenerator.cs @@ -14,7 +14,7 @@ public static partial class PayloadGenerator /// /// The IBAN to validate. /// True if the IBAN is valid; otherwise, false. - private static bool IsValidIban(string iban) + internal static bool IsValidIban(string iban) { //Clean IBAN var ibanCleared = iban.ToUpperInvariant().Replace(" ", "").Replace("-", ""); @@ -48,7 +48,7 @@ private static bool IsValidIban(string iban) /// /// The QR IBAN to validate. /// True if the QR IBAN is valid; otherwise, false. - private static bool IsValidQRIban(string iban) + internal static bool IsValidQRIban(string iban) { var foundQrIid = false; try diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 90abdea7..5e507567 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -746,7 +746,7 @@ private static void ConvertToDecNotationInPlace(Polynom poly) /// Determines the most efficient encoding mode for the given plain text based on its character content /// and a flag indicating whether to force UTF-8 encoding. /// - private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) + internal static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) { if (forceUtf8) return EncodingMode.Byte; diff --git a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs index 997857b7..ea66319c 100644 --- a/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs +++ b/QRCoder/QRCodeGenerator/AlphanumericEncoder.cs @@ -5,14 +5,14 @@ public partial class QRCodeGenerator /// /// Encodes alphanumeric characters (0–9, A–Z (uppercase), space, $, %, *, +, -, period, /, colon) into a binary format suitable for QR codes. /// - private static class AlphanumericEncoder + internal static class AlphanumericEncoder { #if HAS_SPAN // 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. // See: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/ - private static ReadOnlySpan _map => + internal static ReadOnlySpan _map => #else - private static readonly byte[] _map = + internal static readonly byte[] _map = #endif [ // 0..31 diff --git a/QRCoder/QRCodeGenerator/EncodingMode.cs b/QRCoder/QRCodeGenerator/EncodingMode.cs index f200573e..1896b042 100644 --- a/QRCoder/QRCodeGenerator/EncodingMode.cs +++ b/QRCoder/QRCodeGenerator/EncodingMode.cs @@ -5,7 +5,7 @@ public partial class QRCodeGenerator /// /// Specifies the encoding modes for the characters in a QR code. /// - private enum EncodingMode + internal enum EncodingMode { /// /// Numeric encoding mode, which is used to encode numeric data (digits 0-9). diff --git a/QRCoder/QRCodeGenerator/GaloisField.cs b/QRCoder/QRCodeGenerator/GaloisField.cs index 094cf217..3a20869a 100644 --- a/QRCoder/QRCodeGenerator/GaloisField.cs +++ b/QRCoder/QRCodeGenerator/GaloisField.cs @@ -13,10 +13,10 @@ public partial class QRCodeGenerator /// polynomial and used for efficient encoding and decoding operations. /// /// - private static class GaloisField + internal static class GaloisField { - 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 }; - 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 }; + internal 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 }; + internal 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 }; /// /// Retrieves the integer value from the Galois field that corresponds to a given exponent. diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index 0ce46584..00b3c9b0 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -16,6 +16,10 @@ e668b98b-83bb-4e60-b33c-4fd5ed9c0156 + + + + diff --git a/QRCoderTests/PayloadGeneratorTests/IbanTests.cs b/QRCoderTests/PayloadGeneratorTests/IbanTests.cs index e0a2f63e..deeb858a 100644 --- a/QRCoderTests/PayloadGeneratorTests/IbanTests.cs +++ b/QRCoderTests/PayloadGeneratorTests/IbanTests.cs @@ -1,5 +1,3 @@ -using System.Reflection; - namespace QRCoderTests.PayloadGeneratorTests; public class IbanTests @@ -9,8 +7,7 @@ public void iban_validator_validate_german_iban() { var iban = "DE15268500010154131577"; - var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method!.Invoke(null, new object[] { iban })!; + var result = PayloadGenerator.IsValidIban(iban); result.ShouldBe(true); } @@ -20,8 +17,7 @@ public void iban_validator_validate_swiss_iban() { var iban = "CH1900767000U00121977"; - var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method!.Invoke(null, new object[] { iban })!; + var result = PayloadGenerator.IsValidIban(iban); result.ShouldBe(true); } @@ -31,8 +27,7 @@ public void iban_validator_invalidates_iban() { var iban = "DE29268500010154131577"; - var method = typeof(PayloadGenerator).GetMethod("IsValidIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method!.Invoke(null, new object[] { iban })!; + var result = PayloadGenerator.IsValidIban(iban); result.ShouldBe(false); } @@ -42,8 +37,7 @@ public void qriban_validator_validates_iban() { var iban = "CH2430043000000789012"; - var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method!.Invoke(null, new object[] { iban })!; + var result = PayloadGenerator.IsValidQRIban(iban); result.ShouldBe(true); } @@ -53,8 +47,7 @@ public void qriban_validator_invalidates_iban() { var iban = "CH3908704016075473007"; - var method = typeof(PayloadGenerator).GetMethod("IsValidQRIban", BindingFlags.NonPublic | BindingFlags.Static); - var result = (bool)method!.Invoke(null, new object[] { iban })!; + var result = PayloadGenerator.IsValidQRIban(iban); result.ShouldBe(false); } diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 2fafe71c..a177c42e 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -1,7 +1,6 @@ #pragma warning disable CA5350 // Weak cryptography algorithm (SHA1). using System.Linq.Expressions; -using System.Reflection; using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel; @@ -13,11 +12,8 @@ public class QRGeneratorTests [Fact] public void validate_antilogtable() { - var gen = new QRCodeGenerator(); - var checkString = string.Empty; - var tablesType = Type.GetType("QRCoder.QRCodeGenerator+GaloisField, QRCoder"); - var gField = tablesType!.GetField("_galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null).ShouldBeOfType(); + var gField = QRCodeGenerator.GaloisField._galoisFieldByExponentAlpha; gField.Length.ShouldBe(256); for (int i = 0; i < gField.Length; i++) { @@ -25,7 +21,7 @@ public void validate_antilogtable() } checkString.ShouldBe("0,1,:1,2,:2,4,:3,8,:4,16,:5,32,:6,64,:7,128,:8,29,:9,58,:10,116,:11,232,:12,205,:13,135,:14,19,:15,38,:16,76,:17,152,:18,45,:19,90,:20,180,:21,117,:22,234,:23,201,:24,143,:25,3,:26,6,:27,12,:28,24,:29,48,:30,96,:31,192,:32,157,:33,39,:34,78,:35,156,:36,37,:37,74,:38,148,:39,53,:40,106,:41,212,:42,181,:43,119,:44,238,:45,193,:46,159,:47,35,:48,70,:49,140,:50,5,:51,10,:52,20,:53,40,:54,80,:55,160,:56,93,:57,186,:58,105,:59,210,:60,185,:61,111,:62,222,:63,161,:64,95,:65,190,:66,97,:67,194,:68,153,:69,47,:70,94,:71,188,:72,101,:73,202,:74,137,:75,15,:76,30,:77,60,:78,120,:79,240,:80,253,:81,231,:82,211,:83,187,:84,107,:85,214,:86,177,:87,127,:88,254,:89,225,:90,223,:91,163,:92,91,:93,182,:94,113,:95,226,:96,217,:97,175,:98,67,:99,134,:100,17,:101,34,:102,68,:103,136,:104,13,:105,26,:106,52,:107,104,:108,208,:109,189,:110,103,:111,206,:112,129,:113,31,:114,62,:115,124,:116,248,:117,237,:118,199,:119,147,:120,59,:121,118,:122,236,:123,197,:124,151,:125,51,:126,102,:127,204,:128,133,:129,23,:130,46,:131,92,:132,184,:133,109,:134,218,:135,169,:136,79,:137,158,:138,33,:139,66,:140,132,:141,21,:142,42,:143,84,:144,168,:145,77,:146,154,:147,41,:148,82,:149,164,:150,85,:151,170,:152,73,:153,146,:154,57,:155,114,:156,228,:157,213,:158,183,:159,115,:160,230,:161,209,:162,191,:163,99,:164,198,:165,145,:166,63,:167,126,:168,252,:169,229,:170,215,:171,179,:172,123,:173,246,:174,241,:175,255,:176,227,:177,219,:178,171,:179,75,:180,150,:181,49,:182,98,:183,196,:184,149,:185,55,:186,110,:187,220,:188,165,:189,87,:190,174,:191,65,:192,130,:193,25,:194,50,:195,100,:196,200,:197,141,:198,7,:199,14,:200,28,:201,56,:202,112,:203,224,:204,221,:205,167,:206,83,:207,166,:208,81,:209,162,:210,89,:211,178,:212,121,:213,242,:214,249,:215,239,:216,195,:217,155,:218,43,:219,86,:220,172,:221,69,:222,138,:223,9,:224,18,:225,36,:226,72,:227,144,:228,61,:229,122,:230,244,:231,245,:232,247,:233,243,:234,251,:235,235,:236,203,:237,139,:238,11,:239,22,:240,44,:241,88,:242,176,:243,125,:244,250,:245,233,:246,207,:247,131,:248,27,:249,54,:250,108,:251,216,:252,173,:253,71,:254,142,:255,1,:"); - var gField2 = tablesType!.GetField("_galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null).ShouldBeOfType(); + var gField2 = QRCodeGenerator.GaloisField._galoisFieldByIntegerValue; gField2.Length.ShouldBe(256); var checkString2 = string.Empty; for (int i = 0; i < gField2.Length; i++) @@ -380,40 +376,8 @@ public void can_encode_various_strings_various_ecc(int inputChars, ECCLevel eccL [Fact] public void validate_alphanumencdict() { - var encoderType = Type.GetType("QRCoder.QRCodeGenerator+AlphanumericEncoder, QRCoder"); - encoderType.ShouldNotBeNull(); - - // Get the byte array from _map - byte[] mapBytes; -#if NETCOREAPP3_0_OR_GREATER // HAS_SPAN would be defined for .NET Core 2.1 but we don't target that - // When HAS_SPAN is defined in AlphanumericEncoder, _map returns ReadOnlySpan - // We need to compile a function that calls _map and converts it to byte[] (since a span cannot be boxed) - var mapProperty = encoderType.GetProperty("_map", BindingFlags.NonPublic | BindingFlags.Static); - mapProperty.ShouldNotBeNull(); - - // Get the ToArray method from ReadOnlySpan - var toArrayMethod = typeof(ReadOnlySpan).GetMethod("ToArray"); - toArrayMethod.ShouldNotBeNull(); - - // Build an expression tree: () => _map.ToArray() - // First, create an expression to call the _map property getter - var mapPropertyAccess = Expression.Property(null, mapProperty); - - // Then, create an expression to call ToArray() on the result - var toArrayCall = Expression.Call(mapPropertyAccess, toArrayMethod); - - // Compile the expression into a function - var lambda = Expression.Lambda>(toArrayCall); - var getMapFunc = lambda.Compile(); - - // Invoke the compiled function to get the byte array - mapBytes = getMapFunc(); -#else - // When HAS_SPAN is not defined in AlphanumericEncoder, _map returns byte[] directly - var mapField = encoderType.GetField("_map", BindingFlags.NonPublic | BindingFlags.Static); - mapField.ShouldNotBeNull(); - mapBytes = (byte[])mapField.GetValue(null)!; -#endif + // Get the _map + var mapBytes = QRCodeGenerator.AlphanumericEncoder._map; // Create a dictionary from the byte array (char -> int mapping, excluding 255) var charToIntDict = new Dictionary(); @@ -425,15 +389,11 @@ public void validate_alphanumencdict() } } - // Get the CanEncode method - var canEncodeMethod = encoderType.GetMethod("CanEncode", BindingFlags.Public | BindingFlags.Static); - canEncodeMethod.ShouldNotBeNull(); - // Verify CanEncode for all possible char values (0-65535) for (int i = 0; i < 65536; i++) { char c = (char)i; - bool canEncode = (bool)canEncodeMethod.Invoke(null, new object[] { c })!; + bool canEncode = QRCodeGenerator.AlphanumericEncoder.CanEncode(c); bool shouldEncode = charToIntDict.ContainsKey(c); canEncode.ShouldBe(shouldEncode, $"CanEncode mismatch for char '{c}' (code {i})"); @@ -451,9 +411,7 @@ public void validate_alphanumencdict() [Fact] public void can_recognize_enconding_numeric() { - var gen = new QRCodeGenerator(); - var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method!.Invoke(gen, new object[] { "0123456789", false })!; + var result = (int)QRCodeGenerator.GetEncodingFromPlaintext("0123456789", false); result.ShouldBe(1); } @@ -462,9 +420,7 @@ public void can_recognize_enconding_numeric() [Fact] public void can_recognize_enconding_alphanumeric() { - var gen = new QRCodeGenerator(); - var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method!.Invoke(gen, new object[] { "0123456789ABC", false })!; + var result = (int)QRCodeGenerator.GetEncodingFromPlaintext("0123456789ABC", false); result.ShouldBe(2); } @@ -473,9 +429,7 @@ public void can_recognize_enconding_alphanumeric() [Fact] public void can_recognize_enconding_forced_bytemode() { - var gen = new QRCodeGenerator(); - var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method!.Invoke(gen, new object[] { "0123456789", true })!; + var result = (int)QRCodeGenerator.GetEncodingFromPlaintext("0123456789", true); result.ShouldBe(4); } @@ -484,9 +438,7 @@ public void can_recognize_enconding_forced_bytemode() [Fact] public void can_recognize_enconding_byte() { - var gen = new QRCodeGenerator(); - var method = gen.GetType().GetMethod("GetEncodingFromPlaintext", BindingFlags.NonPublic | BindingFlags.Static); - var result = (int)method!.Invoke(gen, new object[] { "0123456789äöüß", false })!; + var result = (int)QRCodeGenerator.GetEncodingFromPlaintext("0123456789äöüß", false); result.ShouldBe(4); } From d9be37da1cc9d70d6de0d046d87caecdc97119f4 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:07:09 -0400 Subject: [PATCH 11/17] Update --- QRCoder/QRCoder.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index 00b3c9b0..2032aea6 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -16,7 +16,7 @@ e668b98b-83bb-4e60-b33c-4fd5ed9c0156 - + From 4ee674274084e453e89d03e7160862c3f3e513af Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:12:24 -0400 Subject: [PATCH 12/17] Update GaloisField too --- QRCoder/QRCodeGenerator/GaloisField.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/QRCoder/QRCodeGenerator/GaloisField.cs b/QRCoder/QRCodeGenerator/GaloisField.cs index 3a20869a..ee373b78 100644 --- a/QRCoder/QRCodeGenerator/GaloisField.cs +++ b/QRCoder/QRCodeGenerator/GaloisField.cs @@ -15,8 +15,18 @@ public partial class QRCodeGenerator /// internal static class GaloisField { - internal 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 }; - internal 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 }; +#if HAS_SPAN + internal static ReadOnlySpan _galoisFieldByExponentAlpha => +#else + internal static readonly byte[] _galoisFieldByExponentAlpha = +#endif + [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]; +#if HAS_SPAN + internal static ReadOnlySpan _galoisFieldByIntegerValue => +#else + internal static readonly byte[] _galoisFieldByIntegerValue = +#endif + [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]; /// /// Retrieves the integer value from the Galois field that corresponds to a given exponent. From 3b4c9880434c263ffda4fed7d02846745fc14349 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:12:52 -0400 Subject: [PATCH 13/17] update --- QRCoder/QRCodeGenerator/GaloisField.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QRCoder/QRCodeGenerator/GaloisField.cs b/QRCoder/QRCodeGenerator/GaloisField.cs index ee373b78..46c8f262 100644 --- a/QRCoder/QRCodeGenerator/GaloisField.cs +++ b/QRCoder/QRCodeGenerator/GaloisField.cs @@ -21,6 +21,7 @@ internal static class GaloisField internal static readonly byte[] _galoisFieldByExponentAlpha = #endif [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]; + #if HAS_SPAN internal static ReadOnlySpan _galoisFieldByIntegerValue => #else From f2ed9bb74ffa03778b7c5add1ccefd9aace67d6c Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:15:11 -0400 Subject: [PATCH 14/17] Update --- QRCoderTests/QRGeneratorTests.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index a177c42e..47dab382 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -1,12 +1,9 @@ #pragma warning disable CA5350 // Weak cryptography algorithm (SHA1). -using System.Linq.Expressions; using ECCLevel = QRCoder.QRCodeGenerator.ECCLevel; - namespace QRCoderTests; - public class QRGeneratorTests { [Fact] From 6b92babe294bed383cd5155346e31690e920c50d Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:32:06 -0400 Subject: [PATCH 15/17] update --- Directory.Build.targets | 2 +- QRCoder/QRCoder.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index a3a4828f..8457e280 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -7,7 +7,7 @@ - + true false diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index 2032aea6..00b3c9b0 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -16,7 +16,7 @@ e668b98b-83bb-4e60-b33c-4fd5ed9c0156 - + From 2417a57116acb5bec9869ee8e64aea1738ffebe0 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 14 Oct 2025 23:49:22 -0400 Subject: [PATCH 16/17] Add strong naming support for QRCoder and update test project configuration --- QRCoder/QRCoder.csproj | 6 +++++- QRCoderTests/QRCoderTests.csproj | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index 00b3c9b0..270286d8 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -16,7 +16,11 @@ e668b98b-83bb-4e60-b33c-4fd5ed9c0156 - + + + + + diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj index 6be37de0..00537872 100644 --- a/QRCoderTests/QRCoderTests.csproj +++ b/QRCoderTests/QRCoderTests.csproj @@ -8,6 +8,7 @@ true 2.1.30 $(NoWarn);CA1707;CA1416;CA1850 + ..\QRCoder\QRCoderStrongName.snk From 599af035962d5b4aaa07decaa48bc6f613b5b9d8 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Wed, 15 Oct 2025 00:07:18 -0400 Subject: [PATCH 17/17] Update --- QRCoder/QRCodeGenerator/OptimizedDataSegment.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/QRCoder/QRCodeGenerator/OptimizedDataSegment.cs b/QRCoder/QRCodeGenerator/OptimizedDataSegment.cs index a7fee75d..7bb8cfdc 100644 --- a/QRCoder/QRCodeGenerator/OptimizedDataSegment.cs +++ b/QRCoder/QRCodeGenerator/OptimizedDataSegment.cs @@ -175,7 +175,7 @@ private static EncodingMode SelectInitialMode(string text, int startPos, int ver if (numericCount < threshold) { var nextPos = startPos + numericCount; - if (nextPos < text.Length && !IsAlphanumericNonDigit(text[nextPos])) + if (nextPos < text.Length && !IsAlphanumeric(text[nextPos])) return EncodingMode.Byte; } // 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 if (numericCount < threshold) { var nextPos = startPos + numericCount; - if (nextPos < text.Length && IsAlphanumericNonDigit(text[nextPos])) + if (nextPos < text.Length && IsAlphanumeric(text[nextPos])) return EncodingMode.Alphanumeric; } return EncodingMode.Numeric; @@ -329,9 +329,6 @@ private static int CountConsecutive(string text, int startPos, Func private static bool IsNumeric(char c) => IsInRange(c, '0', '9'); // Checks if a character is alphanumeric (can be encoded in alphanumeric mode). - private static bool IsAlphanumeric(char c) => IsNumeric(c) || IsAlphanumericNonDigit(c); - - // Checks if a non-digit character can be encoded in alphanumeric mode. - private static bool IsAlphanumericNonDigit(char c) => AlphanumericEncoder.CanEncodeNonDigit(c); + private static bool IsAlphanumeric(char c) => AlphanumericEncoder.CanEncode(c); } }