Skip to content

Conversation

Shane32
Copy link
Owner

@Shane32 Shane32 commented Oct 1, 2025

Note: .NET Standard 2.1 is tested via the existing .NET Core 3.1 tests

Summary by CodeRabbit

  • New Features

    • Added official support for .NET Standard 2.1.
  • Performance

    • Prefer Span-backed operations and pooled buffers when available to reduce allocations and improve speed.
  • Chores

    • Updated target frameworks, build defines, and package references to enable the above optimizations.

@Shane32 Shane32 self-assigned this Oct 1, 2025
Copy link

coderabbitai bot commented Oct 1, 2025

📝 Walkthrough

Walkthrough

Adds netstandard2.1 target and a HAS_SPAN define; migrates framework-version preprocessor guards to HAS_SPAN to enable Span/ArrayPool code paths; adjusts NotNullWhenAttribute compile guard. No public API signature changes.

Changes

Cohort / File(s) Summary
Build / Targeting updates
QRCoder/QRCoder.csproj
Adds netstandard2.1 to TargetFrameworks; defines HAS_SPAN for TFMs that support Span; extends package reference conditions (Encoding.CodePages, System.Drawing.Common) to include netstandard2.1.
Span / ArrayPool feature-flag migration
QRCoder/PngByteQRCode.cs, QRCoder/QRCodeGenerator.cs, QRCoder/QRCodeGenerator/Polynom.cs, QRCoder/QRCodeData.cs, QRCoder/QRCodeGenerator/CodewordBlock.cs
Replaces framework-specific preprocessor guards (e.g., NETCOREAPP2_1_OR_GREATER, NETSTANDARD2_1, NETCOREAPP) with HAS_SPAN to gate System.Buffers/ArrayPool usage and Span-backed code paths; moves using System.Buffers and rent/return calls behind HAS_SPAN. Functional logic preserved.
Nullable attribute compile guard
QRCoder/Attributes/NotNullWhenAttribute.cs
Changes the preprocessor exclusion to #if !NETCOREAPP && !NETSTANDARD2_1, adjusting when the attribute is compiled.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant QRGenerator as QRCodeGenerator
  participant PngRenderer as PngByteQRCode
  participant Pool as ArrayPool<byte>
  participant Heap as ManualAllocation

  Caller->>QRGenerator: Build QR data / ECC
  QRGenerator-->>Caller: QR modules / raw data

  Caller->>PngRenderer: Render PNG bytes
  alt HAS_SPAN defined
    PngRenderer->>Pool: Rent buffers / scanlines
    PngRenderer-->>Caller: Compose PNG from pooled buffers
    PngRenderer->>Pool: Return buffers
  else HAS_SPAN not defined
    PngRenderer->>Heap: Allocate scanline arrays on heap
    PngRenderer-->>Caller: Compose PNG from allocated arrays
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

performance

Suggested reviewers

  • gfoidl

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 accurately summarizes the primary work of reviewing conditional compilation directives and adding support for .NET Standard 2.1, clearly reflecting the scope of the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch review_all_ifdef

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68080e6 and f89ea5e.

📒 Files selected for processing (1)
  • QRCoder/QRCoder.csproj (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • QRCoder/QRCoder.csproj
⏰ 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). (1)
  • GitHub Check: build

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

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
QRCoder/QRCodeGenerator.cs (3)

589-597: Build break: BitArray.RightShift is not gated correctly.

RightShift is not available on netstandard2.1, net5.0, or net6.0. Guard these calls with NET7_0_OR_GREATER (or keep the loop for HAS_SPAN paths).

Apply this diff:

-#if HAS_SPAN
-        fStrEcc.RightShift(num); // Shift towards bit 0
-#else
+#if NET7_0_OR_GREATER
+        fStrEcc.RightShift(num); // Shift towards bit 0
+#else
         for (var i = 0; i < fStrEcc.Length - num; i++)
             fStrEcc[i] = fStrEcc[i + num];
         for (var i = fStrEcc.Length - num; i < fStrEcc.Length; i++)
             fStrEcc[i] = false;
 #endif

601-609: Same issue for BitArray.LeftShift.

LeftShift is not available on ns2.1/net5/net6. Use NET7_0_OR_GREATER.

Apply this diff:

-#if HAS_SPAN
-        fStrEcc.LeftShift(num); // Shift away from bit 0
-#else
+#if NET7_0_OR_GREATER
+        fStrEcc.LeftShift(num); // Shift away from bit 0
+#else
         for (var i = fStrEcc.Length - 1; i >= num; i--)
             fStrEcc[i] = fStrEcc[i - num];
         for (var i = 0; i < num; i++)
             fStrEcc[i] = false;
 #endif

1104-1115: Return pooled byte buffer under all paths.

GetBytes(plainText, codeBytes) can throw; wrap pooling with try/finally to avoid leaks.

Apply this diff:

-        int count = targetEncoding.GetByteCount(plainText);
-        byte[]? bufferFromPool = null;
-        Span<byte> codeBytes = (count <= MAX_STACK_SIZE_IN_BYTES)
-            ? (stackalloc byte[MAX_STACK_SIZE_IN_BYTES])
-            : (bufferFromPool = ArrayPool<byte>.Shared.Rent(count));
-        codeBytes = codeBytes.Slice(0, count);
-        targetEncoding.GetBytes(plainText, codeBytes);
+        int count = targetEncoding.GetByteCount(plainText);
+        byte[]? bufferFromPool = null;
+        Span<byte> codeBytes = (count <= MAX_STACK_SIZE_IN_BYTES)
+            ? (stackalloc byte[MAX_STACK_SIZE_IN_BYTES])
+            : (bufferFromPool = ArrayPool<byte>.Shared.Rent(count));
+        codeBytes = codeBytes.Slice(0, count);
+        try
+        {
+            targetEncoding.GetBytes(plainText, codeBytes);
+        }
+        finally
+        {
+            // no-op for stackalloc; pooled buffer is returned after BitArray conversion below
+        }
@@
-        if (bufferFromPool != null)
-            ArrayPool<byte>.Shared.Return(bufferFromPool);
+        if (bufferFromPool != null)
+            ArrayPool<byte>.Shared.Return(bufferFromPool);

Also applies to: 1135-1138

🧹 Nitpick comments (4)
QRCoder/QRCoder.csproj (3)

9-9: HAS_SPAN define condition looks right.

Using IsTargetFrameworkCompatible(netcoreapp2.1) OR explicit netstandard2.1 correctly lights up Span/ArrayPool paths for netcoreapp2.1+, net5+, net6+, and ns2.1. Consider adding a short comment explaining why ns2.1 needs the explicit OR.


48-50: CodePages for ns1.3/ns2.0/ns2.1 — OK.

Works for ECI encodings. Optionally bump to a recent version if you plan a package update soon.

Would you like a quick script to check current vulnerable versions before release?


52-54: System.Drawing.Common references — check platform intent.

Including 5.0.3 for net5.0 (all) and ns2.0/ns2.1, and 6.0.0 for net6.0-windows aligns with Windows-only guidance for .NET 6+. If you expect non‑Windows net5 usage, keep in mind libgdiplus requirements at runtime.

QRCoder/QRCodeGenerator/Polynom.cs (1)

200-212: Switch to HAS_SPAN for ArrayPool — LGTM.

Fully-qualified System.Buffers usage avoids a using-directive and keeps the non‑HAS_SPAN fallback intact. Optional: Return with clearArray:false explicitly to document intent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e355a87 and 2be9f8e.

📒 Files selected for processing (5)
  • QRCoder/Attributes/NotNullWhenAttribute.cs (1 hunks)
  • QRCoder/PngByteQRCode.cs (4 hunks)
  • QRCoder/QRCodeGenerator.cs (13 hunks)
  • QRCoder/QRCodeGenerator/Polynom.cs (1 hunks)
  • QRCoder/QRCoder.csproj (2 hunks)
⏰ 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). (1)
  • GitHub Check: build
🔇 Additional comments (7)
QRCoder/Attributes/NotNullWhenAttribute.cs (1)

1-1: Correct guard to avoid duplicate attribute on netstandard2.1+.

Excluding NETSTANDARD2_1 (in addition to NETCOREAPP) prevents type redefinition where the BCL already ships this attribute. LGTM.

QRCoder/QRCoder.csproj (1)

4-4: Add netstandard2.1 target — LGTM.

This fills the gap between netstandard2.0 and net5+, enabling Span APIs without moving to a runtime TFM.

QRCoder/PngByteQRCode.cs (2)

1-3: Scoped System.Buffers import — LGTM.

Guarded import matches HAS_SPAN usage below.


100-106: ArrayPool allocation — OK.

Rent/clear pattern is fine. Returning is handled above.

QRCoder/QRCodeGenerator.cs (3)

2-4: Scoped System.Buffers import — LGTM.


1013-1038: Span-based parsing for numeric mode — LGTM.

Appropriate use of int.Parse(ReadOnlySpan) under HAS_SPAN, with non‑SPAN fallback preserved.


700-707: Pooled ECC buffers are returned.
ReturnList invokes ArrayPool.Shared.Return(item.ECCWords.Array!) under #if NETCOREAPP, so rented ECC arrays are properly returned to the pool.

@Shane32 Shane32 linked an issue Oct 2, 2025 that may be closed by this pull request
@Shane32 Shane32 requested a review from gfoidl October 2, 2025 03:13
@Shane32 Shane32 added this to the 1.7.0 milestone Oct 2, 2025
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.

Modulo one nit it LGTM.

@Shane32 Shane32 merged commit f94dc72 into master Oct 2, 2025
12 checks passed
@Shane32 Shane32 deleted the review_all_ifdef branch October 2, 2025 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Define HAS_SPAN compile time constant for code that uses Span
2 participants