ExpandableAllocator 0.3.0-pre

A low-level .NET allocator that grows lazily.

This is a prerelease version of ExpandableAllocator.
Install-Package ExpandableAllocator -Version 0.3.0-pre
dotnet add package ExpandableAllocator --version 0.3.0-pre
<PackageReference Include="ExpandableAllocator" Version="0.3.0-pre" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ExpandableAllocator --version 0.3.0-pre
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

Usage

// Reserve 1 TB of memory.
use alloc = Allocator.Create(Protection.Read, 0x1_000_000_000_000L)

let addr = alloc.Address
let ptr = NativePtr.fromNativeInt addr

// Actually allocate 512 bytes.
alloc.ActualSize <- nativeint 512

// Make sure the pointer hasn't moved.
alloc.Address |> should equal addr

// Read something.
NativePtr.read ptr |> should equal 0

// This would throw an AccessViolationException:
// NativePtr.write ptr 42

// Change the region's protection.
alloc.Protection <- Protection.ReadWrite

// Now, actually write our value.
NativePtr.write ptr 42
NativePtr.read ptr |> should equal 42

// A safe wrapper is also provided.
use stream = new ExpandableStream(alloc)

stream.CanWrite |> should equal true
stream.CanRead  |> should equal true
stream.CanSeek  |> should equal true

How does it work?

Internally, the Allocator reserves memory when it is created
(it does not actually allocate it) using VirtualAlloc
or mmap.
This means that large chunks of memory (ie greater than 1TB) can be reserved
without any allocation.

Then, when more memory is actually needed (using TryReserve or ActualSize),
the Allocator commits the memory using VirtualAlloc or mprotect.

Finally, when the Allocator is Disposed, VirtualFree or munmap
is called on the allocated memory region.

Usage

// Reserve 1 TB of memory.
use alloc = Allocator.Create(Protection.Read, 0x1_000_000_000_000L)

let addr = alloc.Address
let ptr = NativePtr.fromNativeInt addr

// Actually allocate 512 bytes.
alloc.ActualSize <- nativeint 512

// Make sure the pointer hasn't moved.
alloc.Address |> should equal addr

// Read something.
NativePtr.read ptr |> should equal 0

// This would throw an AccessViolationException:
// NativePtr.write ptr 42

// Change the region's protection.
alloc.Protection <- Protection.ReadWrite

// Now, actually write our value.
NativePtr.write ptr 42
NativePtr.read ptr |> should equal 42

// A safe wrapper is also provided.
use stream = new ExpandableStream(alloc)

stream.CanWrite |> should equal true
stream.CanRead  |> should equal true
stream.CanSeek  |> should equal true

How does it work?

Internally, the Allocator reserves memory when it is created
(it does not actually allocate it) using VirtualAlloc
or mmap.
This means that large chunks of memory (ie greater than 1TB) can be reserved
without any allocation.

Then, when more memory is actually needed (using TryReserve or ActualSize),
the Allocator commits the memory using VirtualAlloc or mprotect.

Finally, when the Allocator is Disposed, VirtualFree or munmap
is called on the allocated memory region.

Release Notes

- Added ExpandableStream (unstable).

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
0.3.0-pre 240 2/14/2018
0.2.0 266 2/12/2018
0.1.0 257 2/11/2018