Skip to content

Commit 98762c8

Browse files
committed
Make Sink/Source::buffered no-op for buffered sinks and sources
1 parent 5171a28 commit 98762c8

File tree

6 files changed

+98
-65
lines changed

6 files changed

+98
-65
lines changed

core/common/src/Core.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,24 @@ package kotlinx.io
2424
* Returns a new source that buffers reads from the source. The returned source will perform bulk
2525
* reads into its in-memory buffer. Use this wherever you read a source to get ergonomic and
2626
* efficient access to data.
27+
*
28+
* This method returns the receiver if it is already an instance of [Source].
2729
*/
28-
public fun RawSource.buffered(): Source = RealSource(this)
30+
public fun RawSource.buffered(): Source = when (this) {
31+
is Source -> this
32+
else -> RealSource(this)
33+
}
2934

3035
/**
3136
* Returns a new sink that buffers writes to the sink. The returned sink will batch writes to the sink.
3237
* Use this wherever you write to a sink to get ergonomic and efficient access to data.
38+
*
39+
* This method returns the receiver if it is already an instance of [Sink].
3340
*/
34-
public fun RawSink.buffered(): Sink = RealSink(this)
41+
public fun RawSink.buffered(): Sink = when (this) {
42+
is Sink -> this
43+
else -> RealSink(this)
44+
}
3545

3646
/**
3747
* Returns a sink that writes nowhere.

core/common/test/CommonPlatformTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ class CommonPlatformTest {
3333
}
3434

3535
@Test fun sinkBuffer() {
36-
val sink = Buffer()
37-
val buffered = (sink as RawSink).buffered()
36+
val sink = BufferBackedSink()
37+
val buffered = sink.buffered()
3838
buffered.writeUtf8("a")
39-
assertEquals(sink.size, 0L)
39+
assertEquals(sink.buffer.size, 0L)
4040
buffered.flush()
41-
assertEquals(sink.size, 1L)
41+
assertEquals(sink.buffer.size, 1L)
4242
}
4343

4444
@Test fun blackhole() {

core/common/test/CommonRealSinkTest.kt

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,68 +32,68 @@ import kotlin.test.assertFailsWith
3232
@OptIn(InternalIoApi::class)
3333
class CommonRealSinkTest {
3434
@Test fun bufferedSinkEmitsTailWhenItIsComplete() {
35-
val sink = Buffer()
36-
val bufferedSink = (sink as RawSink).buffered()
35+
val sink = BufferBackedSink()
36+
val bufferedSink = sink.buffered()
3737
bufferedSink.writeUtf8("a".repeat(Segment.SIZE - 1))
38-
assertEquals(0, sink.size)
38+
assertEquals(0, sink.buffer.size)
3939
bufferedSink.writeByte(0)
40-
assertEquals(Segment.SIZE.toLong(), sink.size)
40+
assertEquals(Segment.SIZE.toLong(), sink.buffer.size)
4141
assertEquals(0, bufferedSink.buffer.size)
4242
}
4343

4444
@Test fun bufferedSinkEmitMultipleSegments() {
45-
val sink = Buffer()
46-
val bufferedSink = (sink as RawSink).buffered()
45+
val sink = BufferBackedSink()
46+
val bufferedSink = sink.buffered()
4747
bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 4 - 1))
48-
assertEquals(Segment.SIZE.toLong() * 3L, sink.size)
48+
assertEquals(Segment.SIZE.toLong() * 3L, sink.buffer.size)
4949
assertEquals(Segment.SIZE.toLong() - 1L, bufferedSink.buffer.size)
5050
}
5151

5252
@Test fun bufferedSinkFlush() {
53-
val sink = Buffer()
54-
val bufferedSink = (sink as RawSink).buffered()
53+
val sink = BufferBackedSink()
54+
val bufferedSink = sink.buffered()
5555
bufferedSink.writeByte('a'.code.toByte())
56-
assertEquals(0, sink.size)
56+
assertEquals(0, sink.buffer.size)
5757
bufferedSink.flush()
5858
assertEquals(0, bufferedSink.buffer.size)
59-
assertEquals(1, sink.size)
59+
assertEquals(1, sink.buffer.size)
6060
}
6161

6262
@Test fun bytesEmittedToSinkWithFlush() {
63-
val sink = Buffer()
64-
val bufferedSink = (sink as RawSink).buffered()
63+
val sink = BufferBackedSink()
64+
val bufferedSink = sink.buffered()
6565
bufferedSink.writeUtf8("abc")
6666
bufferedSink.flush()
67-
assertEquals(3, sink.size)
67+
assertEquals(3, sink.buffer.size)
6868
}
6969

7070
@Test fun bytesNotEmittedToSinkWithoutFlush() {
71-
val sink = Buffer()
72-
val bufferedSink = (sink as RawSink).buffered()
71+
val sink = BufferBackedSink()
72+
val bufferedSink = sink.buffered()
7373
bufferedSink.writeUtf8("abc")
74-
assertEquals(0, sink.size)
74+
assertEquals(0, sink.buffer.size)
7575
}
7676

7777
@Test fun bytesEmittedToSinkWithEmit() {
78-
val sink = Buffer()
79-
val bufferedSink = (sink as RawSink).buffered()
78+
val sink = BufferBackedSink()
79+
val bufferedSink = sink.buffered()
8080
bufferedSink.writeUtf8("abc")
8181
bufferedSink.emit()
82-
assertEquals(3, sink.size)
82+
assertEquals(3, sink.buffer.size)
8383
}
8484

8585
@Test fun completeSegmentsEmitted() {
86-
val sink = Buffer()
87-
val bufferedSink = (sink as RawSink).buffered()
86+
val sink = BufferBackedSink()
87+
val bufferedSink = sink.buffered()
8888
bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 3))
89-
assertEquals(Segment.SIZE.toLong() * 3L, sink.size)
89+
assertEquals(Segment.SIZE.toLong() * 3L, sink.buffer.size)
9090
}
9191

9292
@Test fun incompleteSegmentsNotEmitted() {
93-
val sink = Buffer()
94-
val bufferedSink = (sink as RawSink).buffered()
93+
val sink = BufferBackedSink()
94+
val bufferedSink = sink.buffered()
9595
bufferedSink.writeUtf8("a".repeat(Segment.SIZE * 3 - 1))
96-
assertEquals(Segment.SIZE.toLong() * 2L, sink.size)
96+
assertEquals(Segment.SIZE.toLong() * 2L, sink.buffer.size)
9797
}
9898

9999
@Test fun closeWithExceptionWhenWriting() {

core/common/test/CommonRealSourceTest.kt

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,76 +47,79 @@ class CommonRealSourceTest {
4747
}
4848

4949
@Test fun requireTracksBufferFirst() {
50-
val source = Buffer()
51-
source.writeUtf8("bb")
50+
val source = BufferBackedSource()
51+
source.buffer.writeUtf8("bb")
5252

53-
val bufferedSource = (source as RawSource).buffered()
53+
val bufferedSource = source.buffered()
5454
bufferedSource.buffer.writeUtf8("aa")
5555

5656
bufferedSource.require(2)
5757
assertEquals(2, bufferedSource.buffer.size)
58-
assertEquals(2, source.size)
58+
assertEquals(2, source.buffer.size)
5959
}
6060

6161
@Test fun requireIncludesBufferBytes() {
62-
val source = Buffer()
63-
source.writeUtf8("b")
62+
val source = BufferBackedSource()
63+
source.buffer.writeUtf8("b")
6464

65-
val bufferedSource = (source as RawSource).buffered()
65+
val bufferedSource = source.buffered()
6666
bufferedSource.buffer.writeUtf8("a")
6767

6868
bufferedSource.require(2)
6969
assertEquals("ab", bufferedSource.buffer.readUtf8(2))
7070
}
7171

7272
@Test fun requireInsufficientData() {
73-
val source = Buffer()
74-
source.writeUtf8("a")
73+
val source = BufferBackedSource()
74+
source.buffer.writeUtf8("a")
7575

76-
val bufferedSource = (source as RawSource).buffered()
76+
val bufferedSource = source.buffered()
7777

7878
assertFailsWith<EOFException> {
7979
bufferedSource.require(2)
8080
}
8181
}
8282

8383
@Test fun requireReadsOneSegmentAtATime() {
84-
val source = Buffer()
85-
source.writeUtf8("a".repeat(Segment.SIZE))
86-
source.writeUtf8("b".repeat(Segment.SIZE))
84+
val source = BufferBackedSource()
85+
source.buffer.writeUtf8("a".repeat(Segment.SIZE))
86+
source.buffer.writeUtf8("b".repeat(Segment.SIZE))
8787

88-
val bufferedSource = (source as RawSource).buffered()
88+
val bufferedSource = source.buffered()
8989

9090
bufferedSource.require(2)
91-
assertEquals(Segment.SIZE.toLong(), source.size)
91+
assertEquals(Segment.SIZE.toLong(), source.buffer.size)
9292
assertEquals(Segment.SIZE.toLong(), bufferedSource.buffer.size)
9393
}
9494

9595
@Test fun skipReadsOneSegmentAtATime() {
96-
val source = Buffer()
97-
source.writeUtf8("a".repeat(Segment.SIZE))
98-
source.writeUtf8("b".repeat(Segment.SIZE))
99-
val bufferedSource = (source as RawSource).buffered()
96+
val source = BufferBackedSource()
97+
source.buffer.writeUtf8("a".repeat(Segment.SIZE))
98+
source.buffer.writeUtf8("b".repeat(Segment.SIZE))
99+
val bufferedSource = source.buffered()
100100
bufferedSource.skip(2)
101-
assertEquals(Segment.SIZE.toLong(), source.size)
101+
assertEquals(Segment.SIZE.toLong(), source.buffer.size)
102102
assertEquals(Segment.SIZE.toLong() - 2L, bufferedSource.buffer.size)
103103
}
104104

105105
@Test fun skipTracksBufferFirst() {
106-
val source = Buffer()
107-
source.writeUtf8("bb")
106+
val source = BufferBackedSource()
107+
source.buffer.writeUtf8("bb")
108108

109-
val bufferedSource = (source as RawSource).buffered()
109+
val bufferedSource = source.buffered()
110110
bufferedSource.buffer.writeUtf8("aa")
111111

112112
bufferedSource.skip(2)
113113
assertEquals(0, bufferedSource.buffer.size)
114-
assertEquals(2, source.size)
114+
assertEquals(2, source.buffer.size)
115115
}
116116

117117
@Test fun operationsAfterClose() {
118-
val source = Buffer()
119-
val bufferedSource = (source as RawSource).buffered()
118+
val source = object : RawSource {
119+
override fun readAtMostTo(sink: Buffer, byteCount: Long): Long = TODO()
120+
override fun close() = Unit
121+
}
122+
val bufferedSource = source.buffered()
120123
bufferedSource.close()
121124

122125
// Test a sample set of methods.
@@ -138,12 +141,12 @@ class CommonRealSourceTest {
138141
val write2 = Buffer().also { it.writeUtf8("b".repeat(Segment.SIZE)) }
139142
val write3 = Buffer().also { it.writeUtf8("c".repeat(Segment.SIZE)) }
140143

141-
val source = Buffer()
142-
source.writeUtf8(
144+
val source = BufferBackedSource()
145+
source.buffer.writeUtf8(
143146
"${"a".repeat(Segment.SIZE)}${"b".repeat(Segment.SIZE)}${"c".repeat(Segment.SIZE)}")
144147

145148
val mockSink = MockSink()
146-
val bufferedSource = (source as RawSource).buffered()
149+
val bufferedSource = source.buffered()
147150
assertEquals(Segment.SIZE.toLong() * 3L, bufferedSource.transferTo(mockSink))
148151
mockSink.assertLog(
149152
"write($write1, ${write1.size})",

core/common/test/DelicateApiTest.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@ class DelicateApiTest {
1313
@Test
1414
@OptIn(InternalIoApi::class)
1515
fun testWriteIntoBuffer() {
16-
val sink = Buffer()
17-
val rawSink = sink as RawSink
16+
val rawSink = BufferBackedSink()
1817
val bufferedSink = rawSink.buffered()
1918

2019
bufferedSink.writeToInternalBuffer {
2120
it.writeByte(42)
2221
}
2322

24-
assertEquals(0, sink.size)
23+
assertEquals(0, rawSink.buffer.size)
2524
assertEquals(1, bufferedSink.buffer.size)
2625

2726
bufferedSink.writeToInternalBuffer {
@@ -35,7 +34,7 @@ class DelicateApiTest {
3534
it.writeByte(0x34)
3635
}
3736

38-
assertArrayEquals(byteArrayOf(0x0, 0x12), sink.readByteArray())
37+
assertArrayEquals(byteArrayOf(0x0, 0x12), rawSink.buffer.readByteArray())
3938
assertArrayEquals(byteArrayOf(0x34), bufferedSink.buffer.readByteArray())
4039
}
4140
}

core/common/test/util.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,24 @@ fun Char.repeat(count: Int): String {
6767
fun assertArrayEquals(a: ByteArray, b: ByteArray) {
6868
assertEquals(a.contentToString(), b.contentToString())
6969
}
70+
71+
class BufferBackedSink : RawSink {
72+
val buffer = Buffer()
73+
74+
override fun write(source: Buffer, byteCount: Long) {
75+
source.readTo(buffer, byteCount)
76+
}
77+
78+
override fun flush() = Unit
79+
override fun close() = Unit
80+
}
81+
82+
class BufferBackedSource : RawSource {
83+
val buffer = Buffer()
84+
85+
override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
86+
return buffer.readAtMostTo(sink, byteCount)
87+
}
88+
89+
override fun close() = Unit
90+
}

0 commit comments

Comments
 (0)