Don't allocate extra buffers in lua [Push|To]String

This commit is contained in:
Morilli 2024-03-19 12:19:56 +01:00
parent bfae8d0f1e
commit b10f8969ac
4 changed files with 41 additions and 24 deletions

View File

@ -26,4 +26,8 @@
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Memory" />
</ItemGroup>
</Project> </Project>

View File

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices; using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security; using System.Security;
using System.Text; using System.Text;
@ -184,7 +185,7 @@ namespace NLua.Native
internal delegate* unmanaged[Cdecl]<lua_State, charptr_t, size_t, charptr_t> lua_pushlstring; internal delegate* unmanaged[Cdecl]<lua_State, charptr_t, size_t, charptr_t> lua_pushlstring;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public charptr_t PushLString(lua_State luaState, byte[] s, size_t len) public charptr_t PushLString(lua_State luaState, ReadOnlySpan<byte> s, size_t len)
{ {
fixed (byte* _s = s) fixed (byte* _s = s)
{ {

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@ -355,7 +356,7 @@ namespace NLua.Native
/// Pushes binary buffer onto the stack (usually UTF encoded string) or any arbitraty binary data /// Pushes binary buffer onto the stack (usually UTF encoded string) or any arbitraty binary data
/// </summary> /// </summary>
/// <param name="buffer"></param> /// <param name="buffer"></param>
public void PushBuffer(byte[] buffer) public void PushBuffer(ReadOnlySpan<byte> buffer)
{ {
if (buffer == null) if (buffer == null)
{ {
@ -378,8 +379,11 @@ namespace NLua.Native
return; return;
} }
var buffer = Encoding.UTF8.GetBytes(value); // could also use GetByteCount here, but why iterate twice when we're renting the memory anyway?
PushBuffer(buffer); byte[] buffer = ArrayPool<byte>.Shared.Rent(Encoding.UTF8.GetMaxByteCount(value.Length));
int actualLength = Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, 0);
PushBuffer(buffer.AsSpan(0, actualLength));
ArrayPool<byte>.Shared.Return(buffer);
} }
/// <summary> /// <summary>
@ -556,13 +560,7 @@ namespace NLua.Native
public long ToInteger(int index) public long ToInteger(int index)
=> NativeMethods.ToIntegerX(Handle, index, out _); => NativeMethods.ToIntegerX(Handle, index, out _);
/// <summary> private IntPtr ToLString(int index, bool callMetamethod, out int stringLength)
/// Converts the Lua value at the given index to a byte array.
/// </summary>
/// <param name="index"></param>
/// <param name="callMetamethod">Calls __tostring field if present</param>
/// <returns></returns>
public byte[] ToBuffer(int index, bool callMetamethod = true)
{ {
UIntPtr len; UIntPtr len;
IntPtr buff; IntPtr buff;
@ -577,19 +575,26 @@ namespace NLua.Native
buff = NativeMethods.ToLString(Handle, index, out len); buff = NativeMethods.ToLString(Handle, index, out len);
} }
if (buff == IntPtr.Zero) stringLength = (int)len;
{
return null;
}
var length = (int)len; return buff;
if (length == 0) }
{
return Array.Empty<byte>(); /// <summary>
} /// Converts the Lua value at the given index to a byte array.
/// </summary>
/// <param name="index"></param>
/// <param name="callMetamethod">Calls __tostring field if present</param>
/// <returns></returns>
public byte[] ToBuffer(int index, bool callMetamethod = true)
{
IntPtr buffer = ToLString(index, callMetamethod, out int length);
if (buffer == IntPtr.Zero) return null;
if (length == 0) return Array.Empty<byte>();
var output = new byte[length]; var output = new byte[length];
Marshal.Copy(buff, output, 0, length); Marshal.Copy(buffer, output, 0, length);
return output; return output;
} }
@ -601,8 +606,15 @@ namespace NLua.Native
/// <returns></returns> /// <returns></returns>
public string ToString(int index, bool callMetamethod = true) public string ToString(int index, bool callMetamethod = true)
{ {
var buffer = ToBuffer(index, callMetamethod); var buffer = ToLString(index, callMetamethod, out int length);
return buffer == null ? null : Encoding.UTF8.GetString(buffer);
if (buffer == IntPtr.Zero) return null;
if (length == 0) return "";
unsafe
{
return Encoding.UTF8.GetString((byte*)buffer.ToPointer(), length);
}
} }
/// <summary> /// <summary>

Binary file not shown.