fix: reduzir complexidade do gerador e payload#71
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| private static int ScoreFinderPatterns(QRCodeData qrCode, int size) | ||
| { | ||
| messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); | ||
| bitString = bitString.Remove(0, 8); | ||
| var score = 0; | ||
| for (var y = 0; y < size; y++) | ||
| { | ||
| for (var x = 0; x < size - 10; x++) | ||
| { | ||
| if (MatchesFinderPattern(qrCode, x, y)) | ||
| score += 40; | ||
| } | ||
| } | ||
| return score; | ||
| } |
There was a problem hiding this comment.
🔍 New ScoreFinderPatterns helper only checks horizontal patterns, missing vertical
The newly added ScoreFinderPatterns method at QRCoder.Core/Generators/QRCodeGenerator.cs:843-855 only scans rows (horizontal patterns using [y][x+n]), but the original inline Penalty 3 code in the Score method at QRCoder.Core/Generators/QRCodeGenerator.cs:723-780 checks both horizontal AND vertical patterns (the second block uses [x+n][y]). This method is currently dead code — Score at line 672 still has the full inline implementation and is the only method called at line 453. However, if a future refactor replaces the inline code with a call to ScoreFinderPatterns, it would silently halve the Penalty 3 score, producing incorrect QR mask selection.
Was this helpful? React with 👍 or 👎 to provide feedback.
| private static int ScoreBalance(QRCodeData qrCode, int size) | ||
| { | ||
| blocklist.Add(bitString.Substring(i, blockSize)); | ||
| var darkCount = qrCode.ModuleMatrix.Sum(row => row.Cast<bool>().Count(x => x)); | ||
| var totalCount = size * size; | ||
| var k = Math.Abs(darkCount * 100 / totalCount - 50) / 5; | ||
| return k * 10; | ||
| } |
There was a problem hiding this comment.
🔍 New ScoreBalance helper uses integer division instead of floating-point
The new ScoreBalance method at line 887 computes darkCount * 100 / totalCount - 50 using integer arithmetic (darkCount is int from .Count(), totalCount is int). The original Penalty 4 code at QRCoder.Core/Generators/QRCodeGenerator.cs:783-792 uses double blackModules and floating-point division (blackModules / (count * count)) * 100. Integer division truncates differently than floating-point, so these can produce different scores. Like ScoreFinderPatterns, this is currently dead code, but would cause incorrect behavior if wired up.
Was this helpful? React with 👍 or 👎 to provide feedback.
| private static int ScoreLines(QRCodeData qrCode, int size) | ||
| { | ||
| if (leadTermSource.PolyItems[0].Coefficient == 0) | ||
| var score = 0; | ||
| for (var y = 0; y < size; y++) | ||
| { | ||
| leadTermSource.PolyItems.RemoveAt(0); | ||
| leadTermSource.PolyItems.Add(new PolynomItem(0, leadTermSource.PolyItems[leadTermSource.PolyItems.Count - 1].Exponent - 1)); | ||
| score += ScoreLine(qrCode.ModuleMatrix[y].Cast<bool>().ToList()); | ||
| score += ScoreLine(qrCode.ModuleMatrix.Select(row => row[y]).ToList()); | ||
| } | ||
| else | ||
| return score; | ||
| } | ||
|
|
||
| private static int ScoreLine(IReadOnlyList<bool> line) | ||
| { | ||
| var score = 0; | ||
| var runLength = 1; | ||
| for (var i = 1; i < line.Count; i++) | ||
| { | ||
| var resPoly = MultiplyGeneratorPolynomByLeadterm(generatorPolynom, ConvertToAlphaNotation(leadTermSource).PolyItems[0], i); | ||
| resPoly = ConvertToDecNotation(resPoly); | ||
| resPoly = XORPolynoms(leadTermSource, resPoly); | ||
| leadTermSource = resPoly; | ||
| if (line[i] == line[i - 1]) | ||
| runLength++; | ||
| else | ||
| runLength = 1; | ||
|
|
||
| if (runLength == 5) | ||
| score += 3; | ||
| else if (runLength > 5) | ||
| score++; | ||
| } | ||
| return score; | ||
| } | ||
| return leadTermSource.PolyItems.Select(x => DecToBin(x.Coefficient, 8)).ToList(); | ||
| } | ||
|
|
||
| private static Polynom ConvertToAlphaNotation(Polynom poly) | ||
| { | ||
| var newPoly = new Polynom(); | ||
| for (var i = 0; i < poly.PolyItems.Count; i++) | ||
| newPoly.PolyItems.Add( | ||
| new PolynomItem( | ||
| (poly.PolyItems[i].Coefficient != 0 | ||
| ? GetAlphaExpFromIntVal(poly.PolyItems[i].Coefficient) | ||
| : 0), poly.PolyItems[i].Exponent)); | ||
| return newPoly; | ||
| } | ||
|
|
||
| private static Polynom ConvertToDecNotation(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; | ||
| } | ||
|
|
||
| private static int GetVersion(int length, EncodingMode encMode, ECCLevel eccLevel) | ||
| { | ||
| var fittingVersions = capacityTable.Where( | ||
| x => x.Details.Any( | ||
| y => (y.ErrorCorrectionLevel == eccLevel | ||
| && y.CapacityDict[encMode] >= Convert.ToInt32(length) | ||
| ) | ||
| ) | ||
| ).Select(x => new | ||
| { | ||
| version = x.Version, | ||
| capacity = x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel) | ||
| .CapacityDict[encMode] | ||
| }); | ||
|
|
||
| if (fittingVersions.Any()) | ||
| return fittingVersions.Min(x => x.version); | ||
|
|
||
| var maxSizeByte = capacityTable.Where( | ||
| x => x.Details.Any( | ||
| y => (y.ErrorCorrectionLevel == eccLevel)) | ||
| ).Max(x => x.Details.Single(y => y.ErrorCorrectionLevel == eccLevel).CapacityDict[encMode]); | ||
| throw new DataTooLongException(eccLevel.ToString(), encMode.ToString(), maxSizeByte); | ||
| } | ||
|
|
||
| private static EncodingMode GetEncodingFromPlaintext(string plainText, bool forceUtf8) | ||
| { | ||
| if (forceUtf8) return EncodingMode.Byte; | ||
| EncodingMode result = EncodingMode.Numeric; // assume numeric | ||
| foreach (char c in plainText) | ||
| private static int ScoreBlocks(QRCodeData qrCode, int size) | ||
| { | ||
| if (IsInRange(c, '0', '9')) continue; // numeric - char.IsDigit() for Latin1 | ||
| result = EncodingMode.Alphanumeric; // not numeric, assume alphanumeric | ||
| if (IsInRange(c, 'A', 'Z') || alphanumEncTable.Contains(c)) continue; // alphanumeric | ||
| return EncodingMode.Byte; // not numeric or alphanumeric, assume byte | ||
| var score = 0; | ||
| for (var y = 0; y < size - 1; y++) | ||
| { | ||
| for (var x = 0; x < size - 1; x++) | ||
| { | ||
| if (qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y][x + 1] && | ||
| qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x] && | ||
| qrCode.ModuleMatrix[y][x] == qrCode.ModuleMatrix[y + 1][x + 1]) | ||
| score += 3; | ||
| } | ||
| } | ||
| return score; | ||
| } | ||
| return result; // either numeric or alphanumeric | ||
| } | ||
|
|
||
| private static bool IsInRange(char c, char min, char max) | ||
| { | ||
| return (uint)(c - min) <= (uint)(max - min); | ||
| } | ||
|
|
||
| private static Polynom CalculateMessagePolynom(string bitString) | ||
| { | ||
| var messagePol = new Polynom(); | ||
| for (var i = bitString.Length / 8 - 1; i >= 0; i--) | ||
| private static int ScoreFinderPatterns(QRCodeData qrCode, int size) | ||
| { | ||
| messagePol.PolyItems.Add(new PolynomItem(BinToDec(bitString.Substring(0, 8)), i)); | ||
| bitString = bitString.Remove(0, 8); | ||
| var score = 0; | ||
| for (var y = 0; y < size; y++) | ||
| { | ||
| for (var x = 0; x < size - 10; x++) | ||
| { | ||
| if (MatchesFinderPattern(qrCode, x, y)) | ||
| score += 40; | ||
| } | ||
| } | ||
| return score; | ||
| } | ||
| return messagePol; | ||
| } | ||
|
|
||
| private static Polynom CalculateGeneratorPolynom(int numEccWords) | ||
| { | ||
| var generatorPolynom = new Polynom(); | ||
| generatorPolynom.PolyItems.AddRange(new[]{ | ||
| new PolynomItem(0,1), | ||
| new PolynomItem(0,0) | ||
| }); | ||
| for (var i = 1; i <= numEccWords - 1; i++) | ||
| private static bool MatchesFinderPattern(QRCodeData qrCode, int x, int y) | ||
| { | ||
| var multiplierPolynom = new Polynom(); | ||
| multiplierPolynom.PolyItems.AddRange(new[]{ | ||
| new PolynomItem(0,1), | ||
| new PolynomItem(i,0) | ||
| }); | ||
|
|
||
| generatorPolynom = MultiplyAlphaPolynoms(generatorPolynom, multiplierPolynom); | ||
| return (qrCode.ModuleMatrix[y][x] && | ||
| !qrCode.ModuleMatrix[y][x + 1] && | ||
| qrCode.ModuleMatrix[y][x + 2] && | ||
| qrCode.ModuleMatrix[y][x + 3] && | ||
| qrCode.ModuleMatrix[y][x + 4] && | ||
| !qrCode.ModuleMatrix[y][x + 5] && | ||
| qrCode.ModuleMatrix[y][x + 6] && | ||
| !qrCode.ModuleMatrix[y][x + 7] && | ||
| !qrCode.ModuleMatrix[y][x + 8] && | ||
| !qrCode.ModuleMatrix[y][x + 9] && | ||
| !qrCode.ModuleMatrix[y][x + 10]) || | ||
| (!qrCode.ModuleMatrix[y][x] && | ||
| !qrCode.ModuleMatrix[y][x + 1] && | ||
| !qrCode.ModuleMatrix[y][x + 2] && | ||
| !qrCode.ModuleMatrix[y][x + 3] && | ||
| qrCode.ModuleMatrix[y][x + 4] && | ||
| !qrCode.ModuleMatrix[y][x + 5] && | ||
| qrCode.ModuleMatrix[y][x + 6] && | ||
| qrCode.ModuleMatrix[y][x + 7] && | ||
| qrCode.ModuleMatrix[y][x + 8] && | ||
| qrCode.ModuleMatrix[y][x + 9] && | ||
| !qrCode.ModuleMatrix[y][x + 10]); | ||
| } | ||
|
|
||
| return generatorPolynom; | ||
| } | ||
|
|
||
| private static List<string> BinaryStringToBitBlockList(string bitString) | ||
| { | ||
| const int blockSize = 8; | ||
| var numberOfBlocks = (int)Math.Ceiling(bitString.Length / (double)blockSize); | ||
| var blocklist = new List<string>(numberOfBlocks); | ||
|
|
||
| for (int i = 0; i < bitString.Length; i += blockSize) | ||
| private static int ScoreBalance(QRCodeData qrCode, int size) | ||
| { | ||
| blocklist.Add(bitString.Substring(i, blockSize)); | ||
| var darkCount = qrCode.ModuleMatrix.Sum(row => row.Cast<bool>().Count(x => x)); | ||
| var totalCount = size * size; | ||
| var k = Math.Abs(darkCount * 100 / totalCount - 50) / 5; | ||
| return k * 10; | ||
| } |
There was a problem hiding this comment.
🔍 Four new Score helper methods are dead code — never called
ScoreLines (line 797), ScoreBlocks (line 827), ScoreFinderPatterns (line 843), and ScoreBalance (line 883) are all defined but never invoked. The actual scoring still happens inline in MaskPattern.Score at line 672. These appear to be an incomplete refactoring — the helpers were extracted but the original Score method was never updated to delegate to them. This adds ~90 lines of unreachable code to the engine's core file, which the AGENTS.md soft rule S1 flags as requiring extensive testing when modifying QRCodeGenerator.cs.
Was this helpful? React with 👍 or 👎 to provide feedback.
| private static string ReverseString(string inp) | ||
| { | ||
| var sb = new StringBuilder(inp.Length); | ||
| for (var i = inp.Length - 1; i >= 0; i--) | ||
| sb.Append(inp[i]); | ||
| return sb.ToString(); |
There was a problem hiding this comment.
📝 Info: ReverseString and several other methods have inconsistent indentation after extraction
ReverseString (line 352), PlaceAlignmentPatterns (line 580), and IsBlocked (line 624) are indented at the outer class level rather than the ModulePlacer nested class level, even though they are still syntactically inside ModulePlacer (the closing braces at lines 891-893 confirm this). This inconsistent indentation makes the code harder to read and could mislead future contributors about which class these methods belong to.
Was this helpful? React with 👍 or 👎 to provide feedback.
| AppendParameter(payload, "separeference", Uri.EscapeDataString(sepaReference)); | ||
| if (authority == AuthorityType.singledirectdebitsepa) | ||
| { | ||
| bezahlCodePayload += $"account={account}&"; | ||
| bezahlCodePayload += $"bnc={bnc}&"; | ||
| if (postingKey > 0) | ||
| bezahlCodePayload += $"postingkey={postingKey}&"; | ||
| AppendParameter(payload, "creditorid", Uri.EscapeDataString(creditorId)); | ||
| AppendParameter(payload, "mandateid", Uri.EscapeDataString(mandateId)); |
There was a problem hiding this comment.
📝 Info: BezahlCode now calls Uri.EscapeDataString unconditionally on sepaReference, creditorId, mandateId
The old code guarded sepaReference, creditorId, and mandateId with !string.IsNullOrEmpty() before calling Uri.EscapeDataString. The new code at lines 2016, 2019-2020 calls Uri.EscapeDataString() unconditionally and relies on AppendParameter's null/empty check on the result. This works correctly because: (1) the constructor defaults these to "", (2) Uri.EscapeDataString("") returns "", and (3) AppendParameter skips empty values. The behavioral equivalence holds, but the approach is more fragile — if a future constructor path sets these to null, Uri.EscapeDataString would throw ArgumentNullException.
Was this helpful? React with 👍 or 👎 to provide feedback.
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
Co-Authored-By: Afonso Dutra Nogueira Filho <afonsoft@gmail.com>
8486ec2 to
e2e9540
Compare
|
85fc3a6
into
feature/devin-20260712-sonar-quality



All Submissions:
Changes to Core Features:
Summary
Refatora
QRCodeGeneratorePayloadGeneratorpara reduzir complexidade cognitiva e excesso de parâmetros sem alterar a API pública.QRCodeGenerator:v1-v9PayloadGenerator:;e MeCard usar,Verification
dotnet build QRCoder.Core.sln -c Releasedotnet test QRCoder.Core.sln -c ReleaseLink to Devin session: https://app.devin.ai/sessions/644073fe782e408da591b82ae2c25071
Requested by: @afonsoft