Buffers¶
Buffers hold arbitrary Luau bytes. In Darp.Luau you usually see four shapes:
byte[]: managed copy with normal .NET lifetime.ReadOnlySpan<byte>: borrowed bytes that alias Luau memory.LuauBuffer: owned Luau buffer reference that you can keep and dispose.LuauBufferView: borrowed callback-scoped buffer view.
If the data is UTF-8 text, see Strings.
Choose a representation¶
| Shape | Ownership | Common APIs | Use when |
|---|---|---|---|
byte[] |
managed copy | GetBuffer(...), TryReadBuffer(..., out byte[]?), passing byte[] into Luau |
simple data transfer |
ReadOnlySpan<byte> |
borrowed bytes | TryGetBuffer(...), TryReadBuffer(...), LuauBuffer.TryGet(...) |
immediate inspection without allocating |
LuauBuffer |
owned Luau reference | CreateBuffer(...), GetLuauBuffer(...) |
keeping or reusing the same Luau buffer value |
LuauBufferView |
borrowed callback view | TryReadLuauBuffer(...) |
callback code that stays inside the current frame |
The main choice is whether you want a managed copy or the Luau value itself.
Create and push buffers¶
Most code just passes byte[]:
byte[] payload = [0x01, 0x02, 0x03];
lua.Globals.Set("payload", payload);
using LuauFunction send = lua.Globals.GetLuauFunction("send");
send.Invoke<LuauNil>(payload);
Create LuauBuffer only when you want to keep or reuse the same Luau buffer value:
using LuauBuffer payload = lua.CreateBuffer([0x01, 0x02, 0x03]);
lua.Globals.Set("payloadRef", payload);
- Passing
byte[]copies data into a Luau buffer. - Passing
LuauBufferreuses an existing Luau-backed value and works anywhere an API acceptsIntoLuau.
Read buffers from tables and globals¶
byte[] bytes = lua.Globals.GetBuffer("payload");
if (lua.Globals.TryGetBuffer("payload", out ReadOnlySpan<byte> borrowed))
{
Console.WriteLine(Convert.ToHexString(borrowed));
}
using LuauBuffer owned = lua.Globals.GetLuauBuffer("payload");
GetBuffer(...)andTryGetBuffer(..., out byte[]?)copy into managed memory.TryGetBuffer(..., out ReadOnlySpan<byte>)borrows Luau-owned bytes.GetLuauBuffer(...)andTryGetLuauBuffer(...)return owned wrappers that need disposal.GetBufferOrNil(...)andTryGetBufferOrNil(...)handle the usual missing-key-or-nilcase.
If you only need bytes, use the copy or span APIs. Use GetLuauBuffer(...) when you need the Luau buffer as a first-class value.
Read buffers in callbacks¶
For fixed signatures, accept borrowed bytes directly:
static int Size(ReadOnlySpan<byte> bytes) => bytes.Length;
using LuauFunction size = lua.CreateFunction(Size);
lua.Globals.Set("size", size);
For manual callbacks, read exactly the shape you want:
using LuauFunction measure = lua.CreateFunctionBuilder(static args =>
{
if (!args.TryReadBuffer(1, out ReadOnlySpan<byte> bytes, out string? error))
return LuauReturn.Error(error);
return LuauReturn.Ok(bytes.Length);
});
CreateFunction(Size)is the shortest fixed-signature form.TryReadBuffer(..., out ReadOnlySpan<byte>, ...)borrows bytes.TryReadBuffer(..., out byte[]?, ...)copies into managed memory.TryReadLuauBuffer(...)returns aLuauBufferViewwhen you need the Luau value itself.
Lifetime rules¶
byte[]is a managed copy with normal .NET lifetime.ReadOnlySpan<byte>aliases Luau memory, so consume it immediately.LuauBufferViewis valid only during the current callback frame. CallToOwned()before storing it or crossing an async boundary.LuauBufferowns the Luau value and must be disposed, but any span you read from it is still borrowed.
The same promotion rule applies to the other callback *View types. For the broader ownership model, see Lifetimes and ownership.
Buffer library versus buffer values¶
LuauLibraries.Buffer enables Luau's script-side buffer library:
You do not need that flag for host-side buffer interop such as CreateBuffer(...), passing byte[], GetBuffer(...), GetLuauBuffer(...), TryReadBuffer(...), or TryReadLuauBuffer(...).
Enable the library only when your Luau code needs buffer.* helpers.
Guidance¶
- Use
byte[]by default. - Use
ReadOnlySpan<byte>for fast, immediate inspection. - Use
LuauBufferwhen you want to keep, pass around, or return the same Luau buffer value. - Use
LuauBufferViewonly inside the current callback frame, and callToOwned()before keeping it. - Use Strings when the data is text rather than arbitrary bytes.