From a119497becdf2894fb7fa737f106464309dd7947 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 1 Jun 2021 05:16:32 +0200 Subject: String buffers, part 2d: basic string buffer methods. Sponsored by fmad.io. --- src/Makefile.dep | 29 +++--- src/lib_base.c | 19 +++- src/lib_buffer.c | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/lj_asm.c | 1 + src/lj_buf.h | 4 + src/lj_cconv.c | 3 + src/lj_crecord.c | 8 +- src/lj_ctype.h | 1 + src/lj_errmsg.h | 1 + src/lj_gc.c | 6 ++ src/lj_ir.h | 1 + src/lj_lib.c | 54 +++++++++++ src/lj_lib.h | 6 ++ src/lj_meta.c | 13 ++- src/lj_obj.h | 1 + src/lj_serialize.c | 5 +- src/lj_strfmt.c | 8 ++ 17 files changed, 409 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/Makefile.dep b/src/Makefile.dep index 0bf63391..a557d44f 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -2,17 +2,18 @@ lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \ lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ - lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h \ - lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cconv.h \ - lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h lj_strscan.h \ - lj_strfmt.h lj_lib.h lj_libdef.h + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h \ + lj_str.h lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \ + lj_cconv.h lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h \ + lj_strscan.h lj_strfmt.h lj_lib.h lj_libdef.h lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_strscan.h \ lj_strfmt.h lj_ctype.h lj_cdata.h lj_cconv.h lj_carith.h lj_ff.h \ lj_ffdef.h lj_lib.h lj_libdef.h lib_buffer.o: lib_buffer.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ - lj_def.h lj_arch.h lj_gc.h lj_buf.h lj_str.h lj_serialize.h lj_lib.h \ - lj_libdef.h + lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \ + lj_tab.h lj_udata.h lj_meta.h lj_ctype.h lj_cdata.h lj_cconv.h \ + lj_strfmt.h lj_serialize.h lj_lib.h lj_libdef.h lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \ lj_libdef.h @@ -51,10 +52,10 @@ lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \ lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h lj_strfmt.h lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ - lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \ - lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \ - lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_emit_*.h \ - lj_asm_*.h + lj_buf.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h \ + lj_jit.h lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h \ + lj_traceerr.h lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h \ + lj_emit_*.h lj_asm_*.h lj_assert.o: lj_assert.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \ lj_bcdef.h @@ -80,8 +81,8 @@ lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \ lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \ lj_traceerr.h lj_vm.h lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ - lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h \ - lj_ccallback.h + lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_ctype.h \ + lj_cdata.h lj_cconv.h lj_ccallback.h lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h @@ -137,8 +138,8 @@ lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ lj_strfmt.h lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \ lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \ - lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lex.h \ - lj_bcdump.h lj_lib.h + lj_dispatch.h lj_jit.h lj_ir.h lj_ctype.h lj_vm.h lj_strscan.h \ + lj_strfmt.h lj_lex.h lj_bcdump.h lj_lib.h lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \ lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h diff --git a/src/lib_base.c b/src/lib_base.c index cb2e244e..1c8816f0 100644 --- a/src/lib_base.c +++ b/src/lib_base.c @@ -19,6 +19,7 @@ #include "lj_gc.h" #include "lj_err.h" #include "lj_debug.h" +#include "lj_buf.h" #include "lj_str.h" #include "lj_tab.h" #include "lj_meta.h" @@ -406,10 +407,22 @@ LJLIB_CF(load) GCstr *name = lj_lib_optstr(L, 2); GCstr *mode = lj_lib_optstr(L, 3); int status; - if (L->base < L->top && (tvisstr(L->base) || tvisnumber(L->base))) { - GCstr *s = lj_lib_checkstr(L, 1); + if (L->base < L->top && + (tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) { + const char *s; + MSize len; + if (tvisbuf(L->base)) { + SBufExt *sbx = bufV(L->base); + s = sbx->r; + len = sbufxlen(sbx); + if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */ + } else { + GCstr *str = lj_lib_checkstr(L, 1); + s = strdata(str); + len = str->len; + } lua_settop(L, 4); /* Ensure env arg exists. */ - status = luaL_loadbufferx(L, strdata(s), s->len, strdata(name ? name : s), + status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s, mode ? strdata(mode) : NULL); } else { lj_lib_checkfunc(L, 1); diff --git a/src/lib_buffer.c b/src/lib_buffer.c index c9ef9510..78c4eeb9 100644 --- a/src/lib_buffer.c +++ b/src/lib_buffer.c @@ -14,14 +14,286 @@ #if LJ_HASBUFFER #include "lj_gc.h" +#include "lj_err.h" #include "lj_buf.h" +#include "lj_str.h" +#include "lj_tab.h" +#include "lj_udata.h" +#include "lj_meta.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#include "lj_cdata.h" +#include "lj_cconv.h" +#endif +#include "lj_strfmt.h" #include "lj_serialize.h" #include "lj_lib.h" /* ------------------------------------------------------------------------ */ +#define LJLIB_MODULE_buffer_method + +/* Check that the first argument is a string buffer. */ +static SBufExt *buffer_tobuf(lua_State *L) +{ + if (!(L->base < L->top && tvisbuf(L->base))) + lj_err_argtype(L, 1, "buffer"); + return bufV(L->base); +} + +/* Ditto, but for writers. */ +static LJ_AINLINE SBufExt *buffer_tobufw(lua_State *L) +{ + SBufExt *sbx = buffer_tobuf(L); + setsbufXL_(sbx, L); + return sbx; +} + +LJLIB_CF(buffer_method_free) +{ + SBufExt *sbx = buffer_tobuf(L); + lj_bufx_free(G(L), sbx); + lj_bufx_init(L, sbx); + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_reset) +{ + SBufExt *sbx = buffer_tobuf(L); + lj_bufx_reset(sbx); + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_skip) +{ + SBufExt *sbx = buffer_tobuf(L); + MSize n = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); + MSize len = sbufxlen(sbx); + if (n < len) { + sbx->r += n; + } else { + sbx->r = sbx->w = sbx->b; + } + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_set) +{ + SBufExt *sbx = buffer_tobuf(L); + const char *p; + MSize len; +#if LJ_HASFFI + if (tviscdata(L->base+1)) { + CTState *cts = ctype_cts(L); + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, + L->base+1, CCF_ARG(2)); + len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF); + } else +#endif + { + GCstr *str = lj_lib_checkstrx(L, 2); + p = strdata(str); + len = str->len; + } + lj_bufx_free(G(L), sbx); + lj_bufx_init_cow(L, sbx, p, len); + setgcref(sbx->cowref, gcV(L->base+1)); + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_put) +{ + SBufExt *sbx = buffer_tobufw(L); + ptrdiff_t arg, narg = L->top - L->base; + for (arg = 1; arg < narg; arg++) { + cTValue *o = &L->base[arg], *mo = NULL; + retry: + if (tvisstr(o)) { + lj_buf_putstr((SBuf *)sbx, strV(o)); + } else if (tvisint(o)) { + lj_strfmt_putint((SBuf *)sbx, intV(o)); + } else if (tvisnum(o)) { + lj_strfmt_putfnum((SBuf *)sbx, STRFMT_G14, numV(o)); + } else if (tvisbuf(o)) { + SBufExt *sbx2 = bufV(o); + lj_buf_putmem((SBuf *)sbx, sbx2->r, sbufxlen(sbx2)); + } else if (!mo && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { + /* Call __tostring metamethod inline. */ + copyTV(L, L->top++, mo); + copyTV(L, L->top++, o); + lua_call(L, 1, 1); + o = &L->base[arg]; /* The stack may have been reallocated. */ + copyTV(L, &L->base[arg], L->top-1); + L->top = L->base + narg; + goto retry; /* Retry with the result. */ + } else { + lj_err_argtype(L, arg+1, "string/number/__tostring"); + } + /* Probably not useful to inline other __tostring MMs, e.g. FFI numbers. */ + } + L->top = L->base+1; /* Chain buffer object. */ + lj_gc_check(L); + return 1; +} + +LJLIB_CF(buffer_method_putf) +{ + SBufExt *sbx = buffer_tobufw(L); + lj_strfmt_putarg(L, (SBuf *)sbx, 2, 2); + L->top = L->base+1; /* Chain buffer object. */ + lj_gc_check(L); + return 1; +} + +LJLIB_CF(buffer_method_get) +{ + SBufExt *sbx = buffer_tobuf(L); + ptrdiff_t arg, narg = L->top - L->base; + if (narg == 1) { + narg++; + setnilV(L->top++); /* get() is the same as get(nil). */ + } + for (arg = 1; arg < narg; arg++) { + TValue *o = &L->base[arg]; + MSize n = tvisnil(o) ? LJ_MAX_BUF : + (MSize) lj_lib_checkintrange(L, arg+1, 0, LJ_MAX_BUF); + MSize len = sbufxlen(sbx); + if (n > len) n = len; + setstrV(L, o, lj_str_new(L, sbx->r, n)); + sbx->r += n; + } + if (sbx->r == sbx->w) sbx->r = sbx->w = sbx->b; + lj_gc_check(L); + return narg-1; +} + +#if LJ_HASFFI +LJLIB_CF(buffer_method_putcdata) +{ + SBufExt *sbx = buffer_tobufw(L); + const char *p; + MSize len; + if (tviscdata(L->base+1)) { + CTState *cts = ctype_cts(L); + lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, + L->base+1, CCF_ARG(2)); + } else { + lj_err_argtype(L, 2, "cdata"); + } + len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF); + lj_buf_putmem((SBuf *)sbx, p, len); + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_reserve) +{ + SBufExt *sbx = buffer_tobufw(L); + MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); + GCcdata *cd; + lj_buf_more((SBuf *)sbx, len); + ctype_loadffi(L); + cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR); + *(void **)cdataptr(cd) = sbx->w; + setcdataV(L, L->top++, cd); + setintV(L->top++, sbufleft(sbx)); + return 2; +} + +LJLIB_CF(buffer_method_commit) +{ + SBufExt *sbx = buffer_tobuf(L); + MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); + if (len > sbufleft(sbx)) lj_err_arg(L, 2, LJ_ERR_NUMRNG); + sbx->w += len; + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_ref) +{ + SBufExt *sbx = buffer_tobuf(L); + GCcdata *cd; + ctype_loadffi(L); + cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR); + *(void **)cdataptr(cd) = sbx->r; + setcdataV(L, L->top++, cd); + setintV(L->top++, sbufxlen(sbx)); + return 2; +} +#endif + +LJLIB_CF(buffer_method_encode) +{ + SBufExt *sbx = buffer_tobufw(L); + cTValue *o = lj_lib_checkany(L, 2); + lj_serialize_put(sbx, o); + lj_gc_check(L); + L->top = L->base+1; /* Chain buffer object. */ + return 1; +} + +LJLIB_CF(buffer_method_decode) +{ + SBufExt *sbx = buffer_tobufw(L); + setnilV(L->top++); + lj_serialize_get(sbx, L->top-1); + lj_gc_check(L); + return 1; +} + +LJLIB_CF(buffer_method___gc) +{ + SBufExt *sbx = buffer_tobuf(L); + lj_bufx_free(G(L), sbx); + lj_bufx_init(L, sbx); + return 0; +} + +LJLIB_CF(buffer_method___tostring) +{ + SBufExt *sbx = buffer_tobuf(L); + setstrV(L, L->top-1, lj_str_new(L, sbx->r, sbufxlen(sbx))); + lj_gc_check(L); + return 1; +} + +LJLIB_CF(buffer_method___len) +{ + SBufExt *sbx = buffer_tobuf(L); + setintV(L->top-1, (int32_t)sbufxlen(sbx)); + return 1; +} + +LJLIB_PUSH("buffer") LJLIB_SET(__metatable) +LJLIB_PUSH(top-1) LJLIB_SET(__index) + +/* ------------------------------------------------------------------------ */ + #define LJLIB_MODULE_buffer +LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */ + +LJLIB_CF(buffer_new) +{ + MSize sz = L->base == L->top ? 0u : + (MSize)lj_lib_checkintrange(L, 1, 0, LJ_MAX_BUF); + GCtab *env = tabref(curr_func(L)->c.env); + GCudata *ud = lj_udata_new(L, sizeof(SBufExt), env); + SBufExt *sbx = (SBufExt *)uddata(ud); + ud->udtype = UDTYPE_BUFFER; + /* NOBARRIER: The GCudata is new (marked white). */ + setgcref(ud->metatable, obj2gco(env)); + setudataV(L, L->top++, ud); + lj_bufx_init(L, sbx); + if (sz > 0) lj_buf_need2((SBuf *)sbx, sz); + return 1; +} + LJLIB_CF(buffer_encode) { cTValue *o = lj_lib_checkany(L, 1); @@ -35,13 +307,14 @@ LJLIB_CF(buffer_encode) LJLIB_CF(buffer_decode) { - GCstr *str = lj_lib_checkstr(L, 1); + GCstr *str = lj_lib_checkstrx(L, 1); SBufExt sbx; lj_bufx_init_cow(L, &sbx, strdata(str), str->len); /* No need to set sbx.cowref here. */ setnilV(L->top++); lj_serialize_get(&sbx, L->top-1); lj_gc_check(L); + if (sbx.r != sbx.w) lj_err_caller(L, LJ_ERR_BUFFER_LEFTOV); return 1; } @@ -51,6 +324,9 @@ LJLIB_CF(buffer_decode) int luaopen_string_buffer(lua_State *L) { + LJ_LIB_REG(L, NULL, buffer_method); + lua_getfield(L, -1, "__tostring"); + lua_setfield(L, -2, "tostring"); LJ_LIB_REG(L, NULL, buffer); return 1; } diff --git a/src/lj_asm.c b/src/lj_asm.c index 286756c6..0e159e52 100644 --- a/src/lj_asm.c +++ b/src/lj_asm.c @@ -11,6 +11,7 @@ #if LJ_HASJIT #include "lj_gc.h" +#include "lj_buf.h" #include "lj_str.h" #include "lj_tab.h" #include "lj_frame.h" diff --git a/src/lj_buf.h b/src/lj_buf.h index 1fb70146..02f0ac61 100644 --- a/src/lj_buf.h +++ b/src/lj_buf.h @@ -58,6 +58,10 @@ typedef struct SBufExt { (lj_assertG_(G(sbufL(sb)), sbufisext(sb), "not an SBufExt"), (SBufExt *)(sb)) #define setsbufflag(sb, flag) (setmrefu((sb)->L, (flag))) +#define tvisbuf(o) \ + (LJ_HASBUFFER && tvisudata(o) && udataV(o)->udtype == UDTYPE_BUFFER) +#define bufV(o) check_exp(tvisbuf(o), ((SBufExt *)uddata(udataV(o)))) + /* Buffer management */ LJ_FUNC char *LJ_FASTCALL lj_buf_need2(SBuf *sb, MSize sz); LJ_FUNC char *LJ_FASTCALL lj_buf_more2(SBuf *sb, MSize sz); diff --git a/src/lj_cconv.c b/src/lj_cconv.c index f948002c..613f66e2 100644 --- a/src/lj_cconv.c +++ b/src/lj_cconv.c @@ -8,6 +8,7 @@ #if LJ_HASFFI #include "lj_err.h" +#include "lj_buf.h" #include "lj_tab.h" #include "lj_ctype.h" #include "lj_cdata.h" @@ -621,6 +622,8 @@ void lj_cconv_ct_tv(CTState *cts, CType *d, tmpptr = uddata(ud); if (ud->udtype == UDTYPE_IO_FILE) tmpptr = *(void **)tmpptr; + else if (ud->udtype == UDTYPE_BUFFER) + tmpptr = ((SBufExt *)tmpptr)->r; } else if (tvislightud(o)) { tmpptr = lightudV(cts->g, o); } else if (tvisfunc(o)) { diff --git a/src/lj_crecord.c b/src/lj_crecord.c index be23cd62..b0de5423 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -616,10 +616,12 @@ static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval) sp = lj_ir_kptr(J, NULL); } else if (tref_isudata(sp)) { GCudata *ud = udataV(sval); - if (ud->udtype == UDTYPE_IO_FILE) { + if (ud->udtype == UDTYPE_IO_FILE || ud->udtype == UDTYPE_BUFFER) { TRef tr = emitir(IRT(IR_FLOAD, IRT_U8), sp, IRFL_UDATA_UDTYPE); - emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, UDTYPE_IO_FILE)); - sp = emitir(IRT(IR_FLOAD, IRT_PTR), sp, IRFL_UDATA_FILE); + emitir(IRTGI(IR_EQ), tr, lj_ir_kint(J, ud->udtype)); + sp = emitir(IRT(IR_FLOAD, IRT_PTR), sp, + ud->udtype == UDTYPE_IO_FILE ? IRFL_UDATA_FILE : + IRFL_UDATA_BUF_R); } else { sp = emitir(IRT(IR_ADD, IRT_PTR), sp, lj_ir_kintp(J, sizeof(GCudata))); } diff --git a/src/lj_ctype.h b/src/lj_ctype.h index 9589ef2a..700250df 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h @@ -298,6 +298,7 @@ typedef struct CTState { _(P_VOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_VOID) \ _(P_CVOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CVOID) \ _(P_CCHAR, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CCHAR) \ + _(P_UINT8, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_UINT8) \ _(A_CCHAR, -1, CT_ARRAY, CTF_CONST|CTALIGN(0)|CTID_CCHAR) \ _(CTYPEID, 4, CT_ENUM, CTALIGN(2)|CTID_INT32) \ CTTYDEFP(_) \ diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h index a6f638ce..af4a03dd 100644 --- a/src/lj_errmsg.h +++ b/src/lj_errmsg.h @@ -67,6 +67,7 @@ ERRDEF(PROTMT, "cannot change a protected metatable") ERRDEF(UNPACK, "too many results to unpack") ERRDEF(RDRSTR, "reader function must return a string") ERRDEF(PRTOSTR, LUA_QL("tostring") " must return a string to " LUA_QL("print")) +ERRDEF(NUMRNG, "number out of range") ERRDEF(IDXRNG, "index out of range") ERRDEF(BASERNG, "base out of range") ERRDEF(LVLRNG, "level out of range") diff --git a/src/lj_gc.c b/src/lj_gc.c index cfbce037..1f382ea0 100644 --- a/src/lj_gc.c +++ b/src/lj_gc.c @@ -65,6 +65,12 @@ static void gc_mark(global_State *g, GCobj *o) gray2black(o); /* Userdata are never gray. */ if (mt) gc_markobj(g, mt); gc_markobj(g, tabref(gco2ud(o)->env)); + if (LJ_HASBUFFER && gco2ud(o)->udtype == UDTYPE_BUFFER) { + SBufExt *sbx = (SBufExt *)uddata(gco2ud(o)); + if (sbufiscow(sbx) && gcref(sbx->cowref) != NULL) { + gc_markobj(g, gcref(sbx->cowref)); + } + } } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) { GCupval *uv = gco2uv(o); gc_marktv(g, uvval(uv)); diff --git a/src/lj_ir.h b/src/lj_ir.h index aacef2b4..f953ff0e 100644 --- a/src/lj_ir.h +++ b/src/lj_ir.h @@ -204,6 +204,7 @@ IRFPMDEF(FPMENUM) _(UDATA_META, offsetof(GCudata, metatable)) \ _(UDATA_UDTYPE, offsetof(GCudata, udtype)) \ _(UDATA_FILE, sizeof(GCudata)) \ + _(UDATA_BUF_R, sizeof(GCudata) + offsetof(SBufExt, r)) \ _(CDATA_CTYPEID, offsetof(GCcdata, ctypeid)) \ _(CDATA_PTR, sizeof(GCcdata)) \ _(CDATA_INT, sizeof(GCcdata)) \ diff --git a/src/lj_lib.c b/src/lj_lib.c index a962ddc1..21e6a61d 100644 --- a/src/lj_lib.c +++ b/src/lj_lib.c @@ -16,6 +16,9 @@ #include "lj_func.h" #include "lj_bc.h" #include "lj_dispatch.h" +#if LJ_HASFFI +#include "lj_ctype.h" +#endif #include "lj_vm.h" #include "lj_strscan.h" #include "lj_strfmt.h" @@ -301,3 +304,54 @@ int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst) return def; } +/* -- Strict type checks -------------------------------------------------- */ + +/* The following type checks do not coerce between strings and numbers. +** And they handle plain int64_t/uint64_t FFI numbers, too. +*/ + +#if LJ_HASBUFFER +GCstr *lj_lib_checkstrx(lua_State *L, int narg) +{ + TValue *o = L->base + narg-1; + if (!(o < L->top && tvisstr(o))) lj_err_argt(L, narg, LUA_TSTRING); + return strV(o); +} + +int32_t lj_lib_checkintrange(lua_State *L, int narg, int32_t a, int32_t b) +{ + TValue *o = L->base + narg-1; + lj_assertL(b >= 0, "expected range must be non-negative"); + if (o < L->top) { + if (LJ_LIKELY(tvisint(o))) { + int32_t i = intV(o); + if (i >= a && i <= b) return i; + } else if (LJ_LIKELY(tvisnum(o))) { + /* For performance reasons, this doesn't check for integerness or + ** integer overflow. Overflow detection still works, since all FPUs + ** return either MININT or MAXINT, which is then out of range. + */ + int32_t i = (int32_t)numV(o); + if (i >= a && i <= b) return i; +#if LJ_HASFFI + } else if (tviscdata(o)) { + GCcdata *cd = cdataV(o); + if (cd->ctypeid == CTID_INT64) { + int64_t i = *(int64_t *)cdataptr(cd); + if (i >= (int64_t)a && i <= (int64_t)b) return (int32_t)i; + } else if (cd->ctypeid == CTID_UINT64) { + uint64_t i = *(uint64_t *)cdataptr(cd); + if ((a < 0 || i >= (uint64_t)a) && i <= (uint64_t)b) return (int32_t)i; + } +#endif + } else { + goto badtype; + } + lj_err_arg(L, narg, LJ_ERR_NUMRNG); + } +badtype: + lj_err_argt(L, narg, LUA_TNUMBER); + return 0; /* unreachable */ +} +#endif + diff --git a/src/lj_lib.h b/src/lj_lib.h index 718d8eb4..f59e9ea2 100644 --- a/src/lj_lib.h +++ b/src/lj_lib.h @@ -46,6 +46,12 @@ LJ_FUNC GCtab *lj_lib_checktab(lua_State *L, int narg); LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg); LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst); +#if LJ_HASBUFFER +LJ_FUNC GCstr *lj_lib_checkstrx(lua_State *L, int narg); +LJ_FUNC int32_t lj_lib_checkintrange(lua_State *L, int narg, + int32_t a, int32_t b); +#endif + /* Avoid including lj_frame.h. */ #if LJ_GC64 #define lj_lib_upvalue(L, n) \ diff --git a/src/lj_meta.c b/src/lj_meta.c index 07defa55..660dfec0 100644 --- a/src/lj_meta.c +++ b/src/lj_meta.c @@ -240,8 +240,8 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) int fromc = 0; if (left < 0) { left = -left; fromc = 1; } do { - if (!(tvisstr(top) || tvisnumber(top)) || - !(tvisstr(top-1) || tvisnumber(top-1))) { + if (!(tvisstr(top) || tvisnumber(top) || tvisbuf(top)) || + !(tvisstr(top-1) || tvisnumber(top-1) || tvisbuf(top-1))) { cTValue *mo = lj_meta_lookup(L, top-1, MM_concat); if (tvisnil(mo)) { mo = lj_meta_lookup(L, top, MM_concat); @@ -277,10 +277,12 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) ** next step: [...][CAT stack ............] */ TValue *e, *o = top; - uint64_t tlen = tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; + uint64_t tlen = tvisstr(o) ? strV(o)->len : + tvisbuf(o) ? sbufxlen(bufV(o)) : STRFMT_MAXBUF_NUM; SBuf *sb; do { - o--; tlen += tvisstr(o) ? strV(o)->len : STRFMT_MAXBUF_NUM; + o--; tlen += tvisstr(o) ? strV(o)->len : + tvisbuf(o) ? sbufxlen(bufV(o)) : STRFMT_MAXBUF_NUM; } while (--left > 0 && (tvisstr(o-1) || tvisnumber(o-1))); if (tlen >= LJ_MAX_STR) lj_err_msg(L, LJ_ERR_STROV); sb = lj_buf_tmp_(L); @@ -290,6 +292,9 @@ TValue *lj_meta_cat(lua_State *L, TValue *top, int left) GCstr *s = strV(o); MSize len = s->len; lj_buf_putmem(sb, strdata(s), len); + } else if (tvisbuf(o)) { + SBufExt *sbx = bufV(o); + lj_buf_putmem(sb, sbx->r, sbufxlen(sbx)); } else if (tvisint(o)) { lj_strfmt_putint(sb, intV(o)); } else { diff --git a/src/lj_obj.h b/src/lj_obj.h index 9b691e49..0dae5fec 100644 --- a/src/lj_obj.h +++ b/src/lj_obj.h @@ -332,6 +332,7 @@ enum { UDTYPE_USERDATA, /* Regular userdata. */ UDTYPE_IO_FILE, /* I/O library FILE. */ UDTYPE_FFI_CLIB, /* FFI C library namespace. */ + UDTYPE_BUFFER, /* String buffer. */ UDTYPE__MAX }; diff --git a/src/lj_serialize.c b/src/lj_serialize.c index 4e76502a..49a25a7c 100644 --- a/src/lj_serialize.c +++ b/src/lj_serialize.c @@ -346,10 +346,7 @@ SBufExt * LJ_FASTCALL lj_serialize_put(SBufExt *sbx, cTValue *o) SBufExt * LJ_FASTCALL lj_serialize_get(SBufExt *sbx, TValue *o) { - char *r = serialize_get(sbx->r, sbx, o); - if (r != sbx->w) - lj_err_caller(sbufL(sbx), LJ_ERR_BUFFER_LEFTOV); - sbx->r = r; + sbx->r = serialize_get(sbx->r, sbx, o); return sbx; } diff --git a/src/lj_strfmt.c b/src/lj_strfmt.c index a9541d41..5826b539 100644 --- a/src/lj_strfmt.c +++ b/src/lj_strfmt.c @@ -164,6 +164,10 @@ const char *lj_strfmt_wstrnum(lua_State *L, cTValue *o, MSize *lenp) if (tvisstr(o)) { *lenp = strV(o)->len; return strVdata(o); + } else if (tvisbuf(o)) { + SBufExt *sbx = bufV(o); + *lenp = sbufxlen(sbx); + return sbx->r; } else if (tvisint(o)) { sb = lj_strfmt_putint(lj_buf_tmp_(L), intV(o)); } else if (tvisnum(o)) { @@ -421,6 +425,10 @@ int lj_strfmt_putarg(lua_State *L, SBuf *sb, int arg, int retry) if (LJ_LIKELY(tvisstr(o))) { len = strV(o)->len; s = strVdata(o); + } else if (tvisbuf(o)) { + SBufExt *sbx = bufV(o); + len = sbufxlen(sbx); + s = sbx->r; } else { GCstr *str = lj_strfmt_obj(L, o); len = str->len; -- cgit v1.2.3-55-g6feb