ZiggyAlloc 1.0.2

dotnet add package ZiggyAlloc --version 1.0.2
                    
NuGet\Install-Package ZiggyAlloc -Version 1.0.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ZiggyAlloc" Version="1.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ZiggyAlloc" Version="1.0.2" />
                    
Directory.Packages.props
<PackageReference Include="ZiggyAlloc" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ZiggyAlloc --version 1.0.2
                    
#r "nuget: ZiggyAlloc, 1.0.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ZiggyAlloc@1.0.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ZiggyAlloc&version=1.0.2
                    
Install as a Cake Addin
#tool nuget:?package=ZiggyAlloc&version=1.0.2
                    
Install as a Cake Tool

ZiggyAlloc

NuGet License: MIT

High-performance unmanaged memory management for .NET

ZiggyAlloc provides explicit control over unmanaged memory allocation, enabling high-performance scenarios where garbage collection overhead must be avoided.

Why ZiggyAlloc?

๐Ÿš€ Performance-Critical Scenarios

  • Large buffer management without GC pressure
  • Native API interop with contiguous memory requirements
  • Scientific computing with massive datasets
  • Game engines and real-time applications
  • Image/audio processing with custom memory layouts

๐Ÿ’ก Key Advantages Over Standard .NET

Scenario Standard .NET ZiggyAlloc
100MB buffer new byte[100MB] โ†’ GC pressure allocator.Allocate<byte>(100MB) โ†’ No GC
Native interop Complex marshaling Direct pointer access
Custom layouts Limited options Full control over memory layout
Memory tracking GC.GetTotalMemory() Precise allocation tracking

Installation

dotnet add package ZiggyAlloc

Quick Start

using ZiggyAlloc;

// Allocate large buffer without GC pressure
var allocator = new SystemMemoryAllocator();
using var buffer = allocator.Allocate<float>(1_000_000); // 4MB, no GC impact

// Use as high-performance Span<T>
Span<float> data = buffer;
data.Fill(3.14159f);

// Direct native API access
ProcessDataNative(buffer.RawPointer, buffer.Length);

Zig-style Defer Pattern

using ZiggyAlloc;

var allocator = new SystemMemoryAllocator();

// Defer scope ensures cleanup in reverse order
using var defer = DeferScope.Start();

var buffer1 = allocator.AllocateDeferred<int>(defer, 1000);
var buffer2 = allocator.AllocateDeferred<float>(defer, 500);

// Custom cleanup actions
defer.Defer(() => Console.WriteLine("Cleanup complete!"));

// Use buffers...
buffer1[0] = 42;
buffer2[0] = 3.14f;

// Cleanup happens automatically in reverse order:
// 1. Custom action, 2. buffer2, 3. buffer1

Real-World Examples

๐Ÿ”ง Native API Interop

// Allocate memory for native API
using var points = allocator.Allocate<Point3D>(10000);

// Fill with data
for (int i = 0; i < points.Length; i++)
    points[i] = new Point3D(i, i * 2, i * 3);

// Pass directly to native function
NativeLibrary.ProcessPoints(points.RawPointer, points.Length);

๐Ÿ–ผ๏ธ Image Processing

// 4K image buffer (33MB) - no GC pressure
const int width = 3840, height = 2160;
using var imageBuffer = allocator.Allocate<Rgba32>(width * height);

// High-performance processing using Span<T>
Span<Rgba32> pixels = imageBuffer;
ApplyGaussianBlur(pixels, width, height);

๐Ÿงช Scientific Computing

// Struct-of-Arrays for better cache performance
using var positions = allocator.Allocate<Vector3>(1_000_000);
using var velocities = allocator.Allocate<Vector3>(1_000_000);
using var masses = allocator.Allocate<float>(1_000_000);

// Physics simulation with no GC allocations
UpdateParticlePhysics(positions, velocities, masses, deltaTime);

## Core Concepts

### Allocators

ZiggyAlloc provides three main allocator types:

#### SystemMemoryAllocator
High-performance allocator using native system calls:

```csharp
var allocator = new SystemMemoryAllocator();
using var buffer = allocator.Allocate<int>(10, zeroMemory: true);

// Array-like access with bounds checking
buffer[0] = 42;
int value = buffer[9];

// Automatic cleanup with 'using'
// Memory freed when buffer goes out of scope
ScopedMemoryAllocator

Automatically frees all allocations when disposed:

using var allocator = new ScopedMemoryAllocator();
using var buffer1 = allocator.Allocate<int>(5);
using var buffer2 = allocator.Allocate<double>(10);

// All allocations freed automatically when allocator is disposed
DebugMemoryAllocator

Tracks allocations and reports memory leaks with caller information:

using var debugAlloc = new DebugMemoryAllocator("MyComponent", Z.DefaultAllocator);
using var buffer = debugAlloc.Allocate<int>(1);
buffer[0] = 42;

// If you forget to dispose buffer, leak will be reported with file/line info

Memory Safety

  • Bounds checking: Buffer access is bounds-checked
  • Automatic disposal: Buffers integrate with using statements
  • Defer scopes: Zig-style cleanup in reverse order with DeferScope
  • Leak detection: Debug allocator reports unfreed memory with caller info
  • Type safety: Generic allocations with compile-time type checking
  • Span integration: Seamless conversion to Span<T> for safe operations

API Reference

Core Types

  • UnmanagedBuffer<T> - Type-safe buffer with automatic cleanup
  • IUnmanagedMemoryAllocator - Interface for custom allocators
  • SystemMemoryAllocator - High-performance system allocator
  • ScopedMemoryAllocator - Arena-style allocator with automatic cleanup
  • DebugMemoryAllocator - Leak-detecting wrapper allocator
  • DeferScope - Zig-style defer pattern for reverse-order cleanup

Allocator Interface

public interface IUnmanagedMemoryAllocator
{
    UnmanagedBuffer<T> Allocate<T>(int elementCount, bool zeroMemory = false) where T : unmanaged;
    void Free(IntPtr pointer);
    bool SupportsIndividualDeallocation { get; }
    long TotalAllocatedBytes { get; }
}

UnmanagedBuffer<T> Key Members

// Properties
int Length { get; }                    // Number of elements
int SizeInBytes { get; }              // Total size in bytes
IntPtr RawPointer { get; }            // Raw pointer for interop
bool IsEmpty { get; }                 // True if length is 0
bool IsValid { get; }                 // True if pointer is not null

// Indexing
ref T this[int index] { get; }        // Bounds-checked element access
ref T First { get; }                  // Reference to first element
ref T Last { get; }                   // Reference to last element

// Span conversion
Span<T> AsSpan()                      // Convert to Span<T>
ReadOnlySpan<T> AsReadOnlySpan()      // Convert to ReadOnlySpan<T>
implicit operator Span<T>             // Implicit conversion to Span<T>

// Utility methods
void Fill(T value)                    // Fill buffer with value
void Clear()                          // Zero all bytes
void CopyFrom(ReadOnlySpan<T> source) // Copy from span
void Dispose()                        // Free memory if owned

Performance

ZiggyAlloc is designed for minimal overhead:

  • Direct memory access: UnmanagedBuffer compiles to efficient pointer operations
  • Inlined operations: Critical paths are aggressively inlined
  • Native memory: Uses NativeMemory APIs on .NET 6+ for optimal performance
  • No GC pressure: Unmanaged allocations don't affect garbage collection
  • Zero-copy conversions: Seamless integration with Span<T> without copying

Requirements

  • .NET 8.0 or later
  • Unsafe code support (automatically enabled by the package)

Core API

UnmanagedBuffer<T>

The main type for working with unmanaged memory:

using var buffer = allocator.Allocate<int>(1000);

// Array-like access with bounds checking
buffer[0] = 42;
int value = buffer[999];

// High-performance Span<T> conversion
Span<int> span = buffer;
span.Fill(123);

// Properties
Console.WriteLine($"Length: {buffer.Length}");
Console.WriteLine($"Size: {buffer.SizeInBytes} bytes");
Console.WriteLine($"Pointer: 0x{buffer.RawPointer:X}");

// Utility methods
buffer.Clear();           // Zero all bytes
buffer.Fill(42);          // Fill with value
buffer.CopyFrom(source);  // Copy from another buffer/span

SystemMemoryAllocator

High-performance allocator using native system calls:

var allocator = new SystemMemoryAllocator();

// Basic allocation
using var buffer = allocator.Allocate<double>(1000, zeroMemory: true);

// Memory tracking
Console.WriteLine($"Total allocated: {allocator.TotalAllocatedBytes} bytes");

// Wrap existing memory (doesn't own it)
var wrapped = SystemMemoryAllocator.WrapExisting<byte>(ptr, length);
var fromSpan = SystemMemoryAllocator.WrapSpan(existingSpan);

Advanced Features

Memory Tracking & Debugging

// Track allocations and detect leaks
var debugAllocator = new DebugMemoryAllocator("MyComponent", baseAllocator);

using (debugAllocator)
{
    var buffer = debugAllocator.Allocate<int>(100);
    // Forgot to dispose - will report leak with file/line info
}

Custom Allocators

Implement IUnmanagedMemoryAllocator for specialized allocation strategies:

public interface IUnmanagedMemoryAllocator
{
    UnmanagedBuffer<T> Allocate<T>(int elementCount, bool zeroMemory = false) where T : unmanaged;
    void Free(IntPtr pointer);
    bool SupportsIndividualDeallocation { get; }
    long TotalAllocatedBytes { get; }
}

Performance Benefits

Memory Allocation Comparison

// โŒ Standard .NET - causes GC pressure
var managedArray = new byte[100_000_000]; // 100MB on managed heap

// โœ… ZiggyAlloc - no GC impact
using var unmanagedBuffer = allocator.Allocate<byte>(100_000_000);

Interop Performance

// โŒ Standard marshaling - copying overhead
byte[] managedData = GetData();
IntPtr ptr = Marshal.AllocHGlobal(managedData.Length);
Marshal.Copy(managedData, 0, ptr, managedData.Length);
NativeAPI(ptr, managedData.Length);
Marshal.FreeHGlobal(ptr);

// โœ… ZiggyAlloc - direct access, no copying
using var buffer = allocator.Allocate<byte>(dataSize);
FillBuffer(buffer); // Fill directly in unmanaged memory
NativeAPI(buffer.RawPointer, buffer.Length);

When to Use ZiggyAlloc

โœ… Perfect For:

  • Native library interop requiring contiguous memory
  • Large buffer allocations (>85KB) to avoid Large Object Heap
  • Performance-critical code where GC pauses are unacceptable
  • Custom memory layout patterns (struct-of-arrays)
  • Scientific computing with massive datasets
  • Game engines and real-time applications

โŒ Not Ideal For:

  • Small, short-lived allocations (use regular .NET objects)
  • General application development (managed memory is fine)
  • When you don't need explicit memory control

Safety Features

  • Bounds checking on buffer access
  • Automatic disposal with using statements
  • Memory leak detection with debug allocators
  • Type safety through generic constraints
  • Integration with Span<T> for safe operations

Requirements

  • .NET 8.0 or later
  • Unsafe code support (automatically enabled by package)

Contributing

We welcome contributions from the community! Here's how you can help:

๐Ÿ› Report Issues

Found a bug? Have a feature request? Open an issue on GitHub.

๐Ÿ”ง Contribute Code

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes with tests
  4. Submit a pull request

See CONTRIBUTING.md for detailed guidelines.

๐Ÿ’ก Suggest Improvements

  • Performance optimizations
  • New allocator types
  • API enhancements
  • Documentation improvements

๐Ÿงช Help with Testing

  • Test on different platforms
  • Performance benchmarking
  • Memory usage analysis
  • Real-world usage scenarios

Community

  • GitHub Issues: Bug reports and feature requests
  • GitHub Discussions: Questions and general discussion
  • Pull Requests: Code contributions welcome!

License

This project is licensed under the MIT License - see the LICENSE file for details.

Inspiration

This library is inspired by Zig's approach to memory management and context passing. While C# has garbage collection, there are scenarios where explicit memory control is beneficial:

  • High-performance applications
  • Interop with native libraries
  • Memory-constrained environments
  • Deterministic cleanup requirements

ZiggyAlloc brings Zig's elegant patterns to the .NET ecosystem while maintaining C#'s safety and expressiveness.


ZiggyAlloc: When you need the performance of unmanaged memory with the safety of .NET ๐Ÿš€

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.2 88 7/29/2025
1.0.1 83 7/29/2025
1.0.0 88 7/28/2025

See CHANGELOG.md for detailed release notes.