diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-08-23 15:38:54 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-08-23 15:38:54 +0100 |
commit | a8780ddc7c628bd2f5be3d7d795ac347bfd9a25c (patch) | |
tree | 8eec7eb13e7233d660649c532c6c16620e4373d2 /src/lua | |
parent | 343ec6cd4fb610ad3b273227902548ce77c005ac (diff) | |
download | rspamd-a8780ddc7c628bd2f5be3d7d795ac347bfd9a25c.tar.gz rspamd-a8780ddc7c628bd2f5be3d7d795ac347bfd9a25c.zip |
[Feature] Backport pack/unpack routines from Lua 5.3
Diffstat (limited to 'src/lua')
-rw-r--r-- | src/lua/lua_util.c | 790 |
1 files changed, 789 insertions, 1 deletions
diff --git a/src/lua/lua_util.c b/src/lua/lua_util.c index f0ae8ab1a..a26982b62 100644 --- a/src/lua/lua_util.c +++ b/src/lua/lua_util.c @@ -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) { |