Skip to content

Conversation

Shane32
Copy link
Owner

@Shane32 Shane32 commented Oct 14, 2025

Enhance the DataSegment class to support multiple encoding modes and streamline the CreateDataSegment method by utilizing specialized segment classes, improving code structure and readability. Encoding algorithm of text has not changed.

Summary by CodeRabbit

  • New Features
    • Automatic selection of numeric, alphanumeric, or byte encoding per input; byte mode supports optional UTF‑8 BOM and ECI selection.
  • Bug Fixes
    • More accurate bit-lengths, consistent bit ordering and serialization for all modes, improving QR readability and size.
  • Refactor
    • Encoding pipeline reorganized into per-segment, stateless writers with explicit bit-writing.
  • Chores
    • Improved error handling with clearer exceptions for unsupported encodings.

Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

📝 Walkthrough

Walkthrough

Replaces inline text-to-bits logic with an abstract per-segment design: DataSegment is now a private abstract base exposing GetBitLength and WriteTo. New concrete segments (NumericDataSegment, AlphanumericDataSegment, ByteDataSegment) produce mode/count/data bits (Byte supports ECI/UTF‑8/BOM). Generator creates segments by mode and builds BitArray from them.

Changes

Cohort / File(s) Summary
Factory / caller changes
QRCoder/QRCodeGenerator.cs
Replaces previous PlainTextToBinary + GetDataLength path with mode-dispatched creation of NumericDataSegment, AlphanumericDataSegment, or ByteDataSegment; uses segment GetBitLength and WriteTo for BitArray construction; throws on unsupported modes.
DataSegment base & nesting
QRCoder/QRCodeGenerator/DataSegment.cs
Converts DataSegment to a private abstract nested class exposing Text, abstract EncodingMode, GetBitLength(version), WriteTo(BitArray, startIndex, version), and ToBitArray(version); removes chained/ECI fields and prior concrete bit-storage members.
Numeric segment & numeric encoder
QRCoder/QRCodeGenerator/NumericDataSegment.cs, QRCoder/QRCodeGenerator (helpers)
Adds NumericDataSegment implementing numeric bit-length and WriteTo; adds PlainTextToBinaryNumeric helper to encode digit groups (3→10 bits; leftovers 1→4 bits, 2→7 bits) with span/non-span paths.
Alphanumeric encoder refactor
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs, QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs
Adds GetBitLength(string) and WriteToBitArray(string, BitArray, int) to the encoder; introduces AlphanumericDataSegment that writes mode, count, and data via the encoder writer.
Byte-mode segment & encoding logic
QRCoder/QRCodeGenerator/ByteDataSegment.cs
Adds ByteDataSegment handling ECI, forced UTF‑8 and optional UTF‑8 BOM; introduces GetTargetEncoding, bit-length calculations, and PlainTextToBinaryByte overloads that write encoded bytes into an existing BitArray (span/ArrayPool optimizations behind conditionals).
Internal bit-array helpers / refactor
QRCoder/QRCodeGenerator (renamed helpers)
Renames/reorganizes helpers from legacy PlainTextToBinary* to ToBitArray/CopyToBitArray-style APIs; changes parameter lists and bit-copy semantics (MSB-to-LSB preservation), removing previous inlined encoding-specific branches.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant QRGen as QRCodeGenerator
  participant Seg as DataSegment
  participant Bits as BitArray

  Caller->>QRGen: Generate(text, mode, options)
  QRGen->>Seg: CreateSegment(text, mode, options)
  Seg-->>QRGen: segment instance (Numeric|Alphanumeric|Byte)
  QRGen->>Seg: GetBitLength(version)
  Seg-->>QRGen: bitLength
  QRGen->>Bits: Allocate(bitLength)
  QRGen->>Seg: WriteTo(Bits, startIndex, version)
  Seg-->>QRGen: nextIndex
  Note right of Seg: ByteDataSegment may emit ECI header then data (optional UTF‑8 BOM)
  QRGen-->>Caller: BitArray (encoded payload)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • gfoidl

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly captures the primary change by indicating a refactoring of DataSegment to enhance encoding support, directly reflecting the introduction of specialized segment classes and improved encoding modes described in the PR objectives and detailed file summaries.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ 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 datasegment_new

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7c9426 and 00047e4.

📒 Files selected for processing (2)
  • QRCoder/QRCodeGenerator.cs (1 hunks)
  • QRCoder/QRCodeGenerator/DataSegment.cs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
QRCoder/QRCodeGenerator.cs (1)
QRCoder/QRCodeGenerator/DataSegment.cs (6)
  • NumericDataSegment (63-119)
  • NumericDataSegment (74-77)
  • AlphanumericDataSegment (124-180)
  • AlphanumericDataSegment (135-138)
  • ByteDataSegment (185-275)
  • ByteDataSegment (219-225)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
QRCoder/QRCodeGenerator.cs (8)
  • DataSegment (126-138)
  • EncodingMode (749-764)
  • BitArray (996-1005)
  • BitArray (1015-1056)
  • BitArray (1079-1155)
  • GetCountIndicatorLength (892-952)
  • DecToBin (874-884)
  • GetDataLength (962-968)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (1)
  • AlphanumericEncoder (8-76)
⏰ 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). (5)
  • GitHub Check: Test .NET Core 3.1
  • GitHub Check: Test .NET 5.0 Windows
  • GitHub Check: Test .NET 6.0 Windows
  • GitHub Check: Test .NET Core 2.1
  • GitHub Check: additional-tests
🔇 Additional comments (1)
QRCoder/QRCodeGenerator.cs (1)

129-137: LGTM! Clean refactoring with appropriate defensive programming.

The type-dispatched factory pattern improves code structure by leveraging polymorphism. The switch expression correctly maps each encoding mode to its specialized segment class, and parameter passing aligns with the constructors defined in DataSegment.cs.

The default case provides good defensive programming, even though GetEncodingFromPlaintext (lines 749-764) should only return Numeric, Alphanumeric, or Byte under normal operation. The exception message is descriptive and aids debugging.

@Shane32 Shane32 mentioned this pull request Oct 14, 2025
@Shane32 Shane32 self-assigned this Oct 14, 2025
@Shane32 Shane32 added this to the 1.8.0 milestone Oct 14, 2025
@Shane32 Shane32 added the refactoring Refactoring of code without changes to functionality label Oct 14, 2025
@Shane32
Copy link
Owner Author

Shane32 commented Oct 14, 2025

This will clean up further once I have the Micro QR code code use the DataSegment class. But already this PR is "too large".

@Shane32 Shane32 requested a review from gfoidl October 14, 2025 04:41
@Shane32
Copy link
Owner Author

Shane32 commented Oct 14, 2025

I'm going to rewrite #676 to be a derived class of DataSegment so it can calculate the size of the text live with various versions (sizes) of QR code, and then only create the data when requested, and all at once to a single bitarray, instead of doing it a bit at a time and then combining all the bit arrays together.

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: 2

🧹 Nitpick comments (5)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (1)

67-98: Optional: add basic input/capacity validation to harden the writer.

To prevent hard-to-debug IndexOutOfRange/KeyNotFound, consider early checks.

 public static int WriteToBitArray(string plainText, BitArray codeText, int codeIndex)
 {
+    if (codeText == null) throw new ArgumentNullException(nameof(codeText));
+    if (codeIndex < 0) throw new ArgumentOutOfRangeException(nameof(codeIndex));
+    // Ensure target has enough room for the bits we are about to write
+    int required = GetBitLength(plainText);
+    if (codeText.Length - codeIndex < required)
+        throw new ArgumentException("Target BitArray is too small for the encoded data.", nameof(codeText));
+
     var index = 0;
     var count = plainText.Length;
     ...
 }
QRCoder/QRCodeGenerator.cs (1)

1031-1045: CopyToBitArray bit ordering implementation looks correct.

Writes bit j from MSB first using (1 << (7 - j)). Consider guarding offset >= 0.

-    private static void CopyToBitArray(
+    private static void CopyToBitArray(
         ...
         BitArray bitArray,
         int offset)
     {
+        if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
         for (var i = 0; i < byteArray.Length; i++)
         {
QRCoder/QRCodeGenerator/NumericDataSegment.cs (1)

57-61: Avoid intermediate BitArray allocation in WriteTo.

Stream numeric bits directly into the target to reduce allocations.

-            // write data - encode numeric text
-            var data = PlainTextToBinaryNumeric(Text);
-            data.CopyTo(bitArray, 0, index, data.Length);
-            index += data.Length;
+            // write data - encode numeric text directly into bitArray
+            index = PlainTextToBinaryNumeric(Text, bitArray, index);
             return index;

Add this overload to stream bits (outside this hunk):

private static int PlainTextToBinaryNumeric(string plainText, BitArray bitArray, int index)
{
    // Triplets -> 10 bits
    for (int i = 0; i < plainText.Length - 2; i += 3)
    {
#if HAS_SPAN
        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
        index = DecToBin(dec, 10, bitArray, index);
    }
    // Remainder
    int rem = plainText.Length % 3;
    if (rem == 2)
    {
#if HAS_SPAN
        var dec = int.Parse(plainText.AsSpan(plainText.Length - 2, 2), NumberStyles.None, CultureInfo.InvariantCulture);
#else
        var dec = int.Parse(plainText.Substring(plainText.Length - 2, 2), NumberStyles.None, CultureInfo.InvariantCulture);
#endif
        index = DecToBin(dec, 7, bitArray, index);
    }
    else if (rem == 1)
    {
#if HAS_SPAN
        var dec = int.Parse(plainText.AsSpan(plainText.Length - 1, 1), NumberStyles.None, CultureInfo.InvariantCulture);
#else
        var dec = int.Parse(plainText.Substring(plainText.Length - 1, 1), NumberStyles.None, CultureInfo.InvariantCulture);
#endif
        index = DecToBin(dec, 4, bitArray, index);
    }
    return index;
}
QRCoder/QRCodeGenerator/DataSegment.cs (2)

30-37: Docstring mentions chaining, but class no longer supports it.

Remove “Chains to the next segment” to avoid confusion.

-        /// Writes this data segment to an existing BitArray at the specified index for a specific QR code version.
-        /// Chains to the next segment if present.
+        /// Writes this data segment to an existing BitArray at the specified index for a specific QR code version.

51-57: Docstring refers to chained segments; adjust wording.

GetBitLength should describe only this segment.

-        /// Calculates the total bit length for this segment when encoded for a specific QR code version.
-        /// Includes the length of all chained segments.
+        /// Calculates the total bit length for this segment when encoded for a specific QR code 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 9740625 and 686e493.

📒 Files selected for processing (6)
  • QRCoder/QRCodeGenerator.cs (2 hunks)
  • QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs (1 hunks)
  • QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (3 hunks)
  • QRCoder/QRCodeGenerator/ByteDataSegment.cs (1 hunks)
  • QRCoder/QRCodeGenerator/DataSegment.cs (2 hunks)
  • QRCoder/QRCodeGenerator/NumericDataSegment.cs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs (3)
QRCoder/QRCodeGenerator.cs (7)
  • QRCodeGenerator (14-1028)
  • QRCodeGenerator (19-21)
  • DataSegment (126-138)
  • EncodingMode (749-764)
  • GetCountIndicatorLength (892-952)
  • BitArray (996-1005)
  • DecToBin (874-884)
QRCoder/QRCodeGenerator/DataSegment.cs (6)
  • QRCodeGenerator (3-59)
  • DataSegment (8-58)
  • DataSegment (24-27)
  • GetBitLength (57-57)
  • WriteTo (37-37)
  • BitArray (44-49)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (2)
  • AlphanumericEncoder (8-99)
  • WriteToBitArray (76-98)
QRCoder/QRCodeGenerator/ByteDataSegment.cs (1)
QRCoder/QRCodeGenerator.cs (8)
  • QRCodeGenerator (14-1028)
  • QRCodeGenerator (19-21)
  • DataSegment (126-138)
  • EncodingMode (749-764)
  • GetCountIndicatorLength (892-952)
  • BitArray (996-1005)
  • DecToBin (874-884)
  • IsValidISO (973-985)
QRCoder/QRCodeGenerator/NumericDataSegment.cs (2)
QRCoder/QRCodeGenerator.cs (7)
  • QRCodeGenerator (14-1028)
  • QRCodeGenerator (19-21)
  • DataSegment (126-138)
  • EncodingMode (749-764)
  • GetCountIndicatorLength (892-952)
  • BitArray (996-1005)
  • DecToBin (874-884)
QRCoder/QRCodeGenerator/DataSegment.cs (6)
  • QRCodeGenerator (3-59)
  • DataSegment (8-58)
  • DataSegment (24-27)
  • GetBitLength (57-57)
  • WriteTo (37-37)
  • BitArray (44-49)
QRCoder/QRCodeGenerator/DataSegment.cs (5)
QRCoder/QRCodeGenerator.cs (3)
  • DataSegment (126-138)
  • EncodingMode (749-764)
  • BitArray (996-1005)
QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs (2)
  • WriteTo (46-61)
  • GetBitLength (29-37)
QRCoder/QRCodeGenerator/ByteDataSegment.cs (3)
  • WriteTo (76-100)
  • BitArray (191-197)
  • GetBitLength (59-67)
QRCoder/QRCodeGenerator/NumericDataSegment.cs (3)
  • WriteTo (46-63)
  • BitArray (72-113)
  • GetBitLength (29-37)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (2)
  • BitArray (60-65)
  • GetBitLength (48-51)
QRCoder/QRCodeGenerator.cs (5)
QRCoder/QRCodeGenerator/NumericDataSegment.cs (3)
  • NumericDataSegment (8-64)
  • NumericDataSegment (19-22)
  • BitArray (72-113)
QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs (2)
  • AlphanumericDataSegment (8-62)
  • AlphanumericDataSegment (19-22)
QRCoder/QRCodeGenerator/ByteDataSegment.cs (3)
  • ByteDataSegment (12-101)
  • ByteDataSegment (46-52)
  • BitArray (191-197)
QRCoder/QRCodeGenerator/DataSegment.cs (1)
  • BitArray (44-49)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (1)
  • BitArray (60-65)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (2)
QRCoder/QRCodeGenerator/DataSegment.cs (2)
  • GetBitLength (57-57)
  • BitArray (44-49)
QRCoder/QRCodeGenerator.cs (2)
  • BitArray (996-1005)
  • DecToBin (874-884)
⏰ 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 6.0 Windows
  • 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 Framework 4.6.2
  • GitHub Check: additional-tests
🔇 Additional comments (6)
QRCoder/QRCodeGenerator/AlphanumericEncoder.cs (2)

43-51: Bit-length computation is correct and minimal.

Formula (n/2)*11 + (n&1)*6 matches QR alphanumeric spec. LGTM.


62-65: Good delegation to writer without extra allocations.

Pre-sizing via GetBitLength and writing in place avoids temporary arrays.

QRCoder/QRCodeGenerator/AlphanumericDataSegment.cs (2)

29-37: Correct total length calculation.

4 (mode) + count-indicator + AlphanumericEncoder data length. Matches spec.


46-61: Write order and streaming encode look good.

Mode → count (chars) → data via writer; no intermediate allocations. LGTM.

QRCoder/QRCodeGenerator.cs (2)

129-137: Segment factory switch is clear and extensible.

Cleanly routes to Numeric/Alphanumeric/Byte segments; good cohesion.


1010-1029: ToBitArray helper preserves MSB→LSB and supports prefixes.

Pre-sizing and delegating to CopyToBitArray is correct. LGTM.

Copy link
Collaborator

@gfoidl gfoidl left a comment

Choose a reason for hiding this comment

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

Is the bot right with it's suggestion?

I have only a few nits. Besides that LGTM.

@Shane32
Copy link
Owner Author

Shane32 commented Oct 14, 2025

Is the bot right with it's suggestion?

Yes, the text encoding in 1.x is a bit botched. I'll fix in a separate PR. This PR will retain the exact same encoding algorithm as previous versions.

#679

@Shane32 Shane32 merged commit 8e46159 into master Oct 14, 2025
8 checks passed
@Shane32 Shane32 deleted the datasegment_new branch October 14, 2025 15:51
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.

2 participants