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-70-g09d2