Cysharp / Cysharp/MemoryPack

Proposal: Support ref struct buffer writers via allows ref struct constraint (.NET 9+)

Open
#447 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
4.7k
Forks
313
PR merge metrics
No merged PRs in 30d

Description

The Problem
Currently, when using MemoryPack with highly optimized, custom memory allocators (like native/unmanaged memory arenas), users are forced to implement IBufferWriter<byte>. The interface requires implementing GetMemory(), which returns a Memory<byte>. Wrapping a raw pointer in a Memory<byte> necessitates allocating a heap-based MemoryManager<T> per buffer.

Since MemoryPackWriter only ever uses GetSpan() and never actually calls GetMemory(), this forces a completely unnecessary heap allocation just to satisfy the interface constraints, which goes against the high-performance, zero-allocation philosophy of MemoryPack.

Proposed Solution
With the introduction of C# 13 and .NET 9, we can utilize the allows ref struct anti-constraint.

By applying this constraint to the TBufferWriter generic parameter in MemoryPackWriter<TBufferWriter> and its related core interfaces (conditionally under #if NET9_0_OR_GREATER), developers could pass ref struct implementations of IBufferWriter<byte>. This would allow custom writers to throw NotSupportedException in GetMemory() and rely purely on GetSpan(), completely avoiding heap allocations for native memory buffers.

Impact and Considerations

  • Performance: Unlocks true zero-allocation custom buffer writers for unmanaged memory.
  • Backwards Compatibility: We can wrap this in #if NET9_0_OR_GREATER directives so .NET 7 and 8 users are unaffected.
  • Compiler Limitations: There is a known limitation in C# 13 (CS9050) where a ref field cannot refer to a ref struct anti-constraint generic parameter. This would require an internal workaround using ref byte and Unsafe.As.
  • Breaking Changes (.NET 9 only): This would be a source-breaking change for .NET 9 users who have written custom formatters inheriting from MemoryPackFormatter<T>, as they would need to propagate the allows ref struct constraint. (The MemoryPack source generator itself implicitly handles constraints via explicit interface implementations).

I have a working implementation for this on a fork and can submit a PR if this proposal is accepted.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing MemoryPackWriter and the related core interfaces, focusing on their IBufferWriter constraints and the NET9_0_OR_GREATER conditionals. Compare the proposed approach with the working fork implementation and account for the C# 13 CS9050 limitation. Done means ref struct writers can rely on GetSpan() without heap allocation while existing .NET 7 and 8 behavior remains unaffected.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.