Use loops instead of recursion in hot path

Small but measurable perf win
This commit is contained in:
Shadowfacts 2023-11-27 00:04:10 -05:00
parent 29a065049e
commit 31bd174a69
1 changed files with 120 additions and 112 deletions

View File

@ -636,6 +636,7 @@ private extension Tokenizer {
} }
mutating func tokenizeTagName() -> Token? { mutating func tokenizeTagName() -> Token? {
while true {
switch nextChar() { switch nextChar() {
case "\t", "\n", "\u{000C}", " ": case "\t", "\n", "\u{000C}", " ":
state = .beforeAttributeName state = .beforeAttributeName
@ -660,16 +661,17 @@ private extension Tokenizer {
if case .startTag(var s, let selfClosing, let attributes) = currentToken { if case .startTag(var s, let selfClosing, let attributes) = currentToken {
s.append(c) s.append(c)
currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes) currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes)
return tokenizeTagName() continue
} else if case .endTag(var s) = currentToken { } else if case .endTag(var s) = currentToken {
s.append(c) s.append(c)
currentToken = .endTag(s) currentToken = .endTag(s)
return tokenizeTagName() continue
} else { } else {
fatalError("bad current token") fatalError("bad current token")
} }
} }
} }
}
mutating func tokenizeSelfClosingStartTag() -> Token? { mutating func tokenizeSelfClosingStartTag() -> Token? {
switch nextChar() { switch nextChar() {
@ -732,6 +734,7 @@ private extension Tokenizer {
} }
mutating func tokenizeAttributeName() -> Token? { mutating func tokenizeAttributeName() -> Token? {
while true {
let c = nextChar() let c = nextChar()
switch c { switch c {
case "\t", "\n", "\u{000C}", " ", "/", ">", nil: case "\t", "\n", "\u{000C}", " ", "/", ">", nil:
@ -753,14 +756,15 @@ private extension Tokenizer {
if case .startTag(let s, let selfClosing, var attributes) = currentToken { if case .startTag(let s, let selfClosing, var attributes) = currentToken {
attributes[attributes.count - 1].name.append(c) attributes[attributes.count - 1].name.append(c)
currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes) currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes)
return tokenizeAttributeName() continue
} else if case .endTag(_) = currentToken { } else if case .endTag(_) = currentToken {
return tokenizeAttributeName() continue
} else { } else {
fatalError("bad curren token") fatalError("bad curren token")
} }
} }
} }
}
mutating func tokenizeAfterAttributeName() -> Token? { mutating func tokenizeAfterAttributeName() -> Token? {
switch nextChar() { switch nextChar() {
@ -817,6 +821,7 @@ private extension Tokenizer {
} }
mutating func tokenizeAttributeValue(quotes: AttributeValueQuotation) -> Token? { mutating func tokenizeAttributeValue(quotes: AttributeValueQuotation) -> Token? {
while true {
if quotes == .unquoted { if quotes == .unquoted {
switch nextChar() { switch nextChar() {
case "\t", "\n", "\u{000C}", " ": case "\t", "\n", "\u{000C}", " ":
@ -838,7 +843,9 @@ private extension Tokenizer {
if case .startTag(let s, let selfClosing, var attributes) = currentToken { if case .startTag(let s, let selfClosing, var attributes) = currentToken {
attributes[attributes.count - 1].value.append(c) attributes[attributes.count - 1].value.append(c)
currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes) currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes)
return tokenizeAttributeValue(quotes: quotes) continue
} else if case .endTag(_) = currentToken {
continue
} else { } else {
fatalError("bad current token") fatalError("bad current token")
} }
@ -868,15 +875,16 @@ private extension Tokenizer {
if case .startTag(let s, let selfClosing, var attributes) = currentToken { if case .startTag(let s, let selfClosing, var attributes) = currentToken {
attributes[attributes.count - 1].value.append(c) attributes[attributes.count - 1].value.append(c)
currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes) currentToken = .startTag(s, selfClosing: selfClosing, attributes: attributes)
return tokenizeAttributeValue(quotes: quotes) continue
} else if case .endTag(_) = currentToken { } else if case .endTag(_) = currentToken {
return tokenizeAttributeValue(quotes: quotes) continue
} else { } else {
fatalError("bad current token") fatalError("bad current token")
} }
} }
} }
} }
}
mutating func tokenizeAfterAttributeValueQuoted() -> Token? { mutating func tokenizeAfterAttributeValueQuoted() -> Token? {
switch nextChar() { switch nextChar() {