### 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, Reed–Solomon 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` and explicit bounds checks like Java’s `BitSet`/length pair. - Java `DataTooLongException` → C# `DataTooLongException` - Extends `ArgumentException` (close analog to Java’s `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 doesn’t 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 (N1–N4 rules) with identical logic. - Reed–Solomon 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 Java’s `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: ```sh 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 Homebrew’s) 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 Microsoft’s SDK at `/usr/local/share/dotnet`; avoid Homebrew’s SDK confusion. - Behavior Drift: The simplified `QrSegmentAdvanced` may produce larger bitstreams for mixed-content strings vs. Java’s 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 ```csharp 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 } } ```