Skip to content

Commit

Permalink
Fix readUtf8Line with multiple buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
e5l committed May 7, 2024
1 parent ec01ba7 commit d031d72
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
22 changes: 16 additions & 6 deletions ktor-io/common/src/io/ktor/utils/io/ByteReadChannelOperations.kt
Expand Up @@ -357,32 +357,42 @@ public suspend fun ByteReadChannel.discard(max: Long = Long.MAX_VALUE): Long {
return max - remaining
}

@OptIn(InternalAPI::class)
@OptIn(InternalAPI::class, InternalIoApi::class)
public suspend fun ByteReadChannel.readUTF8LineTo(out: Appendable, max: Int): Boolean {
if (isClosedForRead) return false
if (readBuffer.exhausted()) awaitContent()
if (isClosedForRead) return false

val buffer = Buffer()
var cr = readBuffer.indexOf('\r'.code.toByte())
var lf = readBuffer.indexOf('\n'.code.toByte())
var searchEnd = readBuffer.remaining
while (cr < 0 && lf < 0 && readBuffer.remaining < max && awaitContent()) {
while (cr < 0 && lf < 0 && buffer.remaining + readBuffer.remaining <= max && !isClosedForRead) {
readBuffer.transferTo(buffer)
awaitContent()

cr = readBuffer.indexOf('\r'.code.toByte(), searchEnd)
lf = readBuffer.indexOf('\n'.code.toByte(), searchEnd)
searchEnd = readBuffer.remaining
searchEnd = buffer.remaining
}

if (cr < 0 && lf < 0) {
val count = minOf(max.toLong(), readBuffer.remaining)
out.append(readBuffer.readString(count))
if (max > buffer.remaining && !readBuffer.exhausted()) {
readBuffer.readTo(buffer, max - buffer.remaining)
}
out.append(buffer.readString())
} else {
val eol = when {
cr >= 0 && lf >= 0 -> minOf(cr, lf)
cr >= 0 -> cr
else -> lf
}

out.append(readBuffer.readString(eol))
val remaining = eol - buffer.remaining
if (remaining > 0) {
readBuffer.readTo(buffer, remaining)
}
out.append(buffer.readString())
readBuffer.readByte()
if (eol == cr && lf == cr + 1) readBuffer.readByte()
}
Expand Down
27 changes: 27 additions & 0 deletions ktor-io/common/test/ReadUtf8LineTest.kt
@@ -0,0 +1,27 @@
import io.ktor.test.dispatcher.*
import io.ktor.utils.io.*
import kotlin.test.*

/*
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

class ReadUtf8LineTest {

@Test
fun testReadUtf8LineWithLongLineWithLimit() = testSuspend {
val lineSize = 1024
val line = "A".repeat(lineSize)

val channel = writer {
repeat(10) {
channel.writeStringUtf8(line)
}
}.channel

val builder = StringBuilder()
channel.readUTF8LineTo(builder, 8 * 1024)
assertEquals(8 * 1024, builder.length)
assertEquals("A".repeat(8 * 1024), builder.toString())
}
}

0 comments on commit d031d72

Please sign in to comment.