Skip to main content

Array Marshal

Normative: Marshal rules for one-dimensional vector arrays (szarray), multi-dimensional arrays (mdarray), and byte[] between Lua and C#. Related: Type tables, creation, #, get/set../02-TYPE-SYSTEM.md §Arrays; zlua.to_bytes / to_table../05-LIB.md; class ByObj basics → 06-CLASS.md; [LuaMarshalAs]02-MARSHAL-AS.md.

Platform principle: Mono and Il2Cpp share the same Lua-visible semantics; array instances are ByObjUserData (reference types on ObjectRegistry / GCHandle paths).

1. Default Marshal matrix

Without [LuaMarshalAs]:

C# typeC# → LuaLua → C#Notes
T[] (szarray)ByObjUserDataByObjUserData or array-shaped Lua tableSee §2, §3
T[,…] (mdarray)ByObjUserDataOnly ByObjUserDataDoes not accept table
byte[]Same as szarraySame as szarrayUnless [LuaMarshalAs(Bytes)] → ↔ Lua string (§6)

Array instances are always Push'd as ByObjUserData (ObjectUserData + array ByObj instance metatable; payload is Il2CppArray* / managed array reference). Scripts access via get / set, #arr, GetValue / SetValue, etc.; see ../02-TYPE-SYSTEM.md.

2. C# → Lua

ItemRules
ShapeByObjUserData (full userdata + array IMT)
nullnil
Element PushDefault marshal of element type T (primitives → integer/number; references → userdata; enum → integer/number)
[LuaMarshalAs(Bytes)] on byte[]Push Lua string (raw octet sequence, not UTF-8 text semantics)
[LuaMarshalAs(OpaqueValue)]Push OpaqueValue (C#→Lua only); see 04-OPAQUE.md
params T[] return / parameter (C#→Lua)Same as szarray: Push ByObjUserData (does not default Push table)

3. Lua → C# (szarray)

Accept one of the following (plus nil):

Argument shapePop behavior
ByObjUserDataBound type must match target T[] (or element types compatible); pass array reference
Lua table (array shape)Keys 1n contiguous integers, no holes; Pop each element as T, construct T[n]
nilReference-type array → C# null

3.1 Table shape constraints

Same ordered-table rules as 02-MARSHAL-AS.md:

AcceptReject
{ v1, v2, … } with contiguous keys 1..nSparse table
Empty {}T[0] (zero-length)String-key table
0-based pseudo-array (v1 not compatible)
Non-integer keys

When Popping elements, use default marshal of T (including enum integer, nested szarray table or userdata, etc.).

3.2 Examples

-- ByObjUserData
CS.Demo.Process(arr)

-- table → T[n]
CS.Demo.Process({ 1, 2, 3 })
CS.Demo.Process({}) -- T[0]

-- null
CS.Demo.Process(nil)

4. Lua → C# (mdarray)

ArgumentPop behavior
ByObjUserDataBound type must match target mdarray
nilnull
Lua tableNot accepted
OtherluaL_error

[LuaMarshalAs] does not make table acceptable (UserData equals default; OpaqueValue is C#→Lua only).

5. Array element R/W (vs Marshal)

The array instance is ByObjUserData; element R/W uses element-type marshal, not whole-array Pop/Push:

APINotes
arr:get(i1, …) / arr:set(i1, …, value)Indices are C# per-dimension indices (szarray default 0-based); return/accept Lua shape of element type T
GetValue / SetValueStill available; GetValue returns object (boxed)
#arrszarray → Length; mdarray → product of dimension lengths

Details: ../02-TYPE-SYSTEM.md array chapter.

Index difference vs zlua.to_table: to_table produces a 1-based Lua table (t[i]arr[i-1]); get/set use C# indices.

6. byte[] and [LuaMarshalAs(Bytes)]

ConfigC# ↔ Lua
DefaultSame as T[] szarray (ByObjUserData; Lua→C# may also use table)
[LuaMarshalAs(Bytes)]Force C# byte[] ↔ Lua string (octet sequence)

With Bytes:

DirectionRules
C# → LuaPush Lua string
Lua → C#Pop must be string; does not accept ByObjUserData / table

If annotated on a string parameter/return, use the dual byte[] ↔ string rules (resolved by declared type).

7. params T[] parameters

params T[] Marshal matches §1 szarray (ByObjUserData or table); differences are Lua call shape and empty/null semantics. Details: 02-MARSHAL-AS.md §7.

Summary:

PassedC# receives
ByObjUserDataThat array reference
table {}T[0]
table { … }T[n] built from elements
nilnull (not empty array)

Lua does not support C#-style multi-slot implicit collection (Sum(1, 2, 3) is illegal); a single argument must occupy the params slot. No dedicated LuaMarshalType (historical ParamsTable removed).

params on GetFunction-obtained delegate calls / delegate bridges is unsupported; see 09-FUNCTION.md.

8. zlua.to_bytes / zlua.to_table

szarray helpers from ../05-LIB.md (do not change default Pop/Push; convenience only).

8.1 zlua.to_bytes

zlua.to_bytes(szarray) → string
ConstraintNotes
InputOnly szarray userdata (not mdarray)
Element typeblittable: primitives, or structs without reference-type fields (e.g. float[], Vector3[])
ImplementationTreat array memory as a C byte buffer of total data bytes; whole-buffer copy to a Lua binary string (may contain \0); endianness / struct layout matches runtime managed layout
IllegalNon-szarray, or elements with reference fields → luaL_error
local bytes = zlua.to_bytes(int_arr) -- #bytes == #int_arr * 4
local fbytes = zlua.to_bytes(float_arr) -- float[] OK
local vbytes = zlua.to_bytes(vec3_arr) -- Vector3[] (blittable) OK

Native: __zlua_to_bytes

Also: ../05-LIB.md §8.3.

8.2 zlua.to_table

zlua.to_table(szarray) → table
ConstraintNotes
InputOnly szarray userdata
Element typeNo restriction; each element converts with default marshal
OutputSame-length table; keys 1 .. n, n = #szarray
Indexingt[i] ↔ C# arr[i - 1] (Lua 1-based ↔ C# 0-based)
local t = zlua.to_table(obj_arr)
-- t[1] corresponds to arr[0]

Reference elements → userdata; struct elements → corresponding struct marshal shape.

Native: __zlua_to_table

8.3 Difference from Pop table path

zlua.to_tableLua→C# Pop table
DirectionArray userdata → Lua table (read-only conversion)Lua table → construct T[n] into C#
UseScript iteration, serializationMethod parameters / returns
ConstraintsInput must be szarray userdataMust satisfy §3.1 array shape

9. Array type construction (type tables)

Lua does not parse int[] directly via CSharp[...]; use:

local int_arr_type = zlua.make_szarray_type(zlua.types.int32)
local md_type = zlua.make_mdarray_type(zlua.types.int32, 2)

Instance creation:

local arr = zlua.new_szarray_by_element_type(zlua.types.int32, 10)
local matrix = zlua.new_mdarray_by_spec(zlua.types.int32, { 0, 0 }, { 2, 3 })

Details: ../02-TYPE-SYSTEM.md and ../05-LIB.md.

10. ref / out / in array parameters

PathRules
Lua → C#See 03-BYREF.md, 06-CLASS.md §5: shared reference; no rebind
C# → Lua (GetFunction-obtained delegate / delegate bridge)Default OpaqueValue; see 04-OPAQUE.md

In-place mutation of a mutable array (ref int[] changing elements) → visible on Lua; ref arr = otherArray does not write back to the Lua variable.

11. Mono / Il2Cpp consistency

ItemRequirement
szarray Push / PopByObjUserData; table rules match
mdarray PopOnly ByObjUserData
to_bytes / to_tableSemantics match
Bytes annotationSame on both platforms
Error messagesSame or equivalent
DocContents
06-CLASS.mdByObjUserData, façade, ref reference types
02-MARSHAL-AS.mdBytes, OpaqueValue, params (§7)
03-BYREF.mdref / out / in
../02-TYPE-SYSTEM.mdArray type tables, get/set, #
../05-LIB.mdmake_szarray_type, to_bytes, to_table