Skip to content

Commit cf4346d

Browse files
qwwdfsade5l
authored andcommitted
Get rid of a redundant lambdas, significantly reduce amount of inlined bytecode, improve documentation
1 parent 170b45b commit cf4346d

File tree

11 files changed

+107
-128
lines changed

11 files changed

+107
-128
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ internal fun checkBufferAndIndexes(buffer: Buffer, startIndex: Int, endIndex: In
1616
}
1717
}
1818

19-
/**
20-
21-
*/
2219
internal fun checkArrayStartAndLength(array: ByteArray, startIndex: Int, length: Int) {
2320
require(startIndex >= 0) { "Start index ($startIndex) should be positive." }
2421
require(length >= 0) { "Length ($length) should be positive." }

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

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import kotlin.math.*
77
/**
88
* [Input] is an abstract base class for synchronous byte readers.
99
*
10-
* It contains [read*] methods to read primitive types ([readByte], [readInt], ...) and arrays ([readByteArray]).
10+
* It contains `read*` methods to read primitive types ([readByte], [readInt], ...) and arrays ([readByteArray]).
1111
*
1212
* [Input] is buffered. Buffer size depends on [Buffer.size] in the [bufferPool] buffer.
1313
* Buffer size is [DEFAULT_BUFFER_SIZE] by default.
@@ -20,21 +20,21 @@ import kotlin.math.*
2020
*
2121
* To implement [Input] over a custom source, you should override [fill] and [closeSource] methods:
2222
* ```
23-
* class Input42 : Input() {
24-
* private var closed: Boolean = false
23+
* class ConstantInput : Input() {
24+
* private var closed: Boolean = false
2525
*
26-
* override fun fill(buffer: Buffer): Int {
27-
* if (closed) {
28-
* return 0
29-
* }
26+
* override fun fill(buffer: Buffer): Int {
27+
* if (closed) {
28+
* return 0
29+
* }
3030
*
31-
* buffer.storeByteAt(index = 0, value = 42)
32-
* return 1
33-
* }
31+
* buffer.storeByteAt(index = 0, value = 42)
32+
* return 1
33+
* }
3434
*
35-
* override fun closeSource() {
36-
* closed = true
37-
* }
35+
* override fun closeSource() {
36+
* closed = true
37+
* }
3838
* }
3939
* ```
4040
*
@@ -107,31 +107,28 @@ public abstract class Input : Closeable {
107107
}
108108

109109
/**
110-
* Constructs a new Input with the default pool of buffers with the given [bufferSize].
110+
* Constructs a new Input with the default pool of buffers.
111111
*/
112112
protected constructor() : this(DefaultBufferPool.Instance)
113113

114114
/**
115-
* Reads the content of this [Input] to [destination] [Output].
115+
* Reads the available content in current [Input] to the [destination].
116+
* If no bytes are available in the input, [fill] method will be called directly on
117+
* the [destination] underlying buffer without an extra copy.
118+
* Otherwise, available bytes are copied to the destination.
116119
*
117-
* If there are no bytes in cache(stored from last [fill] or in [preview]), [fill] will be called on the
118-
* [destination] buffer directly without an extra copy.
119-
*
120-
* If there are bytes in cache, it'll be flushed to [destination].
121-
*
122-
* Please note that if [Input] and [destination] [Output] aren't sharing same [bufferPool]s or [Input] is in
123-
* [preview], bytes will be copied.
120+
* TODO: fix this one along all the codebase
121+
* If [Input] and [Output] have different buffer pools, available bytes are copied and
122+
* no direct transfer is performed
124123
*/
125124
public fun readAvailableTo(destination: Output): Int {
126125
if (!previewDiscard) {
127126
return copyAvailableTo(destination)
128127
}
129128

130129
val preview = previewBytes
131-
132130
// Do we have single byte in cache?
133131
if (position != limit || preview != null && previewIndex < preview.tail) {
134-
135132
if (bufferPool !== destination.bufferPool) {
136133
// Can't share buffers between different pools.
137134
return copyAvailableTo(destination)
@@ -142,15 +139,18 @@ public abstract class Input : Closeable {
142139
endOffset
143140
}
144141
}
145-
146142
// No bytes in cache: fill [destination] buffer direct.
147143
return destination.writeBuffer { buffer, startIndex, endIndex ->
148144
startIndex + fill(buffer, startIndex, endIndex)
149145
}
150146
}
151147

152148
/**
153-
* Attempts to move chunk from this [Input] to the [destination].
149+
* Reads the available content in current [Input] to the [destination] buffer.
150+
*
151+
* If no bytes are available in the input, [fill] method will be called directly on
152+
* the [destination] buffer without an extra copy.
153+
* Otherwise, available bytes are copied to the destination.
154154
*/
155155
public fun readAvailableTo(
156156
destination: Buffer,
@@ -163,7 +163,6 @@ public abstract class Input : Closeable {
163163
if (position != limit || previewBytes != null) {
164164
return copyAvailableTo(destination, startIndex, endIndex)
165165
}
166-
167166
// No bytes in cache: fill [destination] buffer direct.
168167
return fill(destination, startIndex, endIndex)
169168
}
@@ -187,9 +186,7 @@ public abstract class Input : Closeable {
187186
val markPosition = position
188187

189188
previewDiscard = false
190-
191189
val result = reader()
192-
193190
previewDiscard = markDiscard
194191
position = markPosition
195192

@@ -209,9 +206,7 @@ public abstract class Input : Closeable {
209206
}
210207

211208
/**
212-
* Check if at least 1 byte available to read.
213-
*
214-
* The method could block until source provides a byte.
209+
* Check if at least one byte is available to read.
215210
*/
216211
@Suppress("NOTHING_TO_INLINE")
217212
public inline fun eof(): Boolean = !prefetch(1)
@@ -278,8 +273,6 @@ public abstract class Input : Closeable {
278273
while (remaining > 0) {
279274
val skipCount = readBufferRange { _, startOffset, endOffset ->
280275
val skipCount = min(remaining, endOffset - startOffset)
281-
282-
283276
startOffset + skipCount
284277
}
285278

@@ -313,16 +306,13 @@ public abstract class Input : Closeable {
313306
}
314307

315308
/**
316-
* Requests to fill [buffer] from [startIndex] to [endIndex] exclusive with bytes from source. This method will block and wait
317-
* if no bytes available.
318-
*
319-
* This method copies bytes from source to [buffer] from [startIndex] to [startIndex] + `return-value`. Other bytes
320-
* should not be touched.
309+
* Requests to fill [buffer] from [startIndex] to [endIndex] exclusive from the underlying source.
310+
* This method may block and wait if no bytes are available.
311+
* This method copies bytes from source to [buffer] from [startIndex] to [startIndex] + `return-value`.
321312
*
322313
* The [startIndex] cannot be negative, the [endIndex] should be greater than [startIndex]
323314
* The [buffer] size should be positive, [endIndex] cannot be greater than [buffer] size.
324-
*
325-
* Writing to [buffer] outside of the given range leads to unspecified behaviour.
315+
* Writing and reading to the [buffer] outside of the given range leads to unspecified behaviour.
326316
*
327317
* @return number of bytes were filled (`endIndex - startIndex` at most) or `0` if no more input is available.
328318
*/
@@ -351,13 +341,22 @@ public abstract class Input : Closeable {
351341
primitiveSize: Int,
352342
readDirect: (buffer: Buffer, offset: Int) -> Long
353343
): Long {
344+
val position = preparePrimitiveReadPosition(primitiveSize)
345+
if (position != -1) {
346+
return readDirect(buffer, position)
347+
}
348+
// Nope, doesn't fit in a buffer, read byte by byte
349+
return readPrimitive(primitiveSize)
350+
}
351+
352+
private fun preparePrimitiveReadPosition(primitiveSize: Int): Int {
354353
val currentPosition = position
355354
val currentLimit = limit
356355
val targetLimit = currentPosition + primitiveSize
357356

358357
if (currentLimit >= targetLimit) {
359358
position = targetLimit
360-
return readDirect(buffer, currentPosition)
359+
return currentPosition
361360
}
362361

363362
if (currentLimit == currentPosition) {
@@ -370,28 +369,17 @@ public abstract class Input : Closeable {
370369
// we know we are at zero position here, but limit & buffer could've changed, so can't use cached value
371370
if (limit >= primitiveSize) {
372371
position = primitiveSize
373-
return readDirect(buffer, 0)
372+
return 0
374373
}
375374
}
376-
377-
// Nope, doesn't fit in a buffer, read byte by byte
378-
var result = 0L
379-
readBytes(primitiveSize) {
380-
result = (result shl 8) or it.toLong()
381-
}
382-
383-
return result
375+
return -1
384376
}
385377

386-
/**
387-
* Reads [size] unsigned bytes from an Input and calls [consumer] on each of them.
388-
* @throws EOFException if no more bytes available.
389-
*/
390-
private inline fun readBytes(size: Int, consumer: (unsignedByte: Int) -> Unit) {
378+
private fun readPrimitive(size: Int): Long {
391379
var remaining = size
392-
393380
var current = position
394381
var currentLimit = limit
382+
var result = 0L
395383
while (remaining > 0) {
396384
if (current == currentLimit) {
397385
if (fetchCachedOrFill() == 0) {
@@ -403,14 +391,14 @@ public abstract class Input : Closeable {
403391
}
404392

405393
val byte = buffer.loadByteAt(current).toInt() and 0xFF
406-
consumer(byte)
407-
394+
result = (result shl 8) or byte.toLong()
408395
current++
409396
remaining--
410397
}
411398

412399
position = current
413400
limit = currentLimit
401+
return result
414402
}
415403

416404
/**

0 commit comments

Comments
 (0)