From 11a2421acaf2b39d19ee99933102c35e28fd13f8 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 13 Dec 2017 14:55:33 +1100 Subject: Use Uint8Array to back strings --- src/defs.js | 7 ++++--- src/lauxlib.js | 6 +++--- src/lbaselib.js | 2 +- src/ldblib.js | 4 ++-- src/ldump.js | 23 +++++++++-------------- src/llex.js | 5 ++++- src/llimits.js | 4 ++++ src/lobject.js | 9 +++++---- src/loslib.js | 10 +++++++--- src/lstring.js | 5 +++-- src/lundump.js | 2 +- src/lutf8lib.js | 2 +- src/lvm.js | 29 ++++++++++++++++++++--------- src/lzio.js | 29 ++++++++++++++++++----------- tests/test-suite/ltests.js | 23 ++++++----------------- 15 files changed, 88 insertions(+), 72 deletions(-) diff --git a/src/defs.js b/src/defs.js index 27498c6..381114b 100644 --- a/src/defs.js +++ b/src/defs.js @@ -131,10 +131,10 @@ class lua_Debug { } -const string_of = Array.of; +const string_of = Uint8Array.of.bind(Uint8Array); const is_luastring = function(s) { - return Array.isArray(s); + return s instanceof Uint8Array; }; /* test two lua strings for equality */ @@ -143,7 +143,7 @@ const luastring_cmp = function(a, b) { }; const to_jsstring = function(value, from, to) { - assert(is_luastring(value), "jsstring expects an array of bytes"); + assert(is_luastring(value), "jsstring expects a Uint8Array"); if (to === void 0) { to = value.length; @@ -240,6 +240,7 @@ const to_luastring = function(str, cache) { outU8Array[outIdx++] = 0x80 | (u & 63); } } + outU8Array = Uint8Array.from(outU8Array); if (cache) to_luastring_cache[str] = outU8Array; diff --git a/src/lauxlib.js b/src/lauxlib.js index b9b7078..6acf57f 100644 --- a/src/lauxlib.js +++ b/src/lauxlib.js @@ -381,14 +381,14 @@ const luaL_prepbuffer = function(B) { }; const luaL_addlstring = function(B, s, l) { - B.b = B.b.concat(s.slice(0, l)); + B.b = B.b.concat(Array.from(s.subarray(0, l))); }; const luaL_addstring = luaL_addlstring; const luaL_pushresult = function(B) { let L = B.L; - lua.lua_pushstring(L, B.b); + lua.lua_pushstring(L, Uint8Array.from(B.b)); }; const luaL_addchar = function(B, c) { @@ -558,7 +558,7 @@ const luaL_gsub = function(L, s, p, r) { s = s.slice(wild + p.length); /* continue after 'p' */ } b.push(...s); /* push last suffix */ - lua.lua_pushstring(L, b); + lua.lua_pushstring(L, Uint8Array.from(b)); return lua.lua_tostring(L, -1); }; diff --git a/src/lbaselib.js b/src/lbaselib.js index 30e77fb..103a9ca 100644 --- a/src/lbaselib.js +++ b/src/lbaselib.js @@ -8,7 +8,7 @@ let lua_writeline; if (typeof process === "undefined") { let buff = []; lua_writestring = function(s) { - buff = buff.concat(s); + buff = buff.concat(Array.from(s)); }; lua_writeline = function() { console.log(new TextDecoder("utf-8").decode(Uint8Array.from(buff))); diff --git a/src/ldblib.js b/src/ldblib.js index 746d3bf..df7d2ff 100644 --- a/src/ldblib.js +++ b/src/ldblib.js @@ -306,7 +306,7 @@ const unmakemask = function(mask, smask) { if (mask & lua.LUA_MASKCALL) smask[i++] = "c".charCodeAt(0); if (mask & lua.LUA_MASKRET) smask[i++] = "r".charCodeAt(0); if (mask & lua.LUA_MASKLINE) smask[i++] = "l".charCodeAt(0); - return smask; + return smask.subarray(0, i); }; const db_sethook = function(L) { @@ -344,7 +344,7 @@ const db_sethook = function(L) { const db_gethook = function(L) { let thread = getthread(L); let L1 = thread.thread; - let buff = []; + let buff = new Uint8Array(5); let mask = lua.lua_gethookmask(L1); let hook = lua.lua_gethook(L1); if (hook === null) /* no hook? */ diff --git a/src/ldump.js b/src/ldump.js index 964d6ff..7e62da3 100644 --- a/src/ldump.js +++ b/src/ldump.js @@ -34,31 +34,26 @@ const DumpByte = function(y, D) { }; const DumpInt = function(x, D) { - let dv = new DataView(new ArrayBuffer(4)); + let ab = new ArrayBuffer(4); + let dv = new DataView(ab); dv.setInt32(0, x, true); - let t = []; - for (let i = 0; i < 4; i++) - t.push(dv.getUint8(i, true)); - + let t = new Uint8Array(ab); DumpBlock(t, 4, D); }; const DumpInteger = function(x, D) { - let dv = new DataView(new ArrayBuffer(4)); + let ab = new ArrayBuffer(4); + let dv = new DataView(ab); dv.setInt32(0, x, true); - let t = []; - for (let i = 0; i < 4; i++) - t.push(dv.getUint8(i, true)); + let t = new Uint8Array(ab); DumpBlock(t, 4, D); }; const DumpNumber = function(x, D) { - let dv = new DataView(new ArrayBuffer(8)); + let ab = new ArrayBuffer(8); + let dv = new DataView(ab); dv.setFloat64(0, x, true); - let t = []; - for (let i = 0; i < 8; i++) - t.push(dv.getUint8(i, true)); - + let t = new Uint8Array(ab); DumpBlock(t, 8, D); }; diff --git a/src/llex.js b/src/llex.js index 779557a..9e4bed4 100644 --- a/src/llex.js +++ b/src/llex.js @@ -112,6 +112,8 @@ const save = function(ls, c) { if (b.n + 1 > b.buffer.length) { if (b.buffer.length >= llimits.MAX_INT/2) lexerror(ls, defs.to_luastring("lexical element too long", true), 0); + let newsize = b.buffer.length*2; + lzio.luaZ_resizebuffer(ls.L, b, newsize); } b.buffer[b.n++] = c < 0 ? 255 + c + 1 : c; }; @@ -192,6 +194,7 @@ const luaX_setinput = function(L, ls, z, source, firstchar) { ls.lastline = 1; ls.source = source; ls.envn = lstring.luaS_bless(L, LUA_ENV); + lzio.luaZ_resizebuffer(L, ls.buff, llimits.LUA_MINBUFFER); /* initialize buffer */ }; const check_next1 = function(ls, c) { @@ -369,7 +372,7 @@ const readutf8desc = function(ls) { }; const utf8esc = function(ls) { - let buff = new Array(lobject.UTF8BUFFSZ); + let buff = new Uint8Array(lobject.UTF8BUFFSZ); let n = lobject.luaO_utf8esc(buff, readutf8desc(ls)); for (; n > 0; n--) /* add 'buff' to string */ save(ls, buff[lobject.UTF8BUFFSZ - n]); diff --git a/src/llimits.js b/src/llimits.js index 21a09a0..4a76bbe 100644 --- a/src/llimits.js +++ b/src/llimits.js @@ -3,6 +3,10 @@ const LUAI_MAXCCALLS = 200; module.exports.LUAI_MAXCCALLS = LUAI_MAXCCALLS; +/* minimum size for string buffer */ +const LUA_MINBUFFER = 32; +module.exports.LUA_MINBUFFER = LUA_MINBUFFER; + const luai_nummod = function(L, a, b) { let m = a % b; if ((m*b) < 0) diff --git a/src/lobject.js b/src/lobject.js index 707cdf8..18adb27 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -312,19 +312,20 @@ const luaO_chunkid = function(source, bufflen) { out = source.slice(1); else { /* add '...' before rest of name */ bufflen -= RETS.length; - out = RETS.concat(source.slice(1 + l - bufflen)); + out = Uint8Array.from(Array.from(RETS).concat(Array.from(source.subarray(1 + l - bufflen)))); } } else { /* string; format as [string "source"] */ let nli = source.indexOf(char['\n']); /* find first new line (if any) */ - out = PRE; /* add prefix */ + out = Array.from(PRE); /* add prefix */ bufflen -= PRE.length + RETS.length + POS.length + 1; /* save space for prefix+suffix+'\0' */ if (l < bufflen && nli === -1) { /* small one-line source? */ - out = out.concat(source, POS); /* keep it */ + out = out.concat(Array.from(source), Array.from(POS)); /* keep it */ } else { if (nli !== -1) l = nli; /* stop at first newline */ if (l > bufflen) l = bufflen; - out = out.concat(source.slice(0, l), RETS, POS); + out = out.concat(Array.from(source.subarray(0, l)), Array.from(RETS), Array.from(POS)); } + out = Uint8Array.from(out); } return out; }; diff --git a/src/loslib.js b/src/loslib.js index 19dfd02..f27a7ed 100644 --- a/src/loslib.js +++ b/src/loslib.js @@ -73,7 +73,7 @@ const checkoption = function(L, conv, i, buff) { if (option[o] === '|'.charCodeAt(0)) /* next block? */ oplen++; /* will check options with next length (+1) */ else if (array_cmp(conv, i, option, o, oplen)) { /* match? */ - buff.push(...conv.slice(i, i+oplen)); /* copy valid option to buffer */ + buff.set(conv.slice(i, i+oplen)); /* copy valid option to buffer */ return i + oplen; /* return next item */ } } @@ -102,6 +102,8 @@ const os_date = function(L) { lua.lua_createtable(L, 0, 9); /* 9 = number of fields */ setallfields(L, stm, utc); } else { + let cc = new Uint8Array(4); + cc[0] = "%".charCodeAt(0); let b = new lauxlib.luaL_Buffer(); lauxlib.luaL_buffinit(L, b); while (i < s.length) { @@ -109,8 +111,10 @@ const os_date = function(L) { lauxlib.luaL_addchar(b, s[i++]); } else { i++; /* skip '%' */ - let cc = ["%".charCodeAt(0)]; - i = checkoption(L, s, i, cc); /* copy specifier to 'cc' */ + i = checkoption(L, s, i, cc.subarray(1)); /* copy specifier to 'cc' */ + let len = cc.indexOf(0); + if (len !== -1) + cc = cc.subarray(0, len); let buff = strftime(lua.to_jsstring(cc), stm); lauxlib.luaL_addstring(b, lua.to_luastring(buff)); } diff --git a/src/lstring.js b/src/lstring.js index c85e5b2..c8da56a 100644 --- a/src/lstring.js +++ b/src/lstring.js @@ -31,7 +31,7 @@ const luaS_eqlngstr = function(a, b) { make sure this doesn't conflict with any of the anti-collision strategies in ltable */ const luaS_hash = function(str) { assert(defs.is_luastring(str)); - return str.map(e => `${e}|`).join(''); + return '|'+str.join('|'); }; const luaS_hashlongstr = function(ts) { @@ -44,12 +44,13 @@ const luaS_hashlongstr = function(ts) { /* variant that takes ownership of array */ const luaS_bless = function(L, str) { + assert(str instanceof Uint8Array); return new TString(L, str); }; /* makes a copy */ const luaS_new = function(L, str) { - return luaS_bless(L, str.slice(0)); + return luaS_bless(L, Uint8Array.from(str)); }; /* takes a js string */ diff --git a/src/lundump.js b/src/lundump.js index ff87903..7461813 100644 --- a/src/lundump.js +++ b/src/lundump.js @@ -46,7 +46,7 @@ class BytecodeParser { let u8 = new Uint8Array(size); if(lzio.luaZ_read(this.Z, u8, 0, size) !== 0) this.error("truncated"); - return Array.from(u8); + return u8; } readByte() { diff --git a/src/lutf8lib.js b/src/lutf8lib.js index 3baeeef..f581a9e 100644 --- a/src/lutf8lib.js +++ b/src/lutf8lib.js @@ -216,7 +216,7 @@ const funcs = { }; /* pattern to match a single UTF-8 character */ -const UTF8PATT = [ 91, 0, 45, 127, 194, 45, 244, 93, 91, 128, 45, 191, 93, 42 ]; +const UTF8PATT = Uint8Array.of(91, 0, 45, 127, 194, 45, 244, 93, 91, 128, 45, 191, 93, 42); const luaopen_utf8 = function(L) { lauxlib.luaL_newlib(L, funcs); diff --git a/src/lvm.js b/src/lvm.js index 3cdccb9..9d97c17 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -937,6 +937,18 @@ const isemptystr = function(o) { return o.ttisstring() && o.vslen() === 0; }; +/* copy strings in stack from top - n up to top - 1 to buffer */ +const copy2buff = function(L, top, n, buff) { + let tl = 0; /* size already copied */ + do { + let tv = L.stack[top-n]; + let l = tv.vslen(); /* length of string being copied */ + let s = tv.svalue(); + buff.set(s, tl); + tl += l; + } while (--n > 0); +}; + /* ** Main operation for concatenation: concat 'total' values in the stack, ** from 'L->top - total' up to 'L->top - 1'. @@ -955,16 +967,15 @@ const luaV_concat = function(L, total) { lobject.setobjs2s(L, top - 2, top - 1); } else { /* at least two non-empty string values; get as many as possible */ - let toconcat = new Array(total); - toconcat[total-1] = L.stack[top-1].svalue(); - for (n = 1; n < total; n++) { - if (!tostring(L, top - n - 1)) { - toconcat = toconcat.slice(total-n); - break; - } - toconcat[total-n-1] = L.stack[top - n - 1].svalue(); + let tl = L.stack[top-1].vslen(); + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, top - n - 1); n++) { + let l = L.stack[top - n - 1].vslen(); + tl += l; } - let ts = lstring.luaS_bless(L, Array.prototype.concat.apply([], toconcat)); + let buff = new Uint8Array(tl); + copy2buff(L, top, n, buff); + let ts = lstring.luaS_bless(L, buff); lobject.setsvalue2s(L, top - n, ts); } total -= n - 1; /* got 'n' strings to create 1 new */ diff --git a/src/lzio.js b/src/lzio.js index 3d877aa..efd7909 100644 --- a/src/lzio.js +++ b/src/lzio.js @@ -11,7 +11,7 @@ class MBuffer { } const luaZ_buffer = function(buff) { - return buff.buffer; + return buff.buffer.subarray(0, buff.n); }; const luaZ_buffremove = function(buff, i) { @@ -20,7 +20,13 @@ const luaZ_buffremove = function(buff, i) { const luaZ_resetbuffer = function(buff) { buff.n = 0; - buff.buffer = []; +}; + +const luaZ_resizebuffer = function(L, buff, size) { + let newbuff = new Uint8Array(size); + if (buff.buffer) + newbuff.set(buff.buffer); + buff.buffer = newbuff; }; class ZIO { @@ -51,7 +57,7 @@ const luaZ_fill = function(z) { z.off = 0; size = buff.byteLength - buff.byteOffset; } else { - assert(typeof buff !== "string", "Should only load binary of array of bytes"); + assert(buff instanceof Uint8Array, "Should only load binary of array of bytes"); z.buffer = buff; z.off = 0; size = buff.length; @@ -87,11 +93,12 @@ const luaZ_read = function(z, b, b_offset, n) { return 0; }; -module.exports.EOZ = EOZ; -module.exports.luaZ_buffer = luaZ_buffer; -module.exports.luaZ_buffremove = luaZ_buffremove; -module.exports.luaZ_fill = luaZ_fill; -module.exports.luaZ_read = luaZ_read; -module.exports.luaZ_resetbuffer = luaZ_resetbuffer; -module.exports.MBuffer = MBuffer; -module.exports.ZIO = ZIO; +module.exports.EOZ = EOZ; +module.exports.luaZ_buffer = luaZ_buffer; +module.exports.luaZ_buffremove = luaZ_buffremove; +module.exports.luaZ_fill = luaZ_fill; +module.exports.luaZ_read = luaZ_read; +module.exports.luaZ_resetbuffer = luaZ_resetbuffer; +module.exports.luaZ_resizebuffer = luaZ_resizebuffer; +module.exports.MBuffer = MBuffer; +module.exports.ZIO = ZIO; diff --git a/tests/test-suite/ltests.js b/tests/test-suite/ltests.js index 8d89102..08bb603 100644 --- a/tests/test-suite/ltests.js +++ b/tests/test-suite/ltests.js @@ -60,8 +60,7 @@ const getstring = function(L, buff, pc) { while (pc.script[pc.offset] !== 0 && pc.offset < pc.script.length && delimits.indexOf(pc.script[pc.offset]) < 0) buff[i++] = pc.script[pc.offset++]; } - buff.length = i; - return buff; + return buff.subarray(0, i); }; const getindex = function(L, L1, pc) { @@ -100,7 +99,7 @@ const printstack = function(L) { const ops = "+-*%^/\\&|~<>_!".split('').map(e => e.charCodeAt(0)); const runJS = function(L, L1, pc) { - let buff = []; + let buff = new Uint8Array(300); let status = 0; if (!pc || !pc.script) return lauxlib.luaL_error(L, lua.to_luastring("attempt to runJS null script")); for (;;) { @@ -527,25 +526,15 @@ const udataval = function(L) { const d2s = function(L) { let d = lauxlib.luaL_checknumber(L, 1); - - let dv = new DataView(new ArrayBuffer(8)); - dv.setFloat64(0, d, true); - - let b = []; - for (let i = 0; i < 8; i++) - b.push(dv.getUint8(i, true)); - - lua.lua_pushlstring(L, b, 8); + let b = new ArrayBuffer(8); + new DataView(b).setFloat64(0, d, true); + lua.lua_pushlstring(L, new Uint8Array(b), 8); return 1; }; const s2d = function(L) { let b = lauxlib.luaL_checkstring(L, 1); - - let dv = new DataView(new ArrayBuffer(8)); - for (let i = 0; i < b.length; i++) - dv.setUint8(i, b[i], true); - + let dv = new DataView(b.buffer); lua.lua_pushnumber(L, dv.getFloat64(0, true)); return 1; }; -- cgit v1.2.3-54-g00ecf From 17e4cff6514c75920462397c227408c21336d1ae Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 13 Dec 2017 14:56:26 +1100 Subject: src/: Use .subarray instead of .slice (optimisation) --- src/lauxlib.js | 2 +- src/llex.js | 4 ++-- src/lobject.js | 6 +++--- src/lundump.js | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lauxlib.js b/src/lauxlib.js index 6acf57f..3b2be6e 100644 --- a/src/lauxlib.js +++ b/src/lauxlib.js @@ -62,7 +62,7 @@ const pushglobalfuncname = function(L, ar) { if (findfield(L, top + 1, 2)) { let name = lua.lua_tostring(L, -1); if (name[0] === "_".charCodeAt(0) && name[1] === "G".charCodeAt(0) && name[2] === ".".charCodeAt(0)) { /* name start with '_G.'? */ - lua.lua_pushstring(L, name.slice(3)); /* push name without prefix */ + lua.lua_pushstring(L, name.subarray(3)); /* push name without prefix */ lua.lua_remove(L, -2); /* remove original name */ } lua.lua_copy(L, -1, top + 1); /* move name to proper place */ diff --git a/src/llex.js b/src/llex.js index 9e4bed4..438f288 100644 --- a/src/llex.js +++ b/src/llex.js @@ -328,7 +328,7 @@ const read_long_string = function(ls, seminfo, sep) { } if (seminfo) - seminfo.ts = luaX_newstring(ls, ls.buff.buffer.slice(2 + sep, ls.buff.n - (2 + sep))); + seminfo.ts = luaX_newstring(ls, ls.buff.buffer.subarray(2 + sep, ls.buff.n - (2 + sep))); }; const esccheck = function(ls, c, msg) { @@ -453,7 +453,7 @@ const read_string = function(ls, del, seminfo) { } save_and_next(ls); /* skip delimiter */ - seminfo.ts = luaX_newstring(ls, ls.buff.buffer.slice(1, ls.buff.n-1)); + seminfo.ts = luaX_newstring(ls, ls.buff.buffer.subarray(1, ls.buff.n-1)); }; const token_to_index = Object.create(null); /* don't want to return true for e.g. 'hasOwnProperty' */ diff --git a/src/lobject.js b/src/lobject.js index 18adb27..5ca7cd3 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -531,7 +531,7 @@ const luaO_pushvfstring = function(L, fmt, argp) { for (;;) { e = fmt.indexOf(char['%'], i); if (e == -1) break; - pushstr(L, fmt.slice(i, e)); + pushstr(L, fmt.subarray(i, e)); switch(fmt[e+1]) { case char['s']: { let s = argp[a++]; @@ -540,7 +540,7 @@ const luaO_pushvfstring = function(L, fmt, argp) { /* respect null terminator */ let i = s.indexOf(0); if (i !== -1) - s = s.slice(0, i); + s = s.subarray(0, i); } pushstr(L, s); break; @@ -609,7 +609,7 @@ const luaO_pushvfstring = function(L, fmt, argp) { i = e + 2; } ldo.luaD_checkstack(L, 1); - pushstr(L, fmt.slice(i)); + pushstr(L, fmt.subarray(i)); if (n > 0) lvm.luaV_concat(L, n+1); return L.stack[L.top-1].svalue(); }; diff --git a/src/lundump.js b/src/lundump.js index 7461813..e49c376 100644 --- a/src/lundump.js +++ b/src/lundump.js @@ -25,7 +25,7 @@ class BytecodeParser { assert(defs.is_luastring(name)); if (name[0] == defs.char["@"] || name[0] == defs.char["="]) - this.name = name.slice(1); + this.name = name.subarray(1); else if (name[0] == defs.LUA_SIGNATURE.charCodeAt(0)) this.name = defs.to_luastring("binary string", true); else -- cgit v1.2.3-54-g00ecf From 60181181128144f483bbf344ef51b2ce5385a3c0 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 13 Dec 2017 16:51:04 +1100 Subject: README.md: Mention strings are backed by Uint8Array --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eefbeea..0f197ce 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Fengari implements Lua 5.3 semantics and will hopefully follow future Lua releas Lua strings are 8-bits clean and can embed `\0`. Which means that invalid UTF-8/16 strings are valid Lua strings. Lua functions like `string.dump` even use strings as a way of storing binary data. -To address that issue, Lua strings are represented by an array of bytes in Fengari. To push a JS string on the stack you can use `lua_pushliteral` which will convert it to an array of bytes before pushing it. To get a Lua string on the stack as a JS string you can use `lua_tojsstring` which will attempt to convert it to a UTF-16 JS string. The latter won't give you what you expect if the Lua string is not a valid UTF-16 sequence. You can also convert strings with `lua.to_luastring`, `lua.to_jsstring` and `lua.to_uristring`. +To address that issue, Fengari uses [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) objects containing the raw bytes to implement lua strings. To push a JS string on the stack you can use `lua_pushliteral` which will convert it to an array of bytes before pushing it. To get a Lua string on the stack as a JS string you can use `lua_tojsstring` which will attempt to convert it to a UTF-16 JS string. The latter won't give you what you expect if the Lua string is not a valid UTF-16 sequence. You can also convert strings with `lua.to_luastring`, `lua.to_jsstring` and `lua.to_uristring`. ### Integers -- cgit v1.2.3-54-g00ecf From af95e27d1b2d5f0f39534523778003dfd1fcf417 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 13 Dec 2017 17:17:43 +1100 Subject: src/lzio.js: Don't permit DataView returned from a lua_load reader any more --- src/lzio.js | 15 ++++----------- tests/lstrlib.js | 4 ++-- tests/tests.js | 2 +- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/lzio.js b/src/lzio.js index efd7909..a00a24b 100644 --- a/src/lzio.js +++ b/src/lzio.js @@ -48,22 +48,15 @@ class ZIO { const EOZ = -1; const luaZ_fill = function(z) { - let size; let buff = z.reader(z.L, z.data); if (buff === null) return EOZ; - if (buff instanceof DataView) { - z.buffer = new Uint8Array(buff.buffer, buff.byteOffset, buff.byteLength); - z.off = 0; - size = buff.byteLength - buff.byteOffset; - } else { - assert(buff instanceof Uint8Array, "Should only load binary of array of bytes"); - z.buffer = buff; - z.off = 0; - size = buff.length; - } + assert(buff instanceof Uint8Array, "Should only load binary of array of bytes"); + let size = buff.length; if (size === 0) return EOZ; + z.buffer = buff; + z.off = 0; z.n = size - 1; return z.buffer[z.off++]; }; diff --git a/tests/lstrlib.js b/tests/lstrlib.js index a55fca1..bdf68d8 100644 --- a/tests/lstrlib.js +++ b/tests/lstrlib.js @@ -442,9 +442,9 @@ test('string.dump', function (t) { lua.lua_call(L, 0, -1); - let dv = lua.lua_todataview(L, -1); + let str = lua.lua_tostring(L, -1); - lua.lua_load(L, function(L, s) { let r = s.dv; s.dv = null; return r; }, {dv: dv}, lua.to_luastring("test"), lua.to_luastring("binary")); + lua.lua_load(L, function(L, s) { let r = s.str; s.str = null; return r; }, {str: str}, lua.to_luastring("test"), lua.to_luastring("binary")); lua.lua_call(L, 0, -1); diff --git a/tests/tests.js b/tests/tests.js index 1bc4fc6..1ad9900 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -11,7 +11,7 @@ const toByteCode = function(luaCode) { return 0; }, b, false) !== 0) throw Error("unable to dump given function"); - return new DataView(Uint8Array.from(b).buffer); + return Uint8Array.from(b); }; const getState = function(luaCode) { -- cgit v1.2.3-54-g00ecf From 55ff446d1fdd5ad5982e2ce3f7118fca8c16880b Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 13 Dec 2017 17:21:59 +1100 Subject: src/lauxlib.js: Use a Uint8Array for luaL_loadfilex in both browser and node --- src/lauxlib.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lauxlib.js b/src/lauxlib.js index 3b2be6e..5687242 100644 --- a/src/lauxlib.js +++ b/src/lauxlib.js @@ -713,7 +713,7 @@ class LoadF { constructor() { this.n = NaN; /* number of pre-read characters */ this.f = null; /* file being read */ - this.buff = typeof process === "undefined" ? new Array(1024) : new Buffer(1024); /* area for reading file */ + this.buff = new Uint8Array(1024); /* area for reading file */ this.pos = 0; /* current position in file */ this.err = void 0; } @@ -726,8 +726,8 @@ if (typeof process === "undefined") { if (lf.f !== null && lf.n > 0) { /* are there pre-read characters to be read? */ let bytes = lf.n; /* return them (chars already in buffer) */ lf.n = 0; /* no more pre-read characters */ - lf.f = lf.f.slice(lf.pos); /* we won't use lf.buff anymore */ - return lf.buff.slice(0, bytes); + lf.f = lf.f.subarray(lf.pos); /* we won't use lf.buff anymore */ + return lf.buff.subarray(0, bytes); } let f = lf.f; @@ -789,7 +789,6 @@ if (typeof process === "undefined") { bytes = lf.n; /* return them (chars already in buffer) */ lf.n = 0; /* no more pre-read characters */ } else { /* read a block from file */ - lf.buff.fill(0); try { bytes = fs.readSync(lf.f, lf.buff, 0, lf.buff.length, lf.pos); /* read block */ } catch(e) { @@ -799,7 +798,7 @@ if (typeof process === "undefined") { lf.pos += bytes; } if (bytes > 0) - return lf.buff.slice(0, bytes); /* slice on a node.js Buffer is 'free' */ + return lf.buff.subarray(0, bytes); /* slice on a node.js Buffer is 'free' */ else return null; }; -- cgit v1.2.3-54-g00ecf From b162ffe744bc407ad4035674f5c05848a460b58f Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 15 Dec 2017 14:50:14 +1100 Subject: src/lobject.js: Refactor luaO_chunkid to use Uint8Array.set --- src/lobject.js | 26 ++++++++++++++++++-------- src/luaconf.js | 2 +- tests/test-suite/db.js | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/lobject.js b/src/lobject.js index 5ca7cd3..c02bc91 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -294,7 +294,7 @@ class LocVar { } } -const RETS = defs.to_luastring("...", true); +const RETS = defs.to_luastring("..."); const PRE = defs.to_luastring("[string \""); const POS = defs.to_luastring("\"]"); @@ -305,27 +305,37 @@ const luaO_chunkid = function(source, bufflen) { if (l < bufflen) /* small enough? */ out = source.slice(1); else { /* truncate it */ - out = source.slice(1, bufflen); + out = source.slice(1, bufflen+1); } } else if (source[0] === char['@']) { /* file name */ if (l <= bufflen) /* small enough? */ out = source.slice(1); else { /* add '...' before rest of name */ + out = new Uint8Array(bufflen); + out.set(RETS); bufflen -= RETS.length; - out = Uint8Array.from(Array.from(RETS).concat(Array.from(source.subarray(1 + l - bufflen)))); + out.set(source.subarray(l - bufflen), RETS.length); } } else { /* string; format as [string "source"] */ + out = new Uint8Array(bufflen); let nli = source.indexOf(char['\n']); /* find first new line (if any) */ - out = Array.from(PRE); /* add prefix */ - bufflen -= PRE.length + RETS.length + POS.length + 1; /* save space for prefix+suffix+'\0' */ + out.set(PRE); /* add prefix */ + let out_i = PRE.length; + bufflen -= PRE.length + RETS.length + POS.length; /* save space for prefix+suffix */ if (l < bufflen && nli === -1) { /* small one-line source? */ - out = out.concat(Array.from(source), Array.from(POS)); /* keep it */ + out.set(source, out_i); /* keep it */ + out_i += source.length; } else { if (nli !== -1) l = nli; /* stop at first newline */ if (l > bufflen) l = bufflen; - out = out.concat(Array.from(source.subarray(0, l)), Array.from(RETS), Array.from(POS)); + out.set(source.subarray(0, l), out_i); + out_i += l; + out.set(RETS, out_i); + out_i += RETS.length; } - out = Uint8Array.from(out); + out.set(POS, out_i); + out_i += POS.length; + out = out.subarray(0, out_i); } return out; }; diff --git a/src/luaconf.js b/src/luaconf.js index 00663a6..5331720 100644 --- a/src/luaconf.js +++ b/src/luaconf.js @@ -19,7 +19,7 @@ const LUAI_MAXSTACK = 100000; @@ of a function in debug information. ** CHANGE it if you want a different size. */ -const LUA_IDSIZE = 60; +const LUA_IDSIZE = 60-1; /* fengari uses 1 less than lua as we don't embed the null byte */ const lua_integer2str = function(n) { return sprintf(LUA_INTEGER_FMT, n); diff --git a/tests/test-suite/db.js b/tests/test-suite/db.js index a2e3ce6..ff13ecb 100644 --- a/tests/test-suite/db.js +++ b/tests/test-suite/db.js @@ -64,7 +64,7 @@ test("[test-suite] db: getinfo, ...line...", function (t) { }); -test("[test-suite] db: test file ad string names truncation", function (t) { +test("[test-suite] db: test file and string names truncation", function (t) { let luaCode = ` a = "function f () end" local function dostring (s, x) return load(s, x)() end -- cgit v1.2.3-54-g00ecf From 8249248f80467f1660509a30623f40bb9ff1eba7 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 15 Dec 2017 14:56:04 +1100 Subject: src/lapi.js: Have lua_todataview return view over actual underlying Uint8Array --- src/lapi.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/lapi.js b/src/lapi.js index afa94ce..111735f 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -672,19 +672,8 @@ const lua_toljsstring = function(L, idx) { const lua_tojsstring = lua_toljsstring; const lua_todataview = function(L, idx) { - let o = index2addr(L, idx); - - if (!o.ttisstring()) { - if (!lvm.cvt2str(o)) { /* not convertible? */ - return null; - } - lobject.luaO_tostring(L, o); - } - - let dv = new DataView(new ArrayBuffer(o.vslen())); - o.svalue().forEach((e, i) => dv.setUint8(i, e, true)); - - return dv; + let u8 = lua_tolstring(L, idx); + return new DataView(u8.buffer, u8.byteOffset, u8.byteLength); }; const lua_rawlen = function(L, idx) { -- cgit v1.2.3-54-g00ecf