Skip to content

new1271/SpanUnwrap.Fody

Repository files navigation

SpanUnwrap.Fody

NuGet package

This is an add-in for Fody which lets you unwraps Span<T> and ReadOnlySpan<T> in compile time.



Installation

(Note: The following configurations are intended for SDK-style projects)

1. Core Packages and Settings

  • Install the NuGet packages Fody and SpanUnwrap.Fody. Installing Fody explicitly is needed to enable weaving.

    <PackageReference Include="Fody" Version="*">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="SpanUnwrap.Fody" Version="*">
        <PrivateAssets>all</PrivateAssets>
        <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  • If you already have a FodyWeavers.xml file in the root directory of your project, add the <SpanUnwrap /> tag there. This file will be created on the first build if it doesn't exist:

    <?xml version="1.0" encoding="utf-8" ?>
    <Weavers>
        <SpanUnwrap />
    </Weavers>

See Fody usage for general guidelines, and Fody Configuration for additional options.

2. Legacy Framework Support

If your project targets .NET Standard 2.0 (or lower), .NET Core 2.x (or lower), or .NET Framework, you must include System.Memory manually.

<PackageReference Include="System.Memory" Version="*" />

Tip: Zero-Runtime Dependency: If you only need System.Memory for compile-time constant access and want to keep your runtime clean, use the following configuration instead:

<PackageReference Include="System.Memory" Version="*">
    <PrivateAssets>all</PrivateAssets>
    <ExcludeAssets>runtime</ExcludeAssets>
    <IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

Usage

Use the Unwrap.From() method to unwrap a Span<T> or ReadOnlySpan<T> at compile time and expose its underlying storage directly.

SpanUnwrap.Fody intercepts the call during compilation and optimizes the access based on the data source:

  • For the constant ReadOnlySpan<T> declarations: It will expose the "raw" data field, which is embedded by the compiler in your assembly, as a ref readonly reference.
  • For the Span<T> from stackalloc: It will expose the space you allocated from the stack as a ref reference.
  • For the other cases: It will automatically route the call to the GetPinnableReference() method in the Span<T> or ReadOnlySpan<T> instance.

Example

What you write:

public static void CopyData(ref byte destination)
{
    
    const int Length = 8;
    CopyBlockUnaligned(
        ref destination,
        in Unwrap.From(
            // a constant, length-sealed ReadOnlySpan<byte>.
            new byte[Length] { 0xFF, 0x5E, 0x30, 0x21, 0x44, 0x55, 0x55, 0x1A }
        ),
        Length);
}

What gets compiled:

C# disassmbly:

using System.Runtime.CompilerServices;

public static void CopyData(ref byte destination)
{
    CopyBlockUnaligned(ref destination, in Unsafe.As<long, byte>(
      // The "raw" data field is exposed now, and can be accessed directly by your code
      ref global::<PrivateImplementationDetails>.70DE15E71C9CEE3760BB7174ECC485889F6F554DDDC4947D4ECAF87E48003025)
      , 8u);
}

raw MSIL code:

.method public hidebysig static 
    void CopyData (
        uint8& destination
    ) cil managed 
{
    // Method begins at RVA 0x2626
    // Header size: 1
    // Code size: 14 (0xe)
    .maxstack 8

    IL_0000: ldarg.0
    IL_0001: ldsflda int64 '<PrivateImplementationDetails>'::'70DE15E71C9CEE3760BB7174ECC485889F6F554DDDC4947D4ECAF87E48003025'
    IL_0006: ldc.i4.8
    IL_0007: conv.i
    IL_0008: call void SpanUnwrap.Example.Stores::CopyBlockUnaligned(uint8&, uint8&, native uint)
    IL_000d: ret
} // end of method Stores::CopyData

About

Dissolves the Span<T> and ReadOnlySpan<T> into references

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages