<commit_before>/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * script.c
 * Copyright (C) 2015 Dr.NP <<EMAIL>>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Dr.NP nor the name of any other
 *    contributor may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Dr.NP AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL Dr.NP OR ANY OTHER
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * LUA script container
 *
 * @package bspd::duang
 * @author Dr.NP <<EMAIL>>
 * @update 04/01/2015
 * @changelog
 *      [04/01/2015] - Creation
 */

#include "../bspd.h"

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static BSP_MEMPOOL *mp_task = NULL;
static BSPD_SCRIPT_TASK *task_queue_head = NULL;
static BSPD_SCRIPT_TASK *task_queue_tail = NULL;
static BSP_SPINLOCK task_queue_lock;

inline size_t lua_table_size(lua_State *s, int idx);

int script_init()
{
    if (!mp_task)
    {
        mp_task = bsp_new_mempool(sizeof(BSPD_SCRIPT_TASK), NULL, NULL);
        if (!mp_task)
        {
            return BSP_RTN_ERR_MEMORY;
        }
    }

    bsp_spin_init(&task_queue_lock);

    return BSP_RTN_SUCCESS;
}

static void * _default_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
    (void) ud;

    if (0 == nsize)
    {
        bsp_free(ptr);

        return NULL;
    }
    else
    {
        return bsp_realloc(ptr, nsize);
    }
}

// New lua script container
BSPD_SCRIPT * new_script_container()
{
    BSPD_SCRIPT *ret = bsp_calloc(1, sizeof(BSPD_SCRIPT));
    if (ret)
    {
        ret->state = lua_newstate(_default_allocator, NULL);
        if (!ret->state)
        {
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Create lua state failed");
            bsp_free(ret);

            return NULL;
        }

        luaL_openlibs(ret->state);
    }

    return ret;
}

// Delete script container
int del_script_container(BSPD_SCRIPT *scrt)
{
    if (!scrt)
    {
        return BSP_RTN_INVALID;
    }

    if (scrt->state)
    {
        lua_close(scrt->state);
    }

    bsp_free(scrt);

    return BSP_RTN_SUCCESS;
}

// Reload script state
int restart_script_container(BSPD_SCRIPT *scrt)
{
    if (!scrt)
    {
        return BSP_RTN_INVALID;
    }

    if (scrt->state)
    {
        lua_close(scrt->state);
    }

    scrt->state = lua_newstate(_default_allocator, NULL);
    if (!scrt->state)
    {
        bsp_trace_message(BSP_TRACE_ERROR, "lua", "Create lua state failed");

        return BSP_RTN_ERR_GENERAL;
    }

    luaL_openlibs(scrt->state);

    return BSP_RTN_SUCCESS;
}

// Load script from file
int load_script_file(BSPD_SCRIPT *scrt, const char *script_filename)
{
    if (!scrt || !scrt->state || !script_filename)
    {
        return BSP_RTN_INVALID;
    }

    int ret = BSP_RTN_INVALID;
    int status = luaL_loadfile(scrt->state, script_filename);
    switch (status)
    {
        case LUA_OK : 
            bsp_trace_message(BSP_TRACE_DEBUG, "lua", "Script file %s loaded", script_filename);
            // Call chunk after load
            if (LUA_OK != lua_pcall(scrt->state, 0, 0, 0))
            {
                bsp_trace_message(BSP_TRACE_ERROR, "lua", "Run script chunk error : %s", lua_tostring(scrt->state, -1));
            }
            ret = BSP_RTN_SUCCESS;
            break;
        case LUA_ERRFILE : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Cannot open file", script_filename);
            ret = BSP_RTN_ERR_IO_READ;
            break;
        case LUA_ERRSYNTAX : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Syntax error : ", script_filename, lua_tostring(scrt->state, -1));
            ret = BSP_RTN_ERR_GENERAL;
            break;
        case LUA_ERRMEM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Memory allocation error", script_filename);
            ret = BSP_RTN_ERR_MEMORY;
            break;
        case LUA_ERRGCMM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : GC error", script_filename);
            ret = BSP_RTN_ERR_GENERAL;
            break;
        default : 
            break;
    }

    return ret;
}

// Load script content
int load_script_content(BSPD_SCRIPT *scrt, BSP_STRING *script)
{
    if (!scrt || !scrt->state || !script)
    {
        return BSP_RTN_INVALID;
    }

    int ret = BSP_RTN_INVALID;
    int status = luaL_loadbuffer(scrt->state, STR_STR(script), STR_LEN(script), NULL);
    switch (status)
    {
        case LUA_OK : 
            bsp_trace_message(BSP_TRACE_DEBUG, "lua", "Script loaded from content.");
            if (LUA_OK != lua_pcall(scrt->state, 0, 0, 0))
            {
                bsp_trace_message(BSP_TRACE_ERROR, "lua", "Run script chunk error : %s", lua_tostring(scrt->state, -1));
            }
            ret = BSP_RTN_SUCCESS;
            break;
        case LUA_ERRSYNTAX : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : Syntax error.");
            ret = BSP_RTN_ERR_GENERAL;
            break;
        case LUA_ERRMEM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : Memory allocation error.");
            ret = BSP_RTN_ERR_MEMORY;
            break;
        case LUA_ERRGCMM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : GC error.");
            ret = BSP_RTN_ERR_GENERAL;
            break;
        default : 
            break;
    }

    return ret;
}

// Real length of LUA table
inline size_t lua_table_size(lua_State *s, int idx)
{
    size_t ret = 0;
    idx = lua_absindex(s, idx);
    if (s && lua_istable(s, idx))
    {
        lua_checkstack(s, 2);
        lua_pushnil(s);
        while (0 != lua_next(s, idx))
        {
            ret ++;
            lua_pop(s, 1);
        }
    }

    return ret;
}

// BSP object and lua table
static void _push_value_to_lua(lua_State *s, BSP_VALUE *val);
static void _push_object_to_lua(lua_State *s, BSP_OBJECT *obj);

static void _push_value_to_lua(lua_State *s, BSP_VALUE *val)
{
    if (!s)
    {
        return;
    }

    if (!val)
    {
        lua_pushnil(s);

        return;
    }

    BSP_STRING *str = NULL;
    BSP_OBJECT *sub_obj = NULL;
    switch (val->type)
    {
        case BSP_VALUE_INT8 : 
        case BSP_VALUE_INT16 : 
        case BSP_VALUE_INT32 : 
        case BSP_VALUE_INT64 : 
        case BSP_VALUE_INT29 : 
        case BSP_VALUE_INT : 
        case BSP_VALUE_UINT8 : 
        case BSP_VALUE_UINT16 : 
        case BSP_VALUE_UINT32 : 
        case BSP_VALUE_UINT64 : 
            lua_pushinteger(s, (lua_Integer) V_GET_INT(val));
            break;
        case BSP_VALUE_FLOAT : 
        case BSP_VALUE_DOUBLE : 
            lua_pushnumber(s, (lua_Number) V_GET_FLOAT(val));
            break;
        case BSP_VALUE_BOOLEAN : 
            lua_pushboolean(s, (int) V_GET_BOOLEAN(val));
            break;
        case BSP_VALUE_STRING : 
            str = V_GET_STRING(val);
            if (str && STR_STR(str))
            {
                lua_pushlstring(s, STR_STR(str), STR_LEN(str));
            }
            else
            {
                lua_pushnil(s);
            }
            break;
        case BSP_VALUE_OBJECT : 
            sub_obj = V_GET_OBJECT(val);
            if (sub_obj)
            {
                _push_object_to_lua(s, sub_obj);
            }
            else
            {
                lua_pushnil(s);
            }
            break;
        case BSP_VALUE_POINTER : 
            lua_pushlightuserdata(s, V_GET_POINTER(val));
            break;
        case BSP_VALUE_NULL : 
        case BSP_VALUE_UNKNOWN : 
        default : 
            lua_pushnil(s);
            break;
    }

    return;
}

static void _push_object_to_lua(lua_State *s, BSP_OBJECT *obj)
{
    if (!s)
    {
        return;
    }

    if (!obj)
    {
        lua_pushnil(s);

        return;
    }

    BSP_VALUE *val = NULL;
    BSP_STRING *key = NULL;
    size_t idx, total;
    bsp_spin_lock(&obj->lock);
    bsp_object_reset(obj);
    lua_checkstack(s, 1);
    switch (obj->type)
    {
        case BSP_OBJECT_SINGLE : 
            // Single value
            val = bsp_object_value_single(obj);
            _push_value_to_lua(s, val);
            break;
        case BSP_OBJECT_ARRAY : 
            // Array
            total = bsp_object_size(obj);
            lua_newtable(s);
            for (idx = 0; idx < total; idx ++)
            {
                lua_checkstack(s, 2);
                lua_pushinteger(s, (lua_Integer) idx + 1);
                val = bsp_object_value_array(obj, idx);
                if (val)
                {
                    _push_value_to_lua(s, val);
                }
                else
                {
                    lua_pushnil(s);
                }

                lua_settable(s, -3);
            }

            break;
        case BSP_OBJECT_HASH : 
            // Hash
            lua_newtable(s);
            val = bsp_object_curr(obj, (void **) &key);
            while (val)
            {
                if (key)
                {
                    lua_checkstack(s, 2);
                    lua_pushlstring(s, STR_STR(key), STR_LEN(key));
                    _push_value_to_lua(s, val);
                    lua_settable(s, -3);
                }

                bsp_object_next(obj);
                val = bsp_object_curr(obj, (void **) &key);
            }

            break;
        case BSP_OBJECT_UNDETERMINED : 
        default : 
            break;
    }

    bsp_spin_unlock(&obj->lock);

    return;
}

void object_to_lua(lua_State *s, BSP_OBJECT *obj)
{
    if (!s || !obj)
    {
        return;
    }

    _push_object_to_lua(s, obj);

    return;
}

static BSP_VALUE * _lua_value_to_value(lua_State *s, int idx);
static BSP_OBJECT * _lua_table_to_object(lua_State *s, int idx);

static BSP_VALUE * _lua_value_to_value(lua_State *s, int idx)
{
    if (!s)
    {
        return NULL;
    }

    idx = lua_absindex(s, idx);
    BSP_VALUE *ret = bsp_new_value();
    if (!ret)
    {
        return NULL;
    }

    lua_Number v_number = 0;
    int v_boolean = 0;
    size_t str_len = 0;
    const char *str = NULL;
    void *v_ptr = NULL;
    BSP_STRING *v_str = NULL;
    BSP_OBJECT *v_obj = NULL;
    switch (lua_type(s, idx))
    {
        case LUA_TNIL : 
            V_SET_NULL(ret);
            break;
        case LUA_TNUMBER : 
            v_number = lua_tonumber(s, idx);
            if (v_number == (lua_Number)(int64_t) v_number)
            {
                // Integer
                V_SET_INT(ret, v_number);
            }
            else
            {
                // Double
                V_SET_DOUBLE(ret, v_number);
            }

            break;
        case LUA_TBOOLEAN : 
            v_boolean = lua_toboolean(s, idx);
            if (!v_boolean)
            {
                V_SET_BOOLEAN(ret, BSP_FALSE);
            }
            else
            {
                V_SET_BOOLEAN(ret, BSP_TRUE);
            }

            break;
        case LUA_TSTRING : 
            str = lua_tolstring(s, idx, &str_len);
            v_str = bsp_new_string(str, str_len);
            V_SET_STRING(ret, v_str);
            break;
        case LUA_TUSERDATA : 
        case LUA_TLIGHTUSERDATA : 
            v_ptr = lua_touserdata(s, idx);
            V_SET_POINTER(ret, v_ptr);
            break;
        case LUA_TTABLE : 
            v_obj = _lua_table_to_object(s, idx);
            V_SET_OBJECT(ret, v_obj);
            break;
        default : 
            V_SET_NULL(ret);
            break;
    }

    return ret;
}

static BSP_OBJECT * _lua_table_to_object(lua_State *s, int idx)
{
    if (!s || !lua_istable(s, idx))
    {
        return NULL;
    }

    idx = lua_absindex(s, idx);
    BSP_OBJECT *ret = bsp_new_object();
    BSP_VALUE *val = NULL;
    if (!ret)
    {
        return NULL;
    }

    // Check type, array or hash
    size_t total = luaL_len(s, idx);
    if (total == lua_table_size(s, idx))
    {
        // Array
        ret->type = BSP_OBJECT_ARRAY;
        size_t i;
        for (i = 1; i <= total; i ++)
        {
            lua_rawgeti(s, idx, i);
            val = _lua_value_to_value(s, -1);
            bsp_object_set_array(ret, i - 1, val);
            lua_pop(s, 1);
        }
    }
    else
    {
        // Hash
        ret->type = BSP_OBJECT_HASH;
        const char *key_str = NULL;
        size_t key_len = 0;
        BSP_STRING *key = NULL;
        lua_checkstack(s, 2);
        lua_pushnil(s);
        while (0 != lua_next(s, idx))
        {
            // Key
            key_str = lua_tolstring(s, -2, &key_len);
            key = bsp_new_string(key_str, key_len);

            // Value
            val = _lua_value_to_value(s, -1);

            bsp_object_set_hash(ret, key, val);
            lua_pop(s, 1);
        }
    }

    return ret;
}

BSP_OBJECT * lua_to_object(lua_State *s, int idx)
{
    if (!s)
    {
        return NULL;
    }

    BSP_OBJECT *ret = NULL;
    if (lua_istable(s, idx))
    {
        // Array or hash
        debug_lua_stack(s);
        ret = _lua_table_to_object(s, idx);
    }
    else
    {
        ret = bsp_new_object();
        ret->type = BSP_OBJECT_SINGLE;
        bsp_object_set_single(ret, _lua_value_to_value(s, idx));
    }

    return ret;
}

// Call lua function
static int _cont()<commit_msg>Remove debug<commit_after>/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*-  */
/*
 * script.c
 * Copyright (C) 2015 Dr.NP <<EMAIL>>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Dr.NP nor the name of any other
 *    contributor may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Dr.NP AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL Dr.NP OR ANY OTHER
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * LUA script container
 *
 * @package bspd::duang
 * @author Dr.NP <<EMAIL>>
 * @update 04/01/2015
 * @changelog
 *      [04/01/2015] - Creation
 */

#include "../bspd.h"

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static BSP_MEMPOOL *mp_task = NULL;
static BSPD_SCRIPT_TASK *task_queue_head = NULL;
static BSPD_SCRIPT_TASK *task_queue_tail = NULL;
static BSP_SPINLOCK task_queue_lock;

inline size_t lua_table_size(lua_State *s, int idx);

int script_init()
{
    if (!mp_task)
    {
        mp_task = bsp_new_mempool(sizeof(BSPD_SCRIPT_TASK), NULL, NULL);
        if (!mp_task)
        {
            return BSP_RTN_ERR_MEMORY;
        }
    }

    bsp_spin_init(&task_queue_lock);

    return BSP_RTN_SUCCESS;
}

static void * _default_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
{
    (void) ud;

    if (0 == nsize)
    {
        bsp_free(ptr);

        return NULL;
    }
    else
    {
        return bsp_realloc(ptr, nsize);
    }
}

// New lua script container
BSPD_SCRIPT * new_script_container()
{
    BSPD_SCRIPT *ret = bsp_calloc(1, sizeof(BSPD_SCRIPT));
    if (ret)
    {
        ret->state = lua_newstate(_default_allocator, NULL);
        if (!ret->state)
        {
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Create lua state failed");
            bsp_free(ret);

            return NULL;
        }

        luaL_openlibs(ret->state);
    }

    return ret;
}

// Delete script container
int del_script_container(BSPD_SCRIPT *scrt)
{
    if (!scrt)
    {
        return BSP_RTN_INVALID;
    }

    if (scrt->state)
    {
        lua_close(scrt->state);
    }

    bsp_free(scrt);

    return BSP_RTN_SUCCESS;
}

// Reload script state
int restart_script_container(BSPD_SCRIPT *scrt)
{
    if (!scrt)
    {
        return BSP_RTN_INVALID;
    }

    if (scrt->state)
    {
        lua_close(scrt->state);
    }

    scrt->state = lua_newstate(_default_allocator, NULL);
    if (!scrt->state)
    {
        bsp_trace_message(BSP_TRACE_ERROR, "lua", "Create lua state failed");

        return BSP_RTN_ERR_GENERAL;
    }

    luaL_openlibs(scrt->state);

    return BSP_RTN_SUCCESS;
}

// Load script from file
int load_script_file(BSPD_SCRIPT *scrt, const char *script_filename)
{
    if (!scrt || !scrt->state || !script_filename)
    {
        return BSP_RTN_INVALID;
    }

    int ret = BSP_RTN_INVALID;
    int status = luaL_loadfile(scrt->state, script_filename);
    switch (status)
    {
        case LUA_OK : 
            bsp_trace_message(BSP_TRACE_DEBUG, "lua", "Script file %s loaded", script_filename);
            // Call chunk after load
            if (LUA_OK != lua_pcall(scrt->state, 0, 0, 0))
            {
                bsp_trace_message(BSP_TRACE_ERROR, "lua", "Run script chunk error : %s", lua_tostring(scrt->state, -1));
            }
            ret = BSP_RTN_SUCCESS;
            break;
        case LUA_ERRFILE : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Cannot open file", script_filename);
            ret = BSP_RTN_ERR_IO_READ;
            break;
        case LUA_ERRSYNTAX : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Syntax error : ", script_filename, lua_tostring(scrt->state, -1));
            ret = BSP_RTN_ERR_GENERAL;
            break;
        case LUA_ERRMEM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : Memory allocation error", script_filename);
            ret = BSP_RTN_ERR_MEMORY;
            break;
        case LUA_ERRGCMM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script file %s failed : GC error", script_filename);
            ret = BSP_RTN_ERR_GENERAL;
            break;
        default : 
            break;
    }

    return ret;
}

// Load script content
int load_script_content(BSPD_SCRIPT *scrt, BSP_STRING *script)
{
    if (!scrt || !scrt->state || !script)
    {
        return BSP_RTN_INVALID;
    }

    int ret = BSP_RTN_INVALID;
    int status = luaL_loadbuffer(scrt->state, STR_STR(script), STR_LEN(script), NULL);
    switch (status)
    {
        case LUA_OK : 
            bsp_trace_message(BSP_TRACE_DEBUG, "lua", "Script loaded from content.");
            if (LUA_OK != lua_pcall(scrt->state, 0, 0, 0))
            {
                bsp_trace_message(BSP_TRACE_ERROR, "lua", "Run script chunk error : %s", lua_tostring(scrt->state, -1));
            }
            ret = BSP_RTN_SUCCESS;
            break;
        case LUA_ERRSYNTAX : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : Syntax error.");
            ret = BSP_RTN_ERR_GENERAL;
            break;
        case LUA_ERRMEM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : Memory allocation error.");
            ret = BSP_RTN_ERR_MEMORY;
            break;
        case LUA_ERRGCMM : 
            bsp_trace_message(BSP_TRACE_ERROR, "lua", "Load script failed : GC error.");
            ret = BSP_RTN_ERR_GENERAL;
            break;
        default : 
            break;
    }

    return ret;
}

// Real length of LUA table
inline size_t lua_table_size(lua_State *s, int idx)
{
    size_t ret = 0;
    idx = lua_absindex(s, idx);
    if (s && lua_istable(s, idx))
    {
        lua_checkstack(s, 2);
        lua_pushnil(s);
        while (0 != lua_next(s, idx))
        {
            ret ++;
            lua_pop(s, 1);
        }
    }

    return ret;
}

// BSP object and lua table
static void _push_value_to_lua(lua_State *s, BSP_VALUE *val);
static void _push_object_to_lua(lua_State *s, BSP_OBJECT *obj);

static void _push_value_to_lua(lua_State *s, BSP_VALUE *val)
{
    if (!s)
    {
        return;
    }

    if (!val)
    {
        lua_pushnil(s);

        return;
    }

    BSP_STRING *str = NULL;
    BSP_OBJECT *sub_obj = NULL;
    switch (val->type)
    {
        case BSP_VALUE_INT8 : 
        case BSP_VALUE_INT16 : 
        case BSP_VALUE_INT32 : 
        case BSP_VALUE_INT64 : 
        case BSP_VALUE_INT29 : 
        case BSP_VALUE_INT : 
        case BSP_VALUE_UINT8 : 
        case BSP_VALUE_UINT16 : 
        case BSP_VALUE_UINT32 : 
        case BSP_VALUE_UINT64 : 
            lua_pushinteger(s, (lua_Integer) V_GET_INT(val));
            break;
        case BSP_VALUE_FLOAT : 
        case BSP_VALUE_DOUBLE : 
            lua_pushnumber(s, (lua_Number) V_GET_FLOAT(val));
            break;
        case BSP_VALUE_BOOLEAN : 
            lua_pushboolean(s, (int) V_GET_BOOLEAN(val));
            break;
        case BSP_VALUE_STRING : 
            str = V_GET_STRING(val);
            if (str && STR_STR(str))
            {
                lua_pushlstring(s, STR_STR(str), STR_LEN(str));
            }
            else
            {
                lua_pushnil(s);
            }
            break;
        case BSP_VALUE_OBJECT : 
            sub_obj = V_GET_OBJECT(val);
            if (sub_obj)
            {
                _push_object_to_lua(s, sub_obj);
            }
            else
            {
                lua_pushnil(s);
            }
            break;
        case BSP_VALUE_POINTER : 
            lua_pushlightuserdata(s, V_GET_POINTER(val));
            break;
        case BSP_VALUE_NULL : 
        case BSP_VALUE_UNKNOWN : 
        default : 
            lua_pushnil(s);
            break;
    }

    return;
}

static void _push_object_to_lua(lua_State *s, BSP_OBJECT *obj)
{
    if (!s)
    {
        return;
    }

    if (!obj)
    {
        lua_pushnil(s);

        return;
    }

    BSP_VALUE *val = NULL;
    BSP_STRING *key = NULL;
    size_t idx, total;
    bsp_spin_lock(&obj->lock);
    bsp_object_reset(obj);
    lua_checkstack(s, 1);
    switch (obj->type)
    {
        case BSP_OBJECT_SINGLE : 
            // Single value
            val = bsp_object_value_single(obj);
            _push_value_to_lua(s, val);
            break;
        case BSP_OBJECT_ARRAY : 
            // Array
            total = bsp_object_size(obj);
            lua_newtable(s);
            for (idx = 0; idx < total; idx ++)
            {
                lua_checkstack(s, 2);
                lua_pushinteger(s, (lua_Integer) idx + 1);
                val = bsp_object_value_array(obj, idx);
                if (val)
                {
                    _push_value_to_lua(s, val);
                }
                else
                {
                    lua_pushnil(s);
                }

                lua_settable(s, -3);
            }

            break;
        case BSP_OBJECT_HASH : 
            // Hash
            lua_newtable(s);
            val = bsp_object_curr(obj, (void **) &key);
            while (val)
            {
                if (key)
                {
                    lua_checkstack(s, 2);
                    lua_pushlstring(s, STR_STR(key), STR_LEN(key));
                    _push_value_to_lua(s, val);
                    lua_settable(s, -3);
                }

                bsp_object_next(obj);
                val = bsp_object_curr(obj, (void **) &key);
            }

            break;
        case BSP_OBJECT_UNDETERMINED : 
        default : 
            break;
    }

    bsp_spin_unlock(&obj->lock);

    return;
}

void object_to_lua(lua_State *s, BSP_OBJECT *obj)
{
    if (!s || !obj)
    {
        return;
    }

    _push_object_to_lua(s, obj);

    return;
}

static BSP_VALUE * _lua_value_to_value(lua_State *s, int idx);
static BSP_OBJECT * _lua_table_to_object(lua_State *s, int idx);

static BSP_VALUE * _lua_value_to_value(lua_State *s, int idx)
{
    if (!s)
    {
        return NULL;
    }

    idx = lua_absindex(s, idx);
    BSP_VALUE *ret = bsp_new_value();
    if (!ret)
    {
        return NULL;
    }

    lua_Number v_number = 0;
    int v_boolean = 0;
    size_t str_len = 0;
    const char *str = NULL;
    void *v_ptr = NULL;
    BSP_STRING *v_str = NULL;
    BSP_OBJECT *v_obj = NULL;
    switch (lua_type(s, idx))
    {
        case LUA_TNIL : 
            V_SET_NULL(ret);
            break;
        case LUA_TNUMBER : 
            v_number = lua_tonumber(s, idx);
            if (v_number == (lua_Number)(int64_t) v_number)
            {
                // Integer
                V_SET_INT(ret, v_number);
            }
            else
            {
                // Double
                V_SET_DOUBLE(ret, v_number);
            }

            break;
        case LUA_TBOOLEAN : 
            v_boolean = lua_toboolean(s, idx);
            if (!v_boolean)
            {
                V_SET_BOOLEAN(ret, BSP_FALSE);
            }
            else
            {
                V_SET_BOOLEAN(ret, BSP_TRUE);
            }

            break;
        case LUA_TSTRING : 
            str = lua_tolstring(s, idx, &str_len);
            v_str = bsp_new_string(str, str_len);
            V_SET_STRING(ret, v_str);
            break;
        case LUA_TUSERDATA : 
        case LUA_TLIGHTUSERDATA : 
            v_ptr = lua_touserdata(s, idx);
            V_SET_POINTER(ret, v_ptr);
            break;
        case LUA_TTABLE : 
            v_obj = _lua_table_to_object(s, idx);
            V_SET_OBJECT(ret, v_obj);
            break;
        default : 
            V_SET_NULL(ret);
            break;
    }

    return ret;
}

static BSP_OBJECT * _lua_table_to_object(lua_State *s, int idx)
{
    if (!s || !lua_istable(s, idx))
    {
        return NULL;
    }

    idx = lua_absindex(s, idx);
    BSP_OBJECT *ret = bsp_new_object();
    BSP_VALUE *val = NULL;
    if (!ret)
    {
        return NULL;
    }

    // Check type, array or hash
    size_t total = luaL_len(s, idx);
    if (total == lua_table_size(s, idx))
    {
        // Array
        ret->type = BSP_OBJECT_ARRAY;
        size_t i;
        for (i = 1; i <= total; i ++)
        {
            lua_rawgeti(s, idx, i);
            val = _lua_value_to_value(s, -1);
            bsp_object_set_array(ret, i - 1, val);
            lua_pop(s, 1);
        }
    }
    else
    {
        // Hash
        ret->type = BSP_OBJECT_HASH;
        const char *key_str = NULL;
        size_t key_len = 0;
        BSP_STRING *key = NULL;
        lua_checkstack(s, 2);
        lua_pushnil(s);
        while (0 != lua_next(s, idx))
        {
            // Key
            key_str = lua_tolstring(s, -2, &key_len);
            key = bsp_new_string(key_str, key_len);

            // Value
            val = _lua_value_to_value(s, -1);

            bsp_object_set_hash(ret, key, val);
            lua_pop(s, 1);
        }
    }

    return ret;
}

BSP_OBJECT * lua_to_object(lua_State *s, int idx)
{
    if (!s)
    {
        return NULL;
    }

    BSP_OBJECT *ret = NULL;
    if (lua_istable(s, idx))
    {
        // Array or hash
        ret = _lua_table_to_object(s, idx);
    }
    else
    {
        ret = bsp_new_object();
        ret->type = BSP_OBJECT_SINGLE;
        bsp_object_set_single(ret, _lua_value_to_value(s, idx));
    }

    return ret;
}

// Call lua function
static int _cont()
{'fraction_non_alphanumeric': 0.0987455993437468, 'fraction_numerical': 0.005332057285435964, 'mean_word_length': 1.8000765623504642, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2535248', 'n_tokens_mistral': 10286, 'n_tokens_neox': 9711, 'n_words': 2241}