#define lib_string_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_buf.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_meta.h"
#include "lj_state.h"
#include "lj_ff.h"
#include "lj_bcdump.h"
#include "lj_char.h"
#include "lj_strfmt.h"
#include "lj_lib.h"
#define LJLIB_MODULE_string
LJLIB_LUA(string_len)
LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
{
GCstr *s = lj_lib_checkstr(L, 1);
int32_t len = (int32_t)s->len;
int32_t start = lj_lib_optint(L, 2, 1);
int32_t stop = lj_lib_optint(L, 3, start);
int32_t n, i;
const unsigned char *p;
if (stop < 0) stop += len+1;
if (start < 0) start += len+1;
if (start <= 0) start = 1;
if (stop > len) stop = len;
if (start > stop) return FFH_RES(0);
start--;
n = stop - start;
if ((uint32_t)n > LUAI_MAXCSTACK)
lj_err_caller(L, LJ_ERR_STRSLC);
lj_state_checkstack(L, (MSize)n);
p = (const unsigned char *)strdata(s) + start;
for (i = 0; i < n; i++)
setintV(L->base + i-1-LJ_FR2, p[i]);
return FFH_RES(n);
}
LJLIB_ASM(string_char) LJLIB_REC(.)
{
int i, nargs = (int)(L->top - L->base);
char *buf = lj_buf_tmp(L, (MSize)nargs);
for (i = 1; i <= nargs; i++) {
int32_t k = lj_lib_checkint(L, i);
if (!checku8(k))
lj_err_arg(L, i, LJ_ERR_BADVAL);
buf[i-1] = (char)k;
}
setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs));
return FFH_RES(1);
}
LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
{
lj_lib_checkstr(L, 1);
lj_lib_checkint(L, 2);
setintV(L->base+2, lj_lib_optint(L, 3, -1));
return FFH_RETRY;
}
LJLIB_CF(string_rep) LJLIB_REC(.)
{
GCstr *s = lj_lib_checkstr(L, 1);
int32_t rep = lj_lib_checkint(L, 2);
GCstr *sep = lj_lib_optstr(L, 3);
SBuf *sb = lj_buf_tmp_(L);
if (sep && rep > 1) {
GCstr *s2 = lj_buf_cat2str(L, sep, s);
lj_buf_reset(sb);
lj_buf_putstr(sb, s);
s = s2;
rep--;
}
sb = lj_buf_putstr_rep(sb, s, rep);
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}
LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)
{
lj_lib_checkstr(L, 1);
return FFH_RETRY;
}
LJLIB_ASM_(string_lower) LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower)
LJLIB_ASM_(string_upper) LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper)
static int writer_buf(lua_State *L, const void *p, size_t size, void *sb)
{
lj_buf_putmem((SBuf *)sb, p, (MSize)size);
UNUSED(L);
return 0;
}
LJLIB_CF(string_dump)
{
GCfunc *fn = lj_lib_checkfunc(L, 1);
int strip = L->base+1 < L->top && tvistruecond(L->base+1);
SBuf *sb = lj_buf_tmp_(L);
L->top = L->base+1;
if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip))
lj_err_caller(L, LJ_ERR_STRDUMP);
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}
#define uchar(c) ((unsigned char)(c))
#define CAP_UNFINISHED (-1)
#define CAP_POSITION (-2)
typedef struct MatchState {
const char *src_init;
const char *src_end;
const char *p_end;
lua_State *L;
int level;
int depth;
struct {
const char *init;
ptrdiff_t len;
} capture[LUA_MAXCAPTURES];
} MatchState;
#define L_ESC '%'
static int check_capture(MatchState *ms, int l)
{
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
return l;
}
static int capture_to_close(MatchState *ms)
{
int level = ms->level;
for (level--; level>=0; level--)
if (ms->capture[level].len == CAP_UNFINISHED) return level;
lj_err_caller(ms->L, LJ_ERR_STRPATC);
return 0;
}
static const char *classend(MatchState *ms, const char *p)
{
switch (*p++) {
case L_ESC:
if (p == ms->p_end)
lj_err_caller(ms->L, LJ_ERR_STRPATE);
return p+1;
case '[':
if (*p == '^') p++;
do {
if (p == ms->p_end)
lj_err_caller(ms->L, LJ_ERR_STRPATM);
if (*(p++) == L_ESC && p < ms->p_end)
p++;
} while (*p != ']');
return p+1;
default:
return p;
}
}
static const unsigned char match_class_map[32] = {
0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
};
static int match_class(int c, int cl)
{
if ((cl & 0xc0) == 0x40) {
int t = match_class_map[(cl&0x1f)];
if (t) {
t = lj_char_isa(c, t);
return (cl & 0x20) ? t : !t;
}
if (cl == 'z') return c == 0;
if (cl == 'Z') return c != 0;
}
return (cl == c);
}
static int matchbracketclass(int c, const char *p, const char *ec)
{
int sig = 1;
if (*(p+1) == '^') {
sig = 0;
p++;
}
while (++p < ec) {
if (*p == L_ESC) {
p++;
if (match_class(c, uchar(*p)))
return sig;
}
else if ((*(p+1) == '-') && (p+2 < ec)) {
p+=2;
if (uchar(*(p-2)) <= c && c <= uchar(*p))
return sig;
}
else if (uchar(*p) == c) return sig;
}
return !sig;
}
static int singlematch(MatchState *ms, const char *s, const char *p,
const char *ep) {
if (s >= ms->src_end)
return 0;
else {
int c = uchar(*s);
switch (*p) {
case '.': return 1;
case L_ESC: return match_class(c, uchar(*(p+1)));
case '[': return matchbracketclass(c, p, ep-1);
default: return (uchar(*p) == c);
}
}
}
static const char *match(MatchState *ms, const char *s, const char *p);
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
{
if (p >= ms->p_end - 1)
lj_err_caller(ms->L, LJ_ERR_STRPATU);
if (*s != *p) {
return NULL;
} else {
int b = *p;
int e = *(p+1);
int cont = 1;
while (++s < ms->src_end) {
if (*s == e) {
if (--cont == 0) return s+1;
} else if (*s == b) {
cont++;
}
}
}
return NULL;
}
static const char *max_expand(MatchState *ms, const char *s,
const char *p, const char *ep)
{
ptrdiff_t i = 0;
while (singlematch(ms, s + i, p, ep))
i++;
while (i>=0) {
const char *res = match(ms, (s+i), ep+1);
if (res) return res;
i--;
}
return NULL;
}
static const char *min_expand(MatchState *ms, const char *s,
const char *p, const char *ep)
{
for (;;) {
const char *res = match(ms, s, ep+1);
if (res != NULL)
return res;
else if (singlematch(ms, s, p, ep))
s++;
else
return NULL;
}
}
static const char *start_capture(MatchState *ms, const char *s,
const char *p, int what)
{
const char *res;
int level = ms->level;
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
ms->capture[level].init = s;
ms->capture[level].len = what;
ms->level = level+1;
if ((res=match(ms, s, p)) == NULL)
ms->level--;
return res;
}
static const char *end_capture(MatchState *ms, const char *s,
const char *p)
{
int l = capture_to_close(ms);
const char *res;
ms->capture[l].len = s - ms->capture[l].init;
if ((res = match(ms, s, p)) == NULL)
ms->capture[l].len = CAP_UNFINISHED;
return res;
}
static const char *match_capture(MatchState *ms, const char *s, int l)
{
size_t len;
l = check_capture(ms, l);
len = (size_t)ms->capture[l].len;
if ((size_t)(ms->src_end-s) >= len &&
memcmp(ms->capture[l].init, s, len) == 0)
return s+len;
else
return NULL;
}
static const char *match(MatchState *ms, const char *s, const char *p)
{
if (++ms->depth > LJ_MAX_XLEVEL)
lj_err_caller(ms->L, LJ_ERR_STRPATX);
init:
if (p != ms->p_end) {
switch (*p) {
case '(':
if (*(p+1) == ')')
s = start_capture(ms, s, p+2, CAP_POSITION);
else
s = start_capture(ms, s, p+1, CAP_UNFINISHED);
break;
case ')':
s = end_capture(ms, s, p+1);
break;
case L_ESC:
switch (*(p+1)) {
case 'b':
s = matchbalance(ms, s, p+2);
if (s == NULL) break;
p+=4;
goto init;
case 'f': {
const char *ep; char previous;
p += 2;
if (*p != '[')
lj_err_caller(ms->L, LJ_ERR_STRPATB);
ep = classend(ms, p);
previous = (s == ms->src_init) ? '\0' : *(s-1);
if (matchbracketclass(uchar(previous), p, ep-1) ||
!matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
p=ep;
goto init;
}
default:
if (lj_char_isdigit(uchar(*(p+1)))) {
s = match_capture(ms, s, uchar(*(p+1)));
if (s == NULL) break;
p+=2;
goto init;
}
goto dflt;
}
break;
case '$':
if ((p+1) != ms->p_end) goto dflt;
if (s != ms->src_end) s = NULL;
break;
default: dflt: {
const char *ep = classend(ms, p);
if (!singlematch(ms, s, p, ep)) {
if (*ep == '*' || *ep == '?' || *ep == '-') {
p = ep + 1; goto init;
}
else
s = NULL;
}
else {
switch (*ep) {
case '?': {
const char *res;
if ((res=match(ms, s+1, ep+1)) != NULL)
s = res;
else {
p=ep+1;
goto init;
}
break;
}
case '+':
s++;
case '*':
s = max_expand(ms, s, p, ep);
break;
case '-':
s = min_expand(ms, s, p, ep);
break;
default:
s++; p = ep; goto init;
}
}
break;
}
}
}
ms->depth--;
return s;
}
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
{
if (i >= ms->level) {
if (i == 0)
lua_pushlstring(ms->L, s, (size_t)(e - s));
else
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
} else {
ptrdiff_t l = ms->capture[i].len;
if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
if (l == CAP_POSITION)
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
else
lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
}
}
static int push_captures(MatchState *ms, const char *s, const char *e)
{
int i;
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
luaL_checkstack(ms->L, nlevels, "too many captures");
for (i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e);
return nlevels;
}
static int str_find_aux(lua_State *L, int find)
{
GCstr *s = lj_lib_checkstr(L, 1);
GCstr *p = lj_lib_checkstr(L, 2);
int32_t start = lj_lib_optint(L, 3, 1);
MSize st;
if (start < 0) start += (int32_t)s->len; else start--;
if (start < 0) start = 0;
st = (MSize)start;
if (st > s->len) {
#if LJ_52
setnilV(L->top-1);
return 1;
#else
st = s->len;
#endif
}
if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) ||
!lj_str_haspattern(p))) {
const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len);
if (q) {
setintV(L->top-2, (int32_t)(q-strdata(s)) + 1);
setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len);
return 2;
}
} else {
MatchState ms;
const char *pstr = strdata(p);
const char *sstr = strdata(s) + st;
int anchor = 0;
if (*pstr == '^') { pstr++; anchor = 1; }
ms.L = L;
ms.src_init = strdata(s);
ms.src_end = strdata(s) + s->len;
ms.p_end = strdata(p) + p->len;
do {
const char *q;
ms.level = ms.depth = 0;
q = match(&ms, sstr, pstr);
if (q) {
if (find) {
setintV(L->top++, (int32_t)(sstr-(strdata(s)-1)));
setintV(L->top++, (int32_t)(q-strdata(s)));
return push_captures(&ms, NULL, NULL) + 2;
} else {
return push_captures(&ms, sstr, q);
}
}
} while (sstr++ < ms.src_end && !anchor);
}
setnilV(L->top-1);
return 1;
}
LJLIB_CF(string_find) LJLIB_REC(.)
{
return str_find_aux(L, 1);
}
LJLIB_CF(string_match)
{
return str_find_aux(L, 0);
}
LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
{
GCstr *pat = strV(lj_lib_upvalue(L, 2));
const char *p = strdata(pat);
GCstr *str = strV(lj_lib_upvalue(L, 1));
const char *s = strdata(str);
TValue *tvpos = lj_lib_upvalue(L, 3);
const char *src = s + tvpos->u32.lo;
MatchState ms;
ms.L = L;
ms.src_init = s;
ms.src_end = s + str->len;
ms.p_end = p + pat->len;
for (; src <= ms.src_end; src++) {
const char *e;
ms.level = ms.depth = 0;
if ((e = match(&ms, src, p)) != NULL) {
int32_t pos = (int32_t)(e - s);
if (e == src) pos++;
tvpos->u32.lo = (uint32_t)pos;
return push_captures(&ms, src, e);
}
}
return 0;
}
LJLIB_CF(string_gmatch)
{
lj_lib_checkstr(L, 1);
lj_lib_checkstr(L, 2);
L->top = L->base+3;
(L->top-1)->u64 = 0;
lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
return 1;
}
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
{
size_t l, i;
const char *news = lua_tolstring(ms->L, 3, &l);
for (i = 0; i < l; i++) {
if (news[i] != L_ESC) {
luaL_addchar(b, news[i]);
} else {
i++;
if (!lj_char_isdigit(uchar(news[i]))) {
luaL_addchar(b, news[i]);
} else if (news[i] == '0') {
luaL_addlstring(b, s, (size_t)(e - s));
} else {
push_onecapture(ms, news[i] - '1', s, e);
luaL_addvalue(b);
}
}
}
}
static void add_value(MatchState *ms, luaL_Buffer *b,
const char *s, const char *e)
{
lua_State *L = ms->L;
switch (lua_type(L, 3)) {
case LUA_TNUMBER:
case LUA_TSTRING: {
add_s(ms, b, s, e);
return;
}
case LUA_TFUNCTION: {
int n;
lua_pushvalue(L, 3);
n = push_captures(ms, s, e);
lua_call(L, n, 1);
break;
}
case LUA_TTABLE: {
push_onecapture(ms, 0, s, e);
lua_gettable(L, 3);
break;
}
}
if (!lua_toboolean(L, -1)) {
lua_pop(L, 1);
lua_pushlstring(L, s, (size_t)(e - s));
} else if (!lua_isstring(L, -1)) {
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
}
luaL_addvalue(b);
}
LJLIB_CF(string_gsub)
{
size_t srcl, lp;
const char *src = luaL_checklstring(L, 1, &srcl);
const char *p = luaL_checklstring(L, 2, &lp);
int tr = lua_type(L, 3);
int max_s = luaL_optint(L, 4, (int)(srcl+1));
int anchor = (*p == '^');
int n = 0;
MatchState ms;
luaL_Buffer b;
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
tr == LUA_TFUNCTION || tr == LUA_TTABLE))
lj_err_arg(L, 3, LJ_ERR_NOSFT);
luaL_buffinit(L, &b);
if (anchor) {
p++; lp--;
}
ms.L = L;
ms.src_init = src;
ms.src_end = src+srcl;
ms.p_end = p + lp;
while (n < max_s) {
const char *e;
ms.level = ms.depth = 0;
e = match(&ms, src, p);
if (e) {
n++;
add_value(&ms, &b, src, e);
}
if (e && e>src)
src = e;
else if (src < ms.src_end)
luaL_addchar(&b, *src++);
else
break;
if (anchor)
break;
}
luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
luaL_pushresult(&b);
lua_pushinteger(L, n);
return 2;
}
static GCstr *string_fmt_tostring(lua_State *L, int arg, int retry)
{
TValue *o = L->base+arg-1;
cTValue *mo;
lua_assert(o < L->top);
if (LJ_LIKELY(tvisstr(o)))
return strV(o);
if (retry != 2 && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
copyTV(L, L->top++, mo);
copyTV(L, L->top++, o);
lua_call(L, 1, 1);
copyTV(L, L->base+arg-1, --L->top);
return NULL;
}
return lj_strfmt_obj(L, o);
}
LJLIB_CF(string_format) LJLIB_REC(.)
{
int arg, top = (int)(L->top - L->base);
GCstr *fmt;
SBuf *sb;
FormatState fs;
SFormat sf;
int retry = 0;
again:
arg = 1;
sb = lj_buf_tmp_(L);
fmt = lj_lib_checkstr(L, arg);
lj_strfmt_init(&fs, strdata(fmt), fmt->len);
while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) {
if (sf == STRFMT_LIT) {
lj_buf_putmem(sb, fs.str, fs.len);
} else if (sf == STRFMT_ERR) {
lj_err_callerv(L, LJ_ERR_STRFMT, strdata(lj_str_new(L, fs.str, fs.len)));
} else {
if (++arg > top)
luaL_argerror(L, arg, lj_obj_typename[0]);
switch (STRFMT_TYPE(sf)) {
case STRFMT_INT:
if (tvisint(L->base+arg-1)) {
int32_t k = intV(L->base+arg-1);
if (sf == STRFMT_INT)
lj_strfmt_putint(sb, k);
else
lj_strfmt_putfxint(sb, sf, k);
} else {
lj_strfmt_putfnum_int(sb, sf, lj_lib_checknum(L, arg));
}
break;
case STRFMT_UINT:
if (tvisint(L->base+arg-1))
lj_strfmt_putfxint(sb, sf, intV(L->base+arg-1));
else
lj_strfmt_putfnum_uint(sb, sf, lj_lib_checknum(L, arg));
break;
case STRFMT_NUM:
lj_strfmt_putfnum(sb, sf, lj_lib_checknum(L, arg));
break;
case STRFMT_STR: {
GCstr *str = string_fmt_tostring(L, arg, retry);
if (str == NULL)
retry = 1;
else if ((sf & STRFMT_T_QUOTED))
lj_strfmt_putquoted(sb, str);
else
lj_strfmt_putfstr(sb, sf, str);
break;
}
case STRFMT_CHAR:
lj_strfmt_putfchar(sb, sf, lj_lib_checkint(L, arg));
break;
case STRFMT_PTR:
lj_strfmt_putptr(sb, lj_obj_ptr(L->base+arg-1));
break;
default:
lua_assert(0);
break;
}
}
}
if (retry++ == 1) goto again;
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}
#define MAXSIZE \
(sizeof(size_t) < sizeof(int) ? SIZE_MAX : (size_t)(INT_MAX))
#if !defined(LUAL_PACKPADBYTE)
#define LUAL_PACKPADBYTE 0x00
#endif
#define MAXINTSIZE 16
#define NB CHAR_BIT
#define MC ((1 << NB) - 1)
#define SZINT ((int)sizeof(lua_Integer))
struct cD {
char c;
union { double d; void *p; lua_Integer i; lua_Number n; } u;
};
#define MAXALIGN (offsetof(struct cD, u))
typedef union Ftypes {
float f;
double d;
lua_Number n;
char buff[5 * sizeof(lua_Number)];
} Ftypes;
typedef struct Header {
lua_State *L;
int islittle;
int maxalign;
} Header;
typedef enum KOption {
Kint,
Kuint,
Kfloat,
Kchar,
Kstring,
Kzstr,
Kpadding,
Kpaddalign,
Knop
} KOption;
static int digit (int c) { return '0' <= c && c <= '9'; }
static int getnum (const char **fmt, int df) {
if (!digit(**fmt))
return df;
else {
int a = 0;
do {
a = a*10 + (*((*fmt)++) - '0');
} while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
return a;
}
}
static int getnumlimit (Header *h, const char **fmt, int df) {
int sz = getnum(fmt, df);
if (sz > MAXINTSIZE || sz <= 0)
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
sz, MAXINTSIZE);
return sz;
}
static void initheader (lua_State *L, Header *h) {
h->L = L;
h->islittle = LJ_LE;
h->maxalign = 1;
}
static KOption getoption (Header *h, const char **fmt, int *size) {
int opt = *((*fmt)++);
*size = 0;
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 = LJ_LE; break;
case '!': h->maxalign = getnumlimit(h, fmt, MAXALIGN); break;
default: luaL_error(h->L, "invalid format option '%c'", opt);
}
return Knop;
}
static KOption getdetails (Header *h, size_t totalsize,
const char **fmt, int *psize, int *ntoalign) {
KOption opt = getoption(h, fmt, psize);
int align = *psize;
if (opt == Kpaddalign) {
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)
*ntoalign = 0;
else {
if (align > h->maxalign)
align = h->maxalign;
if ((align & (align - 1)) != 0)
luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
}
return opt;
}
static void packint (SBuf *sb, lua_Unsigned n,
int islittle, int size, int neg) {
char *buff = lj_buf_more(sb, size);
int i;
buff[islittle ? 0 : size - 1] = (char)(n & MC);
for (i = 1; i < size; i++) {
n >>= NB;
buff[islittle ? i : size - 1 - i] = (char)(n & MC);
}
if (neg && size > SZINT) {
for (i = SZINT; i < size; i++)
buff[islittle ? i : size - 1 - i] = (char)MC;
}
setsbufP(sb, buff + size);
}
static void copywithendian (volatile char *dest, volatile const char *src,
int size, int islittle) {
if (islittle == LJ_LE) {
while (size-- != 0)
*(dest++) = *(src++);
}
else {
dest += size - 1;
while (size-- != 0)
*(dest--) = *(src++);
}
}
LJLIB_CF(string_pack)
{
SBuf *sb;
Header h;
const char *fmt = luaL_checkstring(L, 1);
int arg = 1;
size_t totalsize = 0;
initheader(L, &h);
sb = lj_buf_tmp_(L);
while (*fmt != '\0') {
int size, ntoalign;
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
totalsize += ntoalign + size;
while (ntoalign-- > 0)
lj_buf_putchar(sb, LUAL_PACKPADBYTE);
arg++;
switch (opt) {
case Kint: {
lua_Integer n = luaL_checkinteger(L, arg);
if (size < SZINT) {
lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
}
packint(sb, (lua_Unsigned)n, h.islittle, size, (n < 0));
break;
}
case Kuint: {
lua_Integer n = luaL_checkinteger(L, arg);
if (size < SZINT)
luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
arg, "unsigned overflow");
packint(sb, (lua_Unsigned)n, h.islittle, size, 0);
break;
}
case Kfloat: {
volatile Ftypes u;
char *buff = lj_buf_more(sb, size);
lua_Number n = luaL_checknumber(L, arg);
if (size == sizeof(u.f)) u.f = (float)n;
else if (size == sizeof(u.d)) u.d = (double)n;
else u.n = n;
copywithendian(buff, u.buff, size, h.islittle);
setsbufP(sb, buff + size);
break;
}
case Kchar: {
size_t len;
GCstr *s = lj_lib_checkstr(L, arg);
luaL_argcheck(L, s->len <= (size_t)size, arg,
"string longer than given size");
lj_buf_putstr(sb, s);
len = s->len;
while (len++ < (size_t)size)
lj_buf_putchar(sb, LUAL_PACKPADBYTE);
break;
}
case Kstring: {
GCstr *s = lj_lib_checkstr(L, arg);
luaL_argcheck(L, size >= (int)sizeof(size_t) ||
s->len < ((size_t)1 << (size * NB)),
arg, "string length does not fit in given size");
packint(sb, (lua_Unsigned)s->len, h.islittle, size, 0);
lj_buf_putstr(sb, s);
totalsize += s->len;
break;
}
case Kzstr: {
GCstr *s = lj_lib_checkstr(L, arg);
luaL_argcheck(L, strlen(strdata(s)) == s->len, arg, "string contains zeros");
lj_buf_putstr(sb, s);
lj_buf_putchar(sb, '\0');
totalsize += s->len + 1;
break;
}
case Kpadding: lj_buf_putchar(sb, LUAL_PACKPADBYTE);
case Kpaddalign: case Knop:
arg--;
break;
}
}
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}
LJLIB_CF(string_packsize)
{
Header h;
const char *fmt = luaL_checkstring(L, 1);
size_t totalsize = 0;
initheader(L, &h);
while (*fmt != '\0') {
int size, ntoalign;
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
size += ntoalign;
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
"format result too large");
totalsize += size;
switch (opt) {
case Kstring:
case Kzstr:
luaL_argerror(L, 1, "variable-length format");
default: break;
}
}
lua_pushinteger(L, (lua_Integer)totalsize);
return 1;
}
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) {
if (issigned) {
lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1);
res = ((res ^ mask) - mask);
}
}
else if (size > SZINT) {
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;
}
LJLIB_CF(string_unpack)
{
Header h;
const char *fmt = luaL_checkstring(L, 1);
size_t ld;
const char *data = luaL_checklstring(L, 2, &ld);
int n = 0;
size_t pos = lj_lib_optint(L, 3, 1);
if (pos < 0) pos += ld; else pos--;
if (pos < 0) pos = 0;
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;
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;
break;
}
case Kzstr: {
size_t len = (int)strlen(data + pos);
lua_pushlstring(L, data + pos, len);
pos += len + 1;
break;
}
case Kpaddalign: case Kpadding: case Knop:
n--;
break;
}
pos += size;
}
lua_pushinteger(L, pos + 1);
return n + 1;
}
#include "lj_libdef.h"
LUALIB_API int luaopen_string(lua_State *L)
{
GCtab *mt;
global_State *g;
LJ_LIB_REG(L, LUA_STRLIBNAME, string);
mt = lj_tab_new(L, 0, 1);
g = G(L);
setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
mt->nomm = (uint8_t)(~(1u<<MM_index));
return 1;
}