27 An Overview of the C API

Lua is an embedded language. This means that Lua is not a stand-alone application, but a library that we can link with other applications to incorporate Lua facilities into them.

You may be wondering: if Lua is not a stand-alone program, how come we have been using Lua stand-alone through the whole book until now? The solution to this puzzle is the Lua interpreter —the executable lua. This executable is a small application, around six hundred lines of code, that uses the Lua library to implement the stand-alone interpreter. The program handles the interface with the user, taking her files and strings to feed them to the Lua library, which does the bulk of the work (such as actually running Lua code).

This ability to be used as a library to extend an application is what makes Lua an embeddable language. At the same time, a program that uses Lua can register new functions in the Lua environment; such functions are implemented in C (or another language), so that they can add facilities that cannot be written directly in Lua. This is what makes Lua an extensible language.

These two views of Lua (as an embeddable language and as an extensible language) correspond to two kinds of interaction between C and Lua. In the first kind, C has the control and Lua is the library. The C code in this kind of interaction is what we call application code. In the second kind, Lua has the control and C is the library. Here, the C code is called library code. Both application code and library code use the same API to communicate with Lua, the so-called C API.

The C API is the set of functions, constants, and types that allow C code to interact with Lua.[23] It comprises functions to read and write Lua global variables, to call Lua functions, to run pieces of Lua code, to register C functions so that they can later be called by Lua code, and so on. Virtually anything that Lua code can do can also be done by C code through the C API.

The C API follows the modus operandi of C, which is quite different from that of Lua. When programming in C, we must care about type checking, error recovery, memory-allocation errors, and several other sources of complexity. Most functions in the API do not check the correctness of their arguments; it is our responsibility to make sure that the arguments are valid before calling a function.[24] If we make mistakes, we can get a crash instead of a well-behaved error message. Moreover, the API emphasizes flexibility and simplicity, sometimes at the cost of ease of use. Common tasks may involve several API calls. This may be boring, but it gives us full control over all details.

As its title says, the goal of this chapter is to give an overview of what is involved when we use Lua from C. Do not try to understand all the details of what is going on now; we will fill them in later. Nevertheless, do not forget that you always can find more details about specific functions in the Lua reference manual. Moreover, you can find several examples of API uses in the Lua distribution itself. The Lua stand-alone interpreter (lua.c) provides examples of application code, while the standard libraries (lmathlib.c, lstrlib.c, etc.) provide examples of library code.

From now on, we are wearing a C programmer’s hat.

We will start this overview with a simple example of an application program: a stand-alone Lua interpreter. We can write a bare-bones stand-alone interpreter as in Figure 27.1, “A bare-bones stand-alone Lua interpreter”.

The header file lua.h declares the basic functions provided by Lua. It includes functions to create a new Lua environment, to invoke Lua functions, to read and write global variables in the environment, to register new functions to be called by Lua, and so on. Everything declared in lua.h has a lua_ prefix (e.g., lua_pcall).

The header file lauxlib.h declares the functions provided by the auxiliary library (auxlib). All its declarations start with luaL_ (e.g., luaL_loadstring). The auxiliary library uses the basic API provided by lua.h to provide a higher abstraction level, in particular with abstractions used by the standard libraries. The basic API strives for economy and orthogonality, whereas the auxiliary library strives for practicality for a few common tasks. Of course, it is very easy for your program to create other abstractions that it needs, too. Keep in mind that the auxiliary library has no access to the internals of Lua. It does its entire job through the official basic API declared in lua.h. Whatever it does, your program can do too.

The Lua library defines no C global variables at all. It keeps all its state in the dynamic structure lua_State; all functions inside Lua receive a pointer to this structure as an argument. This design makes Lua reentrant and ready to be used in multithreaded code.

As its name implies, the function luaL_newstate creates a new Lua state. When luaL_newstate creates a fresh state, its environment contains no predefined functions, not even print. To keep Lua small, all standard libraries come as separate packages, so that we do not have to use them if we do not need to. The header file lualib.h declares functions to open the libraries. The function luaL_openlibs opens all standard libraries.

After creating a state and populating it with the standard libraries, it is time to handle user input. For each line the user enters, the program first compiles it with luaL_loadstring. If there are no errors, the call returns zero and pushes the resulting function on the stack. (We will discuss this mysterious stack in the next section.) Then the program calls lua_pcall, which pops the function from the stack and runs it in protected mode. Like luaL_loadstring, lua_pcall returns zero if there are no errors. In case of error, both functions push an error message on the stack; we then get this message with lua_tostring and, after printing it, remove it from the stack with lua_pop.

Real error handling can be quite complex in C, and how to do it depends on the nature of our application. The Lua core never writes anything directly to any output stream; it signals errors by returning error messages. Each application can handle these messages in a way most appropriate to its needs. To simplify our discussions, we will assume for our next examples a simple error handler like the following one, which prints an error message, closes the Lua state, and finishes the whole application:

      #include <stdarg.h>
      #include <stdio.h>
      #include <stdlib.h>
      
      void error (lua_State *L, const char *fmt, ...) {
        va_list argp;
        va_start(argp, fmt);
        vfprintf(stderr, fmt, argp);
        va_end(argp);
        lua_close(L);
        exit(EXIT_FAILURE);
      }

Later we will discuss more about error handling in the application code.

Because we can compile Lua as either C or C++ code, lua.h does not include the following boilerplate commonly used in C libraries:

      #ifdef __cplusplus
      extern "C" {
      #endif
         ...
      #ifdef __cplusplus
      }
      #endif

If we have compiled Lua as C code and are using it in C++, we can include lua.hpp instead of lua.h. It is defined as follows:

      extern "C" {
      #include "lua.h"
      }

A major component in the communication between Lua and C is an omnipresent virtual stack. Almost all API calls operate on values on this stack. All data exchange from Lua to C and from C to Lua occurs through this stack. Moreover, we can use the stack to keep intermediate results, too.

We face two problems when trying to exchange values between Lua and C: the mismatch between a dynamic and a static type system and the mismatch between automatic and manual memory management.

In Lua, when we write t[k] = v, both k and v can have several different types; even t can have different types, due to metatables. If we want to offer this operation in C, however, any given settable function must have a fixed type. We would need dozens of different functions for this single operation (one function for each combination of types for the three arguments).

We could solve this problem by declaring some kind of union type in C —let us call it lua_Value— that could represent all Lua values. Then, we could declare settable as

      void lua_settable (lua_Value a, lua_Value k, lua_Value v);

This solution has two drawbacks. First, it can be difficult to map such a complex type to other languages; we designed Lua to interface easily not only with C/C++, but also with Java, Fortran, C#, and the like. Second, Lua does garbage collection: if we keep a Lua table in a C variable, the Lua engine has no way to know about this use; it may (wrongly) assume that this table is garbage and collect it.

Therefore, the Lua API does not define anything like a lua_Value type. Instead, it uses the stack to exchange values between Lua and C. Each slot in this stack can hold any Lua value. Whenever we want to ask for a value from Lua (such as the value of a global variable), we call Lua, which pushes the required value onto the stack. Whenever we want to pass a value to Lua, we first push the value onto the stack, and then we call Lua (which will pop the value). We still need a different function to push each C type onto the stack and a different function to get each C type from the stack, but we avoid combinatorial explosion. Moreover, because this stack is part of the Lua state, the garbage collector knows which values C is using.

Nearly all functions in the API use the stack. As we saw in our first example, luaL_loadstring leaves its result on the stack (either the compiled chunk or an error message); lua_pcall gets the function to be called from the stack and leaves any error message there too.

Lua manipulates this stack in a strict LIFO discipline (Last In, First Out). When we call Lua, it changes only the top part of the stack. Our C code has more freedom; specifically, it can inspect any element in the stack and even insert and delete elements at any position.

The API has a push function for each Lua type with a direct representation in C: lua_pushnil for the constant nil, lua_pushboolean for Booleans (integers, in C), lua_pushnumber for doubles,[25] lua_pushinteger for integers, lua_pushlstring for arbitrary strings (a pointer to char plus a length), and lua_pushstring for zero-terminated strings:

      void lua_pushnil      (lua_State *L);
      void lua_pushboolean  (lua_State *L, int bool);
      void lua_pushnumber   (lua_State *L, lua_Number n);
      void lua_pushinteger  (lua_State *L, lua_Integer n);
      void lua_pushlstring  (lua_State *L, const char *s, size_t len);
      void lua_pushstring   (lua_State *L, const char *s);

There are also functions to push C functions and userdata values onto the stack; we will discuss them later.

The type lua_Number is the numeric float type in Lua. It is double by default, but we can configure Lua at compile time to use float or even long double. The type lua_Integer is the numeric integer type in Lua. Usually, it is defined as long long, which is a signed 64-bit integer. Again, it is trivial to configure Lua to use int or long for this type. The combination floatint, with 32-bit floats and integers, creates what we call Small Lua, which is particularly interesting for small machines and restricted hardware.[26]

Strings in Lua are not zero-terminated; they can contain arbitrary binary data. In consequence, the basic function to push a string onto the stack is lua_pushlstring, which requires an explicit length as an argument. For zero-terminated strings, we can use also lua_pushstring, which uses strlen to supply the string length. Lua never keeps pointers to external strings (or to any other external object except C functions, which are always static). For any string that it has to keep, Lua either makes an internal copy or reuses one. Therefore, we can free or modify our buffers as soon as these functions return.

Whenever we push an element onto the stack, it is our responsibility to ensure that the stack has space for it. Remember, you are a C programmer now; Lua will not spoil you. When Lua starts and any time that Lua calls C, the stack has at least 20 free slots. (The header file lua.h defines this constant as LUA_MINSTACK.) This space is more than enough for most common uses, so usually we do not even think about it. However, some tasks need more stack space, in particular if we have a loop pushing elements onto the stack. In those cases, we need to call lua_checkstack, which checks whether the stack has enough space for our needs:

      int lua_checkstack (lua_State *L, int sz);

Here, sz is the number of extra slots we need. If possible, lua_checkstack grows the stack to accommodate the required extra size. Otherwise, it returns zero.

The auxiliary library offers a higher-level function to check for stack space:

      void luaL_checkstack (lua_State *L, int sz, const char *msg);

This function is similar to lua_checkstack but, if it cannot fulfill the request, it raises an error with the given message, instead of returning an error code.

To refer to elements on the stack, the API uses indices. The first element pushed on the stack has index 1, the next one has index 2, and so on. We can also access elements using the top of the stack as our reference, with negative indices. In this case, -1 refers to the element on top (that is, the last element pushed), -2 to the previous element, and so on. For instance, the call lua_tostring(L, -1) returns the value on the top of the stack as a string. As we will see, there are several occasions when it is natural to index the stack from the bottom (that is, with positive indices), and several other occasions when the natural way is to use negative indices.

To check whether a stack element has a specific type, the API offers a family of functions called lua_is*, where the * can be any Lua type. So, there are lua_isnil, lua_isnumber, lua_isstring, lua_istable, and the like. All these functions have the same prototype:

      int lua_is* (lua_State *L, int index);

Actually, lua_isnumber does not check whether the value has that specific type, but whether the value can be converted to that type; lua_isstring is similar: in particular, any number satisfies lua_isstring.

There is also a function lua_type, which returns the type of an element on the stack. Each type is represented by a respective constant: LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, etc. We use this function mainly in conjunction with a switch statement. It is also useful when we need to check for strings and numbers without potential coercions.

To get a value from the stack, there are the lua_to* functions:

      int          lua_toboolean (lua_State *L, int index);
      const char  *lua_tolstring (lua_State *L, int index,
                                                size_t *len);
      lua_State   *lua_tothread (lua_State *L, int index);
      lua_Number   lua_tonumber  (lua_State *L, int index);
      lua_Integer  lua_tointeger (lua_State *L, int index);

We can call any of these functions even when the given element does not have an appropriate type. The function lua_toboolean works for any type, converting any Lua value to a C Boolean according to the Lua rules for conditions: zero for the values nil and false, and one for any other Lua value. The functions lua_tolstring and lua_tothread return NULL for values with incorrect types. The numeric functions, however, have no way to signal a wrong type, so they simply return zero. Formerly we would need to call lua_isnumber to check the type, but Lua 5.2 introduced the following new functions:

      lua_Number   lua_tonumberx (lua_State *L, int idx, int *isnum);
      lua_Integer  lua_tointegerx (lua_State *L, int idx, int *isnum);

The out parameter isnum returns a Boolean that indicates whether the Lua value was successfully coerced to the desired type.

The function lua_tolstring returns a pointer to an internal copy of the string and stores the string’s length in the position given by len. We must not change this internal copy (there is a const there to remind us). Lua ensures that this pointer is valid as long as the corresponding string value is on the stack. When a C function called by Lua returns, Lua clears its stack; therefore, as a rule, we should never store pointers to Lua strings outside the function that got them.

Any string that lua_tolstring returns always has an extra zero at its end, but it can have other zeros inside it. The size returned through the third argument, len, is the real string’s length. In particular, assuming that the value on the top of the stack is a string, the following assertions are always valid:

      size_t len;
      const char *s = lua_tolstring(L, -1, &len); /* any Lua string */
      assert(s[len] == '\0');
      assert(strlen(s) <= len);

We can call lua_tolstring with NULL as its third argument if we do not need the length. Better yet, we can use the macro lua_tostring, which simply calls lua_tolstring with a NULL third argument.

To illustrate the use of these functions, Figure 27.2, “Dumping the stack” presents a useful helper function that dumps the entire content of the stack.

This function traverses the stack from bottom to top, printing each element according to its type. It prints strings between quotes; for numbers it uses a "%g" format; for values with no C equivalents (tables, functions, etc.), it prints only their types. (lua_typename converts a type code to a type name.)

In Lua 5.3, we can still print all numbers with lua_tonumber and the "%g" format, as integers are always coercible to floats. However, we may prefer to print integers as integers, to avoid losing precision. In that case, we can use the new function lua_isinteger to distinguish integers from floats:

            case LUA_TNUMBER: {  /* numbers */
              if (lua_isinteger(L, i))  /* integer? */
                printf("%lld", lua_tointeger(L, i));
              else  /* float */
                printf("%g", lua_tonumber(L, i));
              break;
            }

Besides the previous functions, which exchange values between C and the stack, the API offers also the following operations for generic stack manipulation:

      int  lua_gettop    (lua_State *L);
      void lua_settop    (lua_State *L, int index);
      void lua_pushvalue (lua_State *L, int index);
      void lua_rotate    (lua_State *L, int index, int n);
      void lua_remove    (lua_State *L, int index);
      void lua_insert    (lua_State *L, int index);
      void lua_replace   (lua_State *L, int index);
      void lua_copy      (lua_State *L, int fromidx, int toidx);

The function lua_gettop returns the number of elements on the stack, which is also the index of the top element. The function lua_settop sets the top (that is, the number of elements on the stack) to a specific value. If the previous top was higher than the new one, the function discards the extra top values. Otherwise, it pushes nils on the stack to get the given size. In particular, lua_settop(L, 0) empties the stack. We can also use negative indices with lua_settop. Using this facility, the API offers the following macro, which pops n elements from the stack:

      #define lua_pop(L,n)  lua_settop(L, -(n) - 1)

The function lua_pushvalue pushes on the stack a copy of the element at the given index.

The function lua_rotate is new in Lua 5.3. As the name implies, it rotates the stack elements from the given index to the top of the stack by n positions. A positive n rotates the elements in the direction of the top; a negative n rotates in the other direction. This is a quite versatile function, and two other API operations are defined as macros using it. One is lua_remove, which removes the element at the given index, shifting down the elements above this position to fill in the gap. Its definition is as follows:

      #define lua_remove(L,idx)  \
                (lua_rotate(L, (idx), -1), lua_pop(L, 1))

That is, it rotates the stack by one position, moving the desired element to the top, and then pops that element. The other macro is lua_insert, which moves the top element into the given position, shifting up the elements above this position to open space:

      #define lua_insert(L,idx)       lua_rotate(L, (idx), 1)

The function lua_replace pops a value and sets it as the value of the given index, without moving anything; finally, lua_copy copies the value at one index to another, leaving the original untouched.[27] Note that the following operations have no effect on a non-empty stack:

      lua_settop(L, -1);  /* set top to its current value */
      lua_insert(L, -1);  /* move top element to the top */
      lua_copy(L, x, x);  /* copy an element to its own position */
      lua_rotate(L, x, 0);  /* rotates by zero positions */

The program in Figure 27.3, “Example of stack manipulation” uses stackDump (defined in Figure 27.2, “Dumping the stack”) to illustrate these stack operations.

Figure 27.3. Example of stack manipulation

      #include <stdio.h>
      #include "lua.h"
      #include "lauxlib.h"
      
      static void stackDump (lua_State *L) {
        as in Figure 27.2, “Dumping the stack”
      }
      
      int main (void) {
        lua_State *L = luaL_newstate();
      
        lua_pushboolean(L, 1);
        lua_pushnumber(L, 10);
        lua_pushnil(L);
        lua_pushstring(L, "hello");
      
        stackDump(L);
          /* will print:  true  10  nil  'hello'  */
      
        lua_pushvalue(L, -4); stackDump(L);
          /* will print:  true  10  nil  'hello'  true  */
      
        lua_replace(L, 3); stackDump(L);
          /* will print:  true  10  true  'hello'  */
      
        lua_settop(L, 6); stackDump(L);
          /* will print:  true  10  true  'hello'  nil  nil  */
      
        lua_rotate(L, 3, 1); stackDump(L);
          /* will print:  true  10  nil  true  'hello'  nil  */
      
        lua_remove(L, -3); stackDump(L);
          /* will print:  true  10  nil  'hello'  nil */
      
        lua_settop(L, -5); stackDump(L);
          /* will print:  true  */
      
        lua_close(L);
        return 0;
      }

All structures in Lua are dynamic: they grow as needed, and eventually shrink again when possible. This means that the possibility of a memory-allocation failure is pervasive in Lua. Almost any operation can face this eventuality. Moreover, many operations can raise other errors; for instance, an access to a global variable can trigger an __index metamethod and that metamethod may raise an error. Finally, operations that allocate memory eventually trigger the garbage collector, which may invoke finalizers, which can raise errors too. In short, the vast majority of functions in the Lua API can result in errors.

Instead of using error codes for each operation in its API, Lua uses exceptions to signal errors. Unlike C++ or Java, the C language does not offer an exception handling mechanism. To circumvent this difficulty, Lua uses the setjmp facility from C, which results in a mechanism somewhat similar to exception handling. Therefore, most API functions can raise an error (that is, call longjmp) instead of returning.

When we write library code (C functions to be called from Lua), the use of long jumps requires no extra work from our part, because Lua catches any error. When we write application code (C code that calls Lua), however, we must provide a way to catch those errors.

When our application calls functions in the Lua API, it is exposed to errors. As we just discussed, Lua usually signals these errors through long jumps. However, if there is no corresponding setjmp, the interpreter cannot make a long jump. In that case, any error in the API causes Lua to call a panic function and, if that function returns, exit the application. We can set our own panic function with lua_atpanic, but there is not much that it can do.

To properly handle errors in our application code, we must call our code through Lua, so that it sets an appropriate context to catch errors —that is, it runs the code in the context of a setjmp. In the same way that we can run Lua code in protected mode using pcall, we can run C code using lua_pcall. More specifically, we pack the code in a function and call that function through Lua, using lua_pcall. With this setting, our C code will run in protected mode. Even in case of memory-allocation failure, lua_pcall returns a proper error code, leaving the interpreter in a consistent state. The following fragment shows the idea:

      static int foo (lua_State *L) {
        code to run in protected mode
        return 0;
      }
      
      int secure_foo (lua_State *L) {
        lua_pushcfunction(L, foo);  /* push 'foo' as a Lua function */
        return (lua_pcall(L, 0, 0, 0) == 0);
      }

In this example, no matter what happens, a call to secure_foo will return a Boolean signaling the success of foo. In particular, note that the stack already has some preallocated slots and that lua_pushcfunction does not allocate memory, so it cannot raise any error. (The prototype of the function foo is a requirement of lua_pushcfunction, which creates a function in Lua representing a C function. We will cover the details about C functions in Lua in the section called “C Functions”.)

The Lua core does not assume anything about how to allocate memory. It calls neither malloc nor realloc to allocate memory. Instead, it does all its memory allocation and deallocation through one single allocation function, which the user must provide when she creates a Lua state.

The function luaL_newstate, which we have been using to create states, is an auxiliary function that creates a Lua state with a default allocation function. This default allocation function uses the standard functions mallocreallocfree from the C standard library, which are (or should be) good enough for most applications. However, it is quite easy to get full control over Lua allocation, by creating our state with the primitive lua_newstate:

      lua_State *lua_newstate (lua_Alloc f, void *ud);

This function takes two arguments: an allocation function and a user data. A state created in this way does all its allocation and deallocation by calling f; even the structure lua_State is allocated by f.

An allocation function must match the type lua_Alloc:

      typedef void * (*lua_Alloc) (void *ud,
                                   void *ptr,
                                   size_t osize,
                                   size_t nsize);

The first parameter is always the user data provided to lua_newstate; the second parameter is the address of the block being (re)allocated or released; the third parameter is the original block size; and the last parameter is the requested block size. If ptr is not NULL, Lua ensures that it was previously allocated with size osize. (When ptr is NULL, the previous size of the block was clearly zero, so Lua uses osize for some debug information.)

Lua uses NULL to represent a block of size zero. When nsize is zero, the allocation function must free the block pointed to by ptr and return NULL, which corresponds to a block of the required size (zero). When ptr is NULL, the function must allocate and return a block with the given size; if it cannot allocate the given block, it must return NULL. If ptr is NULL and nsize is zero, both rules apply: the net result is that the allocation function does nothing and returns NULL.

Finally, when ptr is non-NULL and nsize is non-zero, the allocation function should reallocate the block, like realloc, and return the new address (which may or may not be the same as the original). Again, in case of errors, it must return NULL. Lua assumes that the allocation function never fails when the new size is smaller than or equal to the old one. (Lua shrinks some structures during garbage collection, and it is unable to recover from errors there.)

The standard allocation function used by luaL_newstate has the following definition (extracted directly from the file lauxlib.c):

      void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
        (void)ud; (void)osize;  /* not used */
        if (nsize == 0) {
          free(ptr);
          return NULL;
        }
        else
          return realloc(ptr, nsize);
      }

It assumes that free(NULL) does nothing and that realloc(NULL, size) is equivalent to malloc(size). The ISO C standard mandates both behaviors.

We can recover the memory allocator of a Lua state by calling lua_getallocf:

      lua_Alloc lua_getallocf (lua_State *L, void **ud);

If ud is not NULL, the function sets *ud with the value of the user data for this allocator. We can change the memory allocator of a Lua state by calling lua_setallocf:

      void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);

Keep in mind that any new allocator will be responsible for freeing blocks that were allocated by the previous one. More often than not, the new function is a wrapper around the old one, for instance to trace allocations or to synchronize accesses to the heap.

Internally, Lua does not cache free memory blocks for reuse. It assumes that the allocation function does this caching; good allocators do. Lua also does not attempt to minimize fragmentation. Studies show that fragmentation is more the result of poor allocation strategies than of program behavior; good allocators do not create much fragmentation.

It is difficult to beat a well-implemented allocator, but sometimes you may try. For instance, Lua gives you the old size of any block that it frees or reallocates. Therefore, a specialized allocator does not need to keep information about the block size, reducing the memory overhead for each block.

Another situation where you can improve memory allocation is in multithreading systems. Such systems typically demand synchronization for their memory-allocation functions, as they use a global resource (the heap). However, the access to a Lua state must be synchronized too —or, better yet, restricted to one thread, as in our implementation of lproc in Chapter 33, Threads and States. So, if each Lua state allocates memory from a private pool, the allocator can avoid the costs of extra synchronization.

Exercise 27.1: Compile and run the simple stand-alone interpreter (Figure 27.1, “A bare-bones stand-alone Lua interpreter”).

Exercise 27.2: Assume the stack is empty. What will be its contents after the following sequence of calls?

      lua_pushnumber(L, 3.5);
      lua_pushstring(L, "hello");
      lua_pushnil(L);
      lua_rotate(L, 1, -1);
      lua_pushvalue(L, -2);
      lua_remove(L, 1);
      lua_insert(L, -2);

Exercise 27.3: Use the function stackDump (Figure 27.2, “Dumping the stack”) to check your answer to the previous exercise.

Exercise 27.4: Write a library that allows a script to limit the total amount of memory used by its Lua state. It may offer a single function, setlimit, to set that limit.

The library should set its own allocation function. This function, before calling the original allocator, checks the total memory in use and returns NULL if the requested memory exceeds the limit.

(Hint: the library can use the user data of the allocation function to keep its state: the byte count, the current memory limit, etc.; remember to use the original user data when calling the original allocation function.)



[23] Throughout this text, the term function actually means function or macro. The API implements several facilities as macros.

[25] For historical reasons, the term number in the API refers to doubles.

[26] For these configurations, have a look in the file luaconf.h.

Personal copy of Eric Taylor <jdslkgjf.iapgjflksfg@yandex.com>