From 64b0adec04eac6333e3f2db4000934a79bf8f268 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 6 Nov 2022 12:05:48 -0600 Subject: [PATCH] use a custom implementation instead of memcpy --- Cargo.toml | 3 +- interpreter.js | 86 +++++---------- interpreter_opt.js | 2 +- src/batch.rs | 13 ++- src/builder.rs | 257 ++++++++++++++++++++++++++++----------------- src/element.rs | 1 + 6 files changed, 201 insertions(+), 161 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2822066..c44d37a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,4 @@ categories = ["web-programming", "wasm", "api-bindings"] [dependencies] wasm-bindgen = "0.2.83" web-sys = { version = "0.3.60", features = ["console", "Window", "Document", "Element", "HtmlElement", "HtmlHeadElement"] } -js-sys = "0.3.60" -ufmt = "0.2.0" \ No newline at end of file +js-sys = "0.3.60" \ No newline at end of file diff --git a/interpreter.js b/interpreter.js index ec271f4..602d6f3 100644 --- a/interpreter.js +++ b/interpreter.js @@ -338,6 +338,7 @@ export class JsInterpreter { this.parents = []; this.UpdateMemory(mem); this.last_start_pos; + this.last_str_start; this.metadata_ptr = _metadata_ptr; this.ptr_ptr = _ptr_ptr; this.str_ptr_ptr = _str_ptr_ptr; @@ -364,21 +365,40 @@ export class JsInterpreter { this.last_start_pos = this.view.getUint32(this.ptr_ptr, true); } this.u8BufPos = this.last_start_pos; - if (metadata & 0x02) { + if (metadata & 0x04) { len = this.view.getUint32(this.str_len_ptr, true); - ptr = this.view.getUint32(this.str_ptr_ptr, true); + if (metadata & 0x02) { + this.last_str_start = this.view.getUint32(this.str_ptr_ptr, true); + } // for small strings decoding them in javascript to avoid the overhead of native calls is faster - if (len < 100) { - // the fourth boolean contains information about whether the string is all ascii or utf8 - if (metadata & 0x04) { - this.strings = this.batchedAsciiDecode(ptr, len); + // the fourth boolean contains information about whether the string is all ascii or utf8 and small + if (metadata & 0x08) { + pos = this.last_str_start; + this.strings = ""; + endRounded = pos + ((len / 4) | 0) * 4; + while (pos < endRounded) { + char = this.view.getUint32(pos); + this.strings += String.fromCharCode(char >> 24, (char & 0x00FF0000) >> 16, (char & 0x0000FF00) >> 8, (char & 0x000000FF)); + pos += 4; } - else { - this.strings = this.utf8Decode(ptr, len); + switch (this.last_str_start + len - pos) { + case 3: + char = this.view.getUint32(pos); + this.strings += String.fromCharCode(char >> 24, (char & 0x00FF0000) >> 16, (char & 0x0000FF00) >> 8); + break; + case 2: + char = this.view.getUint16(pos); + this.strings += String.fromCharCode(char >> 8, char & 0xFF); + break; + case 1: + this.strings += String.fromCharCode(this.view.getUint8(pos)); + break; + case 0: + break; } } else { - this.strings = this.decoder.decode(new DataView(this.view.buffer, ptr, len)); + this.strings = this.decoder.decode(new DataView(this.view.buffer, this.last_str_start, len)); } this.strPos = 0; } @@ -508,54 +528,6 @@ export class JsInterpreter { GetNode(id) { return this.nodes[id]; } - - utf8Decode(start, byteLength) { - pos = start; - end = pos + byteLength; - out = ""; - while (pos < end) { - char = this.view.getUint8(pos++); - if ((char & 0x80) === 0) { - // 1 byte - out += String.fromCharCode(char); - } else if ((char & 0xe0) === 0xc0) { - // 2 bytes - out += String.fromCharCode(((char & 0x1f) << 6) | (this.view.getUint8(pos++) & 0x3f)); - } else if ((char & 0xf0) === 0xe0) { - // 3 bytes - out += String.fromCharCode(((char & 0x1f) << 12) | ((this.view.getUint8(pos++) & 0x3f) << 6) | (this.view.getUint8(pos++) & 0x3f)); - } else if ((char & 0xf8) === 0xf0) { - // 4 bytes - let unit = ((char & 0x07) << 0x12) | ((this.view.getUint8(pos++) & 0x3f) << 0x0c) | ((this.view.getUint8(pos++) & 0x3f) << 0x06) | (this.view.getUint8(pos++) & 0x3f); - if (unit > 0xffff) { - unit -= 0x10000; - out += String.fromCharCode(((unit >>> 10) & 0x3ff) | 0xd800); - unit = 0xdc00 | (unit & 0x3ff); - } - out += String.fromCharCode(unit); - } else { - out += String.fromCharCode(char); - } - } - - return out; - } - - batchedAsciiDecode(start, byteLength) { - pos = start; - end = pos + byteLength; - out = ""; - endRounded = pos + ((byteLength / 4) | 0) * 4; - while (pos < endRounded) { - char = this.view.getUint32(pos); - out += String.fromCharCode(char >> 24, (char & 0x00FF0000) >> 16, (char & 0x0000FF00) >> 8, (char & 0x000000FF)); - pos += 4; - } - while (pos < end) { - out += String.fromCharCode(this.view.getUint8(pos++)); - } - return out; - } } const els = [ diff --git a/interpreter_opt.js b/interpreter_opt.js index f495ce3..7734c76 100644 --- a/interpreter_opt.js +++ b/interpreter_opt.js @@ -1 +1 @@ -let t, e, s, i, r, o, u, n, a, d, f, l, h, g, c, P, b, p, B, m, w, v, U, N; export function work_last_created() { P.Work() } export function last_needs_memory() { return !b.byteLength } export function update_last_memory(t) { P.UpdateMemory(t) } function exOp() { switch (t & 31) { case 0: P.lastNode = P.lastNode.firstChild; break; case 1: P.lastNode = P.lastNode.nextSibling; break; case 2: P.lastNode = P.lastNode.parentNode; break; case 3: P.nodes[P.view.getUint32(P.u8BufPos, true)] = P.lastNode; P.u8BufPos += 4; break; case 4: P.lastNode = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4; break; case 5: return true; case 6: P.lastNode = P.createFullElement(); break; case 7: if (t & 32) { B = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { B = P.lastNode } if (t & 64) { e = P.decodeU32(); for (r = 0; r < e; r++) { B.appendChild(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } } else { B.appendChild(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } break; case 8: if (t & 32) { B = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { B = P.lastNode } if (t & 64) { e = P.decodeU32(); m = []; for (r = 0; r < e; r++) { m.push(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } B.replaceWith(...m) } else { B.replaceWith(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } break; case 9: if (t & 32) { B = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { B = P.lastNode } if (t & 64) { e = P.decodeU32(); m = []; for (r = 0; r < e; r++) { m.push(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } B.after(...m) } else { B.after(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } break; case 10: if (t & 32) { B = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { B = P.lastNode } if (t & 64) { e = P.decodeU32(); m = []; for (r = 0; r < e; r++) { m.push(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } B.before(...m) } else { B.before(P.nodes[P.view.getUint32(P.u8BufPos, true)]); P.u8BufPos += 4 } break; case 11: if (t & 32) { P.nodes[P.view.getUint32(P.u8BufPos, true)].remove(); P.u8BufPos += 4 } else { P.lastNode.remove() } break; case 12: P.lastNode = document.createTextNode(P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true))); P.u8BufPos += 2; if (t & 32) { P.nodes[P.view.getUint32(P.u8BufPos, true)] = P.lastNode; P.u8BufPos += 4 } break; case 13: P.lastNode = P.createElement(); if (t & 32) { P.nodes[P.view.getUint32(P.u8BufPos, true)] = P.lastNode; P.u8BufPos += 4 } break; case 14: if (t & 32) { U = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 4; P.nodes[U].textContent = P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true)); P.u8BufPos += 2 } else { P.lastNode.textContent = P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true)); P.u8BufPos += 2 } break; case 15: if (t & 32) { w = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { w = P.lastNode } if (t & 64) { r = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 4; i = P.strings.substring(P.strPos, P.strPos += r & 65535); if (t & 128) { w.setAttributeNS(P.strings.substring(P.strPos, P.strPos += (r & 4294901760) >>> 16), i, P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true))); P.u8BufPos += 2 } else { w.setAttribute(i, P.strings.substring(P.strPos, P.strPos += (r & 4294901760) >>> 16)) } } else { r = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 3; if (t & 128) { s = P.strings.substring(P.strPos, P.strPos += r & 65535); w.setAttributeNS(s, y[(r & 16711680) >>> 16], P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true))); P.u8BufPos += 2 } else { w.setAttribute(y[r & 255], P.strings.substring(P.strPos, P.strPos += (r & 16776960) >>> 8)) } } break; case 16: if (t & 32) { w = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { w = P.lastNode } if (t & 64) { if (t & 128) { r = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 4; i = P.strings.substring(P.strPos, P.strPos += r & 65535); w.removeAttributeNS(P.strings.substring(P.strPos, P.strPos += (r & 4294901760) >>> 16), i) } else { w.removeAttribute(P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true))); P.u8BufPos += 2 } } else { if (t & 128) { r = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 3; i = y[r & 255]; w.removeAttributeNS(P.strings.substring(P.strPos, P.strPos += (r & 16776960) >>> 8), i) } else { w.removeAttribute(y[P.view.getUint8(P.u8BufPos++)]) } } break; case 17: if (t & 32) { w = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { w = P.lastNode } r = P.view.getUint32(P.u8BufPos, true); P.u8BufPos += 4; w.style.setProperty(P.strings.substring(P.strPos, P.strPos += r & 65535), P.strings.substring(P.strPos, P.strPos += (r & 4294901760) >>> 16)); break; case 18: if (t & 32) { w = P.nodes[P.view.getUint32(P.u8BufPos, true)]; P.u8BufPos += 4 } else { w = P.lastNode } w.style.removeProperty(P.strings.substring(P.strPos, P.strPos += P.view.getUint16(P.u8BufPos, true))); P.u8BufPos += 2; break; case 19: if (t & 32) { P.lastNode = P.nodes[P.view.getUint32(P.u8BufPos, true)].cloneNode(true); P.u8BufPos += 4 } else { P.lastNode = P.lastNode.cloneNode(true) } if (t & 64) { P.nodes[P.view.getUint32(P.u8BufPos, true)] = P.lastNode; P.u8BufPos += 4 } break; case 20: if (t & 32) { w = P.nodes[P.view.getUint32(P.u8BufPos, true)].cloneNode(true).firstChild; P.u8BufPos += 4 } else { w = P.lastNode.cloneNode(true).firstChild } for (; w !== null; w = w.nextSibling) { if (P.view.getUint8(P.u8BufPos++) === 1) { P.nodes[P.view.getUint32(P.u8BufPos, true)] = w; P.u8BufPos += 4 } } break; default: break } } export class JsInterpreter { constructor(t, e, s, i, r) { this.lastNode; this.nodes = []; this.parents = []; this.UpdateMemory(t); this.last_start_pos; this.metadata_ptr = e; this.ptr_ptr = s; this.str_ptr_ptr = i; this.str_len_ptr = r; this.strings = ""; this.strPos = 0; this.decoder = new TextDecoder; this.idSize = 1; P = this } NeedsMemory() { return this.view.buffer.byteLength === 0 } UpdateMemory(t) { this.view = new DataView(t.buffer); b = t.buffer } Work() { p = this.view.getUint8(this.metadata_ptr); if (p & 1) { this.last_start_pos = this.view.getUint32(this.ptr_ptr, true) } this.u8BufPos = this.last_start_pos; if (p & 2) { e = this.view.getUint32(this.str_len_ptr, true); a = this.view.getUint32(this.str_ptr_ptr, true); if (e < 100) { if (p & 4) { this.strings = this.batchedAsciiDecode(a, e) } else { this.strings = this.utf8Decode(a, e) } } else { this.strings = this.decoder.decode(new DataView(this.view.buffer, a, e)) } this.strPos = 0 } for (; ;) { t = this.view.getUint32(this.u8BufPos, true); this.u8BufPos += 4; if (exOp()) return; t >>>= 8; if (exOp()) return; t >>>= 8; if (exOp()) return; t >>>= 8; if (exOp()) return } } createElement() { o = this.view.getUint32(this.u8BufPos, true); n = o & 255; switch (n) { case 255: this.u8BufPos += 4; n = document.createElement(k[(o & 65280) >>> 8], this.strings.substring(this.strPos, this.strPos += (o & 4294901760) >>> 16)); return n; case 254: this.u8BufPos += 3; n = document.createElement(this.strings.substring(this.strPos, this.strPos += (o & 16776960) >>> 8)); return n; case 253: this.u8BufPos += 3; n = this.strings.substring(this.strPos, this.strPos += (o & 16776960) >>> 8); n = document.createElementNS(this.strings.substring(this.strPos, this.strPos += this.view.getUint16(this.u8BufPos, true)), n); this.u8BufPos += 2; return n; default: this.u8BufPos++; return document.createElement(k[n]) } } createFullElement() { const t = this.decodeMaybeIdByteBool(), e = this.createElement(); o = this.view.getUint16(this.u8BufPos, true); this.u8BufPos += 2; g = o & 255; const n = (o & 65280) >>> 8; for (r = 0; r < g; r++) { o = this.view.getUint32(this.u8BufPos, true); i = o & 255; switch (i) { case 255: this.u8BufPos += 4; i = y[this.view.getUint8((o & 65280) >>> 8)]; e.setAttributeNS(this.strings.substring(this.strPos, this.strPos += (o & 4294901760) >>> 16), i); break; case 254: this.u8BufPos++; o = this.view.getUint32(this.u8BufPos, true); this.u8BufPos += 4; i = this.strings.substring(this.strPos, this.strPos += o & 65535); e.setAttribute(i, this.strings.substring(this.strPos, this.strPos += (o & 4294901760) >>> 16)); break; case 253: this.u8BufPos += 3; i = this.strings.substring(this.strPos, this.strPos += (o & 16776960) >>> 8); o = this.view.getUint32(this.u8BufPos, true); this.u8BufPos += 4; s = this.strings.substring(this.strPos, this.strPos += o & 65535); u = this.strings.substring(this.strPos, this.strPos += (o & 4294901760) >>> 16); e.setAttributeNS(s, i, u); break; default: this.u8BufPos += 3; e.setAttribute(y[i], this.strings.substring(this.strPos, this.strPos += (o & 16776960) >>> 8)); break } } for (let a = 0; a < n; a++) { e.appendChild(this.createFullElement()) } if (t !== null) { this.nodes[t] = e } return e } decodeMaybeIdByteBool() { if (this.view.getUint8(this.u8BufPos++) === 0) { return null } else { const t = this.view.getUint32(this.u8BufPos, true); this.u8BufPos += 4; return t } } decodeU32() { this.u8BufPos += 4; return this.view.getUint32(this.u8BufPos - 4, true) } SetNode(t, e) { this.nodes[t] = e } GetNode(t) { return this.nodes[t] } utf8Decode(t, e) { d = t; f = d + e; l = ""; while (d < f) { h = this.view.getUint8(d++); if ((h & 128) === 0) { l += String.fromCharCode(h) } else if ((h & 224) === 192) { l += String.fromCharCode((h & 31) << 6 | this.view.getUint8(d++) & 63) } else if ((h & 240) === 224) { l += String.fromCharCode((h & 31) << 12 | (this.view.getUint8(d++) & 63) << 6 | this.view.getUint8(d++) & 63) } else if ((h & 248) === 240) { let s = (h & 7) << 18 | (this.view.getUint8(d++) & 63) << 12 | (this.view.getUint8(d++) & 63) << 6 | this.view.getUint8(d++) & 63; if (s > 65535) { s -= 65536; l += String.fromCharCode(s >>> 10 & 1023 | 55296); s = 56320 | s & 1023 } l += String.fromCharCode(s) } else { l += String.fromCharCode(h) } } return l } batchedAsciiDecode(t, e) { d = t; f = d + e; l = ""; c = d + (e / 4 | 0) * 4; while (d < c) { h = this.view.getUint32(d); l += String.fromCharCode(h >> 24, (h & 16711680) >> 16, (h & 65280) >> 8, h & 255); d += 4 } while (d < f) { l += String.fromCharCode(this.view.getUint8(d++)) } return l } } const k = ["a", "abbr", "acronym", "address", "applet", "area", "article", "aside", "audio", "b", "base", "bdi", "bdo", "bgsound", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "em", "embed", "fieldset", "figcaption", "figure", "font", "footer", "form", "frame", "frameset", "h1", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "kbd", "keygen", "label", "legend", "li", "link", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "noembed", "noframes", "noscript", "object", "ol", "optgroup", "option", "output", "p", "param", "picture", "plaintext", "portal", "pre", "progress", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp", "script", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr", "xmp"]; const y = ["accept-charset", "accept", "accesskey", "action", "align", "allow", "alt", "aria-atomic", "aria-busy", "aria-controls", "aria-current", "aria-describedby", "aria-description", "aria-details", "aria-disabled", "aria-dropeffect", "aria-errormessage", "aria-flowto", "aria-grabbed", "aria-haspopup", "aria-hidden", "aria-invalid", "aria-keyshortcuts", "aria-label", "aria-labelledby", "aria-live", "aria-owns", "aria-relevant", "aria-roledescription", "async", "autocapitalize", "autocomplete", "autofocus", "autoplay", "background", "bgcolor", "border", "buffered", "capture", "challenge", "charset", "checked", "cite", "class", "code", "codebase", "color", "cols", "colspan", "content", "contenteditable", "contextmenu", "controls", "coords", "crossorigin", "csp", "data", "datetime", "decoding", "default", "defer", "dir", "dirname", "disabled", "download", "draggable", "enctype", "enterkeyhint", "for", "form", "formaction", "formenctype", "formmethod", "formnovalidate", "formtarget", "headers", "height", "hidden", "high", "href", "hreflang", "http-equiv", "icon", "id", "importance", "inputmode", "integrity", "intrinsicsize", "ismap", "itemprop", "keytype", "kind", "label", "lang", "language", "list", "loading", "loop", "low", "manifest", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "novalidate", "open", "optimum", "pattern", "ping", "placeholder", "poster", "preload", "radiogroup", "readonly", "referrerpolicy", "rel", "required", "reversed", "role", "rows", "rowspan", "sandbox", "scope", "scoped", "selected", "shape", "size", "sizes", "slot", "span", "spellcheck", "src", "srcdoc", "srclang", "srcset", "start", "step", "style", "summary", "tabindex", "target", "title", "translate", "type", "usemap", "value", "width", "wrap"]; \ No newline at end of file +let t,e,s,i,r,o,u,a,n,d,l,f,h,g,P,c,b,p,B,m,w,v,U,k;export function work_last_created(){c.Work()}export function last_needs_memory(){return!b.byteLength}export function update_last_memory(t){c.UpdateMemory(t)}function exOp(){switch(t&31){case 0:c.lastNode=c.lastNode.firstChild;break;case 1:c.lastNode=c.lastNode.nextSibling;break;case 2:c.lastNode=c.lastNode.parentNode;break;case 3:c.nodes[c.view.getUint32(c.u8BufPos,true)]=c.lastNode;c.u8BufPos+=4;break;case 4:c.lastNode=c.nodes[c.view.getUint32(c.u8BufPos,true)];c.u8BufPos+=4;break;case 5:return true;case 6:c.lastNode=c.createFullElement();break;case 7:if(t&32){B=c.nodes[c.view.getUint32(c.u8BufPos,true)];c.u8BufPos+=4}else{B=c.lastNode}if(t&64){e=c.decodeU32();for(r=0;r>>16),i,c.strings.substring(c.strPos,c.strPos+=c.view.getUint16(c.u8BufPos,true)));c.u8BufPos+=2}else{w.setAttribute(i,c.strings.substring(c.strPos,c.strPos+=(r&4294901760)>>>16))}}else{r=c.view.getUint32(c.u8BufPos,true);c.u8BufPos+=3;if(t&128){s=c.strings.substring(c.strPos,c.strPos+=r&65535);w.setAttributeNS(s,y[(r&16711680)>>>16],c.strings.substring(c.strPos,c.strPos+=c.view.getUint16(c.u8BufPos,true)));c.u8BufPos+=2}else{w.setAttribute(y[r&255],c.strings.substring(c.strPos,c.strPos+=(r&16776960)>>>8))}}break;case 16:if(t&32){w=c.nodes[c.view.getUint32(c.u8BufPos,true)];c.u8BufPos+=4}else{w=c.lastNode}if(t&64){if(t&128){r=c.view.getUint32(c.u8BufPos,true);c.u8BufPos+=4;i=c.strings.substring(c.strPos,c.strPos+=r&65535);w.removeAttributeNS(c.strings.substring(c.strPos,c.strPos+=(r&4294901760)>>>16),i)}else{w.removeAttribute(c.strings.substring(c.strPos,c.strPos+=c.view.getUint16(c.u8BufPos,true)));c.u8BufPos+=2}}else{if(t&128){r=c.view.getUint32(c.u8BufPos,true);c.u8BufPos+=3;i=y[r&255];w.removeAttributeNS(c.strings.substring(c.strPos,c.strPos+=(r&16776960)>>>8),i)}else{w.removeAttribute(y[c.view.getUint8(c.u8BufPos++)])}}break;case 17:if(t&32){w=c.nodes[c.view.getUint32(c.u8BufPos,true)];c.u8BufPos+=4}else{w=c.lastNode}r=c.view.getUint32(c.u8BufPos,true);c.u8BufPos+=4;w.style.setProperty(c.strings.substring(c.strPos,c.strPos+=r&65535),c.strings.substring(c.strPos,c.strPos+=(r&4294901760)>>>16));break;case 18:if(t&32){w=c.nodes[c.view.getUint32(c.u8BufPos,true)];c.u8BufPos+=4}else{w=c.lastNode}w.style.removeProperty(c.strings.substring(c.strPos,c.strPos+=c.view.getUint16(c.u8BufPos,true)));c.u8BufPos+=2;break;case 19:if(t&32){c.lastNode=c.nodes[c.view.getUint32(c.u8BufPos,true)].cloneNode(true);c.u8BufPos+=4}else{c.lastNode=c.lastNode.cloneNode(true)}if(t&64){c.nodes[c.view.getUint32(c.u8BufPos,true)]=c.lastNode;c.u8BufPos+=4}break;case 20:if(t&32){w=c.nodes[c.view.getUint32(c.u8BufPos,true)].cloneNode(true).firstChild;c.u8BufPos+=4}else{w=c.lastNode.cloneNode(true).firstChild}for(;w!==null;w=w.nextSibling){if(c.view.getUint8(c.u8BufPos++)===1){c.nodes[c.view.getUint32(c.u8BufPos,true)]=w;c.u8BufPos+=4}}break;default:break}}export class JsInterpreter{constructor(t,e,s,i,r){this.lastNode;this.nodes=[];this.parents=[];this.UpdateMemory(t);this.last_start_pos;this.last_str_start;this.metadata_ptr=e;this.ptr_ptr=s;this.str_ptr_ptr=i;this.str_len_ptr=r;this.strings="";this.strPos=0;this.decoder=new TextDecoder;this.idSize=1;c=this}NeedsMemory(){return this.view.buffer.byteLength===0}UpdateMemory(t){this.view=new DataView(t.buffer);b=t.buffer}Work(){p=this.view.getUint8(this.metadata_ptr);if(p&1){this.last_start_pos=this.view.getUint32(this.ptr_ptr,true)}this.u8BufPos=this.last_start_pos;if(p&4){e=this.view.getUint32(this.str_len_ptr,true);if(p&2){this.last_str_start=this.view.getUint32(this.str_ptr_ptr,true)}if(p&8){d=this.last_str_start;this.strings="";P=d+(e/4|0)*4;while(d>24,(h&16711680)>>16,(h&65280)>>8,h&255);d+=4}switch(this.last_str_start+e-d){case 3:h=this.view.getUint32(d);this.strings+=String.fromCharCode(h>>24,(h&16711680)>>16,(h&65280)>>8);break;case 2:h=this.view.getUint16(d);this.strings+=String.fromCharCode(h>>8,h&255);break;case 1:this.strings+=String.fromCharCode(this.view.getUint8(d));break;case 0:break}}else{this.strings=this.decoder.decode(new DataView(this.view.buffer,this.last_str_start,e))}this.strPos=0}for(;;){t=this.view.getUint32(this.u8BufPos,true);this.u8BufPos+=4;if(exOp())return;t>>>=8;if(exOp())return;t>>>=8;if(exOp())return;t>>>=8;if(exOp())return}}createElement(){o=this.view.getUint32(this.u8BufPos,true);a=o&255;switch(a){case 255:this.u8BufPos+=4;a=document.createElement(N[(o&65280)>>>8],this.strings.substring(this.strPos,this.strPos+=(o&4294901760)>>>16));return a;case 254:this.u8BufPos+=3;a=document.createElement(this.strings.substring(this.strPos,this.strPos+=(o&16776960)>>>8));return a;case 253:this.u8BufPos+=3;a=this.strings.substring(this.strPos,this.strPos+=(o&16776960)>>>8);a=document.createElementNS(this.strings.substring(this.strPos,this.strPos+=this.view.getUint16(this.u8BufPos,true)),a);this.u8BufPos+=2;return a;default:this.u8BufPos++;return document.createElement(N[a])}}createFullElement(){const t=this.decodeMaybeIdByteBool(),e=this.createElement();o=this.view.getUint16(this.u8BufPos,true);this.u8BufPos+=2;g=o&255;const a=(o&65280)>>>8;for(r=0;r>>8)];e.setAttributeNS(this.strings.substring(this.strPos,this.strPos+=(o&4294901760)>>>16),i);break;case 254:this.u8BufPos++;o=this.view.getUint32(this.u8BufPos,true);this.u8BufPos+=4;i=this.strings.substring(this.strPos,this.strPos+=o&65535);e.setAttribute(i,this.strings.substring(this.strPos,this.strPos+=(o&4294901760)>>>16));break;case 253:this.u8BufPos+=3;i=this.strings.substring(this.strPos,this.strPos+=(o&16776960)>>>8);o=this.view.getUint32(this.u8BufPos,true);this.u8BufPos+=4;s=this.strings.substring(this.strPos,this.strPos+=o&65535);u=this.strings.substring(this.strPos,this.strPos+=(o&4294901760)>>>16);e.setAttributeNS(s,i,u);break;default:this.u8BufPos+=3;e.setAttribute(y[i],this.strings.substring(this.strPos,this.strPos+=(o&16776960)>>>8));break}}for(let n=0;n); } -impl<'a> WritableText for &'a str { - fn write_as_text(self, to: &mut Vec) { - to.extend_from_slice(self.as_bytes()); - } -} - -impl WritableText for Arguments<'_> { - fn write_as_text(self, to: &mut Vec) { - let _ = to.write_fmt(self); - } -} - -/// A wrapper around a `Vec` that can implement `Writable` -pub struct WritableVecWrapper<'a>(&'a mut Vec); - -impl uWrite for WritableVecWrapper<'_> { - type Error = Infallible; - - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - self.0.extend_from_slice(s.as_bytes()); - Ok(()) - } -} - -impl WritableText for u8 { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } -} - -impl WritableText for u16 { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } -} - -impl WritableText for u32 { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } -} - -impl WritableText for u64 { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } -} - -impl WritableText for usize { +impl WritableText for char { fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); + to.push(self as u8); } } -impl WritableText for i8 { +impl<'a> WritableText for &'a str { + #[inline(always)] fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); + let old_len = to.len(); + let len = self.len(); + to.reserve(len); + #[allow(clippy::uninit_vec)] + unsafe { + for o in 0..len { + *to.as_mut_ptr().add(old_len + o) = *self.as_ptr().add(o); + } + to.set_len(old_len + len); + } } } -impl WritableText for i16 { +impl WritableText for Arguments<'_> { fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); + let _ = to.write_fmt(self); } } -impl WritableText for i32 { +impl WritableText for F +where + F: FnOnce(&mut Vec), +{ fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); + self(to); } } -impl WritableText for i64 { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } +macro_rules! write_unsized { + ($t: ty) => { + impl WritableText for $t { + fn write_as_text(self, to: &mut Vec) { + let mut n = self; + let mut n2 = n; + let mut num_digits = 0; + while n2 > 0 { + n2 /= 10; + num_digits += 1; + } + let len = num_digits; + to.reserve(len); + let ptr = to.as_mut_ptr().cast::(); + let old_len = to.len(); + let mut i = len - 1; + loop { + unsafe { ptr.add(old_len + i).write((n % 10) as u8 + b'0') } + n /= 10; + + if n == 0 { + break; + } else { + i -= 1; + } + } + + #[allow(clippy::uninit_vec)] + unsafe { + to.set_len(old_len + (len - i)); + } + } + } + }; } -impl WritableText for isize { - fn write_as_text(self, to: &mut Vec) { - let mut v = WritableVecWrapper(to); - let _ = uwrite!(v, "{}", self); - } +macro_rules! write_sized { + ($t: ty) => { + impl WritableText for $t { + fn write_as_text(self, to: &mut Vec) { + let neg = self < 0; + let mut n = if neg { + match self.checked_abs() { + Some(n) => n, + None => <$t>::MAX / 2 + 1, + } + } else { + self + }; + let mut n2 = n; + let mut num_digits = 0; + while n2 > 0 { + n2 /= 10; + num_digits += 1; + } + let len = if neg { num_digits + 1 } else { num_digits }; + to.reserve(len); + let ptr = to.as_mut_ptr().cast::(); + let old_len = to.len(); + let mut i = len - 1; + loop { + unsafe { ptr.add(old_len + i).write((n % 10) as u8 + b'0') } + n /= 10; + + if n == 0 { + break; + } else { + i -= 1; + } + } + + if neg { + i -= 1; + unsafe { ptr.add(i).write(b'-') } + } + + #[allow(clippy::uninit_vec)] + unsafe { + to.set_len(old_len + (len - i)); + } + } + } + }; } -impl WritableText for F -where - F: FnOnce(WritableVecWrapper), -{ - fn write_as_text(self, to: &mut Vec) { - self(WritableVecWrapper(to)); - } -} +write_unsized!(u8); +write_unsized!(u16); +write_unsized!(u32); +write_unsized!(u64); +write_unsized!(u128); +write_unsized!(usize); + +write_sized!(i8); +write_sized!(i16); +write_sized!(i32); +write_sized!(i64); +write_sized!(i128); +write_sized!(isize); diff --git a/src/element.rs b/src/element.rs index b30c93d..c6a0e0e 100644 --- a/src/element.rs +++ b/src/element.rs @@ -47,6 +47,7 @@ impl<'a, 'b> Element { } impl<'a, 'b> IntoElement<'a, 'b> for Element { + #[inline(always)] fn encode(&self, v: &mut Batch) { v.msg.push(*self as u8); }