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

/* 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);
}