experimentally add linuxx64 and mingwx64 as target platforms

This commit is contained in:
2023-04-03 22:35:41 +06:00
parent d23e005985
commit 617dfb54e0
27 changed files with 1248 additions and 1087 deletions

View File

@@ -1,146 +1,36 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.alloc
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.readBytes
import platform.linux.statvfs
import platform.posix.ACCESSPERMS
import platform.posix.FILE
import platform.posix.F_OK
import platform.posix.SEEK_END
import platform.posix.S_IFDIR
import platform.posix.S_IFMT
import platform.posix.access
import platform.posix.fgets
import platform.posix.fopen
import platform.posix.fseek
import platform.posix.ftell
import platform.posix.remove
import platform.posix.rename
import platform.posix.rmdir
import platform.posix.stat
import okio.FileSystem
import okio.Path
import okio.use
actual data class MPPFile(internal val filename: FileName) {
val path = filename.string
val isFile: Boolean
get() = memScoped {
val stat = alloc<stat>()
if (stat(path, stat.ptr) != 0)
return@memScoped false
(S_IFDIR != (stat.st_mode and S_IFMT.convert()).convert<Int>())
}
val isDirectory: Boolean
get() = memScoped {
val stat = alloc<stat>()
if (stat(path, stat.ptr) != 0)
return@memScoped false
S_IFDIR == (stat.st_mode and S_IFMT.convert()).convert<Int>()
}
val size: Long
get() = memScoped {
val stat = alloc<stat>()
if (stat(path, stat.ptr) != 0)
return@memScoped 0
return stat.st_size.convert()
}
val lastModified: Long
get() = memScoped {
val stat = alloc<stat>()
if (stat(path, stat.ptr) != 0)
return@memScoped 0
return stat.st_ctim.tv_nsec / 1000L
}
val freeSpace: Long
get() =
memScoped {
val stat = alloc<statvfs>()
statvfs(path, stat.ptr)
(stat.f_bfree.toULong() * stat.f_bsize.toULong()).toLong()
}
val availableSpace: Long
get() =
memScoped {
val stat = alloc<statvfs>()
statvfs(path, stat.ptr)
(stat.f_bavail.toULong() * stat.f_bsize.toULong()).toLong()
}
val totalSpace: Long
get() = memScoped {
val stat = alloc<statvfs>()
statvfs(path, stat.ptr)
(stat.f_blocks.toULong() * stat.f_frsize.toULong()).toLong()
}
constructor(vararg path: String) : this(FileName(path.map { it.removeSuffix(SEPARATOR_STRING) }.joinToString(SEPARATOR_STRING)))
constructor(parent: MPPFile, subpath: String) : this("${parent.filename.withoutSlashAtTheEnd}$SEPARATOR${subpath}")
fun createPointer(mode: String = "r") = fopen(filename.name, mode)
fun delete(): Boolean {
return when {
isDirectory -> rmdir(path) == 0
isFile -> remove(path) == 0
else -> false
}
}
fun mkdir(): Boolean = platform.posix.mkdir(path, ACCESSPERMS) == 0
override fun toString(): String = path
override fun equals(other: Any?): Boolean {
if (other !is MPPFile) return false
return path == other.path
}
override fun hashCode(): Int = 31 + path.hashCode()
fun renameTo(newPath: MPPFile): Boolean = rename(path, newPath.path) == 0
fun list(): List<MPPFile> {
val out = ArrayList<MPPFile>()
FileIterator(this).forEach { file ->
out += file
}
return out
}
fun fileChannel(mode: String = "r") = FileChannel(this, mode)
companion object {
val SEPARATOR: Char = '/'
val SEPARATOR_STRING: String = SEPARATOR.toString()
val temporalDirectory: MPPFile?
get() = MPPFile("/tmp").takeIf {
it.isDirectory
}
}
}
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = this.filename
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = memScoped {
val pointer = createPointer()
fseek(pointer, 0L, SEEK_END)
ftell(pointer)
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
memScoped {
fileChannel().readFully()
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}

View File

@@ -1,107 +0,0 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toCValues
import kotlinx.cinterop.toKString
import platform.posix.SEEK_END
import platform.posix.SEEK_SET
import platform.posix.fclose
import platform.posix.feof
import platform.posix.fread
import platform.posix.fseek
import platform.posix.ftell
import platform.posix.fwrite
import platform.posix.size_t
class FileChannel (file: MPPFile, mode: String) : RandomAccess {
internal val handler = file.createPointer(mode)
private var position: ULong
get() = ftell(handler).convert()
set(value) {
fseek(handler, value.convert(), SEEK_SET)
}
private val size: ULong
get() {
val pos = position
gotoEnd()
val result = position
position = pos
return result
}
fun skip(length: Long): Long {
checkClosed()
memScoped { }
if (length == 0L)
return 0L
if (feof(handler) != 0)
return 0L
val endOfFile = size
val position = minOf(endOfFile, this.position + length.toULong())
this.position = position
return (endOfFile - position).toLong()
}
fun read(dest: ByteArray): Int {
checkClosed()
if (feof(handler) != 0)
return 0
memScoped {
val tmp = allocArray<ByteVar>(dest.size);
fread(tmp, Byte.SIZE_BYTES.convert<size_t>(), dest.size.convert(), handler).convert<Int>()
tmp.toKString()
}
return fread(dest.toCValues(), Byte.SIZE_BYTES.convert<size_t>(), dest.size.convert(), handler).convert<Int>()
}
private var closed = false
private fun checkClosed() {
if (closed) {
error("File channel has been closed already")
}
}
fun close() {
checkClosed()
fclose(handler)
closed = true
}
fun write(data: ByteArray): Int {
checkClosed()
if (feof(handler) != 0)
return 0
return fwrite(data.toCValues(), 1.convert<size_t>(), data.size.convert<size_t>(), handler).convert<Int>()
}
fun flush() {
checkClosed()
}
private fun gotoEnd() {
fseek(handler, 0, SEEK_END)
}
fun readFully(): ByteArray {
var result = ByteArray(0)
memScoped {
val tmp = ByteArray(64 * 1024)
do {
val read = read(tmp)
result += tmp.take(read)
} while (read > 0)
}
val tmp = ByteArray(64 * 1024)
return result
}
}

View File

@@ -1,56 +0,0 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.pointed
import kotlinx.cinterop.toKString
import platform.posix.closedir
import platform.posix.dirent
import platform.posix.opendir
import platform.posix.readdir
import kotlin.native.internal.createCleaner
class FileIterator internal constructor(private val file: MPPFile) : Iterator<MPPFile> {
init {
if (!file.isDirectory)
error("\"${file.path}\" is not direction")
}
private val handler = opendir(file.path)
private var next: dirent? = null
private var end = false
override fun hasNext(): Boolean {
while (true) {
if (end)
return false
if (next == null) {
next = readdir(handler)?.pointed
if (next == null) {
end = true
return false
}
val name = next!!.d_name.toKString()
if (name == "." || name == "..") {
next = null
continue
}
return true
}
return true
}
}
override fun next(): MPPFile {
if (!hasNext())
throw NoSuchElementException()
val result = MPPFile(file, next!!.d_name.toKString())
next = null
return result
}
@OptIn(ExperimentalStdlibApi::class)
private val cleaner = createCleaner(handler) {
closedir(it)
}
}

View File

@@ -1,36 +0,0 @@
package dev.inmo.micro_utils.common
import dev.inmo.micro_utils.common.MPPFile
import kotlin.test.Test
import kotlin.test.assertEquals
fun createTestFile(content: String): MPPFile = MPPFile(
MPPFile.temporalDirectory!!,
"tmp.file.txt"
).apply {
val channel = fileChannel("wc")
runCatching {
channel.write(content.encodeToByteArray())
}
channel.close()
}
fun MPPFile.removeTestFile() {
delete()
}
class FileTests {
@Test
fun testReadFromFile() {
val testContent = "Test"
val file = createTestFile(testContent)
try {
val content = runCatching {
file.fileChannel().readFully().contentToString()
}.getOrThrow()
assertEquals(testContent, content)
} finally {
file.removeTestFile()
}
}
}

View File

@@ -0,0 +1,36 @@
package dev.inmo.micro_utils.common
import okio.FileSystem
import okio.Path
import okio.use
actual typealias MPPFile = Path
/**
* @suppress
*/
actual val MPPFile.filename: FileName
get() = FileName(toString())
/**
* @suppress
*/
actual val MPPFile.filesize: Long
get() = FileSystem.SYSTEM.openReadOnly(this).use {
it.size()
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
get() = {
FileSystem.SYSTEM.read(this) {
readByteArray()
}
}
/**
* @suppress
*/
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
get() = {
bytesAllocatorSync()
}

View File

@@ -0,0 +1,26 @@
package dev.inmo.micro_utils.common
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import platform.posix.snprintf
import platform.posix.sprintf
actual fun Float.fixed(signs: Int): Float {
return memScoped {
val buff = allocArray<ByteVar>(Float.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toFloat()
}
}
actual fun Double.fixed(signs: Int): Double {
return memScoped {
val buff = allocArray<ByteVar>(Double.SIZE_BYTES * 2)
sprintf(buff, "%.${signs}f", this@fixed)
buff.toKString().toDouble()
}
}