Skip to main content

dotnext-memory

Allocation-free memory routines from the DotNext and DotNext.Unsafe NuGet packages - SpanOwner (safe stackalloc with pooled fallback), BufferWriterSlim (stack-allocated StringBuilder/buffer replacement with string interpolation), PoolingBufferWriter, PoolingArrayBufferWriter, SparseBufferWriter, and the MemoryAllocator abstraction over ArrayPool, plain arrays and unmanaged memory. Use when reducing allocations in .NET hot paths; when stackalloc length comes from input; when replacing StringBuilder, MemoryStream, RecyclableMemoryStream, ArrayBufferWriter or manual ArrayPool rent/return code; or when making the memory allocation strategy configurable.

Informações da origem

Repositório
dotnet/dotNext
Última atividade na origem
6 de outubro de 2026 às 14:10
Idioma detectado do SKILL.md
inglês
Estrelas
1.967
Forks
157

Opções de instalação

Por padrão, está selecionado o prompt que primeiro revisa a origem. Você pode mudar para um comando direto ou baixar uma cópia local.

Revise os arquivos de origem

Leia o SKILL.md e os arquivos complementares exibidos pelo SkillsMP antes de decidir se vai instalar.

Exibindo SKILL.md

SKILL.md
Instruções da origem · Visualização somente leitura
name
dotnext-memory
description
Allocation-free memory routines from the DotNext and DotNext.Unsafe NuGet packages - SpanOwner (safe stackalloc with pooled fallback), BufferWriterSlim (stack-allocated StringBuilder/buffer replacement with string interpolation), PoolingBufferWriter, PoolingArrayBufferWriter, SparseBufferWriter, and the MemoryAllocator abstraction over ArrayPool, plain arrays and unmanaged memory. Use when reducing allocations in .NET hot paths; when stackalloc length comes from input; when replacing StringBuilder, MemoryStream, RecyclableMemoryStream, ArrayBufferWriter or manual ArrayPool rent/return code; or when making the memory allocation strategy configurable.
license
MIT
metadata
{"dotnext-version":"6.x"}
# Before you start 1. Check which version of `DotNext` and `DotNext.Unsafe` the project references (`.csproj` or `Directory.Packages.props`). This skill describes 6.x. For older versions, verify each member you use against the XML docs/IntelliSense of the referenced version. 2. The types below are in several namespaces. Add the `using` shown next to each type. 3. If this skill doesn't cover what you need (API details, more examples), find the relevant article in the documentation index: https://dotnet.github.io/dotNext/llms.txt Most of the types are in `DotNext` package. In case of unmanaged memory allocations, add `DotNext.Unsafe` dependency as well. # Use Cases - Avoid **stackalloc** which length is an input from the untrusted source: user input, request parameter, etc. If the length is expected to be small, but you need safe fallback, use `SpanOwner<T>` (`DotNext.Buffers`) data type. - Always declare it with `using`. When the length exceeds the threshold, `SpanOwner<T>` rents the memory from the pool, and `Dispose` returns it. For a stack-allocated span, `Dispose` does nothing. - Choose the threshold in bytes, not elements: divide by `Unsafe.SizeOf<T>()` for other element types. - Canonical example: ```cs using DotNext.Buffers; public static void Encode(ReadOnlySpan<char> input, Stream output, Encoding encoding) { const int stackallocThreshold = 512; // in bytes; keep it small, the stack is limited int maxByteCount = encoding.GetMaxByteCount(input.Length); using SpanOwner<byte> buffer = (uint)maxByteCount <= (uint)stackallocThreshold ? stackalloc byte[maxByteCount] : new SpanOwner<byte>(maxByteCount); var bytesWritten = encoding.GetBytes(input, buffer.Span); output.Write(buffer.Span.Slice(0, bytesWritten)); } ``` - Consider `DotNext.Buffers.BufferWriterSlim<T>` type in combination with extension methods from `DotNext.Buffers.Text.CharBuffer` class instead of `System.Text.StringBuilder` in synchronous code because its initial buffer can be allocated on the stack. If the buffer overflows, the writer uses memory allocator to rent the memory (by default it uses `ArrayPool<T>.Shared`). String interpolation for this type is exposed by the `Interpolate` extension method from `DotNext.Text.StringInterpolation` class. - The initial buffer must have the same element type as the writer: `stackalloc char[...]` for `BufferWriterSlim<char>`. - Keep `Interpolate` outside of the `try` block: calling it on a `BufferWriterSlim<char>` local inside `try`/`finally` fails to compile (CS8347/CS8350), and `using var writer` is not allowed either (CS1657, a `using` variable can't be passed by `ref`). If `Interpolate` throws, the rented buffer is not returned to the pool, which is harmless. - `WrittenSpan` becomes invalid after `Dispose()`. Consume or copy it before. - Canonical example: ```cs using DotNext.Buffers; using DotNext.Text; var writer = new BufferWriterSlim<char>(stackalloc char[256]); writer.Interpolate($"Hello, {name}!"); try { ConsumeWrittenChars(writer.WrittenSpan); } finally { writer.Dispose(); } ``` - Consider `PoolingBufferWriter<T>` (`DotNext.Buffers`) for async scenarios, where `BufferWriterSlim<T>` cannot be applied because it's **ref struct**. Set the expected upper bound of the written data via the `Capacity` init-only property, because the constructor only accepts the memory allocator: `new PoolingBufferWriter<byte>(allocator) { Capacity = 4096 }`. The buffer grows automatically in case of overflow, but each growth rents a larger buffer, copies the written data and returns the smaller buffer to the pool, where it remains retained. Frequent growth therefore costs copying and inflates the pool, so a good initial capacity matters. - Use `PoolingArrayBufferWriter<T>` (`DotNext.Buffers`) instead when the consumer needs the written data as an array: its `WrittenArray` property returns `ArraySegment<T>`, for example to pass to an API that accepts `byte[]` with offset and count. It rents arrays from `ArrayPool<T>`. - Consider `SparseBufferWriter<T>` (`DotNext.Buffers`) if there is no way to guess the initial capacity, and the contiguous buffer is not a requirement. It chains chunks instead of growing a single buffer, so it never copies the written data on growth. The written memory can be enumerated chunk by chunk (`foreach (ReadOnlyMemory<T> chunk in writer)`), copied with `CopyTo`, or obtained as `System.Buffers.ReadOnlySequence<T>` through its `ISupplier<ReadOnlySequence<T>>` implementation. This type is not limited to bytes in contrast to `RecyclableMemoryStream` from `Microsoft.IO.RecyclableMemoryStream` package. - Write through its public `Write`/`Add` methods only. Its `IBufferWriter<T>` members are implemented explicitly because using them can leave holes in the sparse buffer. - Abstract away memory rental and allocation by using `MemoryAllocator<T>` delegate (`DotNext.Buffers`). It can be passed to the types mentioned above, so your application can be configured to use one of the predefined allocation strategies: - `System.Buffers.ArrayPool<T>.Shared` which is exposed by `MemoryAllocator<T>.Default` static property - Plain array allocation without pooling, which is exposed by `MemoryAllocator<T>.Array` static property - Unmanaged heap allocation, which is exposed by `MemoryAllocator<T>.Unmanaged` static extension property, which requires a reference to `DotNext.Unsafe` package - A custom `ArrayPool<T>` or `MemoryPool<T>` can be converted with the `ToAllocator()` extension method
Ver no GitHub