Skip to content

Commit d8b106b

Browse files
committed
Improved CLAUDE.md
1 parent ee9ea96 commit d8b106b

1 file changed

Lines changed: 60 additions & 2 deletions

File tree

CLAUDE.md

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
## Purpose
6+
7+
QrCodeGenerator is library to generate QR code. It is designed to be easy to use and performant.
8+
9+
The library only supports limited graphics types and options in order to work without
10+
graphics libraries, which might not run all platforms.
11+
The library can provide the QR code as a two-dimensional array of pixels (called modules by the QR code standard).
12+
It is then up to the application to display the QR code based on this information.
13+
Many demo projects show how to use this approach for different graphics libraries and UI frameworks.
14+
15+
The main target of the library is .NET Standard 2.0 so it runs in virtually any current .NET environment.
16+
17+
518
## Commands
619

720
```bash
@@ -22,12 +35,57 @@ dotnet test --filter "FullyQualifiedName=Net.Codecrete.QrCodeGenerator.Test.QrCo
2235
dotnet pack --no-build
2336
```
2437

38+
## Build targets
39+
40+
- **`QrCodeGenerator/`** (the library) targets `netstandard2.0;net6.0`. The `net6.0` target exists only to enable trimming (`IsTrimmable`). Keep public API and language usage compatible with netstandard2.0 — don't reach for newer BCL/`Span` APIs that aren't available there.
41+
- **`QrCodeGeneratorTest/`** targets `net8.0;net10.0`, plus `net481` on Windows. `dotnet test` runs every target framework.
42+
- **`QrCodeGeneratorProfiling/`** targets `net10.0` (BenchmarkDotNet).
43+
- **`QrCodeAnalyzer/`** is a separate WPF tool in its own solution (`QrCodeAnalyzer/QrCodeAnalyzer.sln`), not part of `QrCodeGenerator.sln`.
44+
45+
Version 3 is a complete rewrite (≈10x faster, more standard-compliant) of what began as a port of Project Nayuki's Java library. The previous public type `QrSegment` is now `DataSegment`.
46+
2547
## Architecture
2648

27-
The library (`QrCodeGenerator/`) targets `netstandard2.0`. Additionally, there is a `net6.0` target to enable trimming. The test project (`QrCodeGeneratorTest/`) and the profiling project (`QrCodeGeneratorProfiling/`) target `net10.0`.
49+
`QrCode` is the only substantial public surface: immutable factory methods (`EncodeText`, `EncodeTextAdvanced`, `EncodeBinary`, `EncodeSegments`, `EncodeTextInMultipleCodes`) plus rendering (`ToSvgString`, `ToGraphicsPath`, `ToPngBitmap`, `ToBmpBitmap`, `GetModule`). It holds a single `BitMatrix` of modules. Almost all real work lives in `internal` types.
50+
51+
### Encoding pipeline
52+
53+
Text/bytes → segments → codewords → matrix, in this order:
54+
55+
1. **`DataSegment.FromText` / `FromBinaryData`** — chooses the text encoding. For automatic ECI: tries Latin-1 (ISO-8859-1) and adds no ECI; falls back to UTF-8 with an ECI designator if Latin-1 is lossy. Segments retain the *original unencoded bytes* as an `ArraySegment` over the caller's array until the bit stream is built — **the source array must not be mutated** in the meantime.
56+
2. **`SegmentCompaction`** — picks the cheapest per-byte mode (numeric > alphanumeric > Kanji > binary), groups consecutive bytes into blocks, then greedily *merges* adjacent blocks when the mode-switch overhead outweighs the savings. Merge cost depends on `version` (count-indicator width changes at versions 1/10/27). This is what produces the "smallest possible QR code" claim.
57+
3. **`QrCodeBuilder.Build`** (the heart of the library) does the rest:
58+
- `FindVersionAndEcc` — smallest version that fits, then boost ECC level for free if it doesn't grow the version.
59+
- `BuildCodewords` — segments → `BitStream` → byte codewords + terminator + 0xEC/0x11 padding.
60+
- `AddErrorCorrection` — splits into blocks, computes Reed-Solomon ECC (`ReedSolomon`), interleaves data and ECC codewords per spec.
61+
- Matrix layout — `CreateWithFixedPatterns` (finder/timing/alignment/format/version) then `FillPayload` zig-zags the codewords into the free modules.
62+
- `ApplyBestPattern` — XORs each of the 8 mask patterns, scores it (`Penalty`), keeps the lowest. `EncodingInfo.ForcedDataMask` can override the choice.
63+
64+
### `BitMatrix` and the transpose trick
65+
66+
`BitMatrix` (`internal readonly struct`) is the central data structure: a square bit grid stored as 4 `ulong`s per row (256-bit rows regardless of size), in row-major order. It exposes fast whole-matrix `And`/`Xor`/`Invert`/`PopCount` and an in-place **64×64 block transpose**.
67+
68+
The transpose is load-bearing, not a convenience: every penalty/format rule that operates on *columns* is implemented by transposing the matrix and reusing the *row*-wise algorithm. `ApplyBestPattern` keeps a `modules` and a `transposed` copy in lock-step, and `Penalty` takes both. `Penalty` itself is bit-parallel (operates on whole `ulong` words, not per-module) and has an early-stop path that bails once the running score exceeds the best-so-far.
69+
70+
### Performance-tuned constants
71+
72+
Two orderings in `QrCodeBuilder`/`Penalty` are deliberately ordered by profiling data (see `QrCodeGeneratorProfiling/README.md`), not arbitrary: `PatternEvaluationOrder` (evaluate likely-best masks first to tighten the early-stop bound) and the rule order inside `Penalty.CalculatePenalty`. Reordering them changes performance, not output. `QrCodeBuilder` also caches fixed patterns and data masks per version in `ConcurrentDictionary` instances; cached `BitMatrix` instances are shared and must not be mutated (callers `Copy()` first).
73+
74+
Everything is keyed by `version` (1–40) and `ecc` (0–3 = L/M/Q/H). The large `static readonly` lookup tables in `QrCodeBuilder` (codeword capacity, block counts, alignment positions, format/version info bits) come straight from ISO/IEC 18004 tables — comments cite the table numbers.
75+
76+
### Rendering and diagnostics
77+
78+
- `Graphics` (SVG/XAML path, BMP) and `PngBuilder` (PNG) take the finished modules; `QrCode` delegates to them. The SVG path merges adjacent dark modules into the largest rectangles to shrink output.
79+
- `StructuredAppend` splits long text across up to 16 linked QR codes (used by `EncodeTextInMultipleCodes`).
80+
- `EncodingInfo` / `PenaltyScore` are opt-in diagnostics: pass an `EncodingInfo` to capture per-mask penalty breakdowns and the chosen segments. This forces *full* penalty evaluation (disables early-stop), so it is slower — it exists for the `QrCodeAnalyzer` tool, not normal use.
2881

2982
## Tests
3083

3184
Uses **xUnit v3** with **Verify.XunitV3** for snapshot/approval testing and **Xunit.Combinatorial** for parameterized matrices.
3285

33-
Snapshot files live alongside the test source in `QrCodeGeneratorTest/`. When a snapshot changes intentionally, run `dotnet test` once — Verify will fail and write the new `.verified.*` file; accept it by deleting the old one or using the Verify tooling.
86+
The test strategy is characterization + cross-validation, not just unit tests:
87+
88+
- **`QrCodeDataProvider.cs`** is a large generated `[ClassData]` source feeding `QrCodeTest`. `TestQrCode` asserts the exact module layout, version, ECC, and mask for each case (golden tests).
89+
- **`VerifyWithZXing`** decodes every generated QR code with **ZXing.Net** and asserts the text round-trips with *zero* errors corrected. `CorruptedModule_IsCorrected_AndReportsErrorsCorrected` is a negative control that flips one module to prove that assertion has teeth. (Some ECI+Kanji combinations are skipped due to a ZXing 0.16.x decode bug — the QR is still valid.)
90+
- **`ReedSolomonTest`** cross-checks ECC against the **STH1123.ReedSolomon** package.
91+
- **Verify** snapshot tests cover rendered output (SVG/PNG/BMP). Snapshot `.verified.*` files live alongside the test source in `QrCodeGeneratorTest/`. When a snapshot changes intentionally, run `dotnet test` once — Verify fails and writes the new `.verified.*` file; accept it by replacing the old one (or via the Verify tooling).

0 commit comments

Comments
 (0)