These builders had many bugs:
1. They would longjmp() across the destructor of a g_autoptr() if a Lua
error was thrown. This will leak the memory in the g_autoptr()
unless Lua is compiled with C++ exceptions.
2. They depended on the iteration order of numerical keys in Lua tables.
Lua explicitly does not specify this order.
3. They would produce nonsensical SPA POD array or choice types with
strings or bytes as values. These would cause undefined behavior if
manipulated by naive C code, or assertion failures if
spa_pod_is_array() and spa_pod_is_choice() are modified to check that
the contents of arrays and choices have sensible types.
4. They silently accepted extra arguments, potentially causing confusion
and making it harder to extend the functions in a
backwards-compatible way.
Solve the first problem by calling functions that can raise a Lua error
in a protected environment (with lua_pcall). If there is a Lua error,
rethrow it after the g_autoptr() destructor has run.
Solve the second problem by first obtaining the number of keys in the
table and then iterating over the keys that are expected to be present.
If any of the keys are not contiguious integers starting at 1, the range
[1..number of keys] will include a number that is not a table key. This
will result in lua_rawgeti pushing a nil onto the Lua stack. An
explicit check throws a useful error in this case.
Solve the third problem by explicitly checking that the type is
reasonable before building an array or choice. If it is wrong,
a Lua error is thrown.
Solve the fourth problem by using luaL_checktype (L, 2, LUA_TNONE) to
check that no unwanted values were passed. The C function called with
lua_pcall is passed every argument passed by Lua, followed by a light
userdata that stores a context pointer. After the light userdata is
popped from the Lua stack, the Lua stack is identical to what Lua
created when it called the outer C function, so the type-checking
functions in the auxillary library can be used to enforce that only the
correct number and type of arguments were passed.
The intention is to make checks for enabled log topics faster.
Every topic has its own structure that is statically defined in the file
where the logs are printed from. The structure is initialized transparently
when it is first used and it contains all the log level flags for the levels
that this topic should print messages. It is then checked on the wp_log()
macro before printing the message.
Topics from SPA/PipeWire are also handled natively, so messages are printed
directly without checking if the topic is enabled, since the PipeWire and SPA
macros do the checking themselves.
Messages coming from GLib are checked inside the handler.
An internal WpLogFields object is used to manage the state of each log
message, populating all the fields appropriately from the place they
are coming from (wp_log, spa_log, glib log), formatting the message and
then printing it. For printing to the journald, we still use the glib
message handler, converting all the needed fields to GLogField on demand.
That message handler does not do any checks for the topic or the level, so
we can just call it to send the message.