Skip to content

Instantly share code, notes, and snippets.

@filimo
Forked from Skeptick/ByteWriteChannel.kt
Created September 19, 2022 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save filimo/fc04dbb2016cba022e85829f8cf47bfa to your computer and use it in GitHub Desktop.
Save filimo/fc04dbb2016cba022e85829f8cf47bfa to your computer and use it in GitHub Desktop.
iOS ByteWriteChannel
import io.ktor.client.utils.*
import io.ktor.utils.io.*
import io.ktor.utils.io.bits.*
import kotlinx.cinterop.*
import kotlinx.coroutines.*
import platform.CoreFoundation.*
import platform.Foundation.*
import platform.UIKit.*
import platform.posix.memcpy
import platform.posix.uint8_tVar
actual fun ByteWriteChannel(fileName: String): ByteWriteChannel {
val tempDirUrl = NSFileManager.defaultManager.temporaryDirectory
val fileUrl = tempDirUrl.URLByAppendingPathComponent(fileName) ?: error("Cannot create temp file")
val output = NSOutputStream.outputStreamWithURL(fileUrl, false) ?: error("Cannot create temp file")
output.open()
return output.toByteWriteChannel(fileName)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun NSOutputStream.toByteWriteChannel(fileName: String): ByteWriteChannel {
val outputStream = this
val coroutineContext = newSingleThreadContext("write-channel-$fileName")
return CoroutineScope(coroutineContext).reader(autoFlush = true) {
memScoped {
try {
val buffer = allocArray<uint8_tVar>(DEFAULT_HTTP_BUFFER_SIZE.convert())
copyChannel(channel, outputStream, buffer)
} catch (cause: Throwable) {
cause.printStackTrace()
channel.cancel(cause)
} finally {
outputStream.close()
coroutineContext.close()
}
}
}.channel
}
private suspend fun copyChannel(input: ByteReadChannel, output: NSOutputStream, buffer: CArrayPointer<uint8_tVar>) {
while (!input.isClosedForRead) {
val memoryBuffer = Memory.of(buffer, DEFAULT_HTTP_BUFFER_SIZE)
input.read { source, start, end ->
val size = end - start
source.copyTo(memoryBuffer, start, size, 0)
memcpy(buffer, source.pointer, DEFAULT_HTTP_BUFFER_SIZE.convert())
output.write(buffer, size.convert())
size.toInt()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment