<sub>2025-03-15 @18:00</sub> #digital-garden #lua #programming #luajit #luajit/ffi # The Problem In C, we commonly need a way to get the number of elements in an array. You can easily create a macro using the `sizeof` operator. ```c #define NELEM(x) (sizeof(x) / sizeof(x[0])) ``` This works because `sizeof(x)` returns the total number of bytes occupied by the array. The size of one element is given by `sizeof(x[0])`. Dividing the two gives the number of elements in the array. In LuaJIT using FFI, I would assume something like the following would be possible. ```lua local ffi = require("ffi") ffi.cdef[[ typedef struct { uint32_t array[3]; uint32_t var; } Foo_t; ]] local foo = ffi.new("Foo_t", {}) local x = ffi.sizeof(foo) / ffi.sizeof(foo.var[0]) ``` However, we cannot do that. The reason is because `foo.var[0]` is not a C type. It is a value. The function `ffi.sizeof()` requires either a C type or a `cdata` object. But it does not accept a Lua number or scalar as an argument. You might think that we could do this instead. ```lua local etype = ffi.typeof(foo.var[0]) local x = ffi.sizeof(foo) / ffi.sizeof(etype) ``` But we would still be wrong. The problem is the same. `ffi.typeof(foo.var[0])` returns a concrete cdata **value type**, not a **type constructor**. So this is still not valid as an argument to `ffi.sizeof()`. LuaJIT’s `ffi.sizeof()` expects either: - A cdata type constructor, such as `ffi.typeof("uint32_t")` - A cdata object, such as `ffi.new("uint32_t")` It does not introspect scalar values and infer their types for size calculations. ## My Best Take To calculate the number of elements, you need the size of a single element. LuaJIT's FFI does not provide a way to extract the element type of an array from a variable at runtime. You must explicitly provide the element type when calling `ffi.sizeof()`. ```lua local element_size = ffi.sizeof("uint32_t") local x = ffi.sizeof(foo.array) / element_size ``` This works because `foo.array` is a reference to the array field inside `foo`. It is treated as a cdata object. So `ffi.sizeof(foo.array)` gives the total size of the array. Dividing by the element size gives the number of elements. If I browse through the FFI Semantics section of the LuaJIT documentation [LuaJIT FFI Semantics](https://luajit.org/ext_ffi_semantics.html), I get insights into how arrays and structs are handled. It mentions that array elements are initialized starting at index zero. It also notes that elements are initialized one by one. However, it does not describe any mechanism for runtime introspection of element types. The [FFI API reference](https://luajit.org/ext_ffi_api.html) describes functions like `ffi.sizeof()` and `ffi.typeof()`. The `ffi.sizeof()` function can take a cdata object or a ctype constructor. But it does not deduce or extract element types from array values. Similarly, `ffi.typeof()` returns the type of a value. But it does not decompose an array type into its element type at runtime. To extract the element type from a known array type, you must explicitly declare the array type first. ```lua local array_type = ffi.typeof("uint32_t[3]") local element_type = ffi.typeof("uint32_t") local n = ffi.sizeof(array_type) / ffi.sizeof(element_type) ``` This is the safest and most accurate way AFAIK to get the array length. ## Conclusion The best approach is to use `ffi.sizeof()` with known and explicitly declared C types. LuaJIT FFI does not support automatic deduction of array element types from cdata values. It requires you to be precise with type declarations. There are alternative techniques. For example, you could cache the element width or hardcode the array length. This is especially useful for multi-dimensional arrays. These techniques involve using parameterized FFI types or metatables. That exercise is better left for another day.