You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
11 KiB
C
411 lines
11 KiB
C
/* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2021 Stefano Trettel
|
|
*
|
|
* Software repository: MoonCCD, https://github.com/stetre/moonccd
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "internal.h"
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Misc utilities |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int noprintf(const char *fmt, ...)
|
|
{
|
|
(void)fmt;
|
|
return 0;
|
|
}
|
|
|
|
int notavailable(lua_State *L, ...)
|
|
{
|
|
return luaL_error(L, "function not available in this CL version");
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Malloc |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
/* We do not use malloc(), free() etc directly. Instead, we inherit the memory
|
|
* allocator from the main Lua state instead (see lua_getallocf in the Lua manual)
|
|
* and use that.
|
|
*
|
|
* By doing so, we can use an alternative malloc() implementation without recompiling
|
|
* this library (we have needs to recompile lua only, or execute it with LD_PRELOAD
|
|
* set to the path to the malloc library we want to use).
|
|
*/
|
|
static lua_Alloc Alloc = NULL;
|
|
static void *AllocUd = NULL;
|
|
|
|
static void malloc_init(lua_State *L)
|
|
{
|
|
if (Alloc)
|
|
unexpected(L);
|
|
Alloc = lua_getallocf(L, &AllocUd);
|
|
}
|
|
|
|
static void *Malloc_(size_t size)
|
|
{
|
|
return Alloc ? Alloc(AllocUd, NULL, 0, size) : NULL;
|
|
}
|
|
|
|
static void Free_(void *ptr)
|
|
{
|
|
if (Alloc)
|
|
Alloc(AllocUd, ptr, 0, 0);
|
|
}
|
|
|
|
void *Malloc(lua_State *L, size_t size)
|
|
{
|
|
void *ptr;
|
|
if (size == 0)
|
|
{
|
|
luaL_error(L, errstring(ERR_MALLOC_ZERO));
|
|
return NULL;
|
|
}
|
|
ptr = Malloc_(size);
|
|
if (ptr == NULL)
|
|
{
|
|
luaL_error(L, errstring(ERR_MEMORY));
|
|
return NULL;
|
|
}
|
|
memset(ptr, 0, size);
|
|
//DBG("Malloc %p\n", ptr);
|
|
return ptr;
|
|
}
|
|
|
|
void *MallocNoErr(lua_State *L, size_t size) /* do not raise errors (check the retval) */
|
|
{
|
|
void *ptr = Malloc_(size);
|
|
(void)L;
|
|
if (ptr == NULL)
|
|
return NULL;
|
|
memset(ptr, 0, size);
|
|
//DBG("MallocNoErr %p\n", ptr);
|
|
return ptr;
|
|
}
|
|
|
|
char *Strdup(lua_State *L, const char *s)
|
|
{
|
|
size_t len = strnlen(s, 256);
|
|
char *ptr = (char *)Malloc(L, len + 1);
|
|
if (len > 0)
|
|
memcpy(ptr, s, len);
|
|
ptr[len] = '\0';
|
|
return ptr;
|
|
}
|
|
|
|
void Free(lua_State *L, void *ptr)
|
|
{
|
|
(void)L;
|
|
//DBG("Free %p\n", ptr);
|
|
if (ptr)
|
|
Free_(ptr);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Time utilities |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
#if defined(LINUX)
|
|
|
|
#if 0
|
|
static double tstosec(const struct timespec *ts)
|
|
{
|
|
return ts->tv_sec*1.0+ts->tv_nsec*1.0e-9;
|
|
}
|
|
#endif
|
|
|
|
static void sectots(struct timespec *ts, double seconds)
|
|
{
|
|
ts->tv_sec = (time_t)seconds;
|
|
ts->tv_nsec = (long)((seconds - ((double)ts->tv_sec)) * 1.0e9);
|
|
}
|
|
|
|
double now(void)
|
|
{
|
|
#if _POSIX_C_SOURCE >= 199309L
|
|
struct timespec ts;
|
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0)
|
|
{
|
|
printf("clock_gettime error\n");
|
|
return -1;
|
|
}
|
|
return ts.tv_sec + ts.tv_nsec * 1.0e-9;
|
|
#else
|
|
struct timeval tv;
|
|
if (gettimeofday(&tv, NULL) != 0)
|
|
{
|
|
printf("gettimeofday error\n");
|
|
return -1;
|
|
}
|
|
return tv.tv_sec + tv.tv_usec * 1.0e-6;
|
|
#endif
|
|
}
|
|
|
|
void sleeep(double seconds)
|
|
{
|
|
#if _POSIX_C_SOURCE >= 199309L
|
|
struct timespec ts, ts1;
|
|
struct timespec *req, *rem, *tmp;
|
|
sectots(&ts, seconds);
|
|
req = &ts;
|
|
rem = &ts1;
|
|
while (1)
|
|
{
|
|
if (nanosleep(req, rem) == 0)
|
|
return;
|
|
tmp = req;
|
|
req = rem;
|
|
rem = tmp;
|
|
}
|
|
#else
|
|
usleep((useconds_t)(seconds * 1.0e6));
|
|
#endif
|
|
}
|
|
|
|
#define time_init(L) \
|
|
do \
|
|
{ \
|
|
(void)L; /* do nothing */ \
|
|
} while (0)
|
|
|
|
#elif defined(MINGW)
|
|
|
|
#include <windows.h>
|
|
|
|
static LARGE_INTEGER Frequency;
|
|
double now(void)
|
|
{
|
|
LARGE_INTEGER ts;
|
|
QueryPerformanceCounter(&ts);
|
|
return ((double)(ts.QuadPart)) / Frequency.QuadPart;
|
|
}
|
|
|
|
void sleeep(double seconds)
|
|
{
|
|
DWORD msec = (DWORD)seconds * 1000;
|
|
//if(msec < 0) return; DWORD seems to be unsigned
|
|
Sleep(msec);
|
|
}
|
|
|
|
static void time_init(lua_State *L)
|
|
{
|
|
(void)L;
|
|
QueryPerformanceFrequency(&Frequency);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Light userdata |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void *checklightuserdata(lua_State *L, int arg)
|
|
{
|
|
if (lua_type(L, arg) != LUA_TLIGHTUSERDATA)
|
|
{
|
|
luaL_argerror(L, arg, "expected lightuserdata");
|
|
return NULL;
|
|
}
|
|
return lua_touserdata(L, arg);
|
|
}
|
|
|
|
void *optlightuserdata(lua_State *L, int arg)
|
|
{
|
|
if (lua_isnoneornil(L, arg))
|
|
return NULL;
|
|
return checklightuserdata(L, arg);
|
|
}
|
|
|
|
void *checklightuserdataorzero(lua_State *L, int arg)
|
|
{
|
|
int val, isnum;
|
|
val = lua_tointegerx(L, arg, &isnum);
|
|
if (!isnum)
|
|
return checklightuserdata(L, arg);
|
|
if (val != 0)
|
|
luaL_argerror(L, arg, "expected lightuserdata or 0");
|
|
return NULL;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Deep copy of a table |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int copytable(lua_State *L)
|
|
/* Deep-copies the contents of the table at arg=-1 to the table at arg=-2
|
|
* Leaves them on the stack.
|
|
*/
|
|
{
|
|
int src = lua_gettop(L);
|
|
int dst = src - 1;
|
|
lua_pushnil(L);
|
|
while (lua_next(L, src))
|
|
{
|
|
lua_pushvalue(L, -2); // key
|
|
if (lua_type(L, -1) == LUA_TTABLE) /* nested */
|
|
{
|
|
lua_newtable(L); //dst
|
|
lua_pushvalue(L, -3); // value
|
|
copytable(L);
|
|
lua_pop(L, 1);
|
|
}
|
|
else
|
|
lua_pushvalue(L, -2); // value
|
|
lua_rawset(L, dst);
|
|
lua_pop(L, 1); // value
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Custom luaL_checkxxx() style functions |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
int checkboolean(lua_State *L, int arg)
|
|
{
|
|
if (!lua_isboolean(L, arg))
|
|
return (int)luaL_argerror(L, arg, "boolean expected");
|
|
return lua_toboolean(L, arg); // ? 1 : 0;
|
|
}
|
|
|
|
int testboolean(lua_State *L, int arg, int *err)
|
|
{
|
|
if (!lua_isboolean(L, arg))
|
|
{
|
|
*err = ERR_TYPE;
|
|
return 0;
|
|
}
|
|
*err = 0;
|
|
return lua_toboolean(L, arg); // ? 1 : 0;
|
|
}
|
|
|
|
int optboolean(lua_State *L, int arg, int d)
|
|
{
|
|
if (!lua_isboolean(L, arg))
|
|
return d;
|
|
return lua_toboolean(L, arg);
|
|
}
|
|
|
|
/* 1-based index to 0-based ------------------------------------------*/
|
|
|
|
int testindex(lua_State *L, int arg, int *err)
|
|
{
|
|
int val;
|
|
if (!lua_isinteger(L, arg))
|
|
{
|
|
*err = ERR_TYPE;
|
|
return 0;
|
|
}
|
|
val = lua_tointeger(L, arg);
|
|
if (val < 1)
|
|
{
|
|
*err = ERR_GENERIC;
|
|
return 0;
|
|
}
|
|
*err = 0;
|
|
return val - 1;
|
|
}
|
|
|
|
int checkindex(lua_State *L, int arg)
|
|
{
|
|
int val = luaL_checkinteger(L, arg);
|
|
if (val < 1)
|
|
return luaL_argerror(L, arg, "positive integer expected");
|
|
return val - 1;
|
|
}
|
|
|
|
int optindex(lua_State *L, int arg, int optval /* 0-based */)
|
|
{
|
|
int val = luaL_optinteger(L, arg, optval + 1);
|
|
if (val < 1)
|
|
return luaL_argerror(L, arg, "positive integer expected");
|
|
return val - 1;
|
|
}
|
|
|
|
void pushindex(lua_State *L, int val)
|
|
{
|
|
lua_pushinteger((L), (val) + 1);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Internal error codes |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
const char *errstring(int err)
|
|
{
|
|
switch (err)
|
|
{
|
|
case ERR_NOTPRESENT:
|
|
return "missing";
|
|
case ERR_SUCCESS:
|
|
return "success";
|
|
case ERR_GENERIC:
|
|
return "generic error";
|
|
case ERR_TABLE:
|
|
return "not a table";
|
|
case ERR_FUNCTION:
|
|
return "not a function";
|
|
case ERR_EMPTY:
|
|
return "empty list";
|
|
case ERR_TYPE:
|
|
return "invalid type";
|
|
case ERR_ELEMTYPE:
|
|
return "invalid element type";
|
|
case ERR_VALUE:
|
|
return "invalid value";
|
|
case ERR_ELEMVALUE:
|
|
return "invalid element value";
|
|
case ERR_MEMORY:
|
|
return "out of memory";
|
|
case ERR_MALLOC_ZERO:
|
|
return "zero bytes malloc";
|
|
case ERR_LENGTH:
|
|
return "invalid length";
|
|
case ERR_POOL:
|
|
return "elements are not from the same pool";
|
|
case ERR_BOUNDARIES:
|
|
return "invalid boundaries";
|
|
case ERR_RANGE:
|
|
return "out of range";
|
|
case ERR_FOPEN:
|
|
return "cannot open file";
|
|
case ERR_OPERATION:
|
|
return "operation failed";
|
|
case ERR_UNKNOWN:
|
|
return "unknown field name";
|
|
default:
|
|
return "???";
|
|
}
|
|
return NULL; /* unreachable */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------*
|
|
| Inits |
|
|
*------------------------------------------------------------------------------*/
|
|
|
|
void moonccd_utils_init(lua_State *L)
|
|
{
|
|
malloc_init(L);
|
|
time_init(L);
|
|
}
|