@cratis/pi 7.102.0 → 7.103.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/package/corpus/harnesses/copilot/instructions/profiles.instructions.md +10 -1
  2. package/package/corpus/harnesses/copilot/instructions/pull-requests.instructions.md +2 -0
  3. package/package/corpus/harnesses/copilot/instructions/secrets.instructions.md +4 -0
  4. package/package/corpus/hooks/README.md +11 -1
  5. package/package/corpus/hooks/scripts/cratis-check-pr.mjs +12 -1
  6. package/package/corpus/hooks/scripts/cratis-guard-secrets.sh +79 -23
  7. package/package/corpus/hooks/scripts/cratis-guard-writes.sh +145 -40
  8. package/package/corpus/rules/profiles.md +10 -1
  9. package/package/corpus/rules/pull-requests.md +2 -0
  10. package/package/corpus/rules/secrets.md +4 -0
  11. package/package/corpus/skills/analyzing-dotnet-performance/LICENSE +23 -0
  12. package/package/corpus/skills/analyzing-dotnet-performance/NOTICE +36 -0
  13. package/package/corpus/skills/analyzing-dotnet-performance/SKILL.md +214 -0
  14. package/package/corpus/skills/analyzing-dotnet-performance/references/async-patterns.md +122 -0
  15. package/package/corpus/skills/analyzing-dotnet-performance/references/collections-and-linq.md +220 -0
  16. package/package/corpus/skills/analyzing-dotnet-performance/references/critical-patterns.md +291 -0
  17. package/package/corpus/skills/analyzing-dotnet-performance/references/io-and-serialization.md +135 -0
  18. package/package/corpus/skills/analyzing-dotnet-performance/references/memory-and-strings.md +219 -0
  19. package/package/corpus/skills/analyzing-dotnet-performance/references/regex-patterns.md +95 -0
  20. package/package/corpus/skills/analyzing-dotnet-performance/references/structural-patterns.md +38 -0
  21. package/package/corpus/skills/binlog-generation/LICENSE +23 -0
  22. package/package/corpus/skills/binlog-generation/NOTICE +22 -0
  23. package/package/corpus/skills/binlog-generation/SKILL.md +129 -0
  24. package/package/corpus/skills/check-bin-obj-clash/LICENSE +23 -0
  25. package/package/corpus/skills/check-bin-obj-clash/NOTICE +27 -0
  26. package/package/corpus/skills/check-bin-obj-clash/SKILL.md +127 -0
  27. package/package/corpus/skills/check-bin-obj-clash/references/common-causes-and-fixes.md +206 -0
  28. package/package/corpus/skills/coverage-analysis/LICENSE +23 -0
  29. package/package/corpus/skills/coverage-analysis/NOTICE +32 -0
  30. package/package/corpus/skills/coverage-analysis/SKILL.md +198 -0
  31. package/package/corpus/skills/coverage-analysis/references/guidelines.md +68 -0
  32. package/package/corpus/skills/coverage-analysis/references/output-format.md +91 -0
  33. package/package/corpus/skills/coverage-analysis/references/report-generation.md +58 -0
  34. package/package/corpus/skills/coverage-analysis/references/setup-discovery.md +307 -0
  35. package/package/corpus/skills/coverage-analysis/scripts/Compute-CrapScores.ps1 +80 -0
  36. package/package/corpus/skills/coverage-analysis/scripts/CoverageData.ps1 +272 -0
  37. package/package/corpus/skills/coverage-analysis/scripts/Extract-MethodCoverage.ps1 +65 -0
  38. package/package/corpus/skills/cratis-screenplay-command-surface/SKILL.md +26 -1
  39. package/package/corpus/skills/cratis-screenplay-projections/SKILL.md +17 -0
  40. package/package/corpus/skills/cratis-screenplay-render-and-gap-fill/SKILL.md +17 -0
  41. package/package/corpus/skills/cratis-screenplay-specifications/SKILL.md +8 -0
  42. package/package/corpus/skills/cratis-screenplay-toolchain/SKILL.md +1 -1
  43. package/package/corpus/skills/cratis-screenplay-toolchain/references/cheat-sheet-example.md +1 -0
  44. package/package/corpus/skills/cratis-screenplay-toolchain/references/fast-path.md +7 -0
  45. package/package/corpus/skills/cratis-screenplay-toolchain/references/traps.md +1 -0
  46. package/package/corpus/skills/cratis-screenplay-ui-composition/SKILL.md +40 -0
  47. package/package/corpus/skills/cratis-screenplay-ui-composition/references/response-continuations.md +1 -0
  48. package/package/corpus/skills/cratis-screenplay-ui-composition/references/worked-flow.md +1 -0
  49. package/package/corpus/skills/cratis-stage-rendering-and-sandbox/references/admission.md +3 -2
  50. package/package/corpus/skills/csharp-refactoring/LICENSE +23 -0
  51. package/package/corpus/skills/csharp-refactoring/NOTICE +22 -0
  52. package/package/corpus/skills/csharp-refactoring/SKILL.md +175 -0
  53. package/package/corpus/skills/csharp-refactoring/references/operation-catalog.md +37 -0
  54. package/package/corpus/skills/directory-build-organization/LICENSE +23 -0
  55. package/package/corpus/skills/directory-build-organization/NOTICE +21 -0
  56. package/package/corpus/skills/directory-build-organization/SKILL.md +205 -0
  57. package/package/corpus/skills/directory-build-organization/references/common-patterns.md +56 -0
  58. package/package/corpus/skills/directory-build-organization/references/multi-level-examples.md +167 -0
  59. package/package/corpus/skills/directory-build-organization/references/targetframework-props-pitfall.md +54 -0
  60. package/package/corpus/skills/dotnet-aot-compat/LICENSE +23 -0
  61. package/package/corpus/skills/dotnet-aot-compat/NOTICE +18 -0
  62. package/package/corpus/skills/dotnet-aot-compat/SKILL.md +265 -0
  63. package/package/corpus/skills/dotnet-aot-compat/references/polyfills.md +43 -0
  64. package/package/corpus/skills/dotnet-trace-collect/LICENSE +23 -0
  65. package/package/corpus/skills/dotnet-trace-collect/NOTICE +65 -0
  66. package/package/corpus/skills/dotnet-trace-collect/SKILL.md +116 -0
  67. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-monitor.md +97 -0
  68. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect-linux.md +84 -0
  69. package/package/corpus/skills/dotnet-trace-collect/references/dotnet-trace-collect.md +91 -0
  70. package/package/corpus/skills/dotnet-trace-collect/references/perfcollect.md +200 -0
  71. package/package/corpus/skills/dotnet-trace-collect/references/perfview.md +141 -0
  72. package/package/corpus/skills/dotnet-trace-collect/references/tool-selection.md +160 -0
  73. package/package/corpus/skills/dump-collect/LICENSE +23 -0
  74. package/package/corpus/skills/dump-collect/NOTICE +40 -0
  75. package/package/corpus/skills/dump-collect/SKILL.md +97 -0
  76. package/package/corpus/skills/dump-collect/references/container-dumps.md +412 -0
  77. package/package/corpus/skills/dump-collect/references/coreclr-dumps.md +108 -0
  78. package/package/corpus/skills/dump-collect/references/nativeaot-dumps.md +280 -0
  79. package/package/corpus/skills/eliminate-redundant-restores/LICENSE +23 -0
  80. package/package/corpus/skills/eliminate-redundant-restores/NOTICE +19 -0
  81. package/package/corpus/skills/eliminate-redundant-restores/SKILL.md +112 -0
  82. package/package/corpus/skills/extension-points/LICENSE +23 -0
  83. package/package/corpus/skills/extension-points/NOTICE +18 -0
  84. package/package/corpus/skills/extension-points/SKILL.md +248 -0
  85. package/package/corpus/skills/filter-syntax/LICENSE +23 -0
  86. package/package/corpus/skills/filter-syntax/NOTICE +17 -0
  87. package/package/corpus/skills/filter-syntax/SKILL.md +179 -0
  88. package/package/corpus/skills/including-generated-files/LICENSE +23 -0
  89. package/package/corpus/skills/including-generated-files/NOTICE +17 -0
  90. package/package/corpus/skills/including-generated-files/SKILL.md +139 -0
  91. package/package/corpus/skills/microbenchmarking/LICENSE +23 -0
  92. package/package/corpus/skills/microbenchmarking/NOTICE +36 -0
  93. package/package/corpus/skills/microbenchmarking/SKILL.md +131 -0
  94. package/package/corpus/skills/microbenchmarking/references/bdn-internals-and-tuning.md +143 -0
  95. package/package/corpus/skills/microbenchmarking/references/comparison-strategies.md +200 -0
  96. package/package/corpus/skills/microbenchmarking/references/diagnosers-and-exporters.md +150 -0
  97. package/package/corpus/skills/microbenchmarking/references/project-setup-and-running.md +189 -0
  98. package/package/corpus/skills/microbenchmarking/references/writing-benchmarks.md +310 -0
  99. package/package/corpus/skills/msbuild/LICENSE +23 -0
  100. package/package/corpus/skills/msbuild/NOTICE +55 -0
  101. package/package/corpus/skills/msbuild/SKILL.md +117 -0
  102. package/package/corpus/skills/msbuild/references/antipatterns/additional-antipatterns.md +291 -0
  103. package/package/corpus/skills/msbuild/references/antipatterns/incremental-build-inputs-outputs.md +57 -0
  104. package/package/corpus/skills/msbuild/references/antipatterns/private-assets.md +29 -0
  105. package/package/corpus/skills/msbuild/references/binlog-failure-analysis.md +85 -0
  106. package/package/corpus/skills/msbuild/references/binlog-generation.md +69 -0
  107. package/package/corpus/skills/msbuild/references/build-perf-baseline.md +493 -0
  108. package/package/corpus/skills/msbuild/references/build-perf-diagnostics.md +268 -0
  109. package/package/corpus/skills/msbuild/references/copy-to-output-directory.md +183 -0
  110. package/package/corpus/skills/msbuild/references/eval-performance.md +190 -0
  111. package/package/corpus/skills/msbuild/references/extension-points.md +281 -0
  112. package/package/corpus/skills/msbuild/references/incremental-build.md +365 -0
  113. package/package/corpus/skills/msbuild/references/msbuild-antipatterns.md +401 -0
  114. package/package/corpus/skills/orleans/LICENSE +21 -0
  115. package/package/corpus/skills/orleans/NOTICE +64 -0
  116. package/package/corpus/skills/orleans/SKILL.md +140 -0
  117. package/package/corpus/skills/orleans/references/anti-patterns.md +660 -0
  118. package/package/corpus/skills/orleans/references/configuration-api.md +933 -0
  119. package/package/corpus/skills/orleans/references/examples.md +66 -0
  120. package/package/corpus/skills/orleans/references/grain-api.md +784 -0
  121. package/package/corpus/skills/orleans/references/grains.md +75 -0
  122. package/package/corpus/skills/orleans/references/hosting.md +71 -0
  123. package/package/corpus/skills/orleans/references/implementation.md +591 -0
  124. package/package/corpus/skills/orleans/references/official-docs-index.md +250 -0
  125. package/package/corpus/skills/orleans/references/patterns.md +811 -0
  126. package/package/corpus/skills/orleans/references/persistence-api.md +495 -0
  127. package/package/corpus/skills/orleans/references/primitive-selection.md +35 -0
  128. package/package/corpus/skills/orleans/references/scheduling-and-services.md +295 -0
  129. package/package/corpus/skills/orleans/references/serialization-api.md +299 -0
  130. package/package/corpus/skills/orleans/references/streaming-api.md +396 -0
  131. package/package/corpus/skills/orleans/references/testing-patterns.md +210 -0
  132. package/package/corpus/skills/platform-detection/LICENSE +23 -0
  133. package/package/corpus/skills/platform-detection/NOTICE +17 -0
  134. package/package/corpus/skills/platform-detection/SKILL.md +230 -0
  135. package/package/corpus/skills/platform-detection/references/command-mode.md +27 -0
  136. package/package/corpus/skills/profiling/LICENSE +21 -0
  137. package/package/corpus/skills/profiling/NOTICE +50 -0
  138. package/package/corpus/skills/profiling/SKILL.md +144 -0
  139. package/package/corpus/skills/profiling/references/commands.md +439 -0
  140. package/package/corpus/skills/profiling/references/patterns.md +586 -0
  141. package/package/corpus/skills/profiling/references/profiling.md +190 -0
  142. package/package/corpus/skills/run-tests/LICENSE +23 -0
  143. package/package/corpus/skills/run-tests/NOTICE +19 -0
  144. package/package/corpus/skills/run-tests/SKILL.md +305 -0
  145. package/package/corpus/skills/target-authoring/LICENSE +23 -0
  146. package/package/corpus/skills/target-authoring/NOTICE +17 -0
  147. package/package/corpus/skills/target-authoring/SKILL.md +165 -0
  148. package/package/corpus/skills/vectorization/LICENSE +23 -0
  149. package/package/corpus/skills/vectorization/NOTICE +17 -0
  150. package/package/corpus/skills/vectorization/SKILL.md +189 -0
  151. package/package/profile-catalog.json +85 -1
  152. package/package.json +1 -1
@@ -0,0 +1,291 @@
1
+ # Critical .NET Performance Anti-Patterns
2
+
3
+ ## Contents
4
+
5
+ - Async / Tasks
6
+ - Memory / Allocation
7
+ - Strings
8
+ - Regular Expressions
9
+ - JSON Serialization
10
+ - Networking
11
+ - General
12
+ - Detection
13
+
14
+ 17 patterns that cause deadlocks, order-of-magnitude regressions, or excessive allocations.
15
+
16
+ ## Async / Tasks
17
+
18
+ ### Never Block on Async (Sync-over-Async)
19
+ 🔴 **AVOID** | .NET Core+
20
+
21
+ ❌
22
+ ```csharp
23
+ public string GetData()
24
+ => GetDataAsync().Result;
25
+ ```
26
+ ✅
27
+ ```csharp
28
+ public async Task<string> GetDataAsync()
29
+ => await GetDataInternalAsync();
30
+ ```
31
+ **Impact: Deadlocks or thread pool starvation; wastes threads, destroys scalability.**
32
+
33
+ ### Never Await a ValueTask Multiple Times
34
+ 🔴 **AVOID** | .NET Core 2.1+
35
+
36
+ ❌
37
+ ```csharp
38
+ ValueTask<int> vt = SomeMethodAsync();
39
+ int a = await vt;
40
+ int b = await vt;
41
+ ```
42
+ ✅
43
+ ```csharp
44
+ int result = await SomeMethodAsync();
45
+ ```
46
+ **Impact: Undefined behavior — silent data corruption or exceptions.**
47
+
48
+ ## Memory / Allocation
49
+
50
+ ### Use Span\<T\> / AsSpan Instead of Substring for Slicing
51
+ 🔴 **DO** | .NET Core 2.1+
52
+
53
+ ❌
54
+ ```csharp
55
+ string sub = input.Substring(5, 10);
56
+ ```
57
+ ✅
58
+ ```csharp
59
+ ReadOnlySpan<char> sub = input.AsSpan(5, 10);
60
+ ```
61
+ **Impact: Eliminates per-slice allocations; 2-4x faster via vectorization.**
62
+
63
+ ### Use ArrayPool\<T\> for Temporary Buffers
64
+ 🔴 **DO** | .NET Core+
65
+
66
+ ❌
67
+ ```csharp
68
+ byte[] buf = new byte[4096];
69
+ ```
70
+ ✅
71
+ ```csharp
72
+ byte[] buf = ArrayPool<byte>.Shared.Rent(4096);
73
+ Process(buf);
74
+ ArrayPool<byte>.Shared.Return(buf);
75
+ ```
76
+ **Impact: Dramatically reduces GC pressure for buffer-heavy workloads.**
77
+
78
+ ### Avoid stackalloc in Loops
79
+ 🔴 **AVOID** | .NET 5+
80
+
81
+ ❌
82
+ ```csharp
83
+ for (int i = 0; i < 10_000; i++)
84
+ Span<byte> buf = stackalloc byte[1024];
85
+ ```
86
+ ✅
87
+ ```csharp
88
+ Span<byte> buf = stackalloc byte[1024];
89
+ for (int i = 0; i < 10_000; i++) { Process(buf); }
90
+ ```
91
+ **Impact: StackOverflowException — unrecoverable, no catch possible.**
92
+
93
+ ### Avoid Boxing Value Types
94
+ 🔴 **AVOID** | .NET 6+
95
+
96
+ ❌
97
+ ```csharp
98
+ string s = string.Format("{0}.{1}", major, minor);
99
+ ```
100
+ ✅
101
+ ```csharp
102
+ string s = $"{major}.{minor}";
103
+ ```
104
+ **Impact: When replacing `string.Format` with C# 10+ interpolation, typical improvements are ~40% faster with significantly less allocation. Actual gains vary by call site.**
105
+
106
+ ## Strings
107
+
108
+ ### Use StringComparison.Ordinal for Non-Linguistic Comparisons
109
+ 🔴 **DO** | .NET Core+
110
+
111
+ ❌
112
+ ```csharp
113
+ bool found = text.IndexOf("Content-Type") >= 0;
114
+ ```
115
+ ✅
116
+ ```csharp
117
+ bool found = text.Contains("Content-Type", StringComparison.Ordinal);
118
+ ```
119
+ **Impact: 2-3x faster; OrdinalIgnoreCase hash codes ~3.3x faster.**
120
+
121
+ ### Use AsSpan Instead of Substring
122
+ 🔴 **DO** | .NET Core 2.1+
123
+
124
+ ❌
125
+ ```csharp
126
+ int val = int.Parse(str.Substring(5, 3));
127
+ ```
128
+ ✅
129
+ ```csharp
130
+ int val = int.Parse(str.AsSpan(5, 3));
131
+ ```
132
+ **Impact: Eliminates one string allocation per parse operation.**
133
+
134
+ ## Regular Expressions
135
+
136
+ ### Use Source-Generated Regex [GeneratedRegex]
137
+ 🔴 **ALWAYS** use `[GeneratedRegex]` for all static regex patterns | .NET 7+
138
+
139
+ ❌
140
+ ```csharp
141
+ private static readonly Regex s_re =
142
+ new(@"\w+@\w+\.\w+", RegexOptions.Compiled);
143
+ ```
144
+ ✅
145
+ ```csharp
146
+ [GeneratedRegex(@"\w+@\w+\.\w+")]
147
+ private static partial Regex EmailRegex();
148
+ ```
149
+ **Impact: Always beneficial or neutral for static patterns — near-zero startup, better throughput, and required for AOT/trimming scenarios.**
150
+
151
+ ### Avoid Nested Quantifiers (Catastrophic Backtracking)
152
+ 🔴 **AVOID** | .NET Core+
153
+
154
+ ❌
155
+ ```csharp
156
+ var r = new Regex(@"^(\w+)+$");
157
+ ```
158
+ ✅
159
+ ```csharp
160
+ var r = new Regex(@"^\w+$", RegexOptions.NonBacktracking);
161
+ ```
162
+ **Impact: Can hang process indefinitely on crafted input.**
163
+
164
+ ### Use TryGetValue Instead of ContainsKey + Indexer
165
+ 🔴 **DO** | .NET Core+
166
+
167
+ ❌
168
+ ```csharp
169
+ if (dict.ContainsKey(key))
170
+ Use(dict[key]);
171
+ ```
172
+ ✅
173
+ ```csharp
174
+ if (dict.TryGetValue(key, out var value))
175
+ Use(value);
176
+ ```
177
+ **Impact: ~2x faster (50% reduction in lookup time).**
178
+
179
+ ### Avoid LINQ in Hot Paths
180
+ 🔴 **AVOID** | .NET Core+
181
+
182
+ ❌
183
+ ```csharp
184
+ bool found = items.Any(x => x.Name == target);
185
+ ```
186
+ ✅
187
+ ```csharp
188
+ bool found = false;
189
+ foreach (var item in items)
190
+ if (item.Name == target) { found = true; break; }
191
+ ```
192
+ **Impact: Eliminates 1-3 allocations per call; measurable in tight loops.**
193
+
194
+ ### Don't Iterate IEnumerable Multiple Times
195
+ 🔴 **AVOID** | .NET Core+
196
+
197
+ ❌
198
+ ```csharp
199
+ foreach (Type t in types) { Validate(t); }
200
+ _types = types.ToArray();
201
+ ```
202
+ ✅
203
+ ```csharp
204
+ Type[] arr = types.ToArray();
205
+ foreach (Type t in arr) { Validate(t); }
206
+ _types = arr;
207
+ ```
208
+ **Impact: Halves enumeration cost; prevents bugs from re-executing deferred queries.**
209
+
210
+ ## JSON Serialization
211
+
212
+ ### Use System.Text.Json Source Generator
213
+ 🔴 **DO** | .NET 6+
214
+
215
+ ❌
216
+ ```csharp
217
+ string json = JsonSerializer.Serialize(post);
218
+ ```
219
+ ✅
220
+ ```csharp
221
+ [JsonSerializable(typeof(BlogPost))]
222
+ internal partial class AppJsonCtx : JsonSerializerContext { }
223
+ string json = JsonSerializer.Serialize(post, AppJsonCtx.Default.BlogPost);
224
+ ```
225
+ **Impact: 37-44% faster; enables trimming and Native AOT.**
226
+
227
+ ### Cache JsonSerializerOptions
228
+ 🔴 **DO** | .NET 5+
229
+
230
+ ❌
231
+ ```csharp
232
+ JsonSerializer.Serialize(obj, new JsonSerializerOptions());
233
+ ```
234
+ ✅
235
+ ```csharp
236
+ private static readonly JsonSerializerOptions s_opts = new();
237
+ JsonSerializer.Serialize(obj, s_opts);
238
+ ```
239
+ **Impact: Up to 592x slower without caching (.NET 6); always cache or use defaults.**
240
+
241
+ ## Networking
242
+
243
+ ### Reuse HttpClient Instances
244
+ 🔴 **DO** | .NET Core 2.1+
245
+
246
+ ❌
247
+ ```csharp
248
+ using var client = new HttpClient();
249
+ await client.GetStringAsync(url);
250
+ ```
251
+ ✅
252
+ ```csharp
253
+ private static readonly HttpClient s_http = new(new SocketsHttpHandler
254
+ { PooledConnectionLifetime = TimeSpan.FromMinutes(5) });
255
+ await s_http.GetStringAsync(url);
256
+ ```
257
+ **Impact: Prevents socket exhaustion; 6-12x faster concurrent HTTPS.**
258
+
259
+ ## General
260
+
261
+ ### Use SearchValues\<T\> for Repeated Set Searches
262
+ 🔴 **DO** | .NET 8+
263
+
264
+ ❌
265
+ ```csharp
266
+ int pos = text.IndexOfAny("ABCDEF".ToCharArray());
267
+ ```
268
+ ✅
269
+ ```csharp
270
+ private static readonly SearchValues<char> s_hex = SearchValues.Create("ABCDEF");
271
+ int pos = text.AsSpan().IndexOfAny(s_hex);
272
+ ```
273
+ **Impact: 2-10x faster for chars; 10-30x faster for multi-string (.NET 9+).**
274
+
275
+ ## Detection
276
+
277
+ Scan recipes for critical anti-patterns. Run these and report exact counts of issues found in each case.
278
+
279
+ ```bash
280
+ # .IndexOf(string) without StringComparison (culture-aware, 2-3x slower)
281
+ grep -rn --include='*.cs' -E '\.IndexOf\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
282
+
283
+ # .Substring( calls (allocates new string — consider AsSpan)
284
+ grep -rn --include='*.cs' '\.Substring(' --exclude-dir=bin --exclude-dir=obj . | wc -l
285
+
286
+ # .StartsWith/.EndsWith without StringComparison (culture-aware, 2-3x slower)
287
+ grep -rn --include='*.cs' -E '\.(StartsWith|EndsWith)\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
288
+
289
+ # .Contains(string) without StringComparison — NOTE: will also match collection .Contains() calls; filter to string receivers
290
+ grep -rn --include='*.cs' -E '\.Contains\("[^"]+"\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
291
+ ```
@@ -0,0 +1,135 @@
1
+ # I/O, Serialization & General Patterns
2
+
3
+ ## Contents
4
+
5
+ - Use HttpCompletionOption.ResponseHeadersRead for Streaming
6
+ - Use Async FileStream Operations
7
+ - Use Memory\<byte\> Overloads for Stream.ReadAsync/WriteAsync
8
+ - Use Span-Based TryFormat for Number Formatting
9
+ - Use static readonly for Runtime Devirtualization
10
+ - Avoid Explicit Static Constructors — Use Field Initializers
11
+ - Detection
12
+ - Patterns Requiring Manual Review
13
+
14
+ ### Use HttpCompletionOption.ResponseHeadersRead for Streaming
15
+ 🟡 **DO** use `ResponseHeadersRead` when downloading large responses | .NET Core 3.0+
16
+
17
+ ❌
18
+ ```csharp
19
+ var response = await client.GetAsync(uri);
20
+ ```
21
+ ✅
22
+ ```csharp
23
+ using var response = await client.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead);
24
+ using var stream = await response.Content.ReadAsStreamAsync();
25
+ await stream.CopyToAsync(destinationStream);
26
+ ```
27
+
28
+ **Impact: ~2x faster for large downloads (10MB+), dramatically reduced memory usage.**
29
+
30
+ ### Use Async FileStream Operations
31
+ 🟡 **DO** use `FileStream` with `useAsync: true` for scalable file I/O | .NET 6+
32
+
33
+ ❌
34
+ ```csharp
35
+ using var fs = new FileStream(path, FileMode.Open);
36
+ ```
37
+ ✅
38
+ ```csharp
39
+ await using var fs = new FileStream(path, FileMode.Open, FileAccess.Read,
40
+ FileShare.Read, bufferSize: 4096, useAsync: true);
41
+
42
+ byte[] buffer = new byte[1024];
43
+ while (await fs.ReadAsync(buffer) != 0) { /* process */ }
44
+ ```
45
+
46
+ **Impact: Up to 3x faster async reads; allocation reduced from megabytes to hundreds of bytes.**
47
+
48
+ ### Use Memory\<byte\> Overloads for Stream.ReadAsync/WriteAsync
49
+ 🟡 **DO** use `Memory<byte>`-based stream overloads instead of `byte[]` overloads | .NET 5+
50
+
51
+ ❌
52
+ ```csharp
53
+ await stream.ReadAsync(buffer, 0, buffer.Length);
54
+ await stream.WriteAsync(buffer, 0, buffer.Length);
55
+ ```
56
+ ✅
57
+ ```csharp
58
+ await stream.ReadAsync(buffer.AsMemory());
59
+ await stream.WriteAsync(buffer.AsMemory());
60
+ ```
61
+
62
+ **Impact: Eliminates ~72 KB allocation per 1,000 read/write pairs on NetworkStream.**
63
+
64
+ ### Use Span-Based TryFormat for Number Formatting
65
+ 🟡 **DO** use `TryFormat` to format numbers into `Span<char>` buffers | .NET Core 2.1+
66
+
67
+ ❌
68
+ ```csharp
69
+ string formatted = value.ToString();
70
+ destination.Write(formatted);
71
+ ```
72
+ ✅
73
+ ```csharp
74
+ Span<char> buffer = stackalloc char[20];
75
+ if (value.TryFormat(buffer, out int charsWritten))
76
+ destination.Write(buffer[..charsWritten]);
77
+ ```
78
+
79
+ **Impact: Int32.ToString() ~2x faster in .NET Core 2.1, Int32 parsing ~5x faster in .NET Core 3.0.**
80
+
81
+ ### Use static readonly for Runtime Devirtualization
82
+ 🟡 **DO** store implementations in `static readonly` fields for JIT devirtualization | .NET Core 3.0+
83
+
84
+ ❌
85
+ ```csharp
86
+ private static Base s_impl = new DerivedImpl();
87
+ s_impl.Process();
88
+ ```
89
+ ✅
90
+ ```csharp
91
+ private static readonly Base s_impl = new DerivedImpl();
92
+ s_impl.Process();
93
+
94
+ private static readonly bool s_feature =
95
+ Environment.GetEnvironmentVariable("Feature") == "1";
96
+ ```
97
+
98
+ **Impact: Virtual call eliminated entirely — can be inlined to zero overhead. Dead code elimination in tier 1.**
99
+
100
+ ### Avoid Explicit Static Constructors — Use Field Initializers
101
+ 🟡 **AVOID** explicit `static` constructors when field initializers suffice | .NET Core 3.0+
102
+
103
+ ❌
104
+ ```csharp
105
+ class Foo
106
+ {
107
+ static readonly int s_value;
108
+ static Foo() { s_value = ComputeValue(); }
109
+ }
110
+ ```
111
+ ✅
112
+ ```csharp
113
+ class Foo
114
+ {
115
+ static readonly int s_value = ComputeValue();
116
+ }
117
+ ```
118
+
119
+ **Impact: Enables better JIT optimization and reduces potential lock overhead on static method access.**
120
+
121
+ ## Detection
122
+
123
+ Scan recipes for I/O and serialization anti-patterns. Run these and report exact counts.
124
+
125
+ ```bash
126
+ # new HttpClient() (socket exhaustion risk)
127
+ grep -rn --include='*.cs' 'new HttpClient(' --exclude-dir=bin --exclude-dir=obj . | wc -l
128
+
129
+ # new JsonSerializerOptions() not cached (592x slower in .NET 6)
130
+ grep -rn --include='*.cs' 'new JsonSerializerOptions' --exclude-dir=bin --exclude-dir=obj . | grep -v 'static\|readonly' | wc -l
131
+ ```
132
+
133
+ ### Patterns Requiring Manual Review
134
+
135
+ - **`JsonSerializer.Serialize/Deserialize` without source-gen context**: Can't determine from grep if a context parameter is passed
@@ -0,0 +1,219 @@
1
+ # Memory & String Patterns
2
+
3
+ ## Contents
4
+
5
+ - Use ReadOnlySpan\<byte\> for Constant Byte Data
6
+ - Use stackalloc for Small Temporary Buffers
7
+ - Use Span.TryWrite for Allocation-Free Interpolation
8
+ - Use Span.Split() for Zero-Allocation Splitting
9
+ - Use UTF8 String Literals (u8 suffix)
10
+ - Use ReadOnlySpan\<char\> Pattern Matching with switch
11
+ - Use params ReadOnlySpan\<T\> to Eliminate Array Allocations
12
+ - Avoid Chained String-Returning Operations
13
+ - Cache char.ToString() for Known Character Sets
14
+ - Detection
15
+ - Patterns Requiring Manual Review
16
+
17
+ ### Use ReadOnlySpan\<byte\> for Constant Byte Data
18
+ 🟡 **DO** assign constant byte arrays to `ReadOnlySpan<byte>` | .NET 5+
19
+
20
+ ❌
21
+ ```csharp
22
+ byte[] data = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
23
+ ```
24
+ ✅
25
+ ```csharp
26
+ ReadOnlySpan<byte> data = [0x48, 0x65, 0x6C, 0x6C, 0x6F];
27
+ ReadOnlySpan<int> primes = [2, 3, 5, 7, 11, 13];
28
+ ```
29
+
30
+ **Impact: ~100x faster access than static byte[] field, zero allocation.**
31
+
32
+ ### Use stackalloc for Small Temporary Buffers
33
+ 🟡 **DO** use `stackalloc` for small, fixed-size temporary buffers | .NET Core+
34
+
35
+ ❌
36
+ ```csharp
37
+ char[] buffer = new char[64];
38
+ guid.TryFormat(buffer, out int written);
39
+ ```
40
+ ✅
41
+ ```csharp
42
+ Span<char> buffer = stackalloc char[64];
43
+ guid.TryFormat(buffer, out int written);
44
+ ```
45
+
46
+ **Impact: Zero heap allocation, no GC pressure, instant alloc/dealloc.**
47
+
48
+ ### Use Span.TryWrite for Allocation-Free Interpolation
49
+ 🟡 **DO** use `MemoryExtensions.TryWrite` to format into `Span<char>` buffers | .NET 6+
50
+
51
+ ❌
52
+ ```csharp
53
+ string formatted = $"Date: {dt:R}";
54
+ destination.Write(formatted);
55
+ ```
56
+ ✅
57
+ ```csharp
58
+ Span<char> buffer = stackalloc char[64];
59
+ buffer.TryWrite($"Date: {dt:R}", out int charsWritten);
60
+ ```
61
+
62
+ **Impact: Zero heap allocation for formatting operations.**
63
+
64
+ ### Use Span.Split() for Zero-Allocation Splitting
65
+ 🟡 **DO** use `MemoryExtensions.Split` for allocation-free string splitting | .NET 9+
66
+
67
+ ❌
68
+ ```csharp
69
+ string[] parts = input.Split(',');
70
+ ```
71
+ ✅
72
+ ```csharp
73
+ foreach (Range range in input.AsSpan().Split(','))
74
+ {
75
+ ReadOnlySpan<char> segment = input.AsSpan(range);
76
+ }
77
+ ```
78
+
79
+ **Impact: 208 bytes → 0 bytes per split, 2x faster.**
80
+
81
+ ### Use UTF8 String Literals (u8 suffix)
82
+ 🟡 **DO** use the `u8` suffix for compile-time UTF8 `ReadOnlySpan<byte>` | .NET 7+
83
+
84
+ ❌
85
+ ```csharp
86
+ byte[] header = Encoding.UTF8.GetBytes("Content-Type");
87
+ ```
88
+ ✅
89
+ ```csharp
90
+ ReadOnlySpan<byte> header = "Content-Type"u8;
91
+ ```
92
+
93
+ **Impact: 17ns → 0.006ns — eliminates runtime transcoding entirely.**
94
+
95
+ ### Use ReadOnlySpan\<char\> Pattern Matching with switch
96
+ 🟡 **DO** use `switch` on `ReadOnlySpan<char>` for allocation-free string matching | C# 11+
97
+
98
+ ❌
99
+ ```csharp
100
+ switch (attr.Value.Trim()) { case "preserve": /* ... */ break; }
101
+ ```
102
+ ✅
103
+ ```csharp
104
+ switch (attr.Value.AsSpan().Trim())
105
+ {
106
+ case "preserve": return Preserve;
107
+ case "default": return Default;
108
+ }
109
+ ```
110
+
111
+ **Impact: Eliminates string allocation from Trim() in switch-based dispatch.**
112
+
113
+ ### Use params ReadOnlySpan\<T\> to Eliminate Array Allocations
114
+ 🟡 **DO** add `params ReadOnlySpan<T>` overloads to library methods | C# 13 / .NET 9+
115
+
116
+ ❌
117
+ ```csharp
118
+ public static void Log(params string[] messages) { /* ... */ }
119
+ Log("Starting", "Processing", "Done");
120
+ ```
121
+ ✅
122
+ ```csharp
123
+ public static void Log(params ReadOnlySpan<string> messages) { /* ... */ }
124
+ Log("Starting", "Processing", "Done");
125
+ ```
126
+
127
+ **Impact: Eliminates params array allocation. E.g., Path.Join with 5+ segments saves 64 bytes per call.**
128
+
129
+ ### Avoid Chained String-Returning Operations
130
+ 🟡 **AVOID** chains of 3+ string-returning method calls that each allocate intermediates | .NET Core+
131
+
132
+ **Pattern 1: Chained .Replace() calls**
133
+
134
+ ❌
135
+ ```csharp
136
+ string result = input.Replace("a", "b").Replace("c", "d").Replace("e", "f");
137
+ ```
138
+ ✅
139
+ ```csharp
140
+ var sb = new StringBuilder(input.Length);
141
+ // single pass replacing all patterns
142
+ ```
143
+
144
+ **Pattern 2: Chained Regex.Replace() calls**
145
+
146
+ ❌
147
+ ```csharp
148
+ public static string Underscore(this string input) =>
149
+ Regex3.Replace(Regex2.Replace(Regex1.Replace(input, "$1_$2"), "$1_$2"), "_").ToLower();
150
+ ```
151
+ ✅
152
+ ```csharp
153
+ return string.Create(totalLength, state, (span, s) => { /* write directly */ });
154
+ ```
155
+
156
+ **Pattern 3: += string concatenation in loops**
157
+
158
+ ❌
159
+ ```csharp
160
+ string result = "";
161
+ foreach (var part in parts)
162
+ result += separator + part;
163
+ ```
164
+ ✅
165
+ ```csharp
166
+ var sb = new StringBuilder();
167
+ foreach (var part in parts)
168
+ sb.Append(separator).Append(part);
169
+ return sb.ToString();
170
+ ```
171
+
172
+ **Impact: Eliminates N-1 intermediate string allocations per chain. For `+=` in loops, eliminates O(n²) total allocation.**
173
+
174
+ ### Cache char.ToString() for Known Character Sets
175
+ 🟡 **DO** cache `char.ToString()` results when the set of characters is small and known | .NET Core+
176
+
177
+ ❌
178
+ ```csharp
179
+ return symbol.ToString();
180
+
181
+ foreach (var prefix in UnitPrefixes)
182
+ input = input.Replace(prefix.Value.Name, prefix.Key.ToString());
183
+ ```
184
+ ✅
185
+ ```csharp
186
+ private static readonly FrozenDictionary<char, string> s_charStrings =
187
+ new Dictionary<char, string>
188
+ {
189
+ ['k'] = "k", ['M'] = "M", ['G'] = "G",
190
+ }.ToFrozenDictionary();
191
+
192
+ return s_charStrings[symbol];
193
+ ```
194
+
195
+ **Impact: Eliminates one string allocation per char.ToString() call. Significant when called in loops or on hot paths.**
196
+
197
+ ## Detection
198
+
199
+ Scan recipes for memory and string anti-patterns. Run these and report exact counts.
200
+
201
+ ```bash
202
+ # .ToLower()/.ToUpper() without culture parameter (allocates + culture-sensitive)
203
+ grep -rn --include='*.cs' -E '\.(ToLower|ToUpper)\(\)' --exclude-dir=bin --exclude-dir=obj . | wc -l
204
+
205
+ # Chained .Replace( calls (3+ on one line — intermediate string allocations)
206
+ grep -rn --include='*.cs' '\.Replace(.*\.Replace(.*\.Replace(' --exclude-dir=bin --exclude-dir=obj . | wc -l
207
+
208
+ # params in method signatures (array allocation per call)
209
+ grep -rn --include='*.cs' 'params ' --exclude-dir=bin --exclude-dir=obj . | wc -l
210
+
211
+ # LINQ on strings — .All/.Any on IEnumerable<char> (replace with foreach loop)
212
+ grep -rn --include='*.cs' -E '\.(All|Any)\(char\.' --exclude-dir=bin --exclude-dir=obj . | wc -l
213
+ ```
214
+
215
+ ### Patterns Requiring Manual Review
216
+
217
+ - **Boxing via string.Format**: Can't determine argument types from grep — needs type analysis
218
+ - **`+=` string concatenation in loops**: `+=` matches all types (int, list, event, string) — needs type context to confirm string
219
+ - **`char.ToString()`**: Requires knowing the variable type is `char` — not reliably greppable