summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Dinsmore <nathan@users.noreply.github.com>2015-06-18 18:35:20 -0400
committerNathan Dinsmore <nathan@users.noreply.github.com>2015-06-18 18:35:20 -0400
commit6fbd2f27896d9cd749d92cc90495c4c4d7ab0ab0 (patch)
tree3342f9a511391b42876dd0f9d5d946db0487a85d
parente1b104948b5162a014fa752f394d0e722664499c (diff)
downloadsnap-6fbd2f27896d9cd749d92cc90495c4c4d7ab0ab0.tar.gz
snap-6fbd2f27896d9cd749d92cc90495c4c4d7ab0ab0.zip
Speed up parser
About an order of magnitude faster (268.5 ms => 31.3 ms).
-rw-r--r--xml.js119
1 files changed, 30 insertions, 89 deletions
diff --git a/xml.js b/xml.js
index d1cdf68..16b03f0 100644
--- a/xml.js
+++ b/xml.js
@@ -61,7 +61,7 @@
*/
-/*global modules, isString, detect, Node, isNil*/
+/*global modules, detect, Node, isNil*/
// Global stuff ////////////////////////////////////////////////////////
@@ -85,7 +85,8 @@ function ReadStream(arrayOrString) {
// ReadStream constants:
-ReadStream.prototype.space = /[\s]/;
+ReadStream.prototype.nonSpace = /\S|$/g;
+ReadStream.prototype.nonWord = /[\s\>\/\=]|$/g;
// ReadStream accessing:
@@ -115,46 +116,26 @@ ReadStream.prototype.atEnd = function () {
// ReadStream accessing String contents:
-ReadStream.prototype.upTo = function (regex) {
- var i, start;
- if (!isString(this.contents)) {return ''; }
- i = this.contents.substr(this.index).search(regex);
- if (i === -1) {
- return '';
- }
- start = this.index;
- this.index += i;
- return this.contents.substring(start, this.index);
+ReadStream.prototype.upTo = function (str) {
+ var i = this.contents.indexOf(str, this.index);
+ return i === -1 ? '' : this.contents.slice(this.index, this.index = i);
};
-ReadStream.prototype.peekUpTo = function (regex) {
- if (!isString(this.contents)) {return ''; }
- var i = this.contents.substr(this.index).search(regex);
- if (i === -1) {
- return '';
- }
- return this.contents.substring(this.index, this.index + i);
+ReadStream.prototype.peekUpTo = function (str) {
+ var i = this.contents.indexOf(str, this.index);
+ return i === -1 ? '' : this.contents.slice(this.index, i);
};
ReadStream.prototype.skipSpace = function () {
- if (!isString(this.contents)) {return ''; }
- var ch = this.peek();
- while (this.space.test(ch) && ch !== '') {
- this.skip();
- ch = this.peek();
- }
+ this.nonSpace.lastIndex = this.index;
+ var result = this.nonSpace.exec(this.contents);
+ if (result) this.index = result.index;
};
ReadStream.prototype.word = function () {
- var i, start;
- if (!isString(this.contents)) {return ''; }
- i = this.contents.substr(this.index).search(/[\s\>\/\=]|$/);
- if (i === -1) {
- return '';
- }
- start = this.index;
- this.index += i;
- return this.contents.substring(start, this.index);
+ this.nonWord.lastIndex = this.index;
+ var result = this.nonWord.exec(this.contents);
+ return result ? this.contents.slice(this.index, this.index = result.index) : '';
};
// XML_Element ///////////////////////////////////////////////////////////
@@ -190,9 +171,7 @@ XML_Element.prototype.init = function (tag, contents, parent) {
XML_Element.uber.init.call(this);
// override inherited properties
- if (parent instanceof XML_Element) {
- parent.addChild(this);
- }
+ if (parent) parent.addChild(this);
};
// XML_Element DOM navigation: (aside from what's inherited from Node)
@@ -318,51 +297,18 @@ XML_Element.prototype.escape = function (string, ignoreQuotes) {
};
XML_Element.prototype.unescape = function (string) {
- var stream = new ReadStream(string),
- result = '',
- ch,
- esc;
-
- function nextPut(str) {
- result += str;
- stream.upTo(';');
- stream.skip();
- }
-
- while (!stream.atEnd()) {
- ch = stream.next();
- if (ch === '&') {
- esc = stream.peekUpTo(';');
- switch (esc) {
- case 'apos':
- nextPut('\'');
- break;
- case 'quot':
- nextPut('\"');
- break;
- case 'lt':
- nextPut('<');
- break;
- case 'gt':
- nextPut('>');
- break;
- case 'amp':
- nextPut('&');
- break;
- case '#xD':
- nextPut('\n');
- break;
- case '#126':
- nextPut('~');
- break;
- default:
- result += ch;
- }
- } else {
- result += ch;
+ return string.replace(/&(amp|apos|quot|lt|gt|#xD|#126);/g, function(_, name) {
+ switch (name) {
+ case 'amp': return '&';
+ case 'apos': return '\'';
+ case 'quot': return '"';
+ case 'lt': return '<';
+ case 'gt': return '>';
+ case '#xD': return '\n';
+ case '#126': return '~';
+ default: console.warn('unreachable');
}
- }
- return result;
+ });
};
// XML_Element parsing:
@@ -375,10 +321,7 @@ XML_Element.prototype.parseString = function (string) {
};
XML_Element.prototype.parseStream = function (stream) {
- var key,
- value,
- ch,
- child;
+ var key, value, ch, child;
// tag:
this.tag = stream.word();
@@ -395,9 +338,7 @@ XML_Element.prototype.parseStream = function (stream) {
stream.skipSpace();
ch = stream.next();
if (ch !== '"' && ch !== "'") {
- throw new Error(
- 'Expected single- or double-quoted attribute value'
- );
+ throw new Error('Expected single- or double-quoted attribute value');
}
value = stream.upTo(ch);
stream.skip(1);
@@ -407,7 +348,7 @@ XML_Element.prototype.parseStream = function (stream) {
}
// empty tag:
- if (stream.peek() === '/') {
+ if (ch === '/') {
stream.skip();
if (stream.next() !== '>') {
throw new Error('Expected ">" after "/" in empty tag');