Skip to content

Commit 3dc4e6f

Browse files
committed
Reimplement some methods to use Unsafe API instead of internal one
1 parent 0a8eff3 commit 3dc4e6f

File tree

11 files changed

+257
-263
lines changed

11 files changed

+257
-263
lines changed

core/apple/src/AppleCore.kt

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package kotlinx.io
99

1010
import kotlinx.cinterop.*
11+
import kotlinx.io.unsafe.UnsafeBufferOperations
1112
import platform.Foundation.NSInputStream
1213
import platform.Foundation.NSOutputStream
1314
import platform.Foundation.NSStreamStatusClosed
@@ -31,29 +32,28 @@ private open class OutputStreamSink(
3132
if (out.streamStatus == NSStreamStatusNotOpen) out.open()
3233
}
3334

35+
@OptIn(UnsafeIoApi::class)
3436
override fun write(source: Buffer, byteCount: Long) {
3537
if (out.streamStatus == NSStreamStatusClosed) throw IOException("Stream Closed")
3638

3739
checkOffsetAndCount(source.size, 0, byteCount)
3840
var remaining = byteCount
41+
var bytesWritten = 0L
3942
while (remaining > 0) {
40-
val head = source.head!!
41-
val toCopy = minOf(remaining, head.limit - head.pos).toInt()
42-
val bytesWritten = head.data.usePinned {
43-
val bytes = it.addressOf(head.pos).reinterpret<uint8_tVar>()
44-
out.write(bytes, toCopy.convert()).toLong()
43+
UnsafeBufferOperations.readFromHead(source) { data, pos, limit ->
44+
val toCopy = minOf(remaining, limit - pos).toInt()
45+
bytesWritten = data.usePinned {
46+
val bytes = it.addressOf(pos).reinterpret<uint8_tVar>()
47+
out.write(bytes, toCopy.convert()).toLong()
48+
}
49+
0
4550
}
4651

4752
if (bytesWritten < 0L) throw IOException(out.streamError?.localizedDescription ?: "Unknown error")
4853
if (bytesWritten == 0L) throw IOException("NSOutputStream reached capacity")
4954

50-
head.pos += bytesWritten.toInt()
55+
source.skip(bytesWritten)
5156
remaining -= bytesWritten
52-
source.sizeMut -= bytesWritten
53-
54-
if (head.pos == head.limit) {
55-
source.recycleHead()
56-
}
5757
}
5858
}
5959

@@ -83,29 +83,26 @@ private open class NSInputStreamSource(
8383
if (input.streamStatus == NSStreamStatusNotOpen) input.open()
8484
}
8585

86+
@OptIn(UnsafeIoApi::class)
8687
override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
8788
if (input.streamStatus == NSStreamStatusClosed) throw IOException("Stream Closed")
8889

8990
if (byteCount == 0L) return 0L
9091
checkByteCount(byteCount)
9192

92-
val tail = sink.writableSegment(1)
93-
val maxToCopy = minOf(byteCount, Segment.SIZE - tail.limit)
94-
val bytesRead = tail.data.usePinned {
95-
val bytes = it.addressOf(tail.limit).reinterpret<uint8_tVar>()
96-
input.read(bytes, maxToCopy.convert()).toLong()
93+
var bytesRead = 0L
94+
UnsafeBufferOperations.writeToTail(sink, 1) { data, pos, limit ->
95+
val maxToCopy = minOf(byteCount, limit - pos)
96+
val read = data.usePinned { ba ->
97+
val bytes = ba.addressOf(pos).reinterpret<uint8_tVar>()
98+
input.read(bytes, maxToCopy.convert()).toLong()
99+
}
100+
bytesRead = read
101+
maxOf(read.toInt(), 0)
97102
}
98103

99104
if (bytesRead < 0L) throw IOException(input.streamError?.localizedDescription ?: "Unknown error")
100-
if (bytesRead == 0L) {
101-
if (tail.pos == tail.limit) {
102-
// We allocated a tail segment, but didn't end up needing it. Recycle!
103-
sink.recycleTail()
104-
}
105-
return -1
106-
}
107-
tail.limit += bytesRead.toInt()
108-
sink.sizeMut += bytesRead
105+
if (bytesRead == 0L) return -1
109106
return bytesRead
110107
}
111108

core/apple/src/BuffersApple.kt

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,69 +8,70 @@
88
package kotlinx.io
99

1010
import kotlinx.cinterop.*
11+
import kotlinx.io.unsafe.UnsafeBufferOperations
12+
import kotlinx.io.unsafe.withData
1113
import platform.Foundation.NSData
1214
import platform.Foundation.create
1315
import platform.Foundation.data
1416
import platform.darwin.NSUIntegerMax
1517
import platform.posix.*
1618

17-
@OptIn(ExperimentalForeignApi::class)
19+
@OptIn(ExperimentalForeignApi::class, UnsafeIoApi::class)
1820
internal fun Buffer.write(source: CPointer<uint8_tVar>, maxLength: Int) {
1921
require(maxLength >= 0) { "maxLength ($maxLength) must not be negative" }
2022

2123
var currentOffset = 0
2224
while (currentOffset < maxLength) {
23-
val tail = writableSegment(1)
24-
25-
val toCopy = minOf(maxLength - currentOffset, Segment.SIZE - tail.limit)
26-
tail.data.usePinned {
27-
memcpy(it.addressOf(tail.limit), source + currentOffset, toCopy.convert())
25+
UnsafeBufferOperations.writeToTail(this, 1) { data, pos, limit ->
26+
val toCopy = minOf(maxLength - currentOffset, limit - pos)
27+
data.usePinned {
28+
memcpy(it.addressOf(pos), source + currentOffset, toCopy.convert())
29+
}
30+
currentOffset += toCopy
31+
toCopy
2832
}
29-
30-
currentOffset += toCopy
31-
tail.limit += toCopy
3233
}
33-
this.sizeMut += maxLength
3434
}
3535

36+
@OptIn(UnsafeIoApi::class)
3637
internal fun Buffer.readAtMostTo(sink: CPointer<uint8_tVar>, maxLength: Int): Int {
3738
require(maxLength >= 0) { "maxLength ($maxLength) must not be negative" }
3839

39-
val s = head ?: return 0
40-
val toCopy = minOf(maxLength, s.limit - s.pos)
41-
s.data.usePinned {
42-
memcpy(sink, it.addressOf(s.pos), toCopy.convert())
43-
}
44-
45-
s.pos += toCopy
46-
this.sizeMut -= toCopy.toLong()
47-
48-
if (s.pos == s.limit) {
49-
recycleHead()
40+
var toCopy = 0
41+
UnsafeBufferOperations.readFromHead(this) { data, pos, limit ->
42+
toCopy = minOf(maxLength, limit - pos)
43+
data.usePinned {
44+
memcpy(sink, it.addressOf(pos), toCopy.convert())
45+
}
46+
toCopy
5047
}
5148

5249
return toCopy
5350
}
5451

55-
@OptIn(BetaInteropApi::class)
52+
@OptIn(BetaInteropApi::class, UnsafeIoApi::class)
5653
internal fun Buffer.snapshotAsNSData(): NSData {
5754
if (size == 0L) return NSData.data()
5855

5956
check(size.toULong() <= NSUIntegerMax) { "Buffer is too long ($size) to be converted into NSData." }
6057

6158
val bytes = malloc(size.convert())?.reinterpret<uint8_tVar>()
6259
?: throw Error("malloc failed: ${strerror(errno)?.toKString()}")
63-
var curr = head
64-
var index = 0
65-
do {
66-
check(curr != null) { "Current segment is null" }
67-
val pos = curr.pos
68-
val length = curr.limit - pos
69-
curr.data.usePinned {
70-
memcpy(bytes + index, it.addressOf(pos), length.convert())
60+
61+
UnsafeBufferOperations.iterate(this) { ctx, head ->
62+
var curr: Segment? = head
63+
var index = 0
64+
while (curr != null) {
65+
val segment: Segment = curr
66+
ctx.withData(segment) { data, pos, limit ->
67+
val length = limit - pos
68+
data.usePinned {
69+
memcpy(bytes + index, it.addressOf(pos), length.convert())
70+
}
71+
index += length
72+
}
73+
curr = ctx.next(segment)
7174
}
72-
curr = curr.next
73-
index += length
74-
} while (curr !== null)
75+
}
7576
return NSData.create(bytesNoCopy = bytes, length = size.convert())
7677
}

core/common/src/Buffer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,9 @@ public class Buffer : Source, Sink {
293293
if (position < 0 || position >= size) {
294294
throw IndexOutOfBoundsException("position ($position) is not within the range [0..size($size))")
295295
}
296+
if (position == 0L) {
297+
return head!!.getUnchecked(0)
298+
}
296299
seek(position) { s, offset ->
297300
return s!!.data[(s.pos + position - offset).toInt()]
298301
}

core/common/src/Buffers.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ public fun Buffer.snapshot(): ByteString {
2222
var curr = head
2323
do {
2424
check(curr != null) { "Current segment is null" }
25-
append(curr.data, curr.pos, curr.limit)
25+
for (idx in 0 until curr.size) {
26+
append(curr.getUnchecked(idx))
27+
}
2628
curr = curr.next
2729
} while (curr !== null)
2830
}

core/common/src/ByteStrings.kt

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import kotlinx.io.bytestring.ByteString
99
import kotlinx.io.bytestring.isEmpty
1010
import kotlinx.io.bytestring.unsafe.UnsafeByteStringApi
1111
import kotlinx.io.bytestring.unsafe.UnsafeByteStringOperations
12+
import kotlinx.io.unsafe.UnsafeBufferOperations
1213
import kotlin.math.min
1314

1415
/**
@@ -24,7 +25,7 @@ import kotlin.math.min
2425
*
2526
* @sample kotlinx.io.samples.ByteStringSamples.writeByteString
2627
*/
27-
@OptIn(DelicateIoApi::class)
28+
@OptIn(DelicateIoApi::class, UnsafeByteStringApi::class, UnsafeIoApi::class)
2829
public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int = byteString.size) {
2930
checkBounds(byteString.size, startIndex, endIndex)
3031
if (endIndex == startIndex) {
@@ -33,21 +34,17 @@ public fun Sink.write(byteString: ByteString, startIndex: Int = 0, endIndex: Int
3334

3435
writeToInternalBuffer { buffer ->
3536
var offset = startIndex
36-
val tail = buffer.head?.prev
37-
if (tail != null) {
38-
val bytesToWrite = min(tail.data.size - tail.limit, endIndex - offset)
39-
byteString.copyInto(tail.data, tail.limit, offset, offset + bytesToWrite)
40-
offset += bytesToWrite
41-
tail.limit += bytesToWrite
42-
buffer.sizeMut += bytesToWrite
43-
}
44-
while (offset < endIndex) {
45-
val bytesToWrite = min(endIndex - offset, Segment.SIZE)
46-
val seg = buffer.writableSegment(bytesToWrite)
47-
byteString.copyInto(seg.data, seg.limit, offset, offset + bytesToWrite)
48-
seg.limit += bytesToWrite
49-
buffer.sizeMut += bytesToWrite
50-
offset += bytesToWrite
37+
38+
UnsafeByteStringOperations.withByteArrayUnsafe(byteString) { data ->
39+
while (offset < endIndex) {
40+
var written = 0
41+
UnsafeBufferOperations.writeToTail(buffer, 1) { segData, pos, limit ->
42+
written = min(endIndex - offset, limit - pos)
43+
data.copyInto(segData, pos, offset, offset + written)
44+
written
45+
}
46+
offset += written
47+
}
5148
}
5249
}
5350
}

core/common/src/Sinks.kt

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package kotlinx.io
77

8+
import kotlinx.io.unsafe.UnsafeBufferOperations
9+
810
private val HEX_DIGIT_BYTES = ByteArray(16) {
911
((if (it < 10) '0'.code else ('a'.code - 10)) + it).toByte()
1012
}
@@ -59,7 +61,7 @@ public fun Sink.writeLongLe(long: Long) {
5961
*
6062
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeDecimalLong
6163
*/
62-
@OptIn(DelicateIoApi::class)
64+
@OptIn(DelicateIoApi::class, UnsafeIoApi::class)
6365
public fun Sink.writeDecimalLong(long: Long) {
6466
var v = long
6567
if (v == 0L) {
@@ -112,20 +114,17 @@ public fun Sink.writeDecimalLong(long: Long) {
112114
}
113115

114116
writeToInternalBuffer { buffer ->
115-
val tail = buffer.writableSegment(width)
116-
val data = tail.data
117-
var pos = tail.limit + width // We write backwards from right to left.
118-
while (v != 0L) {
119-
val digit = (v % 10).toInt()
120-
data[--pos] = HEX_DIGIT_BYTES[digit]
121-
v /= 10
122-
}
123-
if (negative) {
124-
data[--pos] = '-'.code.toByte()
117+
UnsafeBufferOperations.writeToTail(buffer, width) { ctx, segment ->
118+
for (pos in width - 1 downTo if (negative) 1 else 0) {
119+
val digit = (v % 10).toByte()
120+
ctx.setUnchecked(segment, pos, HEX_DIGIT_BYTES[digit.toInt()])
121+
v /= 10
122+
}
123+
if (negative) {
124+
ctx.setUnchecked(segment, 0, '-'.code.toByte())
125+
}
126+
width
125127
}
126-
127-
tail.limit += width
128-
buffer.sizeMut += width.toLong()
129128
}
130129
}
131130

@@ -140,7 +139,7 @@ public fun Sink.writeDecimalLong(long: Long) {
140139
*
141140
* @sample kotlinx.io.samples.KotlinxIoCoreCommonSamples.writeHexLong
142141
*/
143-
@OptIn(DelicateIoApi::class)
142+
@OptIn(DelicateIoApi::class, UnsafeIoApi::class)
144143
public fun Sink.writeHexadecimalUnsignedLong(long: Long) {
145144
var v = long
146145
if (v == 0L) {
@@ -172,17 +171,13 @@ public fun Sink.writeHexadecimalUnsignedLong(long: Long) {
172171
val width = ((x + 3) / 4).toInt()
173172

174173
writeToInternalBuffer { buffer ->
175-
val tail = buffer.writableSegment(width)
176-
val data = tail.data
177-
var pos = tail.limit + width - 1
178-
val start = tail.limit
179-
while (pos >= start) {
180-
data[pos] = HEX_DIGIT_BYTES[(v and 0xF).toInt()]
181-
v = v ushr 4
182-
pos--
174+
UnsafeBufferOperations.writeToTail(buffer, width) { ctx, segment ->
175+
for (pos in width - 1 downTo 0) {
176+
ctx.setUnchecked(segment, pos, HEX_DIGIT_BYTES[v.toInt().and(0xF)])
177+
v = v ushr 4
178+
}
179+
width
183180
}
184-
tail.limit += width
185-
buffer.sizeMut += width.toLong()
186181
}
187182
}
188183

0 commit comments

Comments
 (0)