For the last three years I have used Kotlin MPP to...
# advent-of-code
j
For the last three years I have used Kotlin MPP to do AoC. One thing that always slightly bothered me was that, because there is no common File api, I had to resort to putting the input directly in the source code. Today I finally took some time to attempt to read my input from a file instead. My needs are simple: I want to put a file with inputs in a shared resources directory, say
src\commonMain\resources\day01.txt
and read its contents as text in my solutions. This isn't trivial in Kotlin multi-platform, as a.) there isn't a common file API, and b.) only the JVM provides standardized access to the resources directory. To deal with this platform-dependent stuff, Kotlin MPP offers the `expect`/`actual` mechanism. In common code, I declare that each target platform (JVM, Node and native) should offer a
readInput
function:
Copy code
expect fun readInput(name: String): String
The, each platform can implement this in a platform-specific way: JVM:
Copy code
actual fun readInput(name: String) = {}::class.java.getResource(name).readText()
Since Node and native don't get access to the resources directory, I use an enviroment variable
AOC_INPUT_DIR
to define the directory where they should look for the input files. Their implementations then become: Node:
Copy code
external fun require(name: String): dynamic
val fs = require("fs")
val path = require("path")

actual fun readInput(name: String): String {
    val path = path.join(process.env.AOC_INPUT_DIR, name)
    return fs.readFileSync(path, "utf8") as String
}
Native:
Copy code
actual fun readInput(name: String): String {
    val returnBuffer = StringBuilder()
    val inputDir = getenv("AOC_INPUT_DIR")?.toKString() ?: ""
    val file = fopen("""$inputDir\$name""", "r") ?: throw IllegalArgumentException("Cannot open input file $name")

    try {
        memScoped {
            val readBufferLength = 64 * 1024
            val buffer = allocArray<ByteVar>(readBufferLength)
            var line = fgets(buffer, readBufferLength, file)?.toKString()
            while (line != null) {
                returnBuffer.append(line)
                line = fgets(buffer, readBufferLength, file)?.toKString()
            }
        }
    } finally {
        fclose(file)
    }

    return returnBuffer.toString()
}
I'm not sure I'm actually going to use this, I don't really mind having the input in the source, but at least I know how to do it now 🙂
👏🏻 2
e
hmm. I'm only targeting JVM so resources work for me, but if I weren't, I'd consider embedding the data into source too using gradle to automate it, something like
Copy code
val generateInputSources by tasks.creating {
    inputs.files("*.txt")
    outputs.dir("build/generated/source/inputs")
    doLast {
        // read *.txt, write *.kt
    }
}
sourceSets["main"].java.srcDir(generateSources.outputs.files)
t
That is really cool @Joris PZ wow.
j
Credit where credit's due, the native code to read a file was taken wholesale from https://www.nequalsonelifestyle.com/2020/11/16/kotlin-native-file-io/ btw
t
I haven't done any MPP, but I sure do enjoy the fact that you do this every year. I can't believe there really isn't a kotlin-only standard lib for reading files.
j
Thanks Todd! I don't get to work with MPP in my day job, but I am keeping an eye on it for possible future use, and AOC is a perfect yearly reminder to have a look at it. There is a JetBrains MPP library that vaguely promises sone support for working with files in MPP: https://github.com/Kotlin/kotlinx-io I'm sure writing a proper multiplatform library for this stuff is a lot harder than supporting just my simple case, but yeah, it seems like quite a must-have.
t
Well I learn a lot from you each year and I'm glad you take the trouble to write MPP code and benchmark it on so many platforms! That process, benchmarking, how you have to write code, what you learn to follow and stay away from, that would make a really good article.
🙏 1
b
Hey you know what with gradle kts, you could solve the puzzles, in Kotlin just with your build file 😄
😄 1