From 68cb66ff0eec75087fb6b0474c94172d094e1934 Mon Sep 17 00:00:00 2001 From: meztihn Date: Sun, 4 Feb 2018 13:30:13 +0500 Subject: [PATCH] KT-11208 Remove readLine's lineSeparator parameter --- libraries/stdlib/src/kotlin/io/Console.kt | 32 ++++++++++++----------- libraries/stdlib/test/io/Console.kt | 25 +++++------------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/libraries/stdlib/src/kotlin/io/Console.kt b/libraries/stdlib/src/kotlin/io/Console.kt index 5f0ffddcd73..1c4adcaa46c 100644 --- a/libraries/stdlib/src/kotlin/io/Console.kt +++ b/libraries/stdlib/src/kotlin/io/Console.kt @@ -137,10 +137,7 @@ public inline fun println() { } private const val BUFFER_SIZE: Int = 32 - -// Since System.in can change its value on the course of program running, we should always delegate to current value. -private val stdin: InputStream - get() = System.`in` +private const val LINE_SEPARATOR_MAX_LENGTH: Int = 2 private var cachedDecoder: CharsetDecoder? = null @@ -157,14 +154,13 @@ private fun decoderFor(charset: Charset): CharsetDecoder { * * @return the line read or `null` if the input stream is redirected to a file and the end of file has been reached. */ -fun readLine(lineSeparator: String = System.lineSeparator(), charset: Charset = Charset.defaultCharset()): String? = - readLine(stdin, lineSeparator, decoderFor(charset)) +fun readLine(charset: Charset = Charset.defaultCharset()): String? = readLine(System.`in`, decoderFor(charset)) -internal fun readLine(inputStream: InputStream, lineSeparator: String, decoder: CharsetDecoder): String? { +internal fun readLine(inputStream: InputStream, decoder: CharsetDecoder): String? { require(decoder.maxCharsPerByte() <= 1) { "Encodings with multiple chars per byte are not supported" } val byteBuffer = ByteBuffer.allocate(BUFFER_SIZE) - val charBuffer = CharBuffer.allocate(lineSeparator.length) + val charBuffer = CharBuffer.allocate(LINE_SEPARATOR_MAX_LENGTH) val stringBuilder = StringBuilder() var read = inputStream.read() @@ -172,7 +168,7 @@ internal fun readLine(inputStream: InputStream, lineSeparator: String, decoder: while (read != -1) { byteBuffer.put(read.toByte()) if (decoder.tryDecode(byteBuffer, charBuffer, false)) { - if (charBuffer.contentEquals(lineSeparator)) { + if (charBuffer.containsLineSeparator()) { break } if (!charBuffer.hasRemaining()) { @@ -188,9 +184,16 @@ internal fun readLine(inputStream: InputStream, lineSeparator: String, decoder: } with(charBuffer) { - if (!contentEquals(lineSeparator)) { - flip() - while (hasRemaining()) stringBuilder.append(get()) + val length = position() + val first = get(0) + val second = get(1) + flip() + when (length) { + 2 -> { + if (!(first == '\r' && second == '\n')) stringBuilder.append(first) + if (second != '\n') stringBuilder.append(second) + } + 1 -> if (first != '\n') stringBuilder.append(first) } } @@ -208,9 +211,8 @@ private fun CharsetDecoder.tryDecode(byteBuffer: ByteBuffer, charBuffer: CharBuf } } -private fun CharBuffer.contentEquals(string: String): Boolean { - flip() - return string.contentEquals(this).also { flipBack() } +private fun CharBuffer.containsLineSeparator(): Boolean { + return get(1) == '\n' || get(0) == '\n' } private fun Buffer.flipBack(): Buffer = apply { diff --git a/libraries/stdlib/test/io/Console.kt b/libraries/stdlib/test/io/Console.kt index f4371fd6ee7..70b911ba7e2 100644 --- a/libraries/stdlib/test/io/Console.kt +++ b/libraries/stdlib/test/io/Console.kt @@ -21,7 +21,7 @@ import java.nio.charset.Charset import kotlin.test.* class ConsoleTest { - private val defaultLineSeparator: String = "\\n" + private val defaultLineSeparator: String = "\n" @Test fun shouldReadEmptyLine() { @@ -45,14 +45,8 @@ class ConsoleTest { @Test fun shouldReadWindowsLineSeparator() { - val lineSeparator = "\\r\\n" - testReadLine("first${lineSeparator}second", "first", "second", lineSeparator = lineSeparator) - } - - @Test - fun shouldReadOldMacLineSeparator() { - val lineSeparator = "\\r" - testReadLine("first${lineSeparator}second", "first", "second", lineSeparator = lineSeparator) + val lineSeparator = "\r\n" + testReadLine("first${lineSeparator}second", "first", "second") } @Test @@ -60,20 +54,15 @@ class ConsoleTest { testReadLine("first${defaultLineSeparator}second", "first", "second", charset = Charsets.UTF_32) } - private fun testReadLine( - text: String, - vararg expected: String, - lineSeparator: String = defaultLineSeparator, - charset: Charset = Charsets.UTF_8 - ) { - val actual = readLines(text, lineSeparator, charset) + private fun testReadLine(text: String, vararg expected: String, charset: Charset = Charsets.UTF_8) { + val actual = readLines(text, charset) assertEquals(expected.asList(), actual) } - private fun readLines(text: String, lineSeparator: String, charset: Charset): List { + private fun readLines(text: String, charset: Charset): List { text.byteInputStream(charset).use { stream -> val decoder = charset.newDecoder() - return generateSequence { readLine(stream, lineSeparator, decoder) }.toList().also { + return generateSequence { readLine(stream, decoder) }.toList().also { assertTrue("All bytes should be read") { stream.read() == -1 } } }