|
|
@@ -324,7 +324,7 @@ LUA_FUNCTION_DEF (util, create_file); |
|
|
|
*/ |
|
|
|
LUA_FUNCTION_DEF (util, close_file); |
|
|
|
|
|
|
|
/** |
|
|
|
/*** |
|
|
|
* @function util.random_hex(size) |
|
|
|
* Returns random hex string of the specified size |
|
|
|
* |
|
|
@@ -333,6 +333,79 @@ LUA_FUNCTION_DEF (util, close_file); |
|
|
|
*/ |
|
|
|
LUA_FUNCTION_DEF (util, random_hex); |
|
|
|
|
|
|
|
/*** |
|
|
|
* @function util.pack(fmt, ...) |
|
|
|
* |
|
|
|
* Backport of Lua 5.3 `string.pack` function: |
|
|
|
* Returns a binary string containing the values v1, v2, etc. packed (that is, |
|
|
|
* serialized in binary form) according to the format string `fmt` |
|
|
|
* A format string is a sequence of conversion options. The conversion |
|
|
|
* options are as follows: |
|
|
|
* * <: sets little endian |
|
|
|
* * >: sets big endian |
|
|
|
* * =: sets native endian |
|
|
|
* * ![n]: sets maximum alignment to n (default is native alignment) |
|
|
|
* * b: a signed byte (char) |
|
|
|
* * B: an unsigned byte (char) |
|
|
|
* * h: a signed short (native size) |
|
|
|
* * H: an unsigned short (native size) |
|
|
|
* * l: a signed long (native size) |
|
|
|
* * L: an unsigned long (native size) |
|
|
|
* * j: a lua_Integer |
|
|
|
* * J: a lua_Unsigned |
|
|
|
* * T: a size_t (native size) |
|
|
|
* * i[n]: a signed int with n bytes (default is native size) |
|
|
|
* * I[n]: an unsigned int with n bytes (default is native size) |
|
|
|
* * f: a float (native size) |
|
|
|
* * d: a double (native size) |
|
|
|
* * n: a lua_Number |
|
|
|
* * cn: a fixed-sized string with n bytes |
|
|
|
* * z: a zero-terminated string |
|
|
|
* * s[n]: a string preceded by its length coded as an unsigned integer with |
|
|
|
* * n bytes (default is a size_t) |
|
|
|
* * x: one byte of padding |
|
|
|
* * Xop: an empty item that aligns according to option op (which is otherwise ignored) |
|
|
|
* * ' ': (empty space) ignored |
|
|
|
* |
|
|
|
* (A "[n]" means an optional integral numeral.) Except for padding, spaces, |
|
|
|
* and configurations (options "xX <=>!"), each option corresponds to an |
|
|
|
* argument (in string.pack) or a result (in string.unpack). |
|
|
|
* |
|
|
|
* For options "!n", "sn", "in", and "In", n can be any integer between 1 and |
|
|
|
* All integral options check overflows; string.pack checks whether the given |
|
|
|
* value fits in the given size; string.unpack checks whether the read value |
|
|
|
* fits in a Lua integer. |
|
|
|
* |
|
|
|
* Any format string starts as if prefixed by "!1=", that is, with maximum |
|
|
|
* alignment of 1 (no alignment) and native endianness. |
|
|
|
* |
|
|
|
* Alignment works as follows: For each option, the format gets extra padding |
|
|
|
* until the data starts at an offset that is a multiple of the minimum |
|
|
|
* between the option size and the maximum alignment; this minimum must be a |
|
|
|
* power of 2. Options "c" and "z" are not aligned; option "s" follows the |
|
|
|
* alignment of its starting integer. |
|
|
|
* |
|
|
|
* All padding is filled with zeros by string.pack (and ignored by unpack). |
|
|
|
*/ |
|
|
|
LUA_FUNCTION_DEF (util, pack); |
|
|
|
|
|
|
|
/*** |
|
|
|
* @function util.packsize(fmt) |
|
|
|
* |
|
|
|
* Returns size of the packed binary string returned for the same `fmt` argument |
|
|
|
* by @see util.pack |
|
|
|
*/ |
|
|
|
LUA_FUNCTION_DEF (util, packsize); |
|
|
|
|
|
|
|
/*** |
|
|
|
* @function util.unpack(fmt, s [, pos]) |
|
|
|
* Unpacks string `s` according to the format string `fmt` as described in |
|
|
|
* @see util.pack |
|
|
|
* |
|
|
|
* @returns {multiple} list of unpacked values according to `fmt` |
|
|
|
*/ |
|
|
|
LUA_FUNCTION_DEF (util, unpack); |
|
|
|
|
|
|
|
static const struct luaL_reg utillib_f[] = { |
|
|
|
LUA_INTERFACE_DEF (util, create_event_base), |
|
|
|
LUA_INTERFACE_DEF (util, load_rspamd_config), |
|
|
@@ -368,6 +441,9 @@ static const struct luaL_reg utillib_f[] = { |
|
|
|
LUA_INTERFACE_DEF (util, create_file), |
|
|
|
LUA_INTERFACE_DEF (util, close_file), |
|
|
|
LUA_INTERFACE_DEF (util, random_hex), |
|
|
|
LUA_INTERFACE_DEF (util, pack), |
|
|
|
LUA_INTERFACE_DEF (util, unpack), |
|
|
|
LUA_INTERFACE_DEF (util, packsize), |
|
|
|
{NULL, NULL} |
|
|
|
}; |
|
|
|
|
|
|
@@ -1500,6 +1576,718 @@ lua_util_random_hex (lua_State *L) |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
/* Backport from Lua 5.3 */ |
|
|
|
|
|
|
|
/****************************************************************************** |
|
|
|
* Copyright (C) 1994-2016 Lua.org, PUC-Rio. |
|
|
|
* |
|
|
|
* 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. |
|
|
|
******************************************************************************/ |
|
|
|
|
|
|
|
/* |
|
|
|
** {====================================================== |
|
|
|
** PACK/UNPACK |
|
|
|
** ======================================================= |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
/* value used for padding */ |
|
|
|
#if !defined(LUA_PACKPADBYTE) |
|
|
|
#define LUA_PACKPADBYTE 0x00 |
|
|
|
#endif |
|
|
|
|
|
|
|
/* maximum size for the binary representation of an integer */ |
|
|
|
#define MAXINTSIZE 16 |
|
|
|
|
|
|
|
/* number of bits in a character */ |
|
|
|
#define NB CHAR_BIT |
|
|
|
|
|
|
|
/* mask for one character (NB 1's) */ |
|
|
|
#define MC ((1 << NB) - 1) |
|
|
|
|
|
|
|
/* size of a lua_Integer */ |
|
|
|
#define SZINT ((int)sizeof(lua_Integer)) |
|
|
|
|
|
|
|
#define MAX_SIZET ((size_t)(~(size_t)0)) |
|
|
|
|
|
|
|
#define MAXSIZE \ |
|
|
|
(sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) |
|
|
|
|
|
|
|
|
|
|
|
/* dummy union to get native endianness */ |
|
|
|
static const union { |
|
|
|
int dummy; |
|
|
|
char little; /* true if machine is little endian */ |
|
|
|
} nativeendian = {1}; |
|
|
|
|
|
|
|
|
|
|
|
/* dummy structure to get native alignment requirements */ |
|
|
|
struct cD { |
|
|
|
char c; |
|
|
|
union { |
|
|
|
double d; |
|
|
|
void *p; |
|
|
|
lua_Integer i; |
|
|
|
lua_Number n; |
|
|
|
} u; |
|
|
|
}; |
|
|
|
|
|
|
|
#define MAXALIGN (offsetof(struct cD, u)) |
|
|
|
|
|
|
|
/* |
|
|
|
** Union for serializing floats |
|
|
|
*/ |
|
|
|
typedef union Ftypes { |
|
|
|
float f; |
|
|
|
double d; |
|
|
|
lua_Number n; |
|
|
|
char buff[5 * sizeof (lua_Number)]; /* enough for any float type */ |
|
|
|
} Ftypes; |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** information to pack/unpack stuff |
|
|
|
*/ |
|
|
|
typedef struct Header { |
|
|
|
lua_State *L; |
|
|
|
int islittle; |
|
|
|
int maxalign; |
|
|
|
} Header; |
|
|
|
|
|
|
|
/* |
|
|
|
** options for pack/unpack |
|
|
|
*/ |
|
|
|
typedef enum KOption { |
|
|
|
Kint, /* signed integers */ |
|
|
|
Kuint, /* unsigned integers */ |
|
|
|
Kfloat, /* floating-point numbers */ |
|
|
|
Kchar, /* fixed-length strings */ |
|
|
|
Kstring, /* strings with prefixed length */ |
|
|
|
Kzstr, /* zero-terminated strings */ |
|
|
|
Kpadding, /* padding */ |
|
|
|
Kpaddalign, /* padding for alignment */ |
|
|
|
Knop /* no-op (configuration or spaces) */ |
|
|
|
} KOption; |
|
|
|
|
|
|
|
#if LUA_VERSION_NUM < 503 |
|
|
|
#define lua_Unsigned size_t |
|
|
|
|
|
|
|
typedef struct luaL_Buffer_53 { |
|
|
|
luaL_Buffer b; /* make incorrect code crash! */ |
|
|
|
char *ptr; |
|
|
|
size_t nelems; |
|
|
|
size_t capacity; |
|
|
|
lua_State *L2; |
|
|
|
} luaL_Buffer_53; |
|
|
|
|
|
|
|
#define luaL_Buffer luaL_Buffer_53 |
|
|
|
#define COMPAT53_PREFIX lua |
|
|
|
#undef COMPAT53_API |
|
|
|
#if defined(__GNUC__) || defined(__clang__) |
|
|
|
# define COMPAT53_API __attribute__((__unused__)) static |
|
|
|
#else |
|
|
|
# define COMPAT53_API static |
|
|
|
#endif |
|
|
|
|
|
|
|
#define COMPAT53_CONCAT_HELPER(a, b) a##b |
|
|
|
#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) |
|
|
|
|
|
|
|
#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) |
|
|
|
COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); |
|
|
|
#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) |
|
|
|
COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); |
|
|
|
#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) |
|
|
|
COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); |
|
|
|
#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) |
|
|
|
COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); |
|
|
|
#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) |
|
|
|
COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); |
|
|
|
#undef luaL_buffinitsize |
|
|
|
#define luaL_buffinitsize(L, B, s) \ |
|
|
|
(luaL_buffinit(L, B), luaL_prepbuffsize(B, s)) |
|
|
|
|
|
|
|
#undef luaL_prepbuffer |
|
|
|
#define luaL_prepbuffer(B) \ |
|
|
|
luaL_prepbuffsize(B, LUAL_BUFFERSIZE) |
|
|
|
|
|
|
|
#undef luaL_addchar |
|
|
|
#define luaL_addchar(B, c) \ |
|
|
|
((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize(B, 1)), \ |
|
|
|
((B)->ptr[(B)->nelems++] = (c))) |
|
|
|
|
|
|
|
#undef luaL_addsize |
|
|
|
#define luaL_addsize(B, s) \ |
|
|
|
((B)->nelems += (s)) |
|
|
|
|
|
|
|
#undef luaL_addstring |
|
|
|
#define luaL_addstring(B, s) \ |
|
|
|
luaL_addlstring(B, s, strlen(s)) |
|
|
|
|
|
|
|
#undef luaL_pushresultsize |
|
|
|
#define luaL_pushresultsize(B, s) \ |
|
|
|
(luaL_addsize(B, s), luaL_pushresult(B)) |
|
|
|
|
|
|
|
COMPAT53_API void |
|
|
|
luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) |
|
|
|
{ |
|
|
|
/* make it crash if used via pointer to a 5.1-style luaL_Buffer */ |
|
|
|
B->b.p = NULL; |
|
|
|
B->b.L = NULL; |
|
|
|
B->b.lvl = 0; |
|
|
|
/* reuse the buffer from the 5.1-style luaL_Buffer though! */ |
|
|
|
B->ptr = B->b.buffer; |
|
|
|
B->capacity = LUAL_BUFFERSIZE; |
|
|
|
B->nelems = 0; |
|
|
|
B->L2 = L; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
COMPAT53_API char * |
|
|
|
luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) |
|
|
|
{ |
|
|
|
if (B->capacity - B->nelems < s) { /* needs to grow */ |
|
|
|
char *newptr = NULL; |
|
|
|
size_t newcap = B->capacity * 2; |
|
|
|
if (newcap - B->nelems < s) |
|
|
|
newcap = B->nelems + s; |
|
|
|
if (newcap < B->capacity) /* overflow */ |
|
|
|
luaL_error (B->L2, "buffer too large"); |
|
|
|
newptr = (char *) lua_newuserdata (B->L2, newcap); |
|
|
|
memcpy(newptr, B->ptr, B->nelems); |
|
|
|
if (B->ptr != B->b.buffer) |
|
|
|
lua_replace (B->L2, -2); /* remove old buffer */ |
|
|
|
B->ptr = newptr; |
|
|
|
B->capacity = newcap; |
|
|
|
} |
|
|
|
return B->ptr + B->nelems; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
COMPAT53_API void |
|
|
|
luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) |
|
|
|
{ |
|
|
|
memcpy(luaL_prepbuffsize (B, l), s, l); |
|
|
|
luaL_addsize(B, l); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
COMPAT53_API void |
|
|
|
luaL_addvalue (luaL_Buffer_53 *B) |
|
|
|
{ |
|
|
|
size_t len = 0; |
|
|
|
const char *s = lua_tolstring (B->L2, -1, &len); |
|
|
|
if (!s) |
|
|
|
luaL_error (B->L2, "cannot convert value to string"); |
|
|
|
if (B->ptr != B->b.buffer) |
|
|
|
lua_insert (B->L2, -2); /* userdata buffer must be at stack top */ |
|
|
|
luaL_addlstring (B, s, len); |
|
|
|
lua_remove (B->L2, B->ptr != B->b.buffer ? -2 : -1); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
COMPAT53_API void |
|
|
|
luaL_pushresult (luaL_Buffer_53 *B) |
|
|
|
{ |
|
|
|
lua_pushlstring (B->L2, B->ptr, B->nelems); |
|
|
|
if (B->ptr != B->b.buffer) |
|
|
|
lua_replace (B->L2, -2); /* remove userdata buffer */ |
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
/* |
|
|
|
** Read an integer numeral from string 'fmt' or return 'df' if |
|
|
|
** there is no numeral |
|
|
|
*/ |
|
|
|
static int |
|
|
|
digit (int c) |
|
|
|
{ |
|
|
|
return '0' <= c && c <= '9'; |
|
|
|
} |
|
|
|
|
|
|
|
static int |
|
|
|
getnum (const char **fmt, int df) |
|
|
|
{ |
|
|
|
if (!digit (**fmt)) /* no number? */ |
|
|
|
return df; /* return default value */ |
|
|
|
else { |
|
|
|
int a = 0; |
|
|
|
do { |
|
|
|
a = a * 10 + (*((*fmt)++) - '0'); |
|
|
|
} while (digit (**fmt) && a <= ((int) MAXSIZE - 9) / 10); |
|
|
|
return a; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Read an integer numeral and raises an error if it is larger |
|
|
|
** than the maximum size for integers. |
|
|
|
*/ |
|
|
|
static int |
|
|
|
getnumlimit (Header *h, const char **fmt, int df) |
|
|
|
{ |
|
|
|
int sz = getnum (fmt, df); |
|
|
|
if (sz > MAXINTSIZE || sz <= 0) |
|
|
|
luaL_error (h->L, "integral size (%d) out of limits [1,%d]", |
|
|
|
sz, MAXINTSIZE); |
|
|
|
return sz; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Initialize Header |
|
|
|
*/ |
|
|
|
static void |
|
|
|
initheader (lua_State *L, Header *h) |
|
|
|
{ |
|
|
|
h->L = L; |
|
|
|
h->islittle = nativeendian.little; |
|
|
|
h->maxalign = 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Read and classify next option. 'size' is filled with option's size. |
|
|
|
*/ |
|
|
|
static KOption |
|
|
|
getoption (Header *h, const char **fmt, int *size) |
|
|
|
{ |
|
|
|
int opt = *((*fmt)++); |
|
|
|
*size = 0; /* default */ |
|
|
|
switch (opt) { |
|
|
|
case 'b': |
|
|
|
*size = sizeof (char); |
|
|
|
return Kint; |
|
|
|
case 'B': |
|
|
|
*size = sizeof (char); |
|
|
|
return Kuint; |
|
|
|
case 'h': |
|
|
|
*size = sizeof (short); |
|
|
|
return Kint; |
|
|
|
case 'H': |
|
|
|
*size = sizeof (short); |
|
|
|
return Kuint; |
|
|
|
case 'l': |
|
|
|
*size = sizeof (long); |
|
|
|
return Kint; |
|
|
|
case 'L': |
|
|
|
*size = sizeof (long); |
|
|
|
return Kuint; |
|
|
|
case 'j': |
|
|
|
*size = sizeof (lua_Integer); |
|
|
|
return Kint; |
|
|
|
case 'J': |
|
|
|
*size = sizeof (lua_Integer); |
|
|
|
return Kuint; |
|
|
|
case 'T': |
|
|
|
*size = sizeof (size_t); |
|
|
|
return Kuint; |
|
|
|
case 'f': |
|
|
|
*size = sizeof (float); |
|
|
|
return Kfloat; |
|
|
|
case 'd': |
|
|
|
*size = sizeof (double); |
|
|
|
return Kfloat; |
|
|
|
case 'n': |
|
|
|
*size = sizeof (lua_Number); |
|
|
|
return Kfloat; |
|
|
|
case 'i': |
|
|
|
*size = getnumlimit (h, fmt, sizeof (int)); |
|
|
|
return Kint; |
|
|
|
case 'I': |
|
|
|
*size = getnumlimit (h, fmt, sizeof (int)); |
|
|
|
return Kuint; |
|
|
|
case 's': |
|
|
|
*size = getnumlimit (h, fmt, sizeof (size_t)); |
|
|
|
return Kstring; |
|
|
|
case 'c': |
|
|
|
*size = getnum (fmt, -1); |
|
|
|
if (*size == -1) |
|
|
|
luaL_error (h->L, "missing size for format option 'c'"); |
|
|
|
return Kchar; |
|
|
|
case 'z': |
|
|
|
return Kzstr; |
|
|
|
case 'x': |
|
|
|
*size = 1; |
|
|
|
return Kpadding; |
|
|
|
case 'X': |
|
|
|
return Kpaddalign; |
|
|
|
case ' ': |
|
|
|
break; |
|
|
|
case '<': |
|
|
|
h->islittle = 1; |
|
|
|
break; |
|
|
|
case '>': |
|
|
|
h->islittle = 0; |
|
|
|
break; |
|
|
|
case '=': |
|
|
|
h->islittle = nativeendian.little; |
|
|
|
break; |
|
|
|
case '!': |
|
|
|
h->maxalign = getnumlimit (h, fmt, MAXALIGN); |
|
|
|
break; |
|
|
|
default: |
|
|
|
luaL_error (h->L, "invalid format option '%c'", opt); |
|
|
|
} |
|
|
|
return Knop; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Read, classify, and fill other details about the next option. |
|
|
|
** 'psize' is filled with option's size, 'notoalign' with its |
|
|
|
** alignment requirements. |
|
|
|
** Local variable 'size' gets the size to be aligned. (Kpadal option |
|
|
|
** always gets its full alignment, other options are limited by |
|
|
|
** the maximum alignment ('maxalign'). Kchar option needs no alignment |
|
|
|
** despite its size. |
|
|
|
*/ |
|
|
|
static KOption |
|
|
|
getdetails (Header *h, size_t totalsize, |
|
|
|
const char **fmt, int *psize, int *ntoalign) |
|
|
|
{ |
|
|
|
KOption opt = getoption (h, fmt, psize); |
|
|
|
int align = *psize; /* usually, alignment follows size */ |
|
|
|
if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ |
|
|
|
if (**fmt == '\0' || getoption (h, fmt, &align) == Kchar || align == 0) |
|
|
|
luaL_argerror (h->L, 1, "invalid next option for option 'X'"); |
|
|
|
} |
|
|
|
if (align <= 1 || opt == Kchar) /* need no alignment? */ |
|
|
|
*ntoalign = 0; |
|
|
|
else { |
|
|
|
if (align > h->maxalign) /* enforce maximum alignment */ |
|
|
|
align = h->maxalign; |
|
|
|
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ |
|
|
|
luaL_argerror (h->L, 1, "format asks for alignment not power of 2"); |
|
|
|
*ntoalign = (align - (int) (totalsize & (align - 1))) & (align - 1); |
|
|
|
} |
|
|
|
return opt; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Pack integer 'n' with 'size' bytes and 'islittle' endianness. |
|
|
|
** The final 'if' handles the case when 'size' is larger than |
|
|
|
** the size of a Lua integer, correcting the extra sign-extension |
|
|
|
** bytes if necessary (by default they would be zeros). |
|
|
|
*/ |
|
|
|
static void |
|
|
|
packint (luaL_Buffer *b, lua_Unsigned n, |
|
|
|
int islittle, int size, int neg) |
|
|
|
{ |
|
|
|
char *buff = luaL_prepbuffsize (b, size); |
|
|
|
int i; |
|
|
|
buff[islittle ? 0 : size - 1] = (char) (n & MC); /* first byte */ |
|
|
|
for (i = 1; i < size; i++) { |
|
|
|
n >>= NB; |
|
|
|
buff[islittle ? i : size - 1 - i] = (char) (n & MC); |
|
|
|
} |
|
|
|
if (neg && size > SZINT) { /* negative number need sign extension? */ |
|
|
|
for (i = SZINT; i < size; i++) /* correct extra bytes */ |
|
|
|
buff[islittle ? i : size - 1 - i] = (char) MC; |
|
|
|
} |
|
|
|
luaL_addsize(b, size); /* add result to buffer */ |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if |
|
|
|
** given 'islittle' is different from native endianness. |
|
|
|
*/ |
|
|
|
static void |
|
|
|
copywithendian (volatile char *dest, volatile const char *src, |
|
|
|
int size, int islittle) |
|
|
|
{ |
|
|
|
if (islittle == nativeendian.little) { |
|
|
|
while (size-- != 0) |
|
|
|
*(dest++) = *(src++); |
|
|
|
} |
|
|
|
else { |
|
|
|
dest += size - 1; |
|
|
|
while (size-- != 0) |
|
|
|
*(dest--) = *(src++); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
|
lua_util_pack (lua_State *L) |
|
|
|
{ |
|
|
|
luaL_Buffer b; |
|
|
|
Header h; |
|
|
|
const char *fmt = luaL_checkstring(L, 1); /* format string */ |
|
|
|
int arg = 1; /* current argument to pack */ |
|
|
|
size_t totalsize = 0; /* accumulate total size of result */ |
|
|
|
initheader (L, &h); |
|
|
|
lua_pushnil (L); /* mark to separate arguments from string buffer */ |
|
|
|
luaL_buffinit (L, &b); |
|
|
|
|
|
|
|
while (*fmt != '\0') { |
|
|
|
int size, ntoalign; |
|
|
|
KOption opt = getdetails (&h, totalsize, &fmt, &size, &ntoalign); |
|
|
|
totalsize += ntoalign + size; |
|
|
|
while (ntoalign-- > 0) |
|
|
|
luaL_addchar(&b, LUA_PACKPADBYTE); /* fill alignment */ |
|
|
|
arg++; |
|
|
|
switch (opt) { |
|
|
|
case Kint: { /* signed integers */ |
|
|
|
lua_Integer n = luaL_checkinteger (L, arg); |
|
|
|
if (size < SZINT) { /* need overflow check? */ |
|
|
|
lua_Integer lim = (lua_Integer) 1 << ((size * NB) - 1); |
|
|
|
luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); |
|
|
|
} |
|
|
|
packint (&b, (lua_Unsigned) n, h.islittle, size, (n < 0)); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kuint: { /* unsigned integers */ |
|
|
|
lua_Integer n = luaL_checkinteger (L, arg); |
|
|
|
if (size < SZINT) /* need overflow check? */ |
|
|
|
luaL_argcheck(L, |
|
|
|
(lua_Unsigned) n < ((lua_Unsigned) 1 << (size * NB)), |
|
|
|
arg, |
|
|
|
"unsigned overflow"); |
|
|
|
packint (&b, (lua_Unsigned) n, h.islittle, size, 0); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kfloat: { /* floating-point options */ |
|
|
|
volatile Ftypes u; |
|
|
|
char *buff = luaL_prepbuffsize (&b, size); |
|
|
|
lua_Number n = luaL_checknumber (L, arg); /* get argument */ |
|
|
|
if (size == sizeof (u.f)) |
|
|
|
u.f = (float) n; /* copy it into 'u' */ |
|
|
|
else if (size == sizeof (u.d)) |
|
|
|
u.d = (double) n; |
|
|
|
else |
|
|
|
u.n = n; |
|
|
|
/* move 'u' to final result, correcting endianness if needed */ |
|
|
|
copywithendian (buff, u.buff, size, h.islittle); |
|
|
|
luaL_addsize(&b, size); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kchar: { /* fixed-size string */ |
|
|
|
size_t len; |
|
|
|
const char *s = luaL_checklstring (L, arg, &len); |
|
|
|
if ((size_t) size <= |
|
|
|
len) /* string larger than (or equal to) needed? */ |
|
|
|
luaL_addlstring (&b, |
|
|
|
s, |
|
|
|
size); /* truncate string to asked size */ |
|
|
|
else { /* string smaller than needed */ |
|
|
|
luaL_addlstring (&b, s, len); /* add it all */ |
|
|
|
while (len++ < (size_t) size) /* pad extra space */ |
|
|
|
luaL_addchar(&b, LUA_PACKPADBYTE); |
|
|
|
} |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kstring: { /* strings with length count */ |
|
|
|
size_t len; |
|
|
|
const char *s = luaL_checklstring (L, arg, &len); |
|
|
|
luaL_argcheck(L, size >= (int) sizeof (size_t) || |
|
|
|
len < ((size_t) 1 << (size * NB)), |
|
|
|
arg, "string length does not fit in given size"); |
|
|
|
packint (&b, |
|
|
|
(lua_Unsigned) len, |
|
|
|
h.islittle, |
|
|
|
size, |
|
|
|
0); /* pack length */ |
|
|
|
luaL_addlstring (&b, s, len); |
|
|
|
totalsize += len; |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kzstr: { /* zero-terminated string */ |
|
|
|
size_t len; |
|
|
|
const char *s = luaL_checklstring (L, arg, &len); |
|
|
|
luaL_argcheck(L, strlen (s) == len, arg, "string contains zeros"); |
|
|
|
luaL_addlstring (&b, s, len); |
|
|
|
luaL_addchar(&b, '\0'); /* add zero at the end */ |
|
|
|
totalsize += len + 1; |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kpadding: |
|
|
|
luaL_addchar(&b, LUA_PACKPADBYTE); /* FALLTHROUGH */ |
|
|
|
case Kpaddalign: |
|
|
|
case Knop: |
|
|
|
arg--; /* undo increment */ |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
luaL_pushresult (&b); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int |
|
|
|
lua_util_packsize (lua_State *L) |
|
|
|
{ |
|
|
|
Header h; |
|
|
|
const char *fmt = luaL_checkstring(L, 1); /* format string */ |
|
|
|
size_t totalsize = 0; /* accumulate total size of result */ |
|
|
|
initheader (L, &h); |
|
|
|
while (*fmt != '\0') { |
|
|
|
int size, ntoalign; |
|
|
|
KOption opt = getdetails (&h, totalsize, &fmt, &size, &ntoalign); |
|
|
|
size += ntoalign; /* total space used by option */ |
|
|
|
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, |
|
|
|
"format result too large"); |
|
|
|
totalsize += size; |
|
|
|
switch (opt) { |
|
|
|
case Kstring: /* strings with length count */ |
|
|
|
case Kzstr: /* zero-terminated string */ |
|
|
|
luaL_argerror (L, 1, "variable-length format"); |
|
|
|
/* call never return, but to avoid warnings: *//* FALLTHROUGH */ |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
lua_pushinteger (L, (lua_Integer) totalsize); |
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* |
|
|
|
** Unpack an integer with 'size' bytes and 'islittle' endianness. |
|
|
|
** If size is smaller than the size of a Lua integer and integer |
|
|
|
** is signed, must do sign extension (propagating the sign to the |
|
|
|
** higher bits); if size is larger than the size of a Lua integer, |
|
|
|
** it must check the unread bytes to see whether they do not cause an |
|
|
|
** overflow. |
|
|
|
*/ |
|
|
|
static lua_Integer |
|
|
|
unpackint (lua_State *L, const char *str, |
|
|
|
int islittle, int size, int issigned) |
|
|
|
{ |
|
|
|
lua_Unsigned res = 0; |
|
|
|
int i; |
|
|
|
int limit = (size <= SZINT) ? size : SZINT; |
|
|
|
for (i = limit - 1; i >= 0; i--) { |
|
|
|
res <<= NB; |
|
|
|
res |= (lua_Unsigned) ( |
|
|
|
unsigned char)str[islittle ? i : size - 1 - i]; |
|
|
|
} |
|
|
|
if (size < SZINT) { /* real size smaller than lua_Integer? */ |
|
|
|
if (issigned) { /* needs sign extension? */ |
|
|
|
lua_Unsigned mask = (lua_Unsigned) 1 << (size * NB - 1); |
|
|
|
res = ((res ^ mask) - mask); /* do sign extension */ |
|
|
|
} |
|
|
|
} |
|
|
|
else if (size > SZINT) { /* must check unread bytes */ |
|
|
|
int mask = (!issigned || (lua_Integer) res >= 0) ? 0 : MC; |
|
|
|
for (i = limit; i < size; i++) { |
|
|
|
if ((unsigned char) str[islittle ? i : size - 1 - i] != mask) |
|
|
|
luaL_error (L, |
|
|
|
"%d-byte integer does not fit into Lua Integer", |
|
|
|
size); |
|
|
|
} |
|
|
|
} |
|
|
|
return (lua_Integer) res; |
|
|
|
} |
|
|
|
|
|
|
|
static lua_Integer |
|
|
|
posrelat (lua_Integer pos, size_t len) |
|
|
|
{ |
|
|
|
if (pos >= 0) |
|
|
|
return pos; |
|
|
|
else if (0u - (size_t) pos > len) |
|
|
|
return 0; |
|
|
|
else |
|
|
|
return (lua_Integer) len + pos + 1; |
|
|
|
} |
|
|
|
|
|
|
|
static int |
|
|
|
lua_util_unpack (lua_State *L) |
|
|
|
{ |
|
|
|
Header h; |
|
|
|
const char *fmt = luaL_checkstring(L, 1); |
|
|
|
size_t ld; |
|
|
|
const char *data = luaL_checklstring (L, 2, &ld); |
|
|
|
size_t pos = (size_t) posrelat (luaL_optinteger (L, 3, 1), ld) - 1; |
|
|
|
int n = 0; /* number of results */ |
|
|
|
luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); |
|
|
|
initheader (L, &h); |
|
|
|
while (*fmt != '\0') { |
|
|
|
int size, ntoalign; |
|
|
|
KOption opt = getdetails (&h, pos, &fmt, &size, &ntoalign); |
|
|
|
if ((size_t) ntoalign + size > ~pos || pos + ntoalign + size > ld) |
|
|
|
luaL_argerror (L, 2, "data string too short"); |
|
|
|
pos += ntoalign; /* skip alignment */ |
|
|
|
/* stack space for item + next position */ |
|
|
|
luaL_checkstack (L, 2, "too many results"); |
|
|
|
n++; |
|
|
|
switch (opt) { |
|
|
|
case Kint: |
|
|
|
case Kuint: { |
|
|
|
lua_Integer res = unpackint (L, data + pos, h.islittle, size, |
|
|
|
(opt == Kint)); |
|
|
|
lua_pushinteger (L, res); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kfloat: { |
|
|
|
volatile Ftypes u; |
|
|
|
lua_Number num; |
|
|
|
copywithendian (u.buff, data + pos, size, h.islittle); |
|
|
|
if (size == sizeof (u.f)) |
|
|
|
num = (lua_Number) u.f; |
|
|
|
else if (size == sizeof (u.d)) |
|
|
|
num = (lua_Number) u.d; |
|
|
|
else |
|
|
|
num = u.n; |
|
|
|
lua_pushnumber (L, num); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kchar: { |
|
|
|
lua_pushlstring (L, data + pos, size); |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kstring: { |
|
|
|
size_t len = (size_t) unpackint (L, |
|
|
|
data + pos, |
|
|
|
h.islittle, |
|
|
|
size, |
|
|
|
0); |
|
|
|
luaL_argcheck(L, |
|
|
|
pos + len + size <= ld, |
|
|
|
2, |
|
|
|
"data string too short"); |
|
|
|
lua_pushlstring (L, data + pos + size, len); |
|
|
|
pos += len; /* skip string */ |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kzstr: { |
|
|
|
size_t len = (int) strlen (data + pos); |
|
|
|
lua_pushlstring (L, data + pos, len); |
|
|
|
pos += len + 1; /* skip string plus final '\0' */ |
|
|
|
break; |
|
|
|
} |
|
|
|
case Kpaddalign: |
|
|
|
case Kpadding: |
|
|
|
case Knop: |
|
|
|
n--; /* undo increment */ |
|
|
|
break; |
|
|
|
} |
|
|
|
pos += size; |
|
|
|
} |
|
|
|
lua_pushinteger (L, pos + 1); /* next position */ |
|
|
|
return n + 1; |
|
|
|
} |
|
|
|
|
|
|
|
static gint |
|
|
|
lua_load_util (lua_State * L) |
|
|
|
{ |