From 20f428350e30d5d9a87843c0bab87b12b3872aa6 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 00:02:19 -0400 Subject: [PATCH 01/30] Progress --- QRCoder/QRCodeGenerator.cs | 738 ++++++++++++++++++++----------- QRCoderTests/QRGeneratorTests.cs | 21 +- 2 files changed, 486 insertions(+), 273 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 85c24b17..240360ac 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -14,10 +14,11 @@ public class QRCodeGenerator : IDisposable private static readonly int[] alignmentPatternBaseValues = { 0, 0, 0, 0, 0, 0, 0, 6, 18, 0, 0, 0, 0, 0, 6, 22, 0, 0, 0, 0, 0, 6, 26, 0, 0, 0, 0, 0, 6, 30, 0, 0, 0, 0, 0, 6, 34, 0, 0, 0, 0, 0, 6, 22, 38, 0, 0, 0, 0, 6, 24, 42, 0, 0, 0, 0, 6, 26, 46, 0, 0, 0, 0, 6, 28, 50, 0, 0, 0, 0, 6, 30, 54, 0, 0, 0, 0, 6, 32, 58, 0, 0, 0, 0, 6, 34, 62, 0, 0, 0, 0, 6, 26, 46, 66, 0, 0, 0, 6, 26, 48, 70, 0, 0, 0, 6, 26, 50, 74, 0, 0, 0, 6, 30, 54, 78, 0, 0, 0, 6, 30, 56, 82, 0, 0, 0, 6, 30, 58, 86, 0, 0, 0, 6, 34, 62, 90, 0, 0, 0, 6, 28, 50, 72, 94, 0, 0, 6, 26, 50, 74, 98, 0, 0, 6, 30, 54, 78, 102, 0, 0, 6, 28, 54, 80, 106, 0, 0, 6, 32, 58, 84, 110, 0, 0, 6, 30, 58, 86, 114, 0, 0, 6, 34, 62, 90, 118, 0, 0, 6, 26, 50, 74, 98, 122, 0, 6, 30, 54, 78, 102, 126, 0, 6, 26, 52, 78, 104, 130, 0, 6, 30, 56, 82, 108, 134, 0, 6, 34, 60, 86, 112, 138, 0, 6, 30, 58, 86, 114, 142, 0, 6, 34, 62, 90, 118, 146, 0, 6, 30, 54, 78, 102, 126, 150, 6, 24, 50, 76, 102, 128, 154, 6, 28, 54, 80, 106, 132, 158, 6, 32, 58, 84, 110, 136, 162, 6, 26, 54, 82, 110, 138, 166, 6, 30, 58, 86, 114, 142, 170 }; private static readonly int[] remainderBits = { 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0 }; - private static readonly List alignmentPatternTable = CreateAlignmentPatternTable(); + private static readonly Dictionary alignmentPatternTable = CreateAlignmentPatternTable(); private static readonly List capacityECCTable = CreateCapacityECCTable(); private static readonly List capacityTable = CreateCapacityTable(); - private static readonly List galoisField = CreateAntilogTable(); + private static readonly int[] galoisFieldByExponentAlpha; + private static readonly int[] galoisFieldByIntegerValue; private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); public enum EciMode @@ -35,6 +36,19 @@ public QRCodeGenerator() { } + static QRCodeGenerator() + { + var galoisField = CreateAntilogTable(); + galoisFieldByExponentAlpha = new int[256]; + galoisFieldByIntegerValue = new int[256]; + for (int i = 255; i >= 0; i--) // the value 1 occurs twice in the galois field, so we start from the end to get the correct value + { + var entry = galoisField[i]; + galoisFieldByExponentAlpha[entry.ExponentAlpha] = checked((byte)entry.IntegerValue); + galoisFieldByIntegerValue[entry.IntegerValue] = checked((byte)entry.ExponentAlpha); + } + } + /// /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. /// @@ -129,7 +143,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo int version = requestedVersion; if (version == -1) { - version = GetVersion(dataInputLength+(eciMode != EciMode.Default?2:0), encoding, eccLevel); + version = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); } else { @@ -139,20 +153,32 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo { var maxSizeByte = capacityTable[version - 1].Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; throw new QRCoder.Exceptions.DataTooLongException(eccLevel.ToString(), encoding.ToString(), version, maxSizeByte); - } + } } - string modeIndicator = String.Empty; + var completeBitArrayLength = + (eciMode != EciMode.Default ? 16 : 4) + // Mode indicator + GetCountIndicatorLength(version, encoding) + // Count indicator + codedText.Length; // Data + + var completeBitArray = new BitArray(completeBitArrayLength); + // write mode indicator + var completeBitArrayIndex = 0; if (eciMode != EciMode.Default) { - modeIndicator = DecToBin((int)EncodingMode.ECI, 4); - modeIndicator += DecToBin((int)eciMode, 8); + DecToBin((int)EncodingMode.ECI, 4, completeBitArray, ref completeBitArrayIndex); + DecToBin((int)eciMode, 8, completeBitArray, ref completeBitArrayIndex); + } + DecToBin((int)encoding, 4, completeBitArray, ref completeBitArrayIndex); + // write count indicator + DecToBin(dataInputLength, GetCountIndicatorLength(version, encoding), completeBitArray, ref completeBitArrayIndex); + // write data + for (int i = 0; i < codedText.Length; i++) + { + completeBitArray[completeBitArrayIndex++] = codedText[i]; } - modeIndicator += DecToBin((int)encoding, 4); - var countIndicator = DecToBin(dataInputLength, GetCountIndicatorLength(version, encoding)); - var bitString = modeIndicator + countIndicator + codedText; - return GenerateQrCode(bitString, eccLevel, version); + return GenerateQrCode(completeBitArray, eccLevel, version); } @@ -171,7 +197,7 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); string countIndicator = DecToBin(binaryData.Length, countIndicatorLen); - StringBuilder sb = new StringBuilder(capacity: 4 + countIndicatorLen + (binaryData.Length*8)); + StringBuilder sb = new StringBuilder(capacity: 4 + countIndicatorLen + (binaryData.Length * 8)); sb.Append(modeIndicator).Append(countIndicator); foreach (byte b in binaryData) { @@ -185,56 +211,51 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) } private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) + { + var bitArray = new BitArray(bitString.Length); + for (int i = 0; i < bitString.Length; i++) + { + bitArray[i] = bitString[i] == '1'; + } + return GenerateQrCode(bitArray, eccLevel, version); + } + + private static readonly BitArray _repeatingPattern = new BitArray( + new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true }); + + private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, int version) { //Fill up data code word var eccInfo = capacityECCTable.Single(x => x.Version == version && x.ErrorCorrectionLevel == eccLevel); var dataLength = eccInfo.TotalDataCodewords * 8; - var lengthDiff = dataLength - bitString.Length; + var lengthDiff = dataLength - bitArray.Length; if (lengthDiff > 0) - bitString += new string('0', Math.Min(lengthDiff, 4)); - if ((bitString.Length % 8) != 0) - bitString += new string('0', 8 - (bitString.Length % 8)); - while (bitString.Length < dataLength) - bitString += "1110110000010001"; - if (bitString.Length > dataLength) - bitString = bitString.Substring(0, dataLength); + { + // set 'write index' to end of existing bit array + var index = bitArray.Length; + // extend bit array to required length + bitArray.Length = dataLength; + // pad with 4 zeros (or less if lengthDiff < 4) + index += Math.Min(lengthDiff, 4); + // pad to nearest 8 bit boundary + if (index % 8 != 0) + index += 8 - (index % 8); + // pad with repeating pattern + var repeatingPatternIndex = 0; + while (index < dataLength) + { + bitArray[index++] = _repeatingPattern[repeatingPatternIndex++]; + if (repeatingPatternIndex >= _repeatingPattern.Length) + repeatingPatternIndex = 0; + } + } //Calculate error correction words var codeWordWithECC = new List(eccInfo.BlocksInGroup1 + eccInfo.BlocksInGroup2); - for (var i = 0; i < eccInfo.BlocksInGroup1; i++) - { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup1 * 8, eccInfo.CodewordsInGroup1 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add( - new CodewordBlock(1, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); - } - bitString = bitString.Substring(eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8); - for (var i = 0; i < eccInfo.BlocksInGroup2; i++) - { - var bitStr = bitString.Substring(i * eccInfo.CodewordsInGroup2 * 8, eccInfo.CodewordsInGroup2 * 8); - var bitBlockList = BinaryStringToBitBlockList(bitStr); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); - codeWordWithECC.Add(new CodewordBlock(2, - i + 1, - bitStr, - bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) - ); - } + AddCodeWordBlocks(1, eccInfo.BlocksInGroup1, eccInfo.CodewordsInGroup1, bitArray, 0, bitArray.Length); + int offset = eccInfo.BlocksInGroup1 * eccInfo.CodewordsInGroup1 * 8; + AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset); + //Interleave code words @@ -254,82 +275,181 @@ private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, in interleavedWordsSb.Append(codeBlock.ECCWords[i]); } interleavedWordsSb.Append(new string('0', remainderBits[version - 1])); - var interleavedData = interleavedWordsSb.ToString(); + var interleavedData = ToBitArray(interleavedWordsSb.ToString()); //Place interleaved data on module matrix var qr = new QRCodeData(version); - var blockedModules = new List(); - ModulePlacer.PlaceFinderPatterns(ref qr, ref blockedModules); - ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, ref blockedModules); - ModulePlacer.PlaceAlignmentPatterns(ref qr, alignmentPatternTable.Where(x => x.Version == version).Select(x => x.PatternPositions).First(), ref blockedModules); - ModulePlacer.PlaceTimingPatterns(ref qr, ref blockedModules); - ModulePlacer.PlaceDarkModule(ref qr, version, ref blockedModules); - ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, ref blockedModules); - ModulePlacer.PlaceDataWords(ref qr, interleavedData, ref blockedModules); - var maskVersion = ModulePlacer.MaskCode(ref qr, version, ref blockedModules, eccLevel); + var blockedModules = new List(17); + ModulePlacer.PlaceFinderPatterns(qr, blockedModules); + ModulePlacer.ReserveSeperatorAreas(qr.ModuleMatrix.Count, blockedModules); + ModulePlacer.PlaceAlignmentPatterns(qr, alignmentPatternTable[version].PatternPositions, blockedModules); + ModulePlacer.PlaceTimingPatterns(qr, blockedModules); + ModulePlacer.PlaceDarkModule(qr, version, blockedModules); + ModulePlacer.ReserveVersionAreas(qr.ModuleMatrix.Count, version, blockedModules); + ModulePlacer.PlaceDataWords(qr, interleavedData, blockedModules); + var maskVersion = ModulePlacer.MaskCode(qr, version, blockedModules, eccLevel); var formatStr = GetFormatString(eccLevel, maskVersion); - ModulePlacer.PlaceFormat(ref qr, formatStr); + ModulePlacer.PlaceFormat(qr, formatStr); if (version >= 7) { var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(ref qr, versionString); + ModulePlacer.PlaceVersion(qr, versionString); } - ModulePlacer.AddQuietZone(ref qr); + ModulePlacer.AddQuietZone(qr); return qr; + + void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count) + { + for (var i = 0; i < blocksInGroup; i++) + { + var bitStr = BitArrayToString(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); + // todo: combine next two lines to convert to byte array + //var newBitBlockLlist = BinaryStringToBitBlockByteList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); + var bitBlockList = BinaryStringToBitBlockList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); + var bitBlockListDec = BinaryStringListToDecList(bitBlockList); + // todo: combine next two lines to convert to byte array + var eccWordList = CalculateECCWords(bitStr, eccInfo); + var eccWordListDec = BinaryStringListToDecList(eccWordList); + // todo: update CodewordBlock constructor to take byte arrays + codeWordWithECC.Add(new CodewordBlock(blockNum, + i + 1, + bitStr, + bitBlockList, + eccWordList, + bitBlockListDec, + eccWordListDec) + ); + } + } + } + + private static string BitArrayToString(BitArray bitArray) => BitArrayToString(bitArray, 0, bitArray.Length); + + private static string BitArrayToString(BitArray bitArray, int offset, int count) + { + var bitStringChars = new char[count]; + for (var i = 0; i < count; i++) + bitStringChars[i] = bitArray[i + offset] ? '1' : '0'; + return new string(bitStringChars); } - private static string GetFormatString(ECCLevel level, int maskVersion) + private static readonly BitArray _getFormatGenerator = new BitArray(new bool[] { true, false, true, false, false, true, true, false, true, true, true }); + private static readonly BitArray _getFormatMask = new BitArray(new bool[] { true, false, true, false, true, false, false, false, false, false, true, false, false, true, false }); + private static BitArray GetFormatString(ECCLevel level, int maskVersion) { - var generator = "10100110111"; - var fStrMask = "101010000010010"; + var fStrEcc = new BitArray(15); // Total length including space for mask version and padding + WriteEccLevelAndVersion(); - var fStr = (level == ECCLevel.L) ? "01" : (level == ECCLevel.M) ? "00" : (level == ECCLevel.Q) ? "11" : "10"; - fStr += DecToBin(maskVersion, 3); - var fStrEcc = fStr.PadRight(15, '0').TrimStart('0'); - while (fStrEcc.Length > 10) + int index = 0; + int count = 15; + TrimLeadingZeros(); + while (count > 10) { - var sb = new StringBuilder(); - generator = generator.PadRight(fStrEcc.Length, '0'); - for (var i = 0; i < fStrEcc.Length; i++) - sb.Append((Convert.ToInt32(fStrEcc[i]) ^ Convert.ToInt32(generator[i])).ToString()); - fStrEcc = sb.ToString().TrimStart('0'); + for (var i = 0; i < _getFormatGenerator.Length; i++) + fStrEcc[index + i] ^= _getFormatGenerator[i]; + TrimLeadingZeros(); + } + ShiftTowardsBit0(fStrEcc, index); + fStrEcc.Length = 10 + 5; + ShiftAwayFromBit0(fStrEcc, (10 - count) + 5); + WriteEccLevelAndVersion(); + fStrEcc.Xor(_getFormatMask); + return fStrEcc; + + void WriteEccLevelAndVersion() + { + switch (level) + { + case ECCLevel.L: // 01 + fStrEcc[1] = true; + break; + case ECCLevel.H: // 10 + fStrEcc[0] = true; + break; + case ECCLevel.Q: // 11 + fStrEcc[0] = true; + fStrEcc[1] = true; + break; + default: // M: 00 + break; + } + int indexTemp = 2; + DecToBin(maskVersion, 3, fStrEcc, ref indexTemp); + } + + void TrimLeadingZeros() + { + while (!fStrEcc[index]) + { + index++; + count--; + } } - fStrEcc = fStrEcc.PadLeft(10, '0'); - fStr += fStrEcc; - var sbMask = new StringBuilder(); - for (var i = 0; i < fStr.Length; i++) - sbMask.Append((Convert.ToInt32(fStr[i]) ^ Convert.ToInt32(fStrMask[i])).ToString()); - return sbMask.ToString(); } - private static string GetVersionString(int version) + private static void ShiftTowardsBit0(BitArray fStrEcc, int num) { - var generator = "1111100100101"; +#if NETCOREAPP + fStrEcc.RightShift(num); // Shift towards bit 0 +#else + for (var i = 0; i < fStrEcc.Length - num; i++) + fStrEcc[i] = fStrEcc[i + num]; + for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++) + fStrEcc[i] = false; +#endif + } + + private static void ShiftAwayFromBit0(BitArray fStrEcc, int num) + { +#if NETCOREAPP + fStrEcc.LeftShift(num); // Shift away from bit 0 +#else + for (var i = fStrEcc.Length - 1; i >= num; i--) + fStrEcc[i] = fStrEcc[i - num]; + for (var i = 0; i < num; i++) + fStrEcc[i] = false; +#endif + } - var vStr = DecToBin(version, 6); - var vStrEcc = vStr.PadRight(18, '0').TrimStart('0'); - while (vStrEcc.Length > 12) + private static readonly BitArray _getVersionGenerator = new BitArray(new bool[] { true, true, true, true, true, false, false, true, false, false, true, false, true }); + private static BitArray GetVersionString(int version) + { + var vStr = new BitArray(18); + var index = 0; + DecToBin(version, 6, vStr, ref index); + var count = vStr.Length; + index = 0; + while (!vStr[index]) { - var sb = new StringBuilder(); - generator = generator.PadRight(vStrEcc.Length, '0'); - for (var i = 0; i < vStrEcc.Length; i++) - sb.Append((Convert.ToInt32(vStrEcc[i]) ^ Convert.ToInt32(generator[i])).ToString()); - vStrEcc = sb.ToString().TrimStart('0'); + index++; + count--; } - vStrEcc = vStrEcc.PadLeft(12, '0'); - vStr += vStrEcc; - + while (count > 12) + { + for (var i = 0; i < _getVersionGenerator.Length; i++) + vStr[index + i] ^= _getVersionGenerator[i]; + while (!vStr[index]) + { + index++; + count--; + } + } + ShiftTowardsBit0(vStr, index); + vStr.Length = 12 + 6; + ShiftAwayFromBit0(vStr, (12 - count) + 6); + index = 0; + DecToBin(version, 6, vStr, ref index); return vStr; } private static class ModulePlacer { - public static void AddQuietZone(ref QRCodeData qrCode) + public static void AddQuietZone(QRCodeData qrCode) { var quietLine = new bool[qrCode.ModuleMatrix.Count + 8]; for (var i = 0; i < quietLine.Length; i++) @@ -340,86 +460,71 @@ public static void AddQuietZone(ref QRCodeData qrCode) qrCode.ModuleMatrix.Add(new BitArray(quietLine)); for (var i = 4; i < qrCode.ModuleMatrix.Count - 4; i++) { - bool[] quietPart = { false, false, false, false }; - var tmpLine = new List(quietPart); - tmpLine.AddRange(qrCode.ModuleMatrix[i].Cast()); - tmpLine.AddRange(quietPart); - qrCode.ModuleMatrix[i] = new BitArray(tmpLine.ToArray()); + qrCode.ModuleMatrix[i].Length += 8; + ShiftAwayFromBit0(qrCode.ModuleMatrix[i], 4); } } - private static string ReverseString(string inp) - { - string newStr = string.Empty; - if (inp.Length > 0) - { - for (int i = inp.Length - 1; i >= 0; i--) - newStr += inp[i]; - } - return newStr; - } - - public static void PlaceVersion(ref QRCodeData qrCode, string versionStr) + public static void PlaceVersion(QRCodeData qrCode, BitArray versionStr) { var size = qrCode.ModuleMatrix.Count; - var vStr = ReverseString(versionStr); - for (var x = 0; x < 6; x++) { for (var y = 0; y < 3; y++) { - qrCode.ModuleMatrix[y + size - 11][x] = vStr[x * 3 + y] == '1'; - qrCode.ModuleMatrix[x][y + size - 11] = vStr[x * 3 + y] == '1'; + qrCode.ModuleMatrix[y + size - 11][x] = versionStr[17 - (x * 3 + y)]; + qrCode.ModuleMatrix[x][y + size - 11] = versionStr[17 - (x * 3 + y)]; } } } - public static void PlaceFormat(ref QRCodeData qrCode, string formatStr) + public static void PlaceFormat(QRCodeData qrCode, BitArray formatStr) { var size = qrCode.ModuleMatrix.Count; - var fStr = ReverseString(formatStr); - var modules = new[,] { - { 8, 0, size - 1, 8 }, - { 8, 1, size - 2, 8 }, - { 8, 2, size - 3, 8 }, - { 8, 3, size - 4, 8 }, - { 8, 4, size - 5, 8 }, - { 8, 5, size - 6, 8 }, - { 8, 7, size - 7, 8 }, - { 8, 8, size - 8, 8 }, - { 7, 8, 8, size - 7 }, - { 5, 8, 8, size - 6 }, - { 4, 8, 8, size - 5 }, - { 3, 8, 8, size - 4 }, - { 2, 8, 8, size - 3 }, - { 1, 8, 8, size - 2 }, - { 0, 8, 8, size - 1 } }; + + // { x1, y1, x2, y2 } i + // =============================== + // { 8, 0, size - 1, 8 }, // 0 + // { 8, 1, size - 2, 8 }, // 1 + // { 8, 2, size - 3, 8 }, // 2 + // { 8, 3, size - 4, 8 }, // 3 + // { 8, 4, size - 5, 8 }, // 4 + // { 8, 5, size - 6, 8 }, // 5 + // { 8, 7, size - 7, 8 }, // 6 + // { 8, 8, size - 8, 8 }, // 7 + // { 7, 8, 8, size - 7 }, // 8 + // { 5, 8, 8, size - 6 }, // 9 + // { 4, 8, 8, size - 5 }, // 10 + // { 3, 8, 8, size - 4 }, // 11 + // { 2, 8, 8, size - 3 }, // 12 + // { 1, 8, 8, size - 2 }, // 13 + // { 0, 8, 8, size - 1 } }; // 14 + for (var i = 0; i < 15; i++) { - var p1 = new Point(modules[i, 0], modules[i, 1]); - var p2 = new Point(modules[i, 2], modules[i, 3]); - qrCode.ModuleMatrix[p1.Y][p1.X] = fStr[i] == '1'; - qrCode.ModuleMatrix[p2.Y][p2.X] = fStr[i] == '1'; + // values computed to follow table above + var x1 = i < 8 ? 8 : i == 8 ? 7 : 14 - i; + var y1 = i < 6 ? i : i < 7 ? i + 1 : 8; + var x2 = i < 8 ? size - 1 - i : 8; + var y2 = i < 8 ? 8 : size - (15 - i); + + qrCode.ModuleMatrix[y1][x1] = formatStr[14 - i]; + qrCode.ModuleMatrix[y2][x2] = formatStr[14 - i]; } } - - public static int MaskCode(ref QRCodeData qrCode, int version, ref List blockedModules, ECCLevel eccLevel) + public static int MaskCode(QRCodeData qrCode, int version, List blockedModules, ECCLevel eccLevel) { int? selectedPattern = null; var patternScore = 0; var size = qrCode.ModuleMatrix.Count; - var methods = new Dictionary>(8) { - { 1, MaskPattern.Pattern1 }, {2, MaskPattern.Pattern2 }, {3, MaskPattern.Pattern3 }, {4, MaskPattern.Pattern4 }, - {5, MaskPattern.Pattern5 }, {6, MaskPattern.Pattern6 }, {7, MaskPattern.Pattern7 }, {8, MaskPattern.Pattern8 } - }; - - foreach (var pattern in methods) + var qrTemp = new QRCodeData(version); + foreach (var pattern in MaskPattern.Patterns) { - var qrTemp = new QRCodeData(version); + // reset qrTemp to qrCode for (var y = 0; y < size; y++) { for (var x = 0; x < size; x++) @@ -430,11 +535,11 @@ public static int MaskCode(ref QRCodeData qrCode, int version, ref List= 7) { var versionString = GetVersionString(version); - ModulePlacer.PlaceVersion(ref qrTemp, versionString); + ModulePlacer.PlaceVersion(qrTemp, versionString); } for (var x = 0; x < size; x++) @@ -454,7 +559,7 @@ public static int MaskCode(ref QRCodeData qrCode, int version, ref List score) { selectedPattern = pattern.Key; @@ -468,29 +573,26 @@ public static int MaskCode(ref QRCodeData qrCode, int version, ref List blockedModules) + public static void PlaceDataWords(QRCodeData qrCode, BitArray data, List blockedModules) { var size = qrCode.ModuleMatrix.Count; var up = true; - var datawords = new Queue(); - for (int i = 0; i< data.Length; i++) - { - datawords.Enqueue(data[i] != '0'); - } + var index = 0; + var count = data.Length; for (var x = size - 1; x >= 0; x = x - 2) { if (x == 6) @@ -501,83 +603,78 @@ public static void PlaceDataWords(ref QRCodeData qrCode, string data, ref List 0 && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); - if (datawords.Count > 0 && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); + if (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = data[index++]; + if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = data[index++]; } else { y = yMod - 1; - if (datawords.Count > 0 && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x] = datawords.Dequeue(); - if (datawords.Count > 0 && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) - qrCode.ModuleMatrix[y][x - 1] = datawords.Dequeue(); + if (index < count && !IsBlocked(new Rectangle(x, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x] = data[index++]; + if (index < count && x > 0 && !IsBlocked(new Rectangle(x - 1, y, 1, 1), blockedModules)) + qrCode.ModuleMatrix[y][x - 1] = data[index++]; } } up = !up; } } - public static void ReserveSeperatorAreas(int size, ref List blockedModules) + public static void ReserveSeperatorAreas(int size, List blockedModules) { - blockedModules.AddRange(new[]{ - new Rectangle(7, 0, 1, 8), - new Rectangle(0, 7, 7, 1), - new Rectangle(0, size-8, 8, 1), - new Rectangle(7, size-7, 1, 7), - new Rectangle(size-8, 0, 1, 8), - new Rectangle(size-7, 7, 7, 1) - }); + blockedModules.Add(new Rectangle(7, 0, 1, 8)); + blockedModules.Add(new Rectangle(0, 7, 7, 1)); + blockedModules.Add(new Rectangle(0, size - 8, 8, 1)); + blockedModules.Add(new Rectangle(7, size - 7, 1, 7)); + blockedModules.Add(new Rectangle(size - 8, 0, 1, 8)); + blockedModules.Add(new Rectangle(size - 7, 7, 7, 1)); } - public static void ReserveVersionAreas(int size, int version, ref List blockedModules) + public static void ReserveVersionAreas(int size, int version, List blockedModules) { - blockedModules.AddRange(new[]{ - new Rectangle(8, 0, 1, 6), - new Rectangle(8, 7, 1, 1), - new Rectangle(0, 8, 6, 1), - new Rectangle(7, 8, 2, 1), - new Rectangle(size-8, 8, 8, 1), - new Rectangle(8, size-7, 1, 7) - }); + blockedModules.Add(new Rectangle(8, 0, 1, 6)); + blockedModules.Add(new Rectangle(8, 7, 1, 1)); + blockedModules.Add(new Rectangle(0, 8, 6, 1)); + blockedModules.Add(new Rectangle(7, 8, 2, 1)); + blockedModules.Add(new Rectangle(size - 8, 8, 8, 1)); + blockedModules.Add(new Rectangle(8, size - 7, 1, 7)); if (version >= 7) { - blockedModules.AddRange(new[]{ - new Rectangle(size-11, 0, 3, 6), - new Rectangle(0, size-11, 6, 3) - }); + blockedModules.Add(new Rectangle(size - 11, 0, 3, 6)); + blockedModules.Add(new Rectangle(0, size - 11, 6, 3)); } } - public static void PlaceDarkModule(ref QRCodeData qrCode, int version, ref List blockedModules) + public static void PlaceDarkModule(QRCodeData qrCode, int version, List blockedModules) { qrCode.ModuleMatrix[4 * version + 9][8] = true; blockedModules.Add(new Rectangle(8, 4 * version + 9, 1, 1)); } - public static void PlaceFinderPatterns(ref QRCodeData qrCode, ref List blockedModules) + public static void PlaceFinderPatterns(QRCodeData qrCode, List blockedModules) { var size = qrCode.ModuleMatrix.Count; - int[] locations = { 0, 0, size - 7, 0, 0, size - 7 }; - for (var i = 0; i < 6; i = i + 2) + for (var i = 0; i < 3; i++) { + var locationX = i == 1 ? size - 7 : 0; + var locationY = i == 2 ? size - 7 : 0; for (var x = 0; x < 7; x++) { for (var y = 0; y < 7; y++) { if (!(((x == 1 || x == 5) && y > 0 && y < 6) || (x > 0 && x < 6 && (y == 1 || y == 5)))) { - qrCode.ModuleMatrix[y + locations[i + 1]][x + locations[i]] = true; + qrCode.ModuleMatrix[y + locationY][x + locationX] = true; } } } - blockedModules.Add(new Rectangle(locations[i], locations[i + 1], 7, 7)); + blockedModules.Add(new Rectangle(locationX, locationY, 7, 7)); } } - public static void PlaceAlignmentPatterns(ref QRCodeData qrCode, List alignmentPatternLocations, ref List blockedModules) + public static void PlaceAlignmentPatterns(QRCodeData qrCode, List alignmentPatternLocations, List blockedModules) { foreach (var loc in alignmentPatternLocations) { @@ -608,7 +705,7 @@ public static void PlaceAlignmentPatterns(ref QRCodeData qrCode, List ali } } - public static void PlaceTimingPatterns(ref QRCodeData qrCode, ref List blockedModules) + public static void PlaceTimingPatterns(QRCodeData qrCode, List blockedModules) { var size = qrCode.ModuleMatrix.Count; for (var i = 8; i < size - 8; i++) @@ -619,10 +716,8 @@ public static void PlaceTimingPatterns(ref QRCodeData qrCode, ref List blockedModules) private static class MaskPattern { + public static readonly Dictionary> Patterns = + new Dictionary>(8) { + { 1, MaskPattern.Pattern1 }, {2, MaskPattern.Pattern2 }, {3, MaskPattern.Pattern3 }, {4, MaskPattern.Pattern4 }, + { 5, MaskPattern.Pattern5 }, {6, MaskPattern.Pattern6 }, {7, MaskPattern.Pattern7 }, {8, MaskPattern.Pattern8 } + }; + public static bool Pattern1(int x, int y) { return (x + y) % 2 == 0; @@ -682,7 +783,7 @@ public static bool Pattern8(int x, int y) return (((x + y) % 2) + ((x * y) % 3)) % 2 == 0; } - public static int Score(ref QRCodeData qrCode) + public static int Score(QRCodeData qrCode) { int score1 = 0, score2 = 0, @@ -802,9 +903,9 @@ public static int Score(ref QRCodeData qrCode) blackModules++; var percent = (blackModules / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count)) * 100; - var prevMultipleOf5 = Math.Abs((int) Math.Floor(percent/5)*5 - 50)/5; - var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 -45)/5; - score4 = Math.Min(prevMultipleOf5, nextMultipleOf5)*10; + var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 50) / 5; + var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5; + score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; return score1 + score2 + score3 + score4; } @@ -824,7 +925,7 @@ private static List CalculateECCWords(string bitString, ECCInfo eccInfo) for (var i = 0; i < generatorPolynom.PolyItems.Count; i++) generatorPolynom.PolyItems[i] = new PolynomItem(generatorPolynom.PolyItems[i].Coefficient, - generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count-1)); + generatorPolynom.PolyItems[i].Exponent + (messagePolynom.PolyItems.Count - 1)); var leadTermSource = messagePolynom; for (var i = 0; (leadTermSource.PolyItems.Count > 0 && leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent > 0); i++) @@ -837,7 +938,7 @@ private static List CalculateECCWords(string bitString, ECCInfo eccInfo) else { var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); - resPoly = ConvertToDecNotation(resPoly); + ConvertToDecNotationInPlace(resPoly); resPoly = XORPolynoms(leadTermSource, resPoly); leadTermSource = resPoly; } @@ -847,7 +948,7 @@ private static List CalculateECCWords(string bitString, ECCInfo eccInfo) private static Polynom ConvertToAlphaNotation(Polynom poly) { - var newPoly = new Polynom(); + var newPoly = new Polynom(poly.PolyItems.Count); for (var i = 0; i < poly.PolyItems.Count; i++) newPoly.PolyItems.Add( new PolynomItem( @@ -857,12 +958,10 @@ private static Polynom ConvertToAlphaNotation(Polynom poly) return newPoly; } - private static Polynom ConvertToDecNotation(Polynom poly) + private static void ConvertToDecNotationInPlace(Polynom poly) { - var newPoly = new Polynom(); for (var i = 0; i < poly.PolyItems.Count; i++) - newPoly.PolyItems.Add(new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent)); - return newPoly; + poly.PolyItems[i] = new PolynomItem(GetIntValFromAlphaExp(poly.PolyItems[i].Coefficient), poly.PolyItems[i].Exponent); } private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) @@ -912,7 +1011,7 @@ private static bool IsInRange(char c, char min, char max) private static Polynom CalculateMessagePolynom(string bitString) { - var messagePol = new Polynom(); + var messagePol = new Polynom(bitString.Length / 8); for (var i = bitString.Length / 8 - 1; i >= 0; i--) { messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); @@ -924,18 +1023,15 @@ private static Polynom CalculateMessagePolynom(string bitString) private static Polynom CalculateGeneratorPolynom(int numEccWords) { - var generatorPolynom = new Polynom(); - generatorPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(0,0) - }); + var generatorPolynom = new Polynom(2); + generatorPolynom.PolyItems.Add(new PolynomItem(0, 1)); + generatorPolynom.PolyItems.Add(new PolynomItem(0, 0)); + var multiplierPolynom = new Polynom(numEccWords * 2); for (var i = 1; i <= numEccWords - 1; i++) { - var multiplierPolynom = new Polynom(); - multiplierPolynom.PolyItems.AddRange(new[]{ - new PolynomItem(0,1), - new PolynomItem(i,0) - }); + multiplierPolynom.PolyItems.Clear(); + multiplierPolynom.PolyItems.Add(new PolynomItem(0, 1)); + multiplierPolynom.PolyItems.Add(new PolynomItem(i, 0)); generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); } @@ -943,15 +1039,41 @@ private static Polynom CalculateGeneratorPolynom(int numEccWords) return generatorPolynom; } - private static List BinaryStringToBitBlockList(string bitString) + private static List BinaryStringToBitBlockList(BitArray bitString, int offset, int count) { const int blockSize = 8; - var numberOfBlocks = (int)Math.Ceiling(bitString.Length / (double)blockSize); + var numberOfBlocks = (int)Math.Ceiling(count / (double)blockSize); var blocklist = new List(numberOfBlocks); - for (int i = 0; i < bitString.Length; i += blockSize) + for (int i = 0; i < count; i += blockSize) { - blocklist.Add(bitString.Substring(i, blockSize)); + blocklist.Add(BitArrayToString(bitString, offset + i, blockSize)); + } + + return blocklist; + } + + private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int offset, int count) + { + const int blockSize = 8; + if (count % blockSize != 0) + throw new ArgumentException("Count must be a multiple of 8.", nameof(count)); + var numberOfBlocks = count / blockSize; + var blocklist = new byte[numberOfBlocks]; + + int j = 0; + count += offset; + for (int i = offset; i < count; i += blockSize) + { + blocklist[j++] = (byte)( + (bitString[i] ? 128 : 0) + + (bitString[i + 1] ? 64 : 0) + + (bitString[i + 2] ? 32 : 0) + + (bitString[i + 3] ? 16 : 0) + + (bitString[i + 4] ? 8 : 0) + + (bitString[i + 5] ? 4 : 0) + + (bitString[i + 6] ? 2 : 0) + + (bitString[i + 7] ? 1 : 0)); } return blocklist; @@ -967,6 +1089,17 @@ private static int BinToDec(string binStr) return Convert.ToInt32(binStr, 2); } + private static void DecToBin(int decNum, int bits, BitArray bitList, ref int index) + { + // Convert decNum to binary using a bitwise operation + for (int i = bits - 1; i >= 0; i--) + { + // Check each bit from most significant to least significant + bool bit = (decNum & (1 << i)) != 0; + bitList[index++] = bit; + } + } + private static string DecToBin(int decNum) { return Convert.ToString(decNum, 2); @@ -1013,7 +1146,7 @@ private static int GetCountIndicatorLength(int version, EncodingMode encMode) } } - private static int GetDataLength(EncodingMode encoding, string plainText, string codedText, bool forceUtf8) + private static int GetDataLength(EncodingMode encoding, string plainText, BitArray codedText, bool forceUtf8) { return forceUtf8 || IsUtf8(encoding, plainText, forceUtf8) ? (codedText.Length / 8) : plainText.Length; } @@ -1030,24 +1163,45 @@ private static bool IsValidISO(string input) return String.Equals(input, result); } - private static string PlainTextToBinary(string plainText, EncodingMode encMode, EciMode eciMode, bool utf8BOM, bool forceUtf8) + private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode, EciMode eciMode, bool utf8BOM, bool forceUtf8) { - switch(encMode) + switch (encMode) { case EncodingMode.Alphanumeric: return PlainTextToBinaryAlphanumeric(plainText); case EncodingMode.Numeric: - return PlainTextToBinaryNumeric(plainText); + return ToBitArray(PlainTextToBinaryNumeric(plainText)); case EncodingMode.Byte: - return PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8); + return ToBitArray(PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8)); case EncodingMode.Kanji: - return string.Empty; case EncodingMode.ECI: default: - return string.Empty; + return _emptyBitArray; } } + private static readonly BitArray _emptyBitArray = new BitArray(0); + + private static BitArray ToBitArray(string bitString) + { + var bitArray = new BitArray(bitString.Length); + for (var i = 0; i < bitString.Length; i++) + { + bitArray[i] = bitString[i] == '1'; + } + return bitArray; + } + + private static BitArray ToBitArray(string bitString, int offset, int count) + { + var bitArray = new BitArray(count); + for (var i = 0; i < count; i++) + { + bitArray[i] = bitString[i + offset] == '1'; + } + return bitArray; + } + private static string PlainTextToBinaryNumeric(string plainText) { var codeText = string.Empty; @@ -1071,20 +1225,22 @@ private static string PlainTextToBinaryNumeric(string plainText) return codeText; } - private static string PlainTextToBinaryAlphanumeric(string plainText) + private static BitArray PlainTextToBinaryAlphanumeric(string plainText) { - var codeText = string.Empty; - while (plainText.Length >= 2) + var codeText = new BitArray((plainText.Length / 2) * 11 + (plainText.Length & 1) * 6); + var codeIndex = 0; + var index = 0; + var count = plainText.Length; + while (count >= 2) { - var token = plainText.Substring(0, 2); - var dec = alphanumEncDict[token[0]] * 45 + alphanumEncDict[token[1]]; - codeText += DecToBin(dec, 11); - plainText = plainText.Substring(2); + var dec = alphanumEncDict[plainText[index++]] * 45 + alphanumEncDict[plainText[index++]]; + DecToBin(dec, 11, codeText, ref codeIndex); + count -= 2; } - if (plainText.Length > 0) + if (count > 0) { - codeText += DecToBin(alphanumEncDict[plainText[0]], 6); + DecToBin(alphanumEncDict[plainText[index]], 6, codeText, ref codeIndex); } return codeText; } @@ -1093,7 +1249,7 @@ private string PlainTextToBinaryECI(string plainText) { var codeText = string.Empty; byte[] _bytes = Encoding.GetEncoding("ascii").GetBytes(plainText); - foreach(byte _byte in _bytes) + foreach (byte _byte in _bytes) { codeText += DecToBin(_byte, 8); } @@ -1118,7 +1274,7 @@ private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, b codeBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(plainText); else { - switch(eciMode) + switch (eciMode) { case EciMode.Iso8859_1: codeBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(ConvertToIso8859(plainText, "ISO-8859-1")); @@ -1190,7 +1346,7 @@ private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, Po private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) { - var resultPolynom = new Polynom(); + var resultPolynom = new Polynom(polynomMultiplier.PolyItems.Count * polynomBase.PolyItems.Count); foreach (var polItemBase in polynomMultiplier.PolyItems) { foreach (var polItemMulti in polynomBase.PolyItems) @@ -1203,30 +1359,74 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno resultPolynom.PolyItems.Add(polItemRes); } } - var exponentsToGlue = resultPolynom.PolyItems.GroupBy(x => x.Exponent).Where(x => x.Count() > 1).Select(x => x.First().Exponent); - var toGlue = exponentsToGlue as IList ?? exponentsToGlue.ToList(); - var gluedPolynoms = new List(toGlue.Count); + var toGlue = GetNotUniqueExponents(resultPolynom.PolyItems); + var gluedPolynoms = new PolynomItem[toGlue.Length]; + var gluedPolynomsIndex = 0; foreach (var exponent in toGlue) { - var coefficient = resultPolynom.PolyItems.Where(x => x.Exponent == exponent).Aggregate(0, (current, polynomOld) - => current ^ GetIntValFromAlphaExp(polynomOld.Coefficient)); + var coefficient = 0; + foreach (var polynomOld in resultPolynom.PolyItems) + { + if (polynomOld.Exponent == exponent) + coefficient ^= GetIntValFromAlphaExp(polynomOld.Coefficient); + } + var polynomFixed = new PolynomItem(GetAlphaExpFromIntVal(coefficient), exponent); - gluedPolynoms.Add(polynomFixed); + gluedPolynoms[gluedPolynomsIndex++] = polynomFixed; } - resultPolynom.PolyItems.RemoveAll(x => toGlue.Contains(x.Exponent)); - resultPolynom.PolyItems.AddRange(gluedPolynoms); + for (int i = resultPolynom.PolyItems.Count - 1; i >= 0; i--) + if (toGlue.Contains(resultPolynom.PolyItems[i].Exponent)) + resultPolynom.PolyItems.RemoveAt(i); + foreach (var polynom in gluedPolynoms) + resultPolynom.PolyItems.Add(polynom); resultPolynom.PolyItems.Sort((x, y) => -x.Exponent.CompareTo(y.Exponent)); return resultPolynom; + + int[] GetNotUniqueExponents(List list) + { + var dic = new Dictionary(); + foreach (var row in list) + { +#if NETCOREAPP + if (dic.TryAdd(row.Exponent, false)) + dic[row.Exponent] = true; +#else + if (!dic.ContainsKey(row.Exponent)) + dic.Add(row.Exponent, false); + else + dic[row.Exponent] = true; +#endif + } + + int count = 0; + foreach (var row in dic) + { + if (row.Value) + count++; + } + + var result = new int[count]; + int i = 0; + foreach (var row in dic) + { + if (row.Value) + result[i++] = row.Key; + } + + return result; + } } private static int GetIntValFromAlphaExp(int exp) { - return galoisField.Find(alog => alog.ExponentAlpha == exp).IntegerValue; + return galoisFieldByExponentAlpha[exp]; } private static int GetAlphaExpFromIntVal(int intVal) { - return galoisField.Find(alog => alog.IntegerValue == intVal).ExponentAlpha; + if (intVal == 0) + throw new ArgumentOutOfRangeException(nameof(intVal)); + return galoisFieldByIntegerValue[intVal]; } private static int ShrinkAlphaExp(int alphaExp) @@ -1250,9 +1450,9 @@ private static Dictionary CreateAlphanumEncDict() return localAlphanumEncDict; } - private static List CreateAlignmentPatternTable() + private static Dictionary CreateAlignmentPatternTable() { - var localAlignmentPatternTable = new List(40); + var localAlignmentPatternTable = new Dictionary(40); for (var i = 0; i < (7 * 40); i = i + 7) { @@ -1273,9 +1473,10 @@ private static List CreateAlignmentPatternTable() } } - localAlignmentPatternTable.Add(new AlignmentPattern() + var version = (i + 7) / 7; + localAlignmentPatternTable.Add(version, new AlignmentPattern() { - Version = (i + 7) / 7, + Version = version, PatternPositions = points } ); @@ -1544,6 +1745,11 @@ public Polynom() this.PolyItems = new List(); } + public Polynom(int count) + { + this.PolyItems = new List(count); + } + public List PolyItems { get; set; } public override string ToString() @@ -1559,7 +1765,7 @@ public override string ToString() } } - private class Point + private readonly struct Point { public int X { get; } public int Y { get; } @@ -1570,7 +1776,7 @@ public Point(int x, int y) } } - private class Rectangle + private readonly struct Rectangle { public int X { get; } public int Y { get; } diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 29373ea7..e13298ad 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -19,16 +19,23 @@ public void validate_antilogtable() { var gen = new QRCodeGenerator(); - var checkString = string.Empty; - var gField = gen.GetType().GetField("galoisField", BindingFlags.NonPublic | BindingFlags.Static); - foreach (var listitem in (System.Collections.IEnumerable)gField.GetValue(gen)) + var checkString1 = string.Empty; + var gField1 = gen.GetType().GetField("galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + gField1.Length.ShouldBe(256); + for (int i = 0; i < gField1.Length; i++) { - foreach (PropertyInfo prop in listitem.GetType().GetProperties()) - checkString += prop.GetValue(listitem, null).ToString() + ","; + checkString1 += i + "," + gField1[i] + ",:"; + } + checkString1.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,:"); - checkString += ":"; + var gField2 = gen.GetType().GetField("galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + gField2.Length.ShouldBe(256); + var checkString2 = string.Empty; + for (int i = 0; i < gField2.Length; i++) + { + checkString2 += i + "," + gField2[i] + ",:"; } - 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,:"); + checkString2.ShouldBe("0,0,:1,0,:2,1,:3,25,:4,2,:5,50,:6,26,:7,198,:8,3,:9,223,:10,51,:11,238,:12,27,:13,104,:14,199,:15,75,:16,4,:17,100,:18,224,:19,14,:20,52,:21,141,:22,239,:23,129,:24,28,:25,193,:26,105,:27,248,:28,200,:29,8,:30,76,:31,113,:32,5,:33,138,:34,101,:35,47,:36,225,:37,36,:38,15,:39,33,:40,53,:41,147,:42,142,:43,218,:44,240,:45,18,:46,130,:47,69,:48,29,:49,181,:50,194,:51,125,:52,106,:53,39,:54,249,:55,185,:56,201,:57,154,:58,9,:59,120,:60,77,:61,228,:62,114,:63,166,:64,6,:65,191,:66,139,:67,98,:68,102,:69,221,:70,48,:71,253,:72,226,:73,152,:74,37,:75,179,:76,16,:77,145,:78,34,:79,136,:80,54,:81,208,:82,148,:83,206,:84,143,:85,150,:86,219,:87,189,:88,241,:89,210,:90,19,:91,92,:92,131,:93,56,:94,70,:95,64,:96,30,:97,66,:98,182,:99,163,:100,195,:101,72,:102,126,:103,110,:104,107,:105,58,:106,40,:107,84,:108,250,:109,133,:110,186,:111,61,:112,202,:113,94,:114,155,:115,159,:116,10,:117,21,:118,121,:119,43,:120,78,:121,212,:122,229,:123,172,:124,115,:125,243,:126,167,:127,87,:128,7,:129,112,:130,192,:131,247,:132,140,:133,128,:134,99,:135,13,:136,103,:137,74,:138,222,:139,237,:140,49,:141,197,:142,254,:143,24,:144,227,:145,165,:146,153,:147,119,:148,38,:149,184,:150,180,:151,124,:152,17,:153,68,:154,146,:155,217,:156,35,:157,32,:158,137,:159,46,:160,55,:161,63,:162,209,:163,91,:164,149,:165,188,:166,207,:167,205,:168,144,:169,135,:170,151,:171,178,:172,220,:173,252,:174,190,:175,97,:176,242,:177,86,:178,211,:179,171,:180,20,:181,42,:182,93,:183,158,:184,132,:185,60,:186,57,:187,83,:188,71,:189,109,:190,65,:191,162,:192,31,:193,45,:194,67,:195,216,:196,183,:197,123,:198,164,:199,118,:200,196,:201,23,:202,73,:203,236,:204,127,:205,12,:206,111,:207,246,:208,108,:209,161,:210,59,:211,82,:212,41,:213,157,:214,85,:215,170,:216,251,:217,96,:218,134,:219,177,:220,187,:221,204,:222,62,:223,90,:224,203,:225,89,:226,95,:227,176,:228,156,:229,169,:230,160,:231,81,:232,11,:233,245,:234,22,:235,235,:236,122,:237,117,:238,44,:239,215,:240,79,:241,174,:242,213,:243,233,:244,230,:245,231,:246,173,:247,232,:248,116,:249,214,:250,244,:251,234,:252,168,:253,80,:254,88,:255,175,:"); } [Fact] From 2682aa8ed66f98c45aa469351ffa7bfa89c3364e Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 00:11:10 -0400 Subject: [PATCH 02/30] Update tests --- QRCoderTests/QRGeneratorTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index e13298ad..9c69c57e 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -19,14 +19,14 @@ public void validate_antilogtable() { var gen = new QRCodeGenerator(); - var checkString1 = string.Empty; - var gField1 = gen.GetType().GetField("galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); - gField1.Length.ShouldBe(256); - for (int i = 0; i < gField1.Length; i++) + var checkString = string.Empty; + var gField = gen.GetType().GetField("galoisFieldByExponentAlpha", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); + gField.Length.ShouldBe(256); + for (int i = 0; i < gField.Length; i++) { - checkString1 += i + "," + gField1[i] + ",:"; + checkString += i + "," + gField[i] + ",:"; } - checkString1.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,:"); + 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 = gen.GetType().GetField("galoisFieldByIntegerValue", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null).ShouldBeOfType(); gField2.Length.ShouldBe(256); From 4ab38e4c20ad52ea91068b0440a63e135be72933 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 07:41:24 -0400 Subject: [PATCH 03/30] Cache GetCountIndicatorLength --- QRCoder/QRCodeGenerator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 240360ac..9dcf172f 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -156,9 +156,11 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo } } + var countIndicatorLength = GetCountIndicatorLength(version, encoding); + var completeBitArrayLength = (eciMode != EciMode.Default ? 16 : 4) + // Mode indicator - GetCountIndicatorLength(version, encoding) + // Count indicator + countIndicatorLength + // Count indicator codedText.Length; // Data var completeBitArray = new BitArray(completeBitArrayLength); @@ -171,7 +173,7 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo } DecToBin((int)encoding, 4, completeBitArray, ref completeBitArrayIndex); // write count indicator - DecToBin(dataInputLength, GetCountIndicatorLength(version, encoding), completeBitArray, ref completeBitArrayIndex); + DecToBin(dataInputLength, countIndicatorLength, completeBitArray, ref completeBitArrayIndex); // write data for (int i = 0; i < codedText.Length; i++) { From d9f5e18013e22897109e37cda1956115e974bd18 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 16:44:58 -0400 Subject: [PATCH 04/30] Simplify GenerateQrCode overload --- QRCoder/QRCodeGenerator.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 9dcf172f..71c093c6 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -214,13 +214,8 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) { - var bitArray = new BitArray(bitString.Length); - for (int i = 0; i < bitString.Length; i++) - { - bitArray[i] = bitString[i] == '1'; + return GenerateQrCode(ToBitArray(bitString), eccLevel, version); } - return GenerateQrCode(bitArray, eccLevel, version); - } private static readonly BitArray _repeatingPattern = new BitArray( new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true }); From b3570c322034fd17be857620797196fd1993ac8e Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 16:46:31 -0400 Subject: [PATCH 05/30] Simplifications --- QRCoder/QRCodeGenerator.cs | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 71c093c6..5edab22f 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -343,12 +343,12 @@ private static BitArray GetFormatString(ECCLevel level, int maskVersion) int index = 0; int count = 15; - TrimLeadingZeros(); + TrimLeadingZeros(); // modifies index and count while (count > 10) { for (var i = 0; i < _getFormatGenerator.Length; i++) fStrEcc[index + i] ^= _getFormatGenerator[i]; - TrimLeadingZeros(); + TrimLeadingZeros(); // modifies index and count } ShiftTowardsBit0(fStrEcc, index); fStrEcc.Length = 10 + 5; @@ -1179,15 +1179,7 @@ private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode private static readonly BitArray _emptyBitArray = new BitArray(0); - private static BitArray ToBitArray(string bitString) - { - var bitArray = new BitArray(bitString.Length); - for (var i = 0; i < bitString.Length; i++) - { - bitArray[i] = bitString[i] == '1'; - } - return bitArray; - } + private static BitArray ToBitArray(string bitString) => ToBitArray(bitString, 0, bitString.Length); private static BitArray ToBitArray(string bitString, int offset, int count) { @@ -1296,7 +1288,7 @@ private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, b private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) { - var resultPolynom = new Polynom(); + var resultPolynom = new Polynom(Math.Max(messagePolynom.PolyItems.Count, resPolynom.PolyItems.Count)); Polynom longPoly, shortPoly; if (messagePolynom.PolyItems.Count >= resPolynom.PolyItems.Count) { @@ -1327,7 +1319,7 @@ private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, PolynomItem leadTerm, int lowerExponentBy) { - var resultPolynom = new Polynom(); + var resultPolynom = new Polynom(genPolynom.PolyItems.Count); foreach (var polItemBase in genPolynom.PolyItems) { var polItemRes = new PolynomItem( @@ -1735,13 +1727,8 @@ public PolynomItem(int coefficient, int exponent) public int Exponent { get; } } - private class Polynom + private struct Polynom { - public Polynom() - { - this.PolyItems = new List(); - } - public Polynom(int count) { this.PolyItems = new List(count); From a64ec0bb67958c2c3ff552f793c1026a6440aa75 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 16:52:23 -0400 Subject: [PATCH 06/30] Optimize CalculateECCWords further --- QRCoder/QRCodeGenerator.cs | 235 ++++++++++++++++++++++++++++++++----- 1 file changed, 203 insertions(+), 32 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 5edab22f..db435a79 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -215,7 +215,7 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) { return GenerateQrCode(ToBitArray(bitString), eccLevel, version); - } + } private static readonly BitArray _repeatingPattern = new BitArray( new[] { true, true, true, false, true, true, false, false, false, false, false, true, false, false, false, true }); @@ -301,25 +301,21 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, BitArray bitArray2, int offset2, int count) { + var groupLength = codewordsInGroup * 8; for (var i = 0; i < blocksInGroup; i++) { - var bitStr = BitArrayToString(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); + //var bitStr = BitArrayToString(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); // todo: combine next two lines to convert to byte array - //var newBitBlockLlist = BinaryStringToBitBlockByteList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); - var bitBlockList = BinaryStringToBitBlockList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); - var bitBlockListDec = BinaryStringListToDecList(bitBlockList); + //var newBitBlockList = BinaryStringToBitBlockByteList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); + var bitBlockList = BinaryStringToBitBlockList(bitArray2, offset2, groupLength); // todo: combine next two lines to convert to byte array - var eccWordList = CalculateECCWords(bitStr, eccInfo); - var eccWordListDec = BinaryStringListToDecList(eccWordList); + var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo); // todo: update CodewordBlock constructor to take byte arrays - codeWordWithECC.Add(new CodewordBlock(blockNum, - i + 1, - bitStr, + codeWordWithECC.Add(new CodewordBlock( bitBlockList, - eccWordList, - bitBlockListDec, - eccWordListDec) + eccWordList) ); + offset2 += groupLength; } } } @@ -910,10 +906,10 @@ public static int Score(QRCodeData qrCode) } - private static List CalculateECCWords(string bitString, ECCInfo eccInfo) + private static List CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) { var eccWords = eccInfo.ECCPerBlock; - var messagePolynom = CalculateMessagePolynom(bitString); + var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); var generatorPolynom = CalculateGeneratorPolynom(eccWords); for (var i = 0; i < messagePolynom.PolyItems.Count; i++) @@ -1006,13 +1002,13 @@ private static bool IsInRange(char c, char min, char max) return (uint)(c - min) <= (uint)(max - min); } - private static Polynom CalculateMessagePolynom(string bitString) + private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int count) { - var messagePol = new Polynom(bitString.Length / 8); - for (var i = bitString.Length / 8 - 1; i >= 0; i--) + var messagePol = new Polynom(count / 8); + for (var i = count / 8 - 1; i >= 0; i--) { - messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); - bitString = bitString.Remove(0, 8); + messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); + offset += 8; } return messagePol; } @@ -1086,6 +1082,16 @@ private static int BinToDec(string binStr) return Convert.ToInt32(binStr, 2); } + private static int BinToDec(BitArray bitArray, int offset, int count) + { + var ret = 0; + for (int i = 0; i < count; i++) + { + ret ^= bitArray[offset + i] ? 1 << (count - i - 1) : 0; + } + return ret; + } + private static void DecToBin(int decNum, int bits, BitArray bitList, ref int index) { // Convert decNum to binary using a bitwise operation @@ -1636,25 +1642,14 @@ private struct AlignmentPattern private struct CodewordBlock { - public CodewordBlock(int groupNumber, int blockNumber, string bitString, List codeWords, - List eccWords, List codeWordsInt, List eccWordsInt) + public CodewordBlock(List codeWords, List eccWords) { - this.GroupNumber = groupNumber; - this.BlockNumber = blockNumber; - this.BitString = bitString; this.CodeWords = codeWords; this.ECCWords = eccWords; - this.CodeWordsInt = codeWordsInt; - this.ECCWordsInt = eccWordsInt; } - public int GroupNumber { get; } - public int BlockNumber { get; } - public string BitString { get; } public List CodeWords { get; } - public List CodeWordsInt { get; } public List ECCWords { get; } - public List ECCWordsInt { get; } } private struct ECCInfo @@ -1780,5 +1775,181 @@ public void Dispose() { // left for back-compat } + + public ref struct BitArraySegment + { + public BitArraySegment(BitArray bitArray) + : this(bitArray, 0, bitArray.Length) + { + } + + public BitArraySegment(BitArray bitArray, int offset, int count) + { + _bitArray = bitArray; + _offset = offset; + _count = count; + } + + public BitArraySegment(string bitString) + : this(ToBitArray(bitString)) + { + } + + private BitArray _bitArray { get; set; } + private int _offset { get; set; } + private int _count { get; set; } + + public void TrimLeadingZeros() + { + while (_count > 0 && !_bitArray[_offset]) + { + _offset++; + _count--; + } + } + + public void PadRight(int newLength) + { + if (newLength < _count) + throw new ArgumentOutOfRangeException(nameof(newLength), "Value is shorter than existing data length."); + if (newLength == _count) + return; + + // Calculate the total required length of the bit array + int requiredLength = _offset + newLength; + + // Set the existing unused bits to zero which will be utilized after adjusting the length + for (int i = _offset + _count; i < Math.Min(_bitArray.Length, requiredLength); i++) + _bitArray[i] = false; + + // Check if the current bit array is sufficient + if (requiredLength > _bitArray.Length) + { + // Extend the length of the bit array, setting new values to zero + _bitArray.Length = requiredLength; + } + + // Update the count to reflect the new size + _count = newLength; + } + + public void PadLeft(int newLength) + { + if (newLength < _count) + throw new ArgumentOutOfRangeException(nameof(newLength), "Value is shorter than existing data length."); + if (newLength == _count) + return; + + if (_bitArray.Length < newLength) + _bitArray.Length = newLength; + + // Calculate new offset and count (new offset may be negative) + var oldOffset = _offset; + _offset -= newLength - _count; + _count = newLength; + + // Set the existing unused bits to zero which will be utilized after adjusting the length + for (int i = Math.Max(_offset, 0); i < oldOffset; i++) + _bitArray[i] = false; + + if (_offset < 0) + { + ShiftRightInternal(_bitArray, -_offset); + _offset += -_offset; + } + } + + private void ZeroAlign() + { + if (_offset > 0) + { + ShiftLeftInternal(_bitArray, _offset); + _offset = 0; + } + } + + public void Xor(BitArray bitArray) + { + if (bitArray.Length < _count) + throw new ArgumentOutOfRangeException(nameof(bitArray), "Value is shorter than existing data length."); + + ZeroAlign(); + _bitArray.Xor(bitArray); + } + + private static void ShiftLeftInternal(BitArray fStrEcc, int num) + { +#if NETCOREAPP + fStrEcc.RightShift(num); // Shift towards bit 0 +#else + for (var i = 0; i < fStrEcc.Length - num; i++) + fStrEcc[i] = fStrEcc[i + num]; + for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++) + fStrEcc[i] = false; +#endif + } + + private static void ShiftRightInternal(BitArray fStrEcc, int num) + { +#if NETCOREAPP + fStrEcc.LeftShift(num); // Shift away from bit 0 +#else + for (var i = fStrEcc.Length - 1; i >= num; i--) + fStrEcc[i] = fStrEcc[i - num]; + for (var i = 0; i < num; i++) + fStrEcc[i] = false; +#endif + } + + public bool this[int index] + { + get => _bitArray[_offset + index]; + set => _bitArray[_offset + index] = value; + } + + public int Length + { + get => _count; + set + { + if (value < 0) + throw new ArgumentOutOfRangeException(nameof(value), "Value cannot be negative."); + if (value > _count) + PadRight(value); + else + _count = value; + } + } + + public void Clear() + { + _offset = 0; + _count = 0; + } + + public void Zero() + { + _bitArray.SetAll(false); + } + + public void AppendTo(BitArraySegment bitArraySegment) + { + var offset = bitArraySegment.Length; + bitArraySegment.Length += _count; + for (int i = 0; i < _count; i++) + bitArraySegment[offset + i] = this[i]; + } + + public void CopyTo(BitArraySegment bitArraySegment, int offset) + { + for (int i = 0; i < _count; i++) + bitArraySegment[offset + i] = this[i]; + } + + public override string ToString() + { + return BitArrayToString(_bitArray, _offset, _count); + } + } } } From 00cdd114892a334e1dedcf6992ed55f1c67df55b Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 17:30:38 -0400 Subject: [PATCH 07/30] Optimize interleave code words --- QRCoder/QRCodeGenerator.cs | 55 ++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index db435a79..96243237 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -254,8 +254,40 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i AddCodeWordBlocks(2, eccInfo.BlocksInGroup2, eccInfo.CodewordsInGroup2, bitArray, offset, bitArray.Length - offset); + //Calculate interleaved code word lengths + int interleavedLength = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.CodeWords.Length > i) + interleavedLength += 8; + } + for (var i = 0; i < eccInfo.ECCPerBlock; i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + interleavedLength += 8; + } + interleavedLength += remainderBits[version - 1]; //Interleave code words + var interleavedData = new BitArray(interleavedLength); + int pos = 0; + for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.CodeWords.Length > i) + DecToBin(codeBlock.CodeWords[i], 8, interleavedData, ref pos); + } + for (var i = 0; i < eccInfo.ECCPerBlock; i++) + { + foreach (var codeBlock in codeWordWithECC) + if (codeBlock.ECCWords.Length > i) + DecToBin(codeBlock.ECCWords[i], 8, interleavedData, ref pos); + } + + + /* var interleavedWordsSb = new StringBuilder(); for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) { @@ -273,6 +305,7 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i } interleavedWordsSb.Append(new string('0', remainderBits[version - 1])); var interleavedData = ToBitArray(interleavedWordsSb.ToString()); + */ //Place interleaved data on module matrix @@ -304,13 +337,8 @@ void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, Bi var groupLength = codewordsInGroup * 8; for (var i = 0; i < blocksInGroup; i++) { - //var bitStr = BitArrayToString(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); - // todo: combine next two lines to convert to byte array - //var newBitBlockList = BinaryStringToBitBlockByteList(bitArray2, i * codewordsInGroup * 8 + offset2, codewordsInGroup * 8); - var bitBlockList = BinaryStringToBitBlockList(bitArray2, offset2, groupLength); - // todo: combine next two lines to convert to byte array + var bitBlockList = BinaryStringToBitBlockByteList(bitArray2, offset2, groupLength); var eccWordList = CalculateECCWords(bitArray2, offset2, groupLength, eccInfo); - // todo: update CodewordBlock constructor to take byte arrays codeWordWithECC.Add(new CodewordBlock( bitBlockList, eccWordList) @@ -906,7 +934,7 @@ public static int Score(QRCodeData qrCode) } - private static List CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) + private static byte[] CalculateECCWords(BitArray bitArray, int offset, int count, ECCInfo eccInfo) { var eccWords = eccInfo.ECCPerBlock; var messagePolynom = CalculateMessagePolynom(bitArray, offset, count); @@ -936,7 +964,10 @@ private static List CalculateECCWords(BitArray bitArray, int offset, int leadTermSource = resPoly; } } - return leadTermSource.PolyItems.Select(x => DecToBin(x.Coefficient, 8)).ToList(); + var ret = new byte[leadTermSource.PolyItems.Count]; + for (var i = 0; i < leadTermSource.PolyItems.Count; i++) + ret[i] = (byte)leadTermSource.PolyItems[i].Coefficient; + return ret; } private static Polynom ConvertToAlphaNotation(Polynom poly) @@ -1642,14 +1673,14 @@ private struct AlignmentPattern private struct CodewordBlock { - public CodewordBlock(List codeWords, List eccWords) + public CodewordBlock(byte[] codeWords, byte[] eccWords) { this.CodeWords = codeWords; this.ECCWords = eccWords; } - public List CodeWords { get; } - public List ECCWords { get; } + public byte[] CodeWords { get; } + public byte[] ECCWords { get; } } private struct ECCInfo @@ -1776,6 +1807,7 @@ public void Dispose() // left for back-compat } + /* public ref struct BitArraySegment { public BitArraySegment(BitArray bitArray) @@ -1951,5 +1983,6 @@ public override string ToString() return BitArrayToString(_bitArray, _offset, _count); } } + */ } } From 25d53c2d9c18a483f213ded6fc74cc74912760f1 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 18:23:17 -0400 Subject: [PATCH 08/30] Remaining BitArray conversions --- QRCoder/QRCodeGenerator.cs | 132 ++++++++++++------------------- QRCoderTests/QRGeneratorTests.cs | 20 +++++ 2 files changed, 70 insertions(+), 82 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 96243237..2fef97ee 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using System.Collections; +using System.Globalization; namespace QRCoder { @@ -195,26 +196,13 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) { int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); - string modeIndicator = DecToBin((int)EncodingMode.Byte, 4); int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); - string countIndicator = DecToBin(binaryData.Length, countIndicatorLen); - - StringBuilder sb = new StringBuilder(capacity: 4 + countIndicatorLen + (binaryData.Length * 8)); - sb.Append(modeIndicator).Append(countIndicator); - foreach (byte b in binaryData) - { - sb.Append(DecToBin(b, 8)); - - } - string bitString = sb.ToString(); - - - return GenerateQrCode(bitString, eccLevel, version); - } + var bitArray = ToBitArray(binaryData, 4 + countIndicatorLen); + var index = 0; + DecToBin((int)EncodingMode.Byte, 4, bitArray, ref index); + DecToBin(binaryData.Length, countIndicatorLen, bitArray, ref index); - private static QRCodeData GenerateQrCode(string bitString, ECCLevel eccLevel, int version) - { - return GenerateQrCode(ToBitArray(bitString), eccLevel, version); + return GenerateQrCode(bitArray, eccLevel, version); } private static readonly BitArray _repeatingPattern = new BitArray( @@ -348,16 +336,6 @@ void AddCodeWordBlocks(int blockNum, int blocksInGroup, int codewordsInGroup, Bi } } - private static string BitArrayToString(BitArray bitArray) => BitArrayToString(bitArray, 0, bitArray.Length); - - private static string BitArrayToString(BitArray bitArray, int offset, int count) - { - var bitStringChars = new char[count]; - for (var i = 0; i < count; i++) - bitStringChars[i] = bitArray[i + offset] ? '1' : '0'; - return new string(bitStringChars); - } - private static readonly BitArray _getFormatGenerator = new BitArray(new bool[] { true, false, true, false, false, true, true, false, true, true, true }); private static readonly BitArray _getFormatMask = new BitArray(new bool[] { true, false, true, false, true, false, false, false, false, false, true, false, false, true, false }); private static BitArray GetFormatString(ECCLevel level, int maskVersion) @@ -1063,20 +1041,6 @@ private static Polynom CalculateGeneratorPolynom(int numEccWords) return generatorPolynom; } - private static List BinaryStringToBitBlockList(BitArray bitString, int offset, int count) - { - const int blockSize = 8; - var numberOfBlocks = (int)Math.Ceiling(count / (double)blockSize); - var blocklist = new List(numberOfBlocks); - - for (int i = 0; i < count; i += blockSize) - { - blocklist.Add(BitArrayToString(bitString, offset + i, blockSize)); - } - - return blocklist; - } - private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int offset, int count) { const int blockSize = 8; @@ -1204,9 +1168,9 @@ private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode case EncodingMode.Alphanumeric: return PlainTextToBinaryAlphanumeric(plainText); case EncodingMode.Numeric: - return ToBitArray(PlainTextToBinaryNumeric(plainText)); + return PlainTextToBinaryNumeric(plainText); case EncodingMode.Byte: - return ToBitArray(PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8)); + return PlainTextToBinaryByte(plainText, eciMode, utf8BOM, forceUtf8); case EncodingMode.Kanji: case EncodingMode.ECI: default: @@ -1216,39 +1180,38 @@ private static BitArray PlainTextToBinary(string plainText, EncodingMode encMode private static readonly BitArray _emptyBitArray = new BitArray(0); - private static BitArray ToBitArray(string bitString) => ToBitArray(bitString, 0, bitString.Length); - - private static BitArray ToBitArray(string bitString, int offset, int count) + private static BitArray PlainTextToBinaryNumeric(string plainText) { - var bitArray = new BitArray(count); - for (var i = 0; i < count; i++) - { - bitArray[i] = bitString[i + offset] == '1'; - } - return bitArray; - } - - private static string PlainTextToBinaryNumeric(string plainText) - { - var codeText = string.Empty; - while (plainText.Length >= 3) + var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); + var index = 0; + for (int i = 0; (i + 2) < plainText.Length; i += 3) { - var dec = Convert.ToInt32(plainText.Substring(0, 3)); - codeText += DecToBin(dec, 10); - plainText = plainText.Substring(3); - +#if NET5_0_OR_GREATER + var dec = int.Parse(plainText.AsSpan(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); +#else + var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); +#endif + DecToBin(dec, 10, bitArray, ref index); } - if (plainText.Length == 2) + if (plainText.Length % 3 == 2) { - var dec = Convert.ToInt32(plainText); - codeText += DecToBin(dec, 7); +#if NET5_0_OR_GREATER + var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); +#else + var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); +#endif + DecToBin(dec, 7, bitArray, ref index); } - else if (plainText.Length == 1) + else if (plainText.Length % 3 == 1) { - var dec = Convert.ToInt32(plainText); - codeText += DecToBin(dec, 4); +#if NET5_0_OR_GREATER + var dec = int.Parse(plainText.AsSpan(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); +#else + var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); +#endif + DecToBin(dec, 4, bitArray, ref index); } - return codeText; + return bitArray; } private static BitArray PlainTextToBinaryAlphanumeric(string plainText) @@ -1271,15 +1234,11 @@ private static BitArray PlainTextToBinaryAlphanumeric(string plainText) return codeText; } - private string PlainTextToBinaryECI(string plainText) + private BitArray PlainTextToBinaryECI(string plainText) { var codeText = string.Empty; byte[] _bytes = Encoding.GetEncoding("ascii").GetBytes(plainText); - foreach (byte _byte in _bytes) - { - codeText += DecToBin(_byte, 8); - } - return codeText; + return ToBitArray(_bytes); } private static string ConvertToIso8859(string value, string Iso = "ISO-8859-2") @@ -1291,10 +1250,9 @@ private static string ConvertToIso8859(string value, string Iso = "ISO-8859-2") return iso.GetString(isoBytes); } - private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, bool utf8BOM, bool forceUtf8) + private static BitArray PlainTextToBinaryByte(string plainText, EciMode eciMode, bool utf8BOM, bool forceUtf8) { byte[] codeBytes; - var codeText = string.Empty; if (IsValidISO(plainText) && !forceUtf8) codeBytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(plainText); @@ -1316,12 +1274,22 @@ private static string PlainTextToBinaryByte(string plainText, EciMode eciMode, b } } - foreach (var b in codeBytes) - codeText += DecToBin(b, 8); - - return codeText; + return ToBitArray(codeBytes); } + private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) + { + var bitArray = new BitArray(byteArray.Length * 8 + prefixZeros); + for (var i = 0; i < byteArray.Length; i++) + { + var byteVal = byteArray[i]; + for (var j = 0; j < 8; j++) + { + bitArray[i * 8 + j + prefixZeros] = (byteVal & (1 << (7 - j))) != 0; + } + } + return bitArray; + } private static Polynom XORPolynoms(Polynom messagePolynom, Polynom resPolynom) { diff --git a/QRCoderTests/QRGeneratorTests.cs b/QRCoderTests/QRGeneratorTests.cs index 9c69c57e..49872334 100644 --- a/QRCoderTests/QRGeneratorTests.cs +++ b/QRCoderTests/QRGeneratorTests.cs @@ -110,6 +110,26 @@ public void can_encode_numeric() result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000001110110000001010101100000000000110111100001101110000000000101111010011000001111000000000011101111100010011010000000000000000111110010101100000000111111100010111110001000000001000001000011101110010000000010111010101110110110100000000101110101011100011100000000001011101001100010001110000000010000010101001101010100000000111111101101000001110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); } + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_numeric_2() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("1234567", QRCodeGenerator.ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111011111011111110000000010000010010100100000100000000101110100110001011101000000001011101001110010111010000000010111010001010101110100000000100000100001101000001000000001111111010101011111110000000000000000111110000000000000000110110100110101000001000000000100000000101010111100000000010110110100001101000000000000101110001101000001111000000001110111111000010010010000000000000000100110010011100000000111111100100111111101000000001000001000111101110110000000010111010110110110110100000000101110101101100011100000000001011101000100010011110000000010000010100001101010100000000111111101111000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } + + [Fact] + [Category("QRGenerator/TextEncoding")] + public void can_encode_numeric_3() + { + var gen = new QRCodeGenerator(); + var qrData = gen.CreateQrCode("12345678901", QRCodeGenerator.ECCLevel.L); + var result = string.Join("", qrData.ModuleMatrix.Select(x => x.ToBitString()).ToArray()); + result.ShouldBe("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111010111011111110000000010000010001100100000100000000101110101101001011101000000001011101011001010111010000000010111010100100101110100000000100000100111101000001000000001111111010101011111110000000000000000000110000000000000000111100101111110011101000000001110010101011110011110000000010011010000100000010000000000010010010111001110001000000000101101011001001000100000000000000000111100100100100000000111111100111100101101000000001000001001100001101010000000010111010001011111000100000000101110101011001011010000000001011101011001011011000000000010000010111001001101100000000111111101000010010010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } + [Fact] [Category("QRGenerator/TextEncoding")] public void can_encode_alphanumeric() From 818bef8f8d405a1766dc0774d602d4a324d0fd6e Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 18:23:38 -0400 Subject: [PATCH 09/30] Remove BitArraySegment --- QRCoder/QRCodeGenerator.cs | 178 ------------------------------------- 1 file changed, 178 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 2fef97ee..028f5a8d 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1774,183 +1774,5 @@ public void Dispose() { // left for back-compat } - - /* - public ref struct BitArraySegment - { - public BitArraySegment(BitArray bitArray) - : this(bitArray, 0, bitArray.Length) - { - } - - public BitArraySegment(BitArray bitArray, int offset, int count) - { - _bitArray = bitArray; - _offset = offset; - _count = count; - } - - public BitArraySegment(string bitString) - : this(ToBitArray(bitString)) - { - } - - private BitArray _bitArray { get; set; } - private int _offset { get; set; } - private int _count { get; set; } - - public void TrimLeadingZeros() - { - while (_count > 0 && !_bitArray[_offset]) - { - _offset++; - _count--; - } - } - - public void PadRight(int newLength) - { - if (newLength < _count) - throw new ArgumentOutOfRangeException(nameof(newLength), "Value is shorter than existing data length."); - if (newLength == _count) - return; - - // Calculate the total required length of the bit array - int requiredLength = _offset + newLength; - - // Set the existing unused bits to zero which will be utilized after adjusting the length - for (int i = _offset + _count; i < Math.Min(_bitArray.Length, requiredLength); i++) - _bitArray[i] = false; - - // Check if the current bit array is sufficient - if (requiredLength > _bitArray.Length) - { - // Extend the length of the bit array, setting new values to zero - _bitArray.Length = requiredLength; - } - - // Update the count to reflect the new size - _count = newLength; - } - - public void PadLeft(int newLength) - { - if (newLength < _count) - throw new ArgumentOutOfRangeException(nameof(newLength), "Value is shorter than existing data length."); - if (newLength == _count) - return; - - if (_bitArray.Length < newLength) - _bitArray.Length = newLength; - - // Calculate new offset and count (new offset may be negative) - var oldOffset = _offset; - _offset -= newLength - _count; - _count = newLength; - - // Set the existing unused bits to zero which will be utilized after adjusting the length - for (int i = Math.Max(_offset, 0); i < oldOffset; i++) - _bitArray[i] = false; - - if (_offset < 0) - { - ShiftRightInternal(_bitArray, -_offset); - _offset += -_offset; - } - } - - private void ZeroAlign() - { - if (_offset > 0) - { - ShiftLeftInternal(_bitArray, _offset); - _offset = 0; - } - } - - public void Xor(BitArray bitArray) - { - if (bitArray.Length < _count) - throw new ArgumentOutOfRangeException(nameof(bitArray), "Value is shorter than existing data length."); - - ZeroAlign(); - _bitArray.Xor(bitArray); - } - - private static void ShiftLeftInternal(BitArray fStrEcc, int num) - { -#if NETCOREAPP - fStrEcc.RightShift(num); // Shift towards bit 0 -#else - for (var i = 0; i < fStrEcc.Length - num; i++) - fStrEcc[i] = fStrEcc[i + num]; - for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++) - fStrEcc[i] = false; -#endif - } - - private static void ShiftRightInternal(BitArray fStrEcc, int num) - { -#if NETCOREAPP - fStrEcc.LeftShift(num); // Shift away from bit 0 -#else - for (var i = fStrEcc.Length - 1; i >= num; i--) - fStrEcc[i] = fStrEcc[i - num]; - for (var i = 0; i < num; i++) - fStrEcc[i] = false; -#endif - } - - public bool this[int index] - { - get => _bitArray[_offset + index]; - set => _bitArray[_offset + index] = value; - } - - public int Length - { - get => _count; - set - { - if (value < 0) - throw new ArgumentOutOfRangeException(nameof(value), "Value cannot be negative."); - if (value > _count) - PadRight(value); - else - _count = value; - } - } - - public void Clear() - { - _offset = 0; - _count = 0; - } - - public void Zero() - { - _bitArray.SetAll(false); - } - - public void AppendTo(BitArraySegment bitArraySegment) - { - var offset = bitArraySegment.Length; - bitArraySegment.Length += _count; - for (int i = 0; i < _count; i++) - bitArraySegment[offset + i] = this[i]; - } - - public void CopyTo(BitArraySegment bitArraySegment, int offset) - { - for (int i = 0; i < _count; i++) - bitArraySegment[offset + i] = this[i]; - } - - public override string ToString() - { - return BitArrayToString(_bitArray, _offset, _count); - } - } - */ } } From 6d2f6a168ba34f19b67b7ea1b801b83c31cb50ce Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 18:25:39 -0400 Subject: [PATCH 10/30] Tweak Polynom.ToString --- QRCoder/QRCodeGenerator.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 028f5a8d..1202ab89 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1733,13 +1733,16 @@ public Polynom(int count) public override string ToString() { var sb = new StringBuilder(); - //this.PolyItems.ForEach(x => sb.Append("a^" + x.Coefficient + "*x^" + x.Exponent + " + ")); + foreach (var polyItem in this.PolyItems) { sb.Append("a^" + polyItem.Coefficient + "*x^" + polyItem.Exponent + " + "); } - return sb.ToString().TrimEnd(new[] { ' ', '+' }); + if (sb.Length > 0) + sb.Length -= 3; + + return sb.ToString(); } } From 061869c1f46fcab3aa00fa1d32171811dadc1866 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Tue, 30 Apr 2024 23:47:58 -0400 Subject: [PATCH 11/30] Updates --- QRCoder/QRCodeGenerator.cs | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 1202ab89..4681b144 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -142,14 +142,14 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo var codedText = PlainTextToBinary(plainText, encoding, eciMode, utf8BOM, forceUtf8); var dataInputLength = GetDataLength(encoding, plainText, codedText, forceUtf8); int version = requestedVersion; + int minVersion = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); if (version == -1) { - version = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); + version = minVersion; } else { //Version was passed as fixed version via parameter. Thus let's check if chosen version is valid. - var minVersion = GetVersion(dataInputLength + (eciMode != EciMode.Default ? 2 : 0), encoding, eccLevel); if (minVersion > version) { var maxSizeByte = capacityTable[version - 1].Details.First(x => x.ErrorCorrectionLevel == eccLevel).CapacityDict[encoding]; @@ -157,14 +157,12 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo } } + var modeIndicatorLength = eciMode != EciMode.Default ? 16 : 4; var countIndicatorLength = GetCountIndicatorLength(version, encoding); - - var completeBitArrayLength = - (eciMode != EciMode.Default ? 16 : 4) + // Mode indicator - countIndicatorLength + // Count indicator - codedText.Length; // Data + var completeBitArrayLength = modeIndicatorLength + countIndicatorLength + codedText.Length; var completeBitArray = new BitArray(completeBitArrayLength); + // write mode indicator var completeBitArrayIndex = 0; if (eciMode != EciMode.Default) @@ -197,7 +195,9 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) int version = GetVersion(binaryData.Length, EncodingMode.Byte, eccLevel); int countIndicatorLen = GetCountIndicatorLength(version, EncodingMode.Byte); - var bitArray = ToBitArray(binaryData, 4 + countIndicatorLen); + // Convert byte array to bit array, with prefix padding for mode indicator and count indicator + var bitArray = ToBitArray(binaryData, prefixZeros: 4 + countIndicatorLen); + // Add mode indicator and count indicator var index = 0; DecToBin((int)EncodingMode.Byte, 4, bitArray, ref index); DecToBin(binaryData.Length, countIndicatorLen, bitArray, ref index); @@ -274,28 +274,6 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i DecToBin(codeBlock.ECCWords[i], 8, interleavedData, ref pos); } - - /* - var interleavedWordsSb = new StringBuilder(); - for (var i = 0; i < Math.Max(eccInfo.CodewordsInGroup1, eccInfo.CodewordsInGroup2); i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.CodeWords.Count > i) - interleavedWordsSb.Append(codeBlock.CodeWords[i]); - } - - - for (var i = 0; i < eccInfo.ECCPerBlock; i++) - { - foreach (var codeBlock in codeWordWithECC) - if (codeBlock.ECCWords.Count > i) - interleavedWordsSb.Append(codeBlock.ECCWords[i]); - } - interleavedWordsSb.Append(new string('0', remainderBits[version - 1])); - var interleavedData = ToBitArray(interleavedWordsSb.ToString()); - */ - - //Place interleaved data on module matrix var qr = new QRCodeData(version); var blockedModules = new List(17); From 3b18a12c936c0a5d23e91a91e980771bd2baead6 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Wed, 1 May 2024 17:42:20 -0400 Subject: [PATCH 12/30] Delete unused code --- QRCoder/QRCodeGenerator.cs | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 4681b144..360a3c36 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1045,16 +1045,6 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off return blocklist; } - private static List BinaryStringListToDecList(List binaryStringList) - { - return binaryStringList.Select(binaryString => BinToDec(binaryString)).ToList(); - } - - private static int BinToDec(string binStr) - { - return Convert.ToInt32(binStr, 2); - } - private static int BinToDec(BitArray bitArray, int offset, int count) { var ret = 0; @@ -1081,12 +1071,6 @@ private static string DecToBin(int decNum) return Convert.ToString(decNum, 2); } - private static string DecToBin(int decNum, int padLeftUpTo) - { - var binStr = DecToBin(decNum); - return binStr.PadLeft(padLeftUpTo, '0'); - } - private static int GetCountIndicatorLength(int version, EncodingMode encMode) { if (version < 10) @@ -1212,13 +1196,6 @@ private static BitArray PlainTextToBinaryAlphanumeric(string plainText) return codeText; } - private BitArray PlainTextToBinaryECI(string plainText) - { - var codeText = string.Empty; - byte[] _bytes = Encoding.GetEncoding("ascii").GetBytes(plainText); - return ToBitArray(_bytes); - } - private static string ConvertToIso8859(string value, string Iso = "ISO-8859-2") { Encoding iso = Encoding.GetEncoding(Iso); @@ -1315,7 +1292,6 @@ private static Polynom MultiplyGeneratorPolynomByLeadterm(Polynom genPolynom, Po return resultPolynom; } - private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polynomMultiplier) { var resultPolynom = new Polynom(polynomMultiplier.PolyItems.Count * polynomBase.PolyItems.Count); @@ -1456,7 +1432,6 @@ private static Dictionary CreateAlignmentPatternTable() return localAlignmentPatternTable; } - private static List CreateCapacityECCTable() { var localCapacityECCTable = new List(160); From 6ca45b473b026406e9c18e9fac2481fe58271162 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Wed, 1 May 2024 17:43:30 -0400 Subject: [PATCH 13/30] Delete unused method --- QRCoder/QRCodeGenerator.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 360a3c36..c90aecb2 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1066,11 +1066,6 @@ private static void DecToBin(int decNum, int bits, BitArray bitList, ref int ind } } - private static string DecToBin(int decNum) - { - return Convert.ToString(decNum, 2); - } - private static int GetCountIndicatorLength(int version, EncodingMode encMode) { if (version < 10) From 1e52e19ab83369b0198bf1efe5ff77b469dac4ba Mon Sep 17 00:00:00 2001 From: Shane32 Date: Wed, 1 May 2024 17:45:44 -0400 Subject: [PATCH 14/30] Add comment --- QRCoder/QRCodeGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index c90aecb2..19de55c0 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1229,6 +1229,7 @@ private static BitArray PlainTextToBinaryByte(string plainText, EciMode eciMode, private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) { + // new BitArray(byteArray) is not used because it reverses the bit order within each byte var bitArray = new BitArray(byteArray.Length * 8 + prefixZeros); for (var i = 0; i < byteArray.Length; i++) { From cdc14322f262ab9f561a66eed22f6970cfef428a Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 08:55:06 -0400 Subject: [PATCH 15/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 19de55c0..d20f0401 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -223,8 +223,8 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i // pad with 4 zeros (or less if lengthDiff < 4) index += Math.Min(lengthDiff, 4); // pad to nearest 8 bit boundary - if (index % 8 != 0) - index += 8 - (index % 8); + if ((uint)index % 8 != 0) + index += 8 - (int)((uint)index % 8); // pad with repeating pattern var repeatingPatternIndex = 0; while (index < dataLength) From c3432d192e16b3ae9b7a715e65f9b9e6c06918d0 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 08:56:46 -0400 Subject: [PATCH 16/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index d20f0401..f57367ef 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -884,7 +884,7 @@ public static int Score(QRCodeData qrCode) var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5; score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; - return score1 + score2 + score3 + score4; + return (score1 + score2) + (score3 + score4); } } From 65e91be917c477ca8f0a9e1b5fdb6fbb9e19e8a3 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 08:57:30 -0400 Subject: [PATCH 17/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index f57367ef..1a933f67 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1009,6 +1009,7 @@ private static Polynom CalculateGeneratorPolynom(int numEccWords) var multiplierPolynom = new Polynom(numEccWords * 2); for (var i = 1; i <= numEccWords - 1; i++) { + // Re-use multiplierPolynom, so clear it's content. multiplierPolynom.PolyItems.Clear(); multiplierPolynom.PolyItems.Add(new PolynomItem(0, 1)); multiplierPolynom.PolyItems.Add(new PolynomItem(i, 0)); From fd49cca94970c03c05b3bb0b49c91f9abdcfb548 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 08:58:26 -0400 Subject: [PATCH 18/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 1a933f67..610149c9 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1025,7 +1025,7 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off const int blockSize = 8; if (count % blockSize != 0) throw new ArgumentException("Count must be a multiple of 8.", nameof(count)); - var numberOfBlocks = count / blockSize; + var numberOfBlocks = (int)((uint)count / blockSize); var blocklist = new byte[numberOfBlocks]; int j = 0; From 1049cecad601374dd9f2517f2dcbb489fa6ca151 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 09:02:20 -0400 Subject: [PATCH 19/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 610149c9..b1bb1878 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1104,7 +1104,7 @@ private static int GetCountIndicatorLength(int version, EncodingMode encMode) private static int GetDataLength(EncodingMode encoding, string plainText, BitArray codedText, bool forceUtf8) { - return forceUtf8 || IsUtf8(encoding, plainText, forceUtf8) ? (codedText.Length / 8) : plainText.Length; + return forceUtf8 || IsUtf8(encoding, plainText, forceUtf8) ? (int)((uint)codedText.Length / 8) : plainText.Length; } private static bool IsUtf8(EncodingMode encoding, string plainText, bool forceUtf8) From 59e32c74d74ca39e11c2889cceccf805760ec92e Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 09:02:49 -0400 Subject: [PATCH 20/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index b1bb1878..e294bbc6 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1142,7 +1142,7 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) { var bitArray = new BitArray(plainText.Length / 3 * 10 + (plainText.Length % 3 == 1 ? 4 : plainText.Length % 3 == 2 ? 7 : 0)); var index = 0; - for (int i = 0; (i + 2) < plainText.Length; i += 3) + for (int i = 0; i < plainText.Length - 2; i += 3) { #if NET5_0_OR_GREATER var dec = int.Parse(plainText.AsSpan(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); From 27f3611a4efc4cd6db83a4f79d6b1bc034aa7e5b Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 09:03:21 -0400 Subject: [PATCH 21/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index e294bbc6..8c523063 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1231,7 +1231,7 @@ private static BitArray PlainTextToBinaryByte(string plainText, EciMode eciMode, private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) { // new BitArray(byteArray) is not used because it reverses the bit order within each byte - var bitArray = new BitArray(byteArray.Length * 8 + prefixZeros); + var bitArray = new BitArray((int)((uint)byteArray.Length * 8) + prefixZeros); for (var i = 0; i < byteArray.Length; i++) { var byteVal = byteArray[i]; From 25b139b2cc1b2cf11350b5fc41362bf0c2dfa63b Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 2 May 2024 09:03:42 -0400 Subject: [PATCH 22/30] Update QRCoder/QRCodeGenerator.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 8c523063..564eaa97 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1237,7 +1237,7 @@ private static BitArray ToBitArray(byte[] byteArray, int prefixZeros = 0) var byteVal = byteArray[i]; for (var j = 0; j < 8; j++) { - bitArray[i * 8 + j + prefixZeros] = (byteVal & (1 << (7 - j))) != 0; + bitArray[(int)((uint)i * 8) + j + prefixZeros] = (byteVal & (1 << (7 - j))) != 0; } } return bitArray; From 4bcfadb043154d5b71da9c982cf848faad1efd3d Mon Sep 17 00:00:00 2001 From: Shane32 Date: Thu, 2 May 2024 22:47:54 -0400 Subject: [PATCH 23/30] Make galois field constants --- QRCoder/QRCodeGenerator.cs | 43 ++------------------------------------ 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 564eaa97..b23d3b30 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -18,8 +18,8 @@ public class QRCodeGenerator : IDisposable private static readonly Dictionary alignmentPatternTable = CreateAlignmentPatternTable(); private static readonly List capacityECCTable = CreateCapacityECCTable(); private static readonly List capacityTable = CreateCapacityTable(); - private static readonly int[] galoisFieldByExponentAlpha; - private static readonly int[] galoisFieldByIntegerValue; + 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 }; private static readonly Dictionary alphanumEncDict = CreateAlphanumEncDict(); public enum EciMode @@ -37,19 +37,6 @@ public QRCodeGenerator() { } - static QRCodeGenerator() - { - var galoisField = CreateAntilogTable(); - galoisFieldByExponentAlpha = new int[256]; - galoisFieldByIntegerValue = new int[256]; - for (int i = 255; i >= 0; i--) // the value 1 occurs twice in the galois field, so we start from the end to get the correct value - { - var entry = galoisField[i]; - galoisFieldByExponentAlpha[entry.ExponentAlpha] = checked((byte)entry.IntegerValue); - galoisFieldByIntegerValue[entry.IntegerValue] = checked((byte)entry.ExponentAlpha); - } - } - /// /// Calculates the QR code data which than can be used in one of the rendering classes to generate a graphical representation. /// @@ -1536,21 +1523,6 @@ private static List CreateCapacityTable() return localCapacityTable; } - private static List CreateAntilogTable() - { - var localGaloisField = new List(256); - - int gfItem = 1; - for (var i = 0; i < 256; i++) - { - localGaloisField.Add(new Antilog(i, gfItem)); - gfItem *= 2; - if (gfItem > 255) - gfItem ^= 285; - } - return localGaloisField; - } - /// /// Error correction level. These define the tolerance levels for how much of the code can be lost before the code cannot be recovered. /// @@ -1648,17 +1620,6 @@ public VersionInfoDetails(ECCLevel errorCorrectionLevel, Dictionary CapacityDict { get; } } - private struct Antilog - { - public Antilog(int exponentAlpha, int integerValue) - { - this.ExponentAlpha = exponentAlpha; - this.IntegerValue = integerValue; - } - public int ExponentAlpha { get; } - public int IntegerValue { get; } - } - private struct PolynomItem { public PolynomItem(int coefficient, int exponent) From c3d9d8b0ba9ee1bf4e214f6d9ccc23d9ad744f08 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Thu, 2 May 2024 22:54:52 -0400 Subject: [PATCH 24/30] Optimize DecToBin --- QRCoder/QRCodeGenerator.cs | 45 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index b23d3b30..9e40f489 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -4,6 +4,7 @@ using System.Text; using System.Collections; using System.Globalization; +using System.Runtime.CompilerServices; namespace QRCoder { @@ -154,12 +155,12 @@ public static QRCodeData GenerateQrCode(string plainText, ECCLevel eccLevel, boo var completeBitArrayIndex = 0; if (eciMode != EciMode.Default) { - DecToBin((int)EncodingMode.ECI, 4, completeBitArray, ref completeBitArrayIndex); - DecToBin((int)eciMode, 8, completeBitArray, ref completeBitArrayIndex); + completeBitArrayIndex = DecToBin((int)EncodingMode.ECI, 4, completeBitArray, completeBitArrayIndex); + completeBitArrayIndex = DecToBin((int)eciMode, 8, completeBitArray, completeBitArrayIndex); } - DecToBin((int)encoding, 4, completeBitArray, ref completeBitArrayIndex); + completeBitArrayIndex = DecToBin((int)encoding, 4, completeBitArray, completeBitArrayIndex); // write count indicator - DecToBin(dataInputLength, countIndicatorLength, completeBitArray, ref completeBitArrayIndex); + completeBitArrayIndex = DecToBin(dataInputLength, countIndicatorLength, completeBitArray, completeBitArrayIndex); // write data for (int i = 0; i < codedText.Length; i++) { @@ -185,9 +186,8 @@ public static QRCodeData GenerateQrCode(byte[] binaryData, ECCLevel eccLevel) // Convert byte array to bit array, with prefix padding for mode indicator and count indicator var bitArray = ToBitArray(binaryData, prefixZeros: 4 + countIndicatorLen); // Add mode indicator and count indicator - var index = 0; - DecToBin((int)EncodingMode.Byte, 4, bitArray, ref index); - DecToBin(binaryData.Length, countIndicatorLen, bitArray, ref index); + var index = DecToBin((int)EncodingMode.Byte, 4, bitArray, 0); + DecToBin(binaryData.Length, countIndicatorLen, bitArray, index); return GenerateQrCode(bitArray, eccLevel, version); } @@ -252,13 +252,13 @@ private static QRCodeData GenerateQrCode(BitArray bitArray, ECCLevel eccLevel, i { foreach (var codeBlock in codeWordWithECC) if (codeBlock.CodeWords.Length > i) - DecToBin(codeBlock.CodeWords[i], 8, interleavedData, ref pos); + pos = DecToBin(codeBlock.CodeWords[i], 8, interleavedData, pos); } for (var i = 0; i < eccInfo.ECCPerBlock; i++) { foreach (var codeBlock in codeWordWithECC) if (codeBlock.ECCWords.Length > i) - DecToBin(codeBlock.ECCWords[i], 8, interleavedData, ref pos); + pos = DecToBin(codeBlock.ECCWords[i], 8, interleavedData, pos); } //Place interleaved data on module matrix @@ -341,10 +341,12 @@ void WriteEccLevelAndVersion() default: // M: 00 break; } - int indexTemp = 2; - DecToBin(maskVersion, 3, fStrEcc, ref indexTemp); + DecToBin(maskVersion, 3, fStrEcc, 2); } +#if NETCOREAPP + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif void TrimLeadingZeros() { while (!fStrEcc[index]) @@ -384,10 +386,9 @@ private static void ShiftAwayFromBit0(BitArray fStrEcc, int num) private static BitArray GetVersionString(int version) { var vStr = new BitArray(18); - var index = 0; - DecToBin(version, 6, vStr, ref index); + DecToBin(version, 6, vStr, 0); var count = vStr.Length; - index = 0; + var index = 0; while (!vStr[index]) { index++; @@ -406,8 +407,7 @@ private static BitArray GetVersionString(int version) ShiftTowardsBit0(vStr, index); vStr.Length = 12 + 6; ShiftAwayFromBit0(vStr, (12 - count) + 6); - index = 0; - DecToBin(version, 6, vStr, ref index); + DecToBin(version, 6, vStr, 0); return vStr; } @@ -1043,7 +1043,7 @@ private static int BinToDec(BitArray bitArray, int offset, int count) return ret; } - private static void DecToBin(int decNum, int bits, BitArray bitList, ref int index) + private static int DecToBin(int decNum, int bits, BitArray bitList, int index) { // Convert decNum to binary using a bitwise operation for (int i = bits - 1; i >= 0; i--) @@ -1052,6 +1052,7 @@ private static void DecToBin(int decNum, int bits, BitArray bitList, ref int ind bool bit = (decNum & (1 << i)) != 0; bitList[index++] = bit; } + return index; } private static int GetCountIndicatorLength(int version, EncodingMode encMode) @@ -1136,7 +1137,7 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) #else var dec = int.Parse(plainText.Substring(i, 3), NumberStyles.None, CultureInfo.InvariantCulture); #endif - DecToBin(dec, 10, bitArray, ref index); + index = DecToBin(dec, 10, bitArray, index); } if (plainText.Length % 3 == 2) { @@ -1145,7 +1146,7 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) #else var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 2), NumberStyles.None, CultureInfo.InvariantCulture); #endif - DecToBin(dec, 7, bitArray, ref index); + index = DecToBin(dec, 7, bitArray, index); } else if (plainText.Length % 3 == 1) { @@ -1154,7 +1155,7 @@ private static BitArray PlainTextToBinaryNumeric(string plainText) #else var dec = int.Parse(plainText.Substring(plainText.Length / 3 * 3, 1), NumberStyles.None, CultureInfo.InvariantCulture); #endif - DecToBin(dec, 4, bitArray, ref index); + index = DecToBin(dec, 4, bitArray, index); } return bitArray; } @@ -1168,13 +1169,13 @@ private static BitArray PlainTextToBinaryAlphanumeric(string plainText) while (count >= 2) { var dec = alphanumEncDict[plainText[index++]] * 45 + alphanumEncDict[plainText[index++]]; - DecToBin(dec, 11, codeText, ref codeIndex); + codeIndex = DecToBin(dec, 11, codeText, codeIndex); count -= 2; } if (count > 0) { - DecToBin(alphanumEncDict[plainText[index]], 6, codeText, ref codeIndex); + DecToBin(alphanumEncDict[plainText[index]], 6, codeText, codeIndex); } return codeText; } From 6ffdcad9c6e3808b0fb8be78174bfdc1ba8b6f30 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Thu, 2 May 2024 23:11:01 -0400 Subject: [PATCH 25/30] Optimize score calculations --- QRCoder/QRCodeGenerator.cs | 40 +++++++++++++++----------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 9e40f489..8fa952ce 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Globalization; using System.Runtime.CompilerServices; +using System.Reflection; namespace QRCoder { @@ -310,12 +311,12 @@ private static BitArray GetFormatString(ECCLevel level, int maskVersion) int index = 0; int count = 15; - TrimLeadingZeros(); // modifies index and count + TrimLeadingZeros(fStrEcc, ref index, ref count); while (count > 10) { for (var i = 0; i < _getFormatGenerator.Length; i++) fStrEcc[index + i] ^= _getFormatGenerator[i]; - TrimLeadingZeros(); // modifies index and count + TrimLeadingZeros(fStrEcc, ref index, ref count); } ShiftTowardsBit0(fStrEcc, index); fStrEcc.Length = 10 + 5; @@ -343,19 +344,18 @@ void WriteEccLevelAndVersion() } DecToBin(maskVersion, 3, fStrEcc, 2); } + } #if NETCOREAPP - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - void TrimLeadingZeros() + private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count) + { + while (!fStrEcc[index]) { - while (!fStrEcc[index]) - { - index++; - count--; - } + index++; + count--; } - } private static void ShiftTowardsBit0(BitArray fStrEcc, int num) @@ -389,20 +389,12 @@ private static BitArray GetVersionString(int version) DecToBin(version, 6, vStr, 0); var count = vStr.Length; var index = 0; - while (!vStr[index]) - { - index++; - count--; - } + TrimLeadingZeros(vStr, ref index, ref count); while (count > 12) { for (var i = 0; i < _getVersionGenerator.Length; i++) vStr[index + i] ^= _getVersionGenerator[i]; - while (!vStr[index]) - { - index++; - count--; - } + TrimLeadingZeros(vStr, ref index, ref count); } ShiftTowardsBit0(vStr, index); vStr.Length = 12 + 6; @@ -860,15 +852,15 @@ public static int Score(QRCodeData qrCode) } //Penalty 4 - double blackModules = 0; + int blackModules = 0; foreach (var row in qrCode.ModuleMatrix) foreach (bool bit in row) if (bit) blackModules++; - var percent = (blackModules / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count)) * 100; - var prevMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 50) / 5; - var nextMultipleOf5 = Math.Abs((int)Math.Floor(percent / 5) * 5 - 45) / 5; + var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); + var prevMultipleOf5 = Math.Abs(percentDiv5 - 10); + var nextMultipleOf5 = Math.Abs(percentDiv5 - 9); score4 = Math.Min(prevMultipleOf5, nextMultipleOf5) * 10; return (score1 + score2) + (score3 + score4); From 48ae1ce56e57a5767e1d5e4dfb96a2331b5f2730 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Thu, 2 May 2024 23:15:37 -0400 Subject: [PATCH 26/30] Use throw helpers --- QRCoder/QRCodeGenerator.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 8fa952ce..8f3765fe 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -970,8 +970,8 @@ private static bool IsInRange(char c, char min, char max) private static Polynom CalculateMessagePolynom(BitArray bitArray, int offset, int count) { - var messagePol = new Polynom(count / 8); - for (var i = count / 8 - 1; i >= 0; i--) + var messagePol = new Polynom(count /= 8); + for (var i = count - 1; i >= 0; i--) { messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitArray, offset, 8), i)); offset += 8; @@ -1003,7 +1003,7 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off { const int blockSize = 8; if (count % blockSize != 0) - throw new ArgumentException("Count must be a multiple of 8.", nameof(count)); + ThrowArgumentException(nameof(count)); var numberOfBlocks = (int)((uint)count / blockSize); var blocklist = new byte[numberOfBlocks]; @@ -1023,6 +1023,8 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off } return blocklist; + + void ThrowArgumentException(string paramName) => throw new ArgumentOutOfRangeException(paramName, "Count must be a multiple of 8."); } private static int BinToDec(BitArray bitArray, int offset, int count) @@ -1350,8 +1352,10 @@ private static int GetIntValFromAlphaExp(int exp) private static int GetAlphaExpFromIntVal(int intVal) { if (intVal == 0) - throw new ArgumentOutOfRangeException(nameof(intVal)); + ThrowArgumentOutOfRangeException(nameof(intVal)); return galoisFieldByIntegerValue[intVal]; + + void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); } private static int ShrinkAlphaExp(int alphaExp) From 2bdfd041b6cff5bdcbae9aba4fb457978a258748 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Thu, 2 May 2024 23:55:57 -0400 Subject: [PATCH 27/30] Eliminate foreach across BitArray, implement IEquatable for Point --- QRCoder/QRCodeGenerator.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 8f3765fe..2bd7b41d 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -853,9 +853,9 @@ public static int Score(QRCodeData qrCode) //Penalty 4 int blackModules = 0; - foreach (var row in qrCode.ModuleMatrix) - foreach (bool bit in row) - if (bit) + foreach (var bitArray in qrCode.ModuleMatrix) + for (var x = 0; x < size; x++) + if (bitArray[x]) blackModules++; var percentDiv5 = blackModules * 20 / (qrCode.ModuleMatrix.Count * qrCode.ModuleMatrix.Count); @@ -1311,7 +1311,7 @@ private static Polynom MultiplyAlphaPolynoms(Polynom polynomBase, Polynom polyno int[] GetNotUniqueExponents(List list) { - var dic = new Dictionary(); + var dic = new Dictionary(list.Count); foreach (var row in list) { #if NETCOREAPP @@ -1385,7 +1385,7 @@ private static Dictionary CreateAlignmentPatternTable() for (var i = 0; i < (7 * 40); i = i + 7) { - var points = new List(); + var points = new List(50); for (var x = 0; x < 7; x++) { if (alignmentPatternBaseValues[i + x] != 0) @@ -1654,7 +1654,7 @@ public override string ToString() } } - private readonly struct Point + private readonly struct Point : IEquatable { public int X { get; } public int Y { get; } @@ -1663,6 +1663,12 @@ public Point(int x, int y) this.X = x; this.Y = y; } + + public bool Equals(Point other) + { + return this.X == other.X && this.Y == other.Y; + } + } private readonly struct Rectangle From 0de43ad097e4e624107a00bee0c64d07f4dea9b1 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Fri, 3 May 2024 08:23:09 -0400 Subject: [PATCH 28/30] Updated AggressiveInlining ifdef --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 2bd7b41d..10bbb614 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -346,7 +346,7 @@ void WriteEccLevelAndVersion() } } -#if NETCOREAPP +#if !NETFRAMEWORK [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count) From 1c118b817340770c058c79eee76ff75513c8160e Mon Sep 17 00:00:00 2001 From: Shane32 Date: Fri, 3 May 2024 08:23:18 -0400 Subject: [PATCH 29/30] Update throw helpers --- QRCoder/QRCodeGenerator.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index 10bbb614..a723c62d 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -1003,7 +1003,7 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off { const int blockSize = 8; if (count % blockSize != 0) - ThrowArgumentException(nameof(count)); + ThrowCountMustBeMultipleOf8Exception(); var numberOfBlocks = (int)((uint)count / blockSize); var blocklist = new byte[numberOfBlocks]; @@ -1024,7 +1024,7 @@ private static byte[] BinaryStringToBitBlockByteList(BitArray bitString, int off return blocklist; - void ThrowArgumentException(string paramName) => throw new ArgumentOutOfRangeException(paramName, "Count must be a multiple of 8."); + void ThrowCountMustBeMultipleOf8Exception() => throw new ArgumentOutOfRangeException(nameof(count), "Count must be a multiple of 8."); } private static int BinToDec(BitArray bitArray, int offset, int count) @@ -1352,10 +1352,10 @@ private static int GetIntValFromAlphaExp(int exp) private static int GetAlphaExpFromIntVal(int intVal) { if (intVal == 0) - ThrowArgumentOutOfRangeException(nameof(intVal)); + ThrowIntValOutOfRangeException(); return galoisFieldByIntegerValue[intVal]; - void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); + void ThrowIntValOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(intVal)); } private static int ShrinkAlphaExp(int alphaExp) From f754ea9014d60db78a717c0221043711ce66926f Mon Sep 17 00:00:00 2001 From: Shane32 Date: Fri, 3 May 2024 08:27:01 -0400 Subject: [PATCH 30/30] Update ifdef --- QRCoder/QRCodeGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QRCoder/QRCodeGenerator.cs b/QRCoder/QRCodeGenerator.cs index a723c62d..19ee6fba 100644 --- a/QRCoder/QRCodeGenerator.cs +++ b/QRCoder/QRCodeGenerator.cs @@ -346,7 +346,7 @@ void WriteEccLevelAndVersion() } } -#if !NETFRAMEWORK +#if !NETFRAMEWORK || NET45_OR_GREATER [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif private static void TrimLeadingZeros(BitArray fStrEcc, ref int index, ref int count)