Skip to content
This repository has been archived by the owner on Jul 8, 2022. It is now read-only.

Commit

Permalink
Optimize StrReader and Xml parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Mar 11, 2019
1 parent 12289d9 commit d4e28b8
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
Expand Up @@ -168,17 +168,19 @@ data class Xml(
fun decode(r: StrReader): String = buildString {
while (!r.eof) {
@Suppress("LiftReturnOrAssignment") // Performance?
when (val c = r.readChar()) {
'&' -> {
val value = r.readUntilIncluded(';') ?: ""
val full = "&$value"
append(when {
value.startsWith('#') -> "${value.substring(1, value.length - 1).toInt().toChar()}"
entityToChar.contains(full) -> "${entityToChar[full]}"
else -> full
})
}
else -> append(c)
val plain = r.readUntil('&')
if (plain != null) {
append(plain)
}
if (r.eof) break

r.skipExpect('&')
val value = r.readUntilIncluded(';') ?: ""
val full = "&$value"
when {
value.startsWith('#') -> append(value.substring(1, value.length - 1).toInt().toChar())
entityToChar.contains(full) -> append(entityToChar[full])
else -> append(full)
}
}
}
Expand All @@ -200,13 +202,14 @@ data class Xml(
if (r.eof) break

r.skipExpect('<')
var res: Element? = null
when {
r.tryExpect("![CDATA[") -> {
val start = r.pos
while (!r.eof) {
val end = r.pos
if (r.tryExpect("]]>")) {
yield(Element.Text(r.createRange(start, end).text))
res = Element.Text(r.createRange(start, end).text)
break
}
r.readChar()
Expand All @@ -217,7 +220,7 @@ data class Xml(
while (!r.eof) {
val end = r.pos
if (r.tryExpect("-->")) {
yield(Element.CommentTag(r.createRange(start, end).text))
res = Element.CommentTag(r.createRange(start, end).text)
break
}
r.readChar()
Expand Down Expand Up @@ -253,15 +256,18 @@ data class Xml(
val openclose = r.tryExpect('/')
val processingInstructionEnd = r.tryExpect('?')
r.skipExpect('>')
when {
processingInstruction || processingEntityOrDocType ->
yield(Element.ProcessingInstructionTag(name, attributes))
openclose -> yield(Element.OpenCloseTag(name, attributes))
close -> yield(Element.CloseTag(name))
else -> yield(Element.OpenTag(name, attributes))
res = when {
processingInstruction || processingEntityOrDocType -> Element.ProcessingInstructionTag(name, attributes)
openclose -> Element.OpenCloseTag(name, attributes)
close -> Element.CloseTag(name)
else -> Element.OpenTag(name, attributes)
}
}
}

if (res != null) {
yield(res)
}
}
}

Expand Down
10 changes: 8 additions & 2 deletions korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt
Expand Up @@ -49,8 +49,14 @@ class StrReader(val str: String, val file: String = "file", var pos: Int = 0) {
fun peek(): Char = if (hasMore) this.str[this.pos] else '\u0000'
fun peekChar(): Char = if (hasMore) this.str[this.pos] else '\u0000'
fun read(count: Int): String = this.peek(count).apply { skip(count) }
fun skipUntil(char: Char) = run { while (hasMore && this.peekChar() != char) this.readChar() }
fun skipUntilIncluded(char: Char) = run { while (hasMore && this.readChar() != char) Unit }
fun skipUntil(char: Char) {
val skipPos = this.str.indexOf(char, pos)
pos = (if (skipPos >= 0) skipPos else length)
}
fun skipUntilIncluded(char: Char) {
skipUntil(char)
if (!eof && peekChar() == char) skip(1)
}
//inline fun skipWhile(check: (Char) -> Boolean) = run { while (check(this.peekChar())) this.skip(1) }
inline fun skipWhile(filter: (Char) -> Boolean) = run { while (hasMore && filter(this.peekChar())) this.readChar() }

Expand Down
@@ -0,0 +1,21 @@
package com.soywiz.korio.serialization.xml

import kotlin.test.*

class XmlEntitiesTest {
@Test
fun testDecode() {
assertEquals("hello", Xml.Entities.decode("hello"))
assertEquals("\"", Xml.Entities.decode("&quot;"))
assertEquals("hello\"world", Xml.Entities.decode("hello&quot;world"))
assertEquals("hello\"world\"", Xml.Entities.decode("hello&quot;world&quot;"))
}

@Test
fun testEncode() {
assertEquals("hello", Xml.Entities.encode("hello"))
assertEquals("&quot;", Xml.Entities.encode("\""))
assertEquals("hello&quot;world", Xml.Entities.encode("hello\"world"))
assertEquals("hello&quot;world&quot;", Xml.Entities.encode("hello\"world\""))
}
}

0 comments on commit d4e28b8

Please sign in to comment.