mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-18 14:47:15 +00:00
preview of files realization
This commit is contained in:
parent
e5207f5bc5
commit
d23e005985
146
common/src/linuxX64Main/kotlin/ActualMPPFile.kt
Normal file
146
common/src/linuxX64Main/kotlin/ActualMPPFile.kt
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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 val MPPFile.filename: FileName
|
||||||
|
get() = this.filename
|
||||||
|
actual val MPPFile.filesize: Long
|
||||||
|
get() = memScoped {
|
||||||
|
val pointer = createPointer()
|
||||||
|
fseek(pointer, 0L, SEEK_END)
|
||||||
|
ftell(pointer)
|
||||||
|
}
|
||||||
|
actual val MPPFile.bytesAllocatorSync: ByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
memScoped {
|
||||||
|
fileChannel().readFully()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual val MPPFile.bytesAllocator: SuspendByteArrayAllocator
|
||||||
|
get() = {
|
||||||
|
bytesAllocatorSync()
|
||||||
|
}
|
||||||
|
|
107
common/src/linuxX64Main/kotlin/FileChannel.kt
Normal file
107
common/src/linuxX64Main/kotlin/FileChannel.kt
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
56
common/src/linuxX64Main/kotlin/FileIterator.kt
Normal file
56
common/src/linuxX64Main/kotlin/FileIterator.kt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
26
common/src/linuxX64Main/kotlin/fixed.kt
Normal file
26
common/src/linuxX64Main/kotlin/fixed.kt
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
36
common/src/linuxX64Test/kotlin/FileTests.kt
Normal file
36
common/src/linuxX64Test/kotlin/FileTests.kt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,6 @@ kotlin {
|
|||||||
android {
|
android {
|
||||||
publishAllLibraryVariants()
|
publishAllLibraryVariants()
|
||||||
}
|
}
|
||||||
linuxArm64()
|
|
||||||
linuxX64()
|
linuxX64()
|
||||||
mingwX64()
|
mingwX64()
|
||||||
|
|
||||||
@ -53,12 +52,6 @@ kotlin {
|
|||||||
implementation libs.android.espresso
|
implementation libs.android.espresso
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
linuxArm64Test {
|
|
||||||
dependencies {
|
|
||||||
implementation kotlin('test-js')
|
|
||||||
implementation kotlin('test-junit')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mingwX64Test {
|
mingwX64Test {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation kotlin('test-js')
|
implementation kotlin('test-js')
|
||||||
|
Loading…
Reference in New Issue
Block a user