Skip to content

Commit 72fa4ee

Browse files
authored
[PR 2/5] Introduce unsafe API for bulk read/write ops (#334)
* Introduce unsafe API for bulk read/write ops. The API aimed to facilitate integration with other frameworks and libraries. Implemented API was initially described in the "Bulk API" subsection of #135 (comment)
1 parent 6f95a1b commit 72fa4ee

28 files changed

+1563
-81
lines changed

core/api/kotlinx-io-core.api

+44
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ public final class kotlinx/io/Buffer : kotlinx/io/Sink, kotlinx/io/Source {
1010
public fun flush ()V
1111
public final fun get (J)B
1212
public fun getBuffer ()Lkotlinx/io/Buffer;
13+
public final synthetic fun getHead ()Lkotlinx/io/Segment;
1314
public final fun getSize ()J
15+
public final synthetic fun getSizeMut ()J
16+
public final synthetic fun getTail ()Lkotlinx/io/Segment;
1417
public fun hintEmit ()V
1518
public fun peek ()Lkotlinx/io/Source;
1619
public fun readAtMostTo (Lkotlinx/io/Buffer;J)J
@@ -20,12 +23,17 @@ public final class kotlinx/io/Buffer : kotlinx/io/Sink, kotlinx/io/Source {
2023
public fun readLong ()J
2124
public fun readShort ()S
2225
public fun readTo (Lkotlinx/io/RawSink;J)V
26+
public final synthetic fun recycleTail ()V
2327
public fun request (J)Z
2428
public fun require (J)V
29+
public final synthetic fun setHead (Lkotlinx/io/Segment;)V
30+
public final synthetic fun setSizeMut (J)V
31+
public final synthetic fun setTail (Lkotlinx/io/Segment;)V
2532
public fun skip (J)V
2633
public fun toString ()Ljava/lang/String;
2734
public fun transferFrom (Lkotlinx/io/RawSource;)J
2835
public fun transferTo (Lkotlinx/io/RawSink;)J
36+
public final synthetic fun writableSegment (I)Lkotlinx/io/Segment;
2937
public fun write (Lkotlinx/io/Buffer;J)V
3038
public fun write (Lkotlinx/io/RawSource;J)V
3139
public fun write ([BII)V
@@ -92,6 +100,24 @@ public abstract interface class kotlinx/io/RawSource : java/lang/AutoCloseable {
92100
public abstract fun readAtMostTo (Lkotlinx/io/Buffer;J)J
93101
}
94102

103+
public final class kotlinx/io/Segment {
104+
public synthetic fun <init> ([BIIZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
105+
public final synthetic fun dataAsByteArray (Z)[B
106+
public final synthetic fun getLimit ()I
107+
public final synthetic fun getNext ()Lkotlinx/io/Segment;
108+
public final synthetic fun getPos ()I
109+
public final synthetic fun getRemainingCapacity ()I
110+
public final synthetic fun getSize ()I
111+
public final synthetic fun setLimit (I)V
112+
public final synthetic fun setNext (Lkotlinx/io/Segment;)V
113+
public final synthetic fun setPos (I)V
114+
public final synthetic fun writeBackData ([BI)V
115+
}
116+
117+
public final class kotlinx/io/SegmentKt {
118+
public static final fun isEmpty (Lkotlinx/io/Segment;)Z
119+
}
120+
95121
public abstract interface class kotlinx/io/Sink : kotlinx/io/RawSink {
96122
public abstract fun emit ()V
97123
public abstract fun flush ()V
@@ -186,6 +212,9 @@ public final class kotlinx/io/SourcesKt {
186212
public static final fun startsWith (Lkotlinx/io/Source;B)Z
187213
}
188214

215+
public abstract interface annotation class kotlinx/io/UnsafeIoApi : java/lang/annotation/Annotation {
216+
}
217+
189218
public final class kotlinx/io/Utf8Kt {
190219
public static final fun readCodePointValue (Lkotlinx/io/Source;)I
191220
public static final fun readLine (Lkotlinx/io/Source;)Ljava/lang/String;
@@ -253,3 +282,18 @@ public final class kotlinx/io/files/PathsKt {
253282
public static final fun sourceDeprecated (Lkotlinx/io/files/Path;)Lkotlinx/io/Source;
254283
}
255284

285+
public final class kotlinx/io/unsafe/UnsafeBufferOperations {
286+
public static final field INSTANCE Lkotlinx/io/unsafe/UnsafeBufferOperations;
287+
public final fun getMaxSafeWriteCapacity ()I
288+
public final fun moveToTail (Lkotlinx/io/Buffer;[BII)V
289+
public static synthetic fun moveToTail$default (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;[BIIILjava/lang/Object;)V
290+
public final fun readFromHead (Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function3;)V
291+
public final fun writeToTail (Lkotlinx/io/Buffer;ILkotlin/jvm/functions/Function3;)V
292+
}
293+
294+
public final class kotlinx/io/unsafe/UnsafeBufferOperationsJvmKt {
295+
public static final fun readBulk (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;[Ljava/nio/ByteBuffer;Lkotlin/jvm/functions/Function2;)V
296+
public static final fun readFromHead (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;Lkotlin/jvm/functions/Function1;)V
297+
public static final fun writeToTail (Lkotlinx/io/unsafe/UnsafeBufferOperations;Lkotlinx/io/Buffer;ILkotlin/jvm/functions/Function1;)V
298+
}
299+

core/api/kotlinx-io-core.klib.api

+40-1
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,14 @@ final class kotlinx.io/Buffer : kotlinx.io/Sink, kotlinx.io/Source { // kotlinx.
5555
final fun readLong(): kotlin/Long // kotlinx.io/Buffer.readLong|readLong(){}[0]
5656
final fun readShort(): kotlin/Short // kotlinx.io/Buffer.readShort|readShort(){}[0]
5757
final fun readTo(kotlinx.io/RawSink, kotlin/Long) // kotlinx.io/Buffer.readTo|readTo(kotlinx.io.RawSink;kotlin.Long){}[0]
58+
final fun recycleTail() // kotlinx.io/Buffer.recycleTail|recycleTail(){}[0]
5859
final fun request(kotlin/Long): kotlin/Boolean // kotlinx.io/Buffer.request|request(kotlin.Long){}[0]
5960
final fun require(kotlin/Long) // kotlinx.io/Buffer.require|require(kotlin.Long){}[0]
6061
final fun skip(kotlin/Long) // kotlinx.io/Buffer.skip|skip(kotlin.Long){}[0]
6162
final fun toString(): kotlin/String // kotlinx.io/Buffer.toString|toString(){}[0]
6263
final fun transferFrom(kotlinx.io/RawSource): kotlin/Long // kotlinx.io/Buffer.transferFrom|transferFrom(kotlinx.io.RawSource){}[0]
6364
final fun transferTo(kotlinx.io/RawSink): kotlin/Long // kotlinx.io/Buffer.transferTo|transferTo(kotlinx.io.RawSink){}[0]
65+
final fun writableSegment(kotlin/Int): kotlinx.io/Segment // kotlinx.io/Buffer.writableSegment|writableSegment(kotlin.Int){}[0]
6466
final fun write(kotlin/ByteArray, kotlin/Int, kotlin/Int) // kotlinx.io/Buffer.write|write(kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
6567
final fun write(kotlinx.io/Buffer, kotlin/Long) // kotlinx.io/Buffer.write|write(kotlinx.io.Buffer;kotlin.Long){}[0]
6668
final fun write(kotlinx.io/RawSource, kotlin/Long) // kotlinx.io/Buffer.write|write(kotlinx.io.RawSource;kotlin.Long){}[0]
@@ -70,8 +72,34 @@ final class kotlinx.io/Buffer : kotlinx.io/Sink, kotlinx.io/Source { // kotlinx.
7072
final fun writeShort(kotlin/Short) // kotlinx.io/Buffer.writeShort|writeShort(kotlin.Short){}[0]
7173
final val buffer // kotlinx.io/Buffer.buffer|{}buffer[0]
7274
final fun <get-buffer>(): kotlinx.io/Buffer // kotlinx.io/Buffer.buffer.<get-buffer>|<get-buffer>(){}[0]
73-
final var size // kotlinx.io/Buffer.size|{}size[0]
75+
final val size // kotlinx.io/Buffer.size|{}size[0]
7476
final fun <get-size>(): kotlin/Long // kotlinx.io/Buffer.size.<get-size>|<get-size>(){}[0]
77+
final var head // kotlinx.io/Buffer.head|{}head[0]
78+
final fun <get-head>(): kotlinx.io/Segment? // kotlinx.io/Buffer.head.<get-head>|<get-head>(){}[0]
79+
final fun <set-head>(kotlinx.io/Segment?) // kotlinx.io/Buffer.head.<set-head>|<set-head>(kotlinx.io.Segment?){}[0]
80+
final var sizeMut // kotlinx.io/Buffer.sizeMut|{}sizeMut[0]
81+
final fun <get-sizeMut>(): kotlin/Long // kotlinx.io/Buffer.sizeMut.<get-sizeMut>|<get-sizeMut>(){}[0]
82+
final fun <set-sizeMut>(kotlin/Long) // kotlinx.io/Buffer.sizeMut.<set-sizeMut>|<set-sizeMut>(kotlin.Long){}[0]
83+
final var tail // kotlinx.io/Buffer.tail|{}tail[0]
84+
final fun <get-tail>(): kotlinx.io/Segment? // kotlinx.io/Buffer.tail.<get-tail>|<get-tail>(){}[0]
85+
final fun <set-tail>(kotlinx.io/Segment?) // kotlinx.io/Buffer.tail.<set-tail>|<set-tail>(kotlinx.io.Segment?){}[0]
86+
}
87+
final class kotlinx.io/Segment { // kotlinx.io/Segment|null[0]
88+
final fun dataAsByteArray(kotlin/Boolean): kotlin/ByteArray // kotlinx.io/Segment.dataAsByteArray|dataAsByteArray(kotlin.Boolean){}[0]
89+
final fun writeBackData(kotlin/ByteArray, kotlin/Int) // kotlinx.io/Segment.writeBackData|writeBackData(kotlin.ByteArray;kotlin.Int){}[0]
90+
final val remainingCapacity // kotlinx.io/Segment.remainingCapacity|{}remainingCapacity[0]
91+
final fun <get-remainingCapacity>(): kotlin/Int // kotlinx.io/Segment.remainingCapacity.<get-remainingCapacity>|<get-remainingCapacity>(){}[0]
92+
final val size // kotlinx.io/Segment.size|{}size[0]
93+
final fun <get-size>(): kotlin/Int // kotlinx.io/Segment.size.<get-size>|<get-size>(){}[0]
94+
final var limit // kotlinx.io/Segment.limit|{}limit[0]
95+
final fun <get-limit>(): kotlin/Int // kotlinx.io/Segment.limit.<get-limit>|<get-limit>(){}[0]
96+
final fun <set-limit>(kotlin/Int) // kotlinx.io/Segment.limit.<set-limit>|<set-limit>(kotlin.Int){}[0]
97+
final var next // kotlinx.io/Segment.next|{}next[0]
98+
final fun <get-next>(): kotlinx.io/Segment? // kotlinx.io/Segment.next.<get-next>|<get-next>(){}[0]
99+
final fun <set-next>(kotlinx.io/Segment?) // kotlinx.io/Segment.next.<set-next>|<set-next>(kotlinx.io.Segment?){}[0]
100+
final var pos // kotlinx.io/Segment.pos|{}pos[0]
101+
final fun <get-pos>(): kotlin/Int // kotlinx.io/Segment.pos.<get-pos>|<get-pos>(){}[0]
102+
final fun <set-pos>(kotlin/Int) // kotlinx.io/Segment.pos.<set-pos>|<set-pos>(kotlin.Int){}[0]
75103
}
76104
final fun (kotlinx.io.files/Path).kotlinx.io.files/sink(): kotlinx.io/Sink // kotlinx.io.files/sink|sink@kotlinx.io.files.Path(){}[0]
77105
final fun (kotlinx.io.files/Path).kotlinx.io.files/source(): kotlinx.io/Source // kotlinx.io.files/source|source@kotlinx.io.files.Path(){}[0]
@@ -81,6 +109,7 @@ final fun (kotlinx.io/Buffer).kotlinx.io/readString(): kotlin/String // kotlinx.
81109
final fun (kotlinx.io/Buffer).kotlinx.io/snapshot(): kotlinx.io.bytestring/ByteString // kotlinx.io/snapshot|snapshot@kotlinx.io.Buffer(){}[0]
82110
final fun (kotlinx.io/RawSink).kotlinx.io/buffered(): kotlinx.io/Sink // kotlinx.io/buffered|buffered@kotlinx.io.RawSink(){}[0]
83111
final fun (kotlinx.io/RawSource).kotlinx.io/buffered(): kotlinx.io/Source // kotlinx.io/buffered|buffered@kotlinx.io.RawSource(){}[0]
112+
final fun (kotlinx.io/Segment).kotlinx.io/isEmpty(): kotlin/Boolean // kotlinx.io/isEmpty|isEmpty@kotlinx.io.Segment(){}[0]
84113
final fun (kotlinx.io/Sink).kotlinx.io/write(kotlinx.io.bytestring/ByteString, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io/write|write@kotlinx.io.Sink(kotlinx.io.bytestring.ByteString;kotlin.Int;kotlin.Int){}[0]
85114
final fun (kotlinx.io/Sink).kotlinx.io/writeCodePointValue(kotlin/Int) // kotlinx.io/writeCodePointValue|writeCodePointValue@kotlinx.io.Sink(kotlin.Int){}[0]
86115
final fun (kotlinx.io/Sink).kotlinx.io/writeDecimalLong(kotlin/Long) // kotlinx.io/writeDecimalLong|writeDecimalLong@kotlinx.io.Sink(kotlin.Long){}[0]
@@ -135,6 +164,13 @@ final fun kotlinx.io.files/Path(kotlin/String, kotlin/Array<out kotlin/String>..
135164
final fun kotlinx.io.files/Path(kotlinx.io.files/Path, kotlin/Array<out kotlin/String>...): kotlinx.io.files/Path // kotlinx.io.files/Path|Path(kotlinx.io.files.Path;kotlin.Array<out|kotlin.String>...){}[0]
136165
final fun kotlinx.io/discardingSink(): kotlinx.io/RawSink // kotlinx.io/discardingSink|discardingSink(){}[0]
137166
final inline fun (kotlinx.io/Sink).kotlinx.io/writeToInternalBuffer(kotlin/Function1<kotlinx.io/Buffer, kotlin/Unit>) // kotlinx.io/writeToInternalBuffer|writeToInternalBuffer@kotlinx.io.Sink(kotlin.Function1<kotlinx.io.Buffer,kotlin.Unit>){}[0]
167+
final object kotlinx.io.unsafe/UnsafeBufferOperations { // kotlinx.io.unsafe/UnsafeBufferOperations|null[0]
168+
final fun moveToTail(kotlinx.io/Buffer, kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // kotlinx.io.unsafe/UnsafeBufferOperations.moveToTail|moveToTail(kotlinx.io.Buffer;kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
169+
final inline fun readFromHead(kotlinx.io/Buffer, kotlin/Function3<kotlin/ByteArray, kotlin/Int, kotlin/Int, kotlin/Int>) // kotlinx.io.unsafe/UnsafeBufferOperations.readFromHead|readFromHead(kotlinx.io.Buffer;kotlin.Function3<kotlin.ByteArray,kotlin.Int,kotlin.Int,kotlin.Int>){}[0]
170+
final inline fun writeToTail(kotlinx.io/Buffer, kotlin/Int, kotlin/Function3<kotlin/ByteArray, kotlin/Int, kotlin/Int, kotlin/Int>) // kotlinx.io.unsafe/UnsafeBufferOperations.writeToTail|writeToTail(kotlinx.io.Buffer;kotlin.Int;kotlin.Function3<kotlin.ByteArray,kotlin.Int,kotlin.Int,kotlin.Int>){}[0]
171+
final val maxSafeWriteCapacity // kotlinx.io.unsafe/UnsafeBufferOperations.maxSafeWriteCapacity|{}maxSafeWriteCapacity[0]
172+
final fun <get-maxSafeWriteCapacity>(): kotlin/Int // kotlinx.io.unsafe/UnsafeBufferOperations.maxSafeWriteCapacity.<get-maxSafeWriteCapacity>|<get-maxSafeWriteCapacity>(){}[0]
173+
}
138174
final val kotlinx.io.files/SystemFileSystem // kotlinx.io.files/SystemFileSystem|{}SystemFileSystem[0]
139175
final fun <get-SystemFileSystem>(): kotlinx.io.files/FileSystem // kotlinx.io.files/SystemFileSystem.<get-SystemFileSystem>|<get-SystemFileSystem>(){}[0]
140176
final val kotlinx.io.files/SystemPathSeparator // kotlinx.io.files/SystemPathSeparator|{}SystemPathSeparator[0]
@@ -147,6 +183,9 @@ open annotation class kotlinx.io/DelicateIoApi : kotlin/Annotation { // kotlinx.
147183
open annotation class kotlinx.io/InternalIoApi : kotlin/Annotation { // kotlinx.io/InternalIoApi|null[0]
148184
constructor <init>() // kotlinx.io/InternalIoApi.<init>|<init>(){}[0]
149185
}
186+
open annotation class kotlinx.io/UnsafeIoApi : kotlin/Annotation { // kotlinx.io/UnsafeIoApi|null[0]
187+
constructor <init>() // kotlinx.io/UnsafeIoApi.<init>|<init>(){}[0]
188+
}
150189
open class kotlinx.io.files/FileNotFoundException : kotlinx.io/IOException { // kotlinx.io.files/FileNotFoundException|null[0]
151190
constructor <init>(kotlin/String?) // kotlinx.io.files/FileNotFoundException.<init>|<init>(kotlin.String?){}[0]
152191
}

core/apple/src/AppleCore.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private open class OutputStreamSink(
4949

5050
head.pos += bytesWritten.toInt()
5151
remaining -= bytesWritten
52-
source.size -= bytesWritten
52+
source.sizeMut -= bytesWritten
5353

5454
if (head.pos == head.limit) {
5555
source.recycleHead()
@@ -105,7 +105,7 @@ private open class NSInputStreamSource(
105105
return -1
106106
}
107107
tail.limit += bytesRead.toInt()
108-
sink.size += bytesRead
108+
sink.sizeMut += bytesRead
109109
return bytesRead
110110
}
111111

core/apple/src/BuffersApple.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal fun Buffer.write(source: CPointer<uint8_tVar>, maxLength: Int) {
3030
currentOffset += toCopy
3131
tail.limit += toCopy
3232
}
33-
size += maxLength
33+
this.sizeMut += maxLength
3434
}
3535

3636
internal fun Buffer.readAtMostTo(sink: CPointer<uint8_tVar>, maxLength: Int): Int {
@@ -43,7 +43,7 @@ internal fun Buffer.readAtMostTo(sink: CPointer<uint8_tVar>, maxLength: Int): In
4343
}
4444

4545
s.pos += toCopy
46-
size -= toCopy.toLong()
46+
this.sizeMut -= toCopy.toLong()
4747

4848
if (s.pos == s.limit) {
4949
recycleHead()

core/common/src/Annotations.kt

+16-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
2+
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
33
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
44
*/
55

@@ -38,4 +38,18 @@ public annotation class DelicateIoApi
3838
"Make sure you fully read and understand documentation of the declaration that " +
3939
"is marked as an internal API."
4040
)
41-
public annotation class InternalIoApi
41+
public annotation class InternalIoApi
42+
43+
/**
44+
* Marks API that may cause data corruption or loss or behave unpredictable when used with invalid argument values.
45+
*
46+
* Consider using other APIs instead when possible.
47+
* Otherwise, make sure to read documentation describing an unsafe API.
48+
*/
49+
@Retention(AnnotationRetention.BINARY)
50+
@RequiresOptIn(
51+
level = RequiresOptIn.Level.WARNING,
52+
message = "This is an unsafe API and its use requires care. " +
53+
"Make sure you fully understand documentation of the declaration marked as UnsafeIoApi"
54+
)
55+
public annotation class UnsafeIoApi

0 commit comments

Comments
 (0)