You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
QR-Code-generator/csharp/PORTING.md

7.7 KiB

Porting Summary: Java → C# QR Code Generator

Scope

  • Ported the core QR Code generator from the Java implementation (java/src/main/java/io/nayuki/qrcodegen/) to C#.
  • Implemented a class library QrCodeGen and a minimal console demo QrCodeGen.Demo.

Goals

  • Maintain functional parity with the Java reference (encoding pipeline, EC levels, masking, penalties, ReedSolomon ECC, layout drawing).
  • Provide idiomatic and safe C# while preserving algorithmic behavior and outputs.

Note

  • This PR is 100% generated by JetBrains Junie Pro AI code assistant.
  • QR Code Generate Demo console app is a minimal port of the Java demo and working.

Repository Layout (Relevant to this PR)

  • csharp/QR-Code-generator.sln — Solution file containing two projects.
  • csharp/QrCodeGen/ — Class library (C# port of the Java library):
    • BitBuffer.cs
    • DataTooLongException.cs
    • QrSegment.cs
    • QrSegmentAdvanced.cs (simplified vs. Java; see Feature Parity section)
    • QrCode.cs
  • csharp/QrCodeGen.Demo/ — Console demo app (depends on QrCodeGen).

Class-by-Class Mapping

  • Java BitBuffer → C# BitBuffer

    • API preserved: append bits, append buffer, length, get bit, cloning.
    • Implementation uses List<bool> and explicit bounds checks like Javas BitSet/length pair.
  • Java DataTooLongException → C# DataTooLongException

    • Extends ArgumentException (close analog to Javas IllegalArgumentException).
  • Java QrSegment → C# QrSegment

    • Static factories: MakeBytes, MakeNumeric, MakeAlphanumeric, MakeEci.
    • Helpers: IsNumeric, IsAlphanumeric via regexes mirroring Java patterns.
    • Mode enum and character-count bits mapping provided via extension methods.
    • GetTotalBits() faithfully ported.
  • Java QrSegmentAdvanced → C# QrSegmentAdvanced

    • Included as an optional module. Current implementation mirrors the default segmentation path (numeric/alphanumeric/byte) and returns reasonably optimal segments for common inputs, but does not implement full DP-based optimal segmentation and Kanji mode. See Feature Parity and Future Work.
  • Java QrCode → C# QrCode

    • High-level APIs: EncodeText, EncodeBinary.
    • Mid-level API: EncodeSegments with minVersion, maxVersion, mask, and boostEcl parameters.
    • Constructor path builds function patterns, computes ECC, interleaves, draws codewords, applies mask, and computes penalties identically.
    • Tables ECC_CODEWORDS_PER_BLOCK and NUM_ERROR_CORRECTION_BLOCKS ported exactly. Signed sbyte used to allow -1 sentinels.
    • All penalty and mask rules implemented verbatim.

Feature Parity

  • Achieved parity:

    • Version selection by capacity.
    • Error correction level boosting (when it doesnt increase version).
    • Data stream building, terminator and padding, interleaving across ECC blocks.
    • Drawing: finder, timing, alignment patterns; format and version information.
    • Mask application and penalty scoring (N1N4 rules) with identical logic.
    • ReedSolomon divisor/remainder algorithms and multiplication over GF(2^8/0x11D).
  • Intentional differences / limitations:

    • QrSegmentAdvanced does not implement:
      • Full dynamic-programming optimal segmentation across all modes.
      • Kanji segment encoding.
    • The default path still matches Javas QrSegment.makeSegments() selection (numeric/alphanumeric/byte) and therefore preserves functional outputs for typical text.
  • Behavioral equivalence expectations:

    • For inputs limited to numeric/alphanumeric/byte, outputs should match Java for the same chosen mask (or mask-agnostic when mask=-1) and ECL.

Notable C#-Specific Decisions

  • sbyte[][] tables for ECC metadata to accommodate -1 sentinel (Java byte is signed; C# byte is unsigned).
  • Enum helpers via extension methods for mapping to mode bits and character-count bits.
  • BitBuffer cloning implemented to maintain immutability of exposed data as in Java.
  • Exceptions aligned to .NET conventions (e.g., ArgumentException, ArgumentOutOfRangeException).

Build and Run Instructions

  • Target frameworks: net8.0 (both projects). If your machine only has .NET 9, either install .NET 8 or retarget both projects to net9.0.

Commands from repo root:

dotnet restore csharp/QR-Code-generator.sln
dotnet build csharp/QR-Code-generator.sln -c Debug
# Run demo
dotnet run --project csharp/QrCodeGen.Demo

Rider/VS:

  • Open csharp/QR-Code-generator.sln and build. Ensure your IDE uses the Microsoft .NET SDK (not Homebrews) and that the installed SDK matches net8.0 or update TFM to net9.0.

Verification Performed

  • Build succeeded locally after addressing initial compile issues (sbyte tables, enum constructor, finder pattern drawing, bit-buffer bounds).
  • Demo run prints QR metadata and ASCII rendering for “Hello, world!”.
  • Corrected a runtime bug where data bytes were read beyond BitBuffer length; now we read numDataBytes = bb.BitLength / 8 and then apply 0xEC/0x11 padding.

Reviewer Checklist

  • API Parity:
    • QrCode: factory methods, mid-level encode, mask selection, ECC boosting.
    • QrSegment: mode mapping, regex correctness, bit-length calculations.
    • BitBuffer: bounds checks, append semantics, cloning.
  • Tables: Verify ECC and block count tables match Java constants exactly.
  • Masking/Penalties: Confirm penalty N1..N4 and zigzag codeword placement logic mirror Java.
  • Error Paths: Proper exceptions for out-of-range versions/masks/lengths.
  • Padding: Verify 0xEC/0x11 alternating pad applied after real bytes from BitBuffer.
  • Public API surface: Namespaces and visibility appropriate for a library; no leaking internals.

Known Gaps / Future Work

  • Implement full QrSegmentAdvanced:
    • DP-based optimal segmentation across modes.
    • Kanji mode encoding per the Java version.
  • Add unit tests:
    • Cross-verify bitwise outputs vs. Java for a fixed mask across sample inputs.
    • Randomized round-trips using known test vectors.
  • Add simple image renderer (PNG/SVG) for easier visual verification.
  • Performance: Consider bit-packing in BitBuffer to reduce allocations for very large payloads.

Migration Risks and Mitigations

  • SDK Mismatch (net8.0 vs installed SDK): Documented; either install .NET 8 or retarget to net9.0 in both .csproj files.
  • Environment: Ensure the IDE uses Microsofts SDK at /usr/local/share/dotnet; avoid Homebrews SDK confusion.
  • Behavior Drift: The simplified QrSegmentAdvanced may produce larger bitstreams for mixed-content strings vs. Javas optimal splitter. This does not affect correctness, only compactness.

File List (Added/Modified)

  • Added:
    • csharp/QR-Code-generator.sln
    • csharp/QrCodeGen/QrCodeGen.csproj
    • csharp/QrCodeGen/BitBuffer.cs
    • csharp/QrCodeGen/DataTooLongException.cs
    • csharp/QrCodeGen/QrSegment.cs
    • csharp/QrCodeGen/QrSegmentAdvanced.cs
    • csharp/QrCodeGen/QrCode.cs
    • csharp/QrCodeGen.Demo/QrCodeGen.Demo.csproj
    • csharp/QrCodeGen.Demo/Program.cs

How Reviewers Can Reproduce

  • Build and run demo as above.
  • Optionally compare module patterns vs. Java output with a fixed mask (pass mask in EncodeSegments) over several sample strings.

License Alignment

  • The C# port continues to follow the MIT License consistent with the Java version and includes the original copyright attribution.

Appendix: Usage Example

using Io.Nayuki.QrCodeGen;

var qr = QrCode.EncodeText("Hello, world!", QrCode.Ecc.MEDIUM);
for (int y = 0; y < qr.Size; y++) {
    for (int x = 0; x < qr.Size; x++) {
        bool dark = qr.GetModule(x, y);
        // render module
    }
}