Skip to content

Commit 07c5001

Browse files
committed
Fix #329 where parsing of a string that starts at a buffer boundary incorrectly incorporates the entire buffer.
1 parent 474b152 commit 07c5001

File tree

4 files changed

+1756
-5
lines changed

4 files changed

+1756
-5
lines changed

core/base/src/commonMain/kotlin/nl/adaptivity/xmlutil/XmlWriter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024-2025.
2+
* Copyright (c) 2024-2026.
33
*
44
* This file is part of xmlutil.
55
*
@@ -599,7 +599,7 @@ public fun XmlWriter.writeElementContent(missingNamespaces: MutableMap<String, S
599599
reader.forEach { type ->
600600
// We already moved to the next event. Add the namespaces before writing as for a DOM implementation
601601
// it is too late to do it afterwards.
602-
if (reader.eventType == EventType.START_ELEMENT && missingNamespaces != null) {
602+
if (type == EventType.START_ELEMENT && missingNamespaces != null) {
603603
@Suppress("DEPRECATION")
604604
addUndeclaredNamespaces(reader, missingNamespaces)
605605
}

core/base/src/commonMain/kotlin/nl/adaptivity/xmlutil/core/KtXmlReader.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024-2025.
2+
* Copyright (c) 2024-2026.
33
*
44
* This file is part of xmlutil.
55
*
@@ -1026,6 +1026,7 @@ public class KtXmlReader internal constructor(
10261026
var innerLoopEnd = minOf(bufCount, BUF_SIZE)
10271027
var curPos = srcBufPos
10281028

1029+
// XXX (should not be loop)
10291030
while (curPos < innerLoopEnd) {
10301031
when (bufLeft[curPos]) {
10311032
' ', '\t', '\n', '\r' -> break // whitespace
@@ -1048,8 +1049,11 @@ public class KtXmlReader internal constructor(
10481049
if (right > left + 1) pushRange(bufLeft, left, right)
10491050
right = -1
10501051
val peekChar = when (curPos + 1) {
1051-
bufCount -> '\u0000'
1052-
BUF_SIZE -> bufRight[0]
1052+
bufCount ->
1053+
'\u0000'
1054+
1055+
BUF_SIZE ->
1056+
bufRight[0]
10531057
else -> bufLeft[curPos + 1]
10541058
}
10551059
if (peekChar != '\n') {
@@ -1096,6 +1100,7 @@ public class KtXmlReader internal constructor(
10961100
if (curPos == BUF_SIZE) { // swap the buffers
10971101
srcBufPos = curPos
10981102
swapInputBuffer()
1103+
right = -1 //set it to -1 in all cases as at this point we probably parsed nothing
10991104
curPos = srcBufPos
11001105
bufCount = srcBufCount
11011106
innerLoopEnd = minOf(bufCount, BUF_SIZE)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) 2026.
3+
*
4+
* This file is part of xmlutil.
5+
*
6+
* This file is licenced to you under the Apache License, Version 2.0
7+
* (the "License"); you may not use this file except in compliance
8+
* with the License. You should have received a copy of the license
9+
* with the source distribution. Alternatively, you may obtain a copy
10+
* of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17+
* implied. See the License for the specific language governing
18+
* permissions and limitations under the License.
19+
*/
20+
21+
package net.devrieze.xmlutil.serialization.kxio.test
22+
23+
import kotlinx.serialization.SerialName
24+
import kotlinx.serialization.Serializable
25+
import nl.adaptivity.xmlutil.XmlDeclMode
26+
import nl.adaptivity.xmlutil.core.impl.multiplatform.InputStream
27+
import nl.adaptivity.xmlutil.newReader
28+
import nl.adaptivity.xmlutil.serialization.XML
29+
import nl.adaptivity.xmlutil.serialization.XmlChildrenName
30+
import nl.adaptivity.xmlutil.serialization.XmlElement
31+
import nl.adaptivity.xmlutil.serialization.XmlSerializationPolicy
32+
import nl.adaptivity.xmlutil.xmlStreaming
33+
import java.net.URI
34+
import kotlin.test.Test
35+
36+
/** Test for #329 where a Maven POM file has content invalidly parsed as whitespace */
37+
class InvalidParseToWhitespaceKXIO329 {
38+
39+
private fun defaultFormat() = XML.recommended_1_0 {
40+
policy {
41+
autoPolymorphic = false
42+
ignoreUnknownChildren()
43+
encodeDefault = XmlSerializationPolicy.XmlEncodeDefault.NEVER
44+
}
45+
xmlDeclMode = XmlDeclMode.None
46+
defaultToGenericParser = true
47+
}
48+
49+
private fun getInputStream(online: Boolean = true): InputStream = when (online) {
50+
true -> URI.create("https://repo.maven.apache.org/maven2/org/htmlunit/htmlunit/4.21.0/htmlunit-4.21.0.pom").toURL().openStream()
51+
else -> javaClass.getResourceAsStream("/htmlunit-4.21.0.pom")!!
52+
}
53+
54+
@Test
55+
fun decodingHtmlUnitShouldWorkOnline() {
56+
getInputStream(true).use { ins ->
57+
xmlStreaming.newReader(ins).use { reader ->
58+
val _ = defaultFormat().decodeFromReader<MavenInfo>(reader)
59+
}
60+
}
61+
}
62+
63+
@Test
64+
fun decodingHtmlUnitShouldWorkOffline() {
65+
getInputStream(false).use { ins ->
66+
xmlStreaming.newReader(ins).use { reader ->
67+
val _ = defaultFormat().decodeFromReader<MavenInfo>(reader)
68+
}
69+
}
70+
}
71+
72+
73+
74+
@Serializable
75+
@SerialName("project")
76+
internal data class MavenInfo(
77+
@XmlElement(true) val name: String? = null,
78+
@XmlElement(true) val url: String? = null,
79+
@XmlChildrenName("dependency") val dependencies: List<Dependency> = emptyList(),
80+
val scm: SCMInfos? = null,
81+
)
82+
83+
@Serializable
84+
@SerialName("dependency")
85+
internal data class Dependency(
86+
@XmlElement(true) val groupId: String,
87+
@XmlElement(true) val artifactId: String,
88+
@XmlElement(true) val version: String? = null,
89+
)
90+
91+
@Serializable
92+
@SerialName("scm")
93+
internal data class SCMInfos(
94+
@XmlElement(true) val url: String? = null,
95+
)
96+
97+
}

0 commit comments

Comments
 (0)