|
| 1 | +/* |
| 2 | + * Copyright 2010-2025 JetBrains s.r.o. and respective authors and developers. |
| 3 | + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENCE file. |
| 4 | + */ |
| 5 | + |
| 6 | +package kotlinx.io |
| 7 | + |
| 8 | +import kotlinx.cinterop.* |
| 9 | +import kotlinx.io.unsafe.UnsafeBufferOperations |
| 10 | +import platform.posix.memcpy |
| 11 | + |
| 12 | +/** |
| 13 | + * Reads at most [byteCount] bytes from this [Source](this), writes them into [ptr] and returns the number of |
| 14 | + * bytes read. |
| 15 | + * |
| 16 | + * **Note that this function does not verify whether the [ptr] points to a writeable memory region.** |
| 17 | + * |
| 18 | + * @param ptr The memory region to write data into. |
| 19 | + * @param byteCount The maximum number of bytes to read from this source. |
| 20 | + * |
| 21 | + * @throws IllegalArgumentException when [byteCount] is negative. |
| 22 | + * @throws IOException when some I/O error happens. |
| 23 | + */ |
| 24 | +@DelicateIoApi |
| 25 | +@OptIn(ExperimentalForeignApi::class, InternalIoApi::class, UnsafeIoApi::class, UnsafeNumber::class) |
| 26 | +public fun Source.readAtMostTo(ptr: CPointer<ByteVar>, byteCount: Long): Long { |
| 27 | + require(byteCount >= 0L) { "byteCount shouldn't be negative: $byteCount" } |
| 28 | + |
| 29 | + if (byteCount == 0L) return 0L |
| 30 | + |
| 31 | + if (!request(1L)) { |
| 32 | + return if (exhausted()) -1L else 0L |
| 33 | + } |
| 34 | + |
| 35 | + var consumed = 0L |
| 36 | + UnsafeBufferOperations.readFromHead(buffer) { array, startIndex, endIndex -> |
| 37 | + val toRead = minOf(endIndex - startIndex, byteCount).toInt() |
| 38 | + |
| 39 | + array.usePinned { |
| 40 | + memcpy(ptr, it.addressOf(startIndex), toRead.convert()) |
| 41 | + } |
| 42 | + |
| 43 | + consumed += toRead |
| 44 | + toRead |
| 45 | + } |
| 46 | + |
| 47 | + return consumed |
| 48 | +} |
| 49 | + |
| 50 | +/** |
| 51 | + * Reads exactly [byteCount] bytes from this [Source](this) and writes them into a memory region pointed by [ptr]. |
| 52 | + * |
| 53 | + * **Note that this function does not verify whether the [ptr] points to a writeable memory region.** |
| 54 | + * |
| 55 | + * This function consumes data from the source even if an error occurs. |
| 56 | + * |
| 57 | + * @param ptr The memory region to write data into. |
| 58 | + * @param byteCount The exact number of bytes to read from this source. |
| 59 | + * |
| 60 | + * @throws IllegalArgumentException when [byteCount] is negative. |
| 61 | + * @throws EOFException when the source exhausts before [byteCount] were read. |
| 62 | + * @throws IOException when some I/O error happens. |
| 63 | + */ |
| 64 | +@DelicateIoApi |
| 65 | +@OptIn(ExperimentalForeignApi::class, InternalIoApi::class, UnsafeIoApi::class, UnsafeNumber::class) |
| 66 | +public fun Source.readTo(ptr: CPointer<ByteVar>, byteCount: Long) { |
| 67 | + require(byteCount >= 0L) { "byteCount shouldn't be negative: $byteCount" } |
| 68 | + |
| 69 | + if (byteCount == 0L) return |
| 70 | + |
| 71 | + var consumed = 0L |
| 72 | + |
| 73 | + while (consumed < byteCount) { |
| 74 | + if (!request(1L)) { |
| 75 | + if (exhausted()) { |
| 76 | + throw EOFException("The source is exhausted before reading $byteCount bytes " + |
| 77 | + "(it contained only $consumed bytes)") |
| 78 | + } |
| 79 | + } |
| 80 | + UnsafeBufferOperations.readFromHead(buffer) { array, startIndex, endIndex -> |
| 81 | + val toRead = minOf(endIndex - startIndex, byteCount - consumed).toInt() |
| 82 | + |
| 83 | + array.usePinned { |
| 84 | + memcpy(ptr + consumed, it.addressOf(startIndex), toRead.convert()) |
| 85 | + } |
| 86 | + |
| 87 | + consumed += toRead |
| 88 | + toRead |
| 89 | + } |
| 90 | + } |
| 91 | +} |
0 commit comments