Skip to content

Commit

Permalink
Better temporary memory (#229)
Browse files Browse the repository at this point in the history
* - Added public `Define` methods for all externs.
 - Removed method which accepted an `object` parameter and dynamically checked that it was an extern.

* Fixed name/module byte conversion

* Improved system for converting `string` into `Span<byte>` with zero allocations. Replaces ad-hoc implementations used around the codebase.
  • Loading branch information
martindevans authored Mar 8, 2023
1 parent 1aac330 commit 9562c78
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 38 deletions.
8 changes: 2 additions & 6 deletions src/Caller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ namespace Wasmtime
/// </summary>
public readonly ref struct Caller
{
private const int StackallocThreshold = 256;

internal Caller(IntPtr handle)
{
if (handle == IntPtr.Zero)
Expand Down Expand Up @@ -50,13 +48,11 @@ internal Caller(IntPtr handle)
public bool TryGetMemorySpan<T>(string name, long address, int length, out Span<T> result)
where T : unmanaged
{
var nameLength = Encoding.UTF8.GetByteCount(name);
var nameBytes = nameLength <= StackallocThreshold ? stackalloc byte[nameLength] : new byte[nameLength];
Encoding.UTF8.GetBytes(name, nameBytes);
using var nameBytes = name.ToUTF8(stackalloc byte[Math.Min(64, name.Length * 2)]);

unsafe
{
fixed (byte* ptr = nameBytes)
fixed (byte* ptr = nameBytes.Span)
{
if (!Native.wasmtime_caller_export_get(handle, ptr, (UIntPtr)nameBytes.Length, out var item))
{
Expand Down
43 changes: 11 additions & 32 deletions src/Linker.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
Expand Down Expand Up @@ -61,18 +60,13 @@ private void Define<T>(string module, string name, T item)
}

var ext = item.AsExtern();

var nameLength = Encoding.UTF8.GetByteCount(name);
var nameBytes = nameLength <= StackallocThreshold ? stackalloc byte[nameLength] : new byte[nameLength];
Encoding.UTF8.GetBytes(name, nameBytes);

var moduleLength = Encoding.UTF8.GetByteCount(module);
var moduleBytes = moduleLength <= StackallocThreshold ? stackalloc byte[moduleLength] : new byte[moduleLength];
Encoding.UTF8.GetBytes(module, moduleBytes);

using var nameBytes = name.ToUTF8(stackalloc byte[Math.Min(64, name.Length * 2)]);
using var moduleBytes = module.ToUTF8(stackalloc byte[Math.Min(64, module.Length * 2)]);

unsafe
{
fixed (byte* modulePtr = moduleBytes, namePtr = nameBytes)
fixed (byte* modulePtr = moduleBytes.Span, namePtr = nameBytes.Span)
{
var error = Native.wasmtime_linker_define(handle, store.Context.handle, modulePtr, (UIntPtr)moduleBytes.Length, namePtr, (UIntPtr)nameBytes.Length, ext);
if (error != IntPtr.Zero)
Expand Down Expand Up @@ -442,20 +436,13 @@ public void DefineFunction(string module, string name, Function.UntypedCallbackD
return Function.InvokeUntypedCallback(callback, callerPtr, args, (int)nargs, results, (int)nresults, resultKinds);
};

byte[]? moduleBytesBuffer = null;
var moduleLength = Encoding.UTF8.GetByteCount(module);
Span<byte> moduleBytes = moduleLength <= StackallocThreshold ? stackalloc byte[moduleLength] : (moduleBytesBuffer = ArrayPool<byte>.Shared.Rent(moduleLength)).AsSpan()[..moduleLength];
Encoding.UTF8.GetBytes(module, moduleBytes);

byte[]? nameBytesBuffer = null;
var nameLength = Encoding.UTF8.GetByteCount(name);
Span<byte> nameBytes = nameLength <= StackallocThreshold ? stackalloc byte[nameLength] : (nameBytesBuffer = ArrayPool<byte>.Shared.Rent(nameLength)).AsSpan()[..nameLength];
Encoding.UTF8.GetBytes(name, nameBytes);
using var nameBytes = name.ToUTF8(stackalloc byte[Math.Min(64, name.Length * 2)]);
using var moduleBytes = module.ToUTF8(stackalloc byte[Math.Min(64, module.Length * 2)]);

var funcType = Function.CreateFunctionType(parameterKinds, resultKinds);
try
{
fixed (byte* modulePtr = moduleBytes, namePtr = nameBytes)
fixed (byte* modulePtr = moduleBytes.Span, namePtr = nameBytes.Span)
{
var error = Native.wasmtime_linker_define_func(
handle,
Expand All @@ -478,15 +465,6 @@ public void DefineFunction(string module, string name, Function.UntypedCallbackD
finally
{
Function.Native.wasm_functype_delete(funcType);

if (moduleBytesBuffer is not null)
{
ArrayPool<byte>.Shared.Return(moduleBytesBuffer);
}
if (nameBytesBuffer is not null)
{
ArrayPool<byte>.Shared.Return(nameBytesBuffer);
}
}
}
}
Expand All @@ -495,9 +473,10 @@ private bool TryGetExtern(StoreContext context, string module, string name, out
{
unsafe
{
var moduleBytes = Encoding.UTF8.GetBytes(module);
var nameBytes = Encoding.UTF8.GetBytes(name);
fixed (byte* modulePtr = moduleBytes, namePtr = nameBytes)
using var moduleBytes = module.ToUTF8(stackalloc byte[Math.Min(64, module.Length * 2)]);
using var nameBytes = name.ToUTF8(stackalloc byte[Math.Min(64, name.Length * 2)]);

fixed (byte* modulePtr = moduleBytes.Span, namePtr = nameBytes.Span)
{
return Native.wasmtime_linker_get(handle, context.handle, modulePtr, (UIntPtr)moduleBytes.Length, namePtr, (UIntPtr)nameBytes.Length, out ext);
}
Expand Down
54 changes: 54 additions & 0 deletions src/TemporaryAllocation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Buffers;
using System.Text;

namespace Wasmtime
{
internal static class StringExtensions
{
public static TemporaryAllocation ToUTF8(this string value, Span<byte> bytes)
{
return TemporaryAllocation.FromString(value, bytes);
}
}

internal readonly ref struct TemporaryAllocation
{
public readonly Span<byte> Span;
private readonly byte[]? _rented;

public int Length => Span.Length;

private TemporaryAllocation(Span<byte> span, byte[]? rented)
{
Span = span;
_rented = rented;
}

public static TemporaryAllocation FromString(string str, Span<byte> output)
{
var length = Encoding.UTF8.GetByteCount(str);

if (length <= output.Length)
{
Encoding.UTF8.GetBytes(str, output);
return new TemporaryAllocation(output[..length], null);
}

var rented = ArrayPool<byte>.Shared.Rent(length);
Encoding.UTF8.GetBytes(str, rented);
return new TemporaryAllocation(rented[..length], rented);
}

/// <summary>
/// Recycle rented memory
/// </summary>
public void Dispose()
{
if (_rented != null)
{
ArrayPool<byte>.Shared.Return(_rented);
}
}
}
}

0 comments on commit 9562c78

Please sign in to comment.