diff --git a/lib/saxes.js b/lib/saxes.js index 2049dfef..f0d05d6f 100644 --- a/lib/saxes.js +++ b/lib/saxes.js @@ -1554,20 +1554,40 @@ class SaxesParser { /** @private */ sAttribValueQuoted() { + // We deliberately do not use captureTo here. The specialized code we use + // here is faster than using captureTo. const { q } = this; - const c = this.captureTo([q, AMP, LESS], "text"); - if (c === q) { - this.pushAttrib(this.name, this.text); - this.name = this.text = ""; - this.q = null; - this.state = S_ATTRIB_VALUE_CLOSED; - } - else if (c === AMP) { - this.state = S_ENTITY; - this.entityReturnState = S_ATTRIB_VALUE_QUOTED; - } - else if (c === LESS) { - this.fail("disallowed character."); + const { chunk, limit, i: start } = this; + // eslint-disable-next-line no-constant-condition + while (true) { + if (this.i >= limit) { + // This is faster than adding codepoints one by one. + this.text += chunk.substring(start); + return; + } + const code = this.getCode(); + if (code === q || code === AMP || code === LESS) { + // This is faster than adding codepoints one by one. + const slice = chunk.substring(start, + this.i - (code <= 0xFFFF ? 1 : 2)); + switch (code) { + case q: + this.pushAttrib(this.name, this.text + slice); + this.name = this.text = ""; + this.q = null; + this.state = S_ATTRIB_VALUE_CLOSED; + return; + case AMP: + this.text += slice; + this.state = S_ENTITY; + this.entityReturnState = S_ATTRIB_VALUE_QUOTED; + return; + default: + this.text += slice; + this.fail("disallowed character."); + return; + } + } } }