Skip to content

Commit 2b54f00

Browse files
authored
Introduce interoperability with java.io (#95)
1 parent 8cf9c5d commit 2b54f00

File tree

14 files changed

+437
-29
lines changed

14 files changed

+437
-29
lines changed

TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Implementation
99
- Index preconditions
1010
- Prototype `PipedOutput`
11+
- Benchmark overhead `java.io` integration
1112
- Test
1213
- Verify pool has no leaks
1314
- Documentation

core/commonMain/src/kotlinx/io/Input.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,12 @@ public abstract class Input : Closeable {
146146
}
147147

148148
/**
149-
* Reads the available content in current [Input] to the [destination] buffer.
149+
* Reads the available content in the current [Input] to the [destination] buffer.
150150
*
151-
* If no bytes are available in the input, [fill] method will be called directly on
152-
* the [destination] buffer without an extra copy.
151+
* If no bytes are available, [fill] method will be called directly on the [destination] buffer without an extra copy.
153152
* Otherwise, available bytes are copied to the destination.
154153
*
155-
* @return number of bytes written in the [destination].
154+
* @return number of bytes written to the [destination].
156155
*/
157156
public fun readAvailableTo(
158157
destination: Buffer,

core/commonMain/src/kotlinx/io/InputOperations.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ public fun Input.discardExact(count: Int): Int {
147147
return count
148148
}
149149

150-
151150
/**
152151
* Reads a [Byte] from this Input.
153152
*

core/commonTest/src/kotlinx/io/LimitingInputTest.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,4 @@ class LimitingInputTest {
5454
assertTrue(closed)
5555
}
5656

57-
private fun StringInput(str: String) = ByteArrayInput(str.encodeToByteArray())
5857
}

core/commonTest/src/kotlinx/io/TestUtils.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package kotlinx.io
33
import kotlin.test.*
44

55
fun assertArrayEquals(expected: ByteArray, actual: ByteArray) {
6-
assertEquals(expected.size, actual.size)
6+
assertEquals(expected.size, actual.size, "Expected array lengths to be equal")
77
assertEquals(expected.toHexString(), actual.toHexString())
88
}
99

@@ -17,4 +17,6 @@ internal fun Bytes.useInput(block: Input.() -> Unit) {
1717
} finally {
1818
close()
1919
}
20-
}
20+
}
21+
22+
public fun StringInput(string: String) = ByteArrayInput(string.encodeToByteArray())

core/jvmMain/src/kotlinx/io/Inputs.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package kotlinx.io
2+
3+
import kotlinx.io.internal.*
4+
import java.io.*
5+
6+
/**
7+
* Returns an [InputStream] that uses the current [Input] as an underlying source of data.
8+
* Closing the resulting [InputStream] will close the input.
9+
*/
10+
public fun Input.asInputStream(): InputStream = InputStreamFromInput(this)
11+
12+
/**
13+
* Returns an [Input] that uses the current [InputStream] as an underlying source of data.
14+
* Closing the resulting [Input] will close the input stream.
15+
*/
16+
public fun InputStream.asInput(): Input = InputFromInputStream(this)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package kotlinx.io
2+
3+
import kotlinx.io.internal.*
4+
import java.io.*
5+
6+
/**
7+
* Returns an [OutputStream] that uses the current [Output] as the destination.
8+
* Closing the resulting [OutputStream] will close the input.
9+
*/
10+
public fun Output.asOutputStream(): OutputStream = OutputStreamFromOutput(this)
11+
12+
/**
13+
* Returns an [Output] that uses the current [OutputStream] as the destination.
14+
* Closing the resulting [Output] will close the input stream.
15+
*/
16+
public fun OutputStream.asOutput(): Output = OutputFromOutputStream(this)

core/jvmMain/src/kotlinx/io/internal/Atomic.kt

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package kotlinx.io.internal
2+
3+
import kotlinx.io.*
4+
import kotlinx.io.buffer.*
5+
import java.io.*
6+
7+
internal class InputStreamFromInput(private val input: Input) : InputStream() {
8+
override fun read(): Int {
9+
if (input.exhausted()) {
10+
return -1
11+
}
12+
return input.readByte().toInt() and 0xFF
13+
}
14+
15+
override fun read(b: ByteArray): Int {
16+
if (b.isEmpty()) return 0
17+
val result = input.readAvailableTo(bufferOf(b))
18+
if (result == 0) return -1
19+
return result
20+
}
21+
22+
override fun read(b: ByteArray, off: Int, len: Int): Int {
23+
if (len == 0) return 0
24+
val result = input.readAvailableTo(bufferOf(b), off, off + len)
25+
if (result == 0) return -1
26+
return result
27+
}
28+
29+
override fun close() {
30+
input.close()
31+
}
32+
}
33+
34+
internal class InputFromInputStream(private val inputStream: InputStream) : Input() {
35+
override fun closeSource() {
36+
inputStream.close()
37+
}
38+
39+
override fun fill(buffer: Buffer, startIndex: Int, endIndex: Int): Int {
40+
// Zero-copy attempt
41+
if (buffer.buffer.hasArray()) {
42+
val result = inputStream.read(buffer.buffer.array(), startIndex, endIndex - startIndex)
43+
return result.coerceAtLeast(0) // -1 when IS is closed
44+
}
45+
46+
for (i in startIndex until endIndex) {
47+
val byte = inputStream.read()
48+
if (byte == -1) return (i - startIndex)
49+
buffer[i] = byte.toByte()
50+
}
51+
return endIndex - startIndex
52+
}
53+
}
54+
55+
internal class OutputStreamFromOutput(private val output: Output) : OutputStream() {
56+
override fun write(b: Int) {
57+
output.writeByte(b.toByte())
58+
}
59+
60+
override fun write(b: ByteArray) {
61+
output.writeBuffer(bufferOf(b))
62+
}
63+
64+
override fun write(b: ByteArray, off: Int, len: Int) {
65+
output.writeBuffer(bufferOf(b), off, off + len)
66+
}
67+
68+
override fun flush() {
69+
output.flush()
70+
}
71+
72+
override fun close() {
73+
output.close()
74+
}
75+
}
76+
77+
internal class OutputFromOutputStream(private val outputStream: OutputStream) : Output() {
78+
79+
override fun closeSource() {
80+
outputStream.close()
81+
}
82+
83+
override fun flush(source: Buffer, startIndex: Int, endIndex: Int) {
84+
if (source.buffer.hasArray()) {
85+
return outputStream.write(source.buffer.array(), startIndex, endIndex - startIndex)
86+
}
87+
88+
for (i in startIndex until endIndex) {
89+
outputStream.write(source[i].toInt())
90+
}
91+
}
92+
}

core/jvmMain/src/kotlinx/io/pool/DefaultPool.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package kotlinx.io.pool
22

3-
import kotlinx.io.internal.*
43
import java.util.concurrent.atomic.*
54

65
private const val MULTIPLIER = 4
@@ -92,7 +91,6 @@ actual abstract class DefaultPool<T : Any> actual constructor(actual final overr
9291
}
9392

9493
companion object {
95-
// todo: replace with atomicfu, remove companion object
96-
private val Top = longUpdater(DefaultPool<*>::top)
94+
private val Top = AtomicLongFieldUpdater.newUpdater(DefaultPool::class.java, DefaultPool<*>::top.name)
9795
}
9896
}

core/jvmTest/src/kotlinx/io/InputOutputTest.kt renamed to core/jvmTest/src/kotlinx/io/CustomPoolTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package kotlinx.io
44
import kotlinx.io.buffer.*
55
import kotlin.test.*
66

7-
class InputOutputTestJvm {
7+
class CustomPoolTest {
88

99
@Test
1010
fun testCustomPools() {
@@ -38,4 +38,4 @@ class InputOutputTestJvm {
3838
input.readAvailableTo(output)
3939
output.flush()
4040
}
41-
}
41+
}

0 commit comments

Comments
 (0)