Skip to content

Conversation

@Shane32
Copy link
Owner

@Shane32 Shane32 commented Oct 12, 2025

Prep to support multi-mode QR codes

Summary by CodeRabbit

  • New Features

    • Added a public API to compute the minimum QR code version needed for given data and error correction.
  • Refactor

    • QR generation reworked into a segment-based pipeline for more accurate version selection; public APIs remain compatible.
  • Bug Fixes

    • Improved detection and reporting when a requested version cannot contain the data.
  • Documentation

    • Added docs for the new segment-based helpers and versioning behavior.

@Shane32 Shane32 added this to the 1.7.1 milestone Oct 12, 2025
@Shane32 Shane32 self-assigned this Oct 12, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 12, 2025

📝 Walkthrough

Walkthrough

Refactors QR code generation into a segmented flow: input is converted to a new DataSegment, CapacityTables can compute/validate the minimum version, a BitArray is built from the segment, and final QR construction uses that BitArray. Public APIs unchanged; CapacityTables gains a public TryCalculateMinimumVersion.

Changes

Cohort / File(s) Summary of changes
Segmented encoding refactor
QRCoder/QRCodeGenerator.cs
Replace inline text-to-bits logic with a segment-driven flow: CreateDataSegment, DetermineVersion, BuildBitArrayFromSegment, then existing GenerateQrCode from BitArray. Add private helper methods, adjust error path to throw DataTooLongException via helper, minor inner-call-site tweak, new using directives and XML docs.
Version capacity calculation
QRCoder/QRCodeGenerator/CapacityTables.cs
Add public static TryCalculateMinimumVersion(DataSegment segment, ECCLevel eccLevel, out int version) to iterate versions (1–40), compute segment bit lengths for appropriate count-indicator ranges, and return the minimum fitting version (returns false/version=0 if none).
Data segment type
QRCoder/QRCodeGenerator/DataSegment.cs
Add private readonly DataSegment struct nested in QRCodeGenerator with fields EncodingMode, CharacterCount, Data (BitArray), EciMode, property HasEciMode, constructor, and GetBitLength(int version) which accounts for ECI, mode and count indicators, and data bits. Includes XML documentation.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant QR as QRCodeGenerator
  participant Cap as CapacityTables

  Caller->>QR: GenerateQrCode(plainText, eccLevel, [version], options)
  QR->>QR: CreateDataSegment(plainText, forceUtf8, utf8BOM, eciMode)
  alt no fixed version
    QR->>Cap: TryCalculateMinimumVersion(segment, eccLevel)
    Cap-->>QR: minVersion / no-fit
    alt minVersion found
      QR->>QR: DetermineVersion(segment, eccLevel, null) -> minVersion
    else no-fit
      QR-->>Caller: throw DataTooLongException
    end
  else fixed version provided
    QR->>QR: DetermineVersion(segment, eccLevel, fixedVersion) -> validate or throw
  end
  QR->>QR: BuildBitArrayFromSegment(segment, version)
  QR->>QR: GenerateQrCode(bitArray, eccLevel, version)
  QR-->>Caller: QRCodeData
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly describes the core change of refactoring how text is converted into data segments, which aligns with the main alterations in the QR code generation flow and helper methods introduced in this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
QRCoder/QRCodeGenerator/CapacityTables.cs (1)

119-143: Public method uses a private type; restrict visibility or adjust API

This public method’s signature exposes the private DataSegment type. Even though the containing class is private, the declared public modifier is misleading and may trigger accessibility inconsistencies. Since it’s only used internally, make the method non-public to avoid confusion.

Apply:

-        public static bool TryCalculateMinimumVersion(DataSegment segment, ECCLevel eccLevel, out int version)
+        internal static bool TryCalculateMinimumVersion(DataSegment segment, ECCLevel eccLevel, out int version)

Alternatively, keep it public but change the parameter to an internal/public DTO not tied to a private type.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3fd5e and 9f12ead.

📒 Files selected for processing (3)
  • QRCoder/QRCodeGenerator.cs (4 hunks)
  • QRCoder/QRCodeGenerator/CapacityTables.cs (1 hunks)
  • QRCoder/QRCodeGenerator/DataSegment.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
QRCoder/QRCodeGenerator.cs (8)
  • QRCodeGenerator (19-21)
  • DataSegment (126-132)
  • EncodingMode (777-792)
  • BitArray (169-198)
  • BitArray (1025-1034)
  • BitArray (1044-1085)
  • BitArray (1108-1184)
  • GetCountIndicatorLength (920-980)
QRCoder/QRCodeGenerator/CapacityTables.cs (1)
  • QRCodeGenerator (3-420)
QRCoder/QRCodeGenerator.cs (3)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
  • DataSegment (35-41)
  • GetBitLength (48-53)
QRCoder/QRCodeGenerator/CapacityTables.cs (2)
  • CapacityTables (9-419)
  • TryCalculateMinimumVersion (127-143)
QRCoder/Exceptions/DataTooLongException.cs (3)
  • DataTooLongException (6-30)
  • DataTooLongException (14-17)
  • DataTooLongException (26-29)
QRCoder/QRCodeGenerator/CapacityTables.cs (2)
QRCoder/QRCodeGenerator.cs (1)
  • DataSegment (126-132)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
  • DataSegment (35-41)
  • GetBitLength (48-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test .NET Framework 4.6.2
  • GitHub Check: Test .NET 6.0 Windows
  • GitHub Check: Test .NET Core 2.1
  • GitHub Check: Test .NET Core 3.1
  • GitHub Check: Test .NET 5.0 Windows
  • GitHub Check: additional-tests
🔇 Additional comments (4)
QRCoder/QRCodeGenerator.cs (4)

114-121: Segmented flow: clear and cohesive

Creating a DataSegment, determining version, then building the BitArray improves clarity and reusability. LGTM.


138-164: Version determination path looks solid

Good separation of concerns and error path via Throw helper. Once HasEciMode is fixed, min-version calculation should be accurate.

Please re-run tests for boundary payloads (near version capacity) after fixing HasEciMode to ensure no false DataTooLongException.


169-178: BitArray assembly consistent with spec and segment model

Writes ECI (when present), mode, count, then data. This matches GetBitLength once HasEciMode is corrected. LGTM.

Also applies to: 197-197


407-407: Good: discard unused parameter to silence warning

Safe and explicit suppression.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
QRCoder/QRCodeGenerator/CapacityTables.cs (1)

119-167: LGTM! Solid implementation of segment-based version calculation.

The method correctly accounts for version-dependent mode and count indicator lengths by grouping versions into three ranges (1-9, 10-26, 27-40) where these lengths remain constant. The logic is sound, edge cases are handled properly, and the Try-pattern return convention is idiomatic.

Optional: Reduce code duplication.

The three loops share nearly identical structure. Consider extracting a helper method:

+private static bool TryFindVersionInRange(DataSegment segment, ECCLevel eccLevel, int startVersion, int endVersion, int bitLengthVersion, out int version)
+{
+    var segmentBitLength = segment.GetBitLength(bitLengthVersion);
+    for (version = startVersion; version <= endVersion; version++)
+    {
+        var eccInfo = GetEccInfo(version, eccLevel);
+        if (eccInfo.TotalDataBits >= segmentBitLength)
+        {
+            return true;
+        }
+    }
+    version = 0;
+    return false;
+}
+
 public static bool TryCalculateMinimumVersion(DataSegment segment, ECCLevel eccLevel, out int version)
 {
-    // Versions 1-9: Count indicator length is constant within this range
-    var segmentBitLength = segment.GetBitLength(1);
-    for (version = 1; version <= 9; version++)
-    {
-        var eccInfo = GetEccInfo(version, eccLevel);
-        // Check if this version has enough capacity for the segment's total bits
-        if (eccInfo.TotalDataBits >= segmentBitLength)
-        {
-            return true;
-        }
-    }
-
-    // Versions 10-26: Count indicator length is constant within this range
-    segmentBitLength = segment.GetBitLength(10);
-    for (version = 10; version <= 26; version++)
-    {
-        var eccInfo = GetEccInfo(version, eccLevel);
-        // Check if this version has enough capacity for the segment's total bits
-        if (eccInfo.TotalDataBits >= segmentBitLength)
-        {
-            return true;
-        }
-    }
-
-    // Versions 27-40: Count indicator length is constant within this range
-    segmentBitLength = segment.GetBitLength(27);
-    for (version = 27; version <= 40; version++)
-    {
-        var eccInfo = GetEccInfo(version, eccLevel);
-        // Check if this version has enough capacity for the segment's total bits
-        if (eccInfo.TotalDataBits >= segmentBitLength)
-        {
-            return true;
-        }
-    }
-
-    version = 0;
-    return false;
+    return TryFindVersionInRange(segment, eccLevel, 1, 9, 1, out version)
+        || TryFindVersionInRange(segment, eccLevel, 10, 26, 10, out version)
+        || TryFindVersionInRange(segment, eccLevel, 27, 40, 27, out version);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0d74390 and 1086d8d.

📒 Files selected for processing (1)
  • QRCoder/QRCodeGenerator/CapacityTables.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
QRCoder/QRCodeGenerator/CapacityTables.cs (1)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
  • DataSegment (35-41)
  • GetBitLength (48-53)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test .NET Core 3.1
  • GitHub Check: Test .NET 5.0 Windows
  • GitHub Check: Test .NET Core 2.1
  • GitHub Check: Test .NET 6.0 Windows
  • GitHub Check: Test .NET Framework 4.6.2
  • GitHub Check: additional-tests

@Shane32 Shane32 requested a review from gfoidl October 12, 2025 18:07
@Shane32 Shane32 merged commit ab04613 into master Oct 12, 2025
8 checks passed
@Shane32 Shane32 deleted the refactor1 branch October 12, 2025 21:52
@Shane32 Shane32 added the refactoring Refactoring of code without changes to functionality label Oct 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring Refactoring of code without changes to functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants