agentleFS
glennawatsonglennawatson/CSharpAgentSkills/skills/dotnet-11-libraries/SKILL.md

dotnet-11-libraries

Skill2 starsChanged 42 days agoMITView on GitHub

Use when writing or reviewing C#/.NET code that could use a .NET 11 base library API, such as span-based ZLib/Deflate/GZip/Zstandard decoders and encoders, ReadOnlyMemoryStream, MemoryExtensions.Min/Max, Vector128 Unzip/Zip/Reverse, BitArray span constructors, Utf16 validation and RunePosition, and when the project also targets net10.0. Covers exact signatures, status results, the per-thread decoder pattern, the Compat helper rule that keeps `#if NET11_0_OR_GREATER` out of feature code, and managed MD5 for FIPS systems. Checked against the 11.0.0-rc.1 SDK and reference pack.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 0 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Keep every #if NET11_0_OR_GREATER branch inside one small static class in a Compat/ folder.

    line 12
  2. Very specific:

    All of these are in System.IO.Compression.

    line 35
  3. Very specific:

    After DestinationTooSmall you can continue with source[consumed..] and a destination starting at written without calling Reset().

    line 58
  4. Very specific:

    Create the decoder once per thread and call Reset() before each stream.

    line 62

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

.NET 11 base library additions

Everything here was checked against SDK 11.0.100-rc.1 (runtime 11.0.0-rc.1), the Microsoft.NETCore.App.Ref packs for 10.0.12 and 11.0.0-rc.1, and probe apps run on both runtimes. Re-check after the final release. Default to net10.0;net11.0 targets (see dotnet-multi-targeting), so each API below needs a net10.0 fallback.

The Compat helper rule

Keep every #if NET11_0_OR_GREATER branch inside one small static class in a Compat/ folder. Both branches sit behind the same signature. Feature code calls the helper and never contains #if.

DoDo not
ZLibCompat.Inflate(source, dest, out c, out w) in feature code#if NET11_0_OR_GREATER around a decoder call in a parser
One Compat file per API familyOne large Compat.cs that grows without limit
Same status results on both branches, or document the gap in the doc commentA fallback that silently behaves differently
Cover both branches with the same testsTesting only the net11.0 leg

ZLibCompat.cs is a working example. Verified on both runtimes:

Casenet11.0 (ZLibDecoder)net10.0 (ZLibStream fallback)
Valid dataDoneDone
Corrupt dataInvalidDataInvalidData
Destination too smallDestinationTooSmall, partial output keptDestinationTooSmall, partial output kept
Truncated dataNeedMoreData, partial output keptDone (cannot tell truncated from complete)
Allocation per call, steady state0 bytesabout 312 bytes (stream objects)

The truncated row is a real difference. If truncation matters on net10.0, compare the output length with a size you know, such as the expected row bytes of an image.

Span-based compression (new in .NET 11)

All of these are in System.IO.Compression. None exist on net10.0.

TypeShape
ZLibDecoder, DeflateDecoder, GZipDecodersealed class, IDisposable, parameterless constructor
MembersOperationStatus Decompress(ReadOnlySpan<byte> source, Span<byte> destination, out int bytesConsumed, out int bytesWritten), void Reset(), static bool TryDecompress(ReadOnlySpan<byte>, Span<byte>, out int bytesWritten)
ZLibEncoder, DeflateEncoder, GZipEncodersealed class, IDisposable. Constructors: (), (int quality), (int quality, int windowLog2), (ZLibCompressionOptions)
Encoder membersOperationStatus Compress(ReadOnlySpan<byte>, Span<byte>, out int consumed, out int written, bool isFinalBlock), Flush(Span<byte>, out int written), Reset(), static long GetMaxCompressedLength(long), static bool TryCompress(...) with optional quality and windowLog2
ZLibCompressionOptionsNew WindowLog2 property, plus DefaultWindowLog2 (15), MinWindowLog2 (8) and MaxWindowLog2 (15)
ZstandardZstandardEncoder, ZstandardDecoder (same shape, plus SetPrefix, TryGetMaxDecompressedLength), ZstandardStream, ZstandardDictionary (Create, Train), ZstandardCompressionOptions, ZstandardDecompressionOptions

TryDecompress and TryCompress are static, so calling them on an instance is error CS0176. TryDecompress writes nothing and returns false when the destination is short, so use the instance Decompress when the output size is unknown.

Status results, measured:

InputDecompress returnsException
Complete valid streamDonenone
First half of a streamNeedMoreData, decoded prefix is in the destinationnone
Bad zlib headerInvalidDatanone
Bad Adler-32 trailerInvalidData, but all output was already writtennone
Destination too smallDestinationTooSmall, bytesConsumed and bytesWritten show progressnone

After DestinationTooSmall you can continue with source[consumed..] and a destination starting at written without calling Reset(). A grow-and-retry loop with ArrayPool<byte> is therefore straightforward (see InflateToArray in the example).

Per-thread decoder, zero steady-state allocation

Create the decoder once per thread and call Reset() before each stream. 1000 decodes of an 8000-byte output allocated 0 bytes. A fresh new ZLibDecoder() per call allocated about 144 bytes each, plus native state.

[ThreadStatic]
private static ZLibDecoder? _decoder;

ZLibDecoder decoder = _decoder ??= new ZLibDecoder();
decoder.Reset();
OperationStatus status = decoder.Decompress(source, destination, out int consumed, out int written);

[ThreadStatic] instances are never disposed. That is acceptable for a long-lived worker thread; use a pool of decoders if threads come and go.

Zstandard has no mandated place in container formats such as PDF. Do not add it to a reader without a format reason.

In-memory streams

TypeNamespaceUse
ReadOnlyMemoryStream(ReadOnlyMemory<byte>)System.IORead-only (CanWrite is false). Write throws NotSupportedException
WritableMemoryStream(Memory<byte>)System.IOFixed size. Writing past the end throws NotSupportedException
ReadOnlySequenceStream(ReadOnlySequence<byte>)System.BuffersWraps a pipe's segments without flattening

StringStream does not exist in 11.0.0-rc.1. Do not use it.

Small additions

APINotes (verified)
MemoryExtensions.Min<T>(ReadOnlySpan<T>) and Max, with an IComparer<T> overloadAn empty span throws InvalidOperationException. Guard with IsEmpty
Vector128/256/512.Unzip, UnzipEven, UnzipOdd, Zip, ZipLower, ZipUpper, ConcatLowerLower, ConcatLowerUpper, ConcatUpperLower, ConcatUpperUpper, Reverse, CreateAlternatingSequenceAlso on Vector64. Unzip returns a tuple of two vectors. Unzipping the bytes 0 to 31 gives the evens 0,2,..30 and the odds 1,3,..31. There is no Concat or ConcatLower method. On net10.0 use Vector128.Shuffle with constant indices
BitArray(ReadOnlySpan<byte>), BitArray(ReadOnlySpan<bool>), BitArray(ReadOnlySpan<int>), PopCount()LSB first. Byte 0x01 sets [0], byte 0x80 sets [7]. Formats that number bits from the most significant bit (PDF and TIFF bitmaps) read the wrong bits. Read those with shifts: (b >> (7 - i)) & 1
Utf16.IsValid(ReadOnlySpan<char>), Utf16.IndexOfInvalidSubsequence, Utf8.IndexOfInvalidSubsequenceNew in 11. Utf8.IsValid is older (.NET 8). A lone high surrogate reports the index of the bad char
RunePosition.EnumerateUtf8(ReadOnlySpan<byte>), EnumerateUtf16(ReadOnlySpan<char>)Yields Rune, StartIndex, Length and WasReplaced per code point. "aé😀"u8 gives starts 0, 1, 3 and lengths 1, 2, 4
CryptographicOperations.FixedTimeEquals(ReadOnlySpan<byte>, byte)New in 11: compares every byte with one value. The span-to-span overload is old
Base64.EncodeToUtf8, DecodeFromUtf8 and TryDecodeFromUtf8 convenience overloadsNew in 11 in System.Buffers.Text

Crypto notes

  • MD5.HashData goes through the platform crypto library. On Linux with OpenSSL in FIPS mode MD5 can be refused. This is reported behaviour; this host does not run FIPS, so it was not reproduced. If a format mandates MD5 (PDF RC4 and AES-128 key derivation), use a managed implementation. ManagedMd5.cs is about 100 lines of RFC 1321, allocation-free in Release builds, and matched MD5.HashData on the RFC test strings and on random inputs of 0 to 599 bytes. Report a FIPS refusal as a clear error, not a crash.
  • Compare password hashes and MACs with CryptographicOperations.FixedTimeEquals, never SequenceEqual.
  • RC4 is not in the base library. Write it in managed code (about 30 lines).

Checklist

  • Every #if NET11_0_OR_GREATER branch lives in a Compat/ helper with one signature for both branches
  • Differences between the two branches are in the helper's doc comment and covered by tests on both TFMs
  • Decoders and encoders are cached per thread and reset, not created per call
  • Output size unknown: instance Decompress in a grow loop, not TryDecompress
  • DestinationTooSmall, NeedMoreData and InvalidData handled as results, not exceptions
  • No StringStream, and no BitArray span constructor on MSB-first data
  • MD5 behind a managed fallback where FIPS systems are in scope

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.