All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nodeFilesystemSharedMain.files.PathsNodeJs.kt Maven / Gradle / Ivy

There is a newer version: 0.6.0
Show newest version
/*
 * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
 */

package kotlinx.io.files

import kotlinx.io.*
import kotlinx.io.node.buffer
import kotlinx.io.node.fs
import kotlinx.io.unsafe.UnsafeBufferOperations
import kotlinx.io.node.path as nodeJsPath

public actual class Path internal constructor(
    rawPath: String,
    @Suppress("UNUSED_PARAMETER") any: Any?
) {
    internal val path: String = removeTrailingSeparators(rawPath)

    public actual val parent: Path?
        get() {
            if (path.isEmpty()) return null
            if (isWindows) {
                if (!path.contains(UnixPathSeparator) && !path.contains(WindowsPathSeparator)) {
                    return null
                }
            } else if (!path.contains(SystemPathSeparator)) {
                return null
            }
            val p = nodeJsPath.dirname(path)
            return when {
                p.isEmpty() -> null
                p == path -> null
                else -> Path(p)
            }
        }

    public actual val isAbsolute: Boolean
        get() {
            return nodeJsPath.isAbsolute(path)
        }

    public actual val name: String
        get() {
            when {
                path.isEmpty() -> return ""
            }
            val p = nodeJsPath.basename(path)
            return when {
                p.isEmpty() -> ""
                else -> p
            }
        }

    public actual override fun toString(): String = path

    actual override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is Path) return false

        return path == other.path
    }

    actual override fun hashCode(): Int {
        return path.hashCode()
    }
}

public actual val SystemPathSeparator: Char by lazy {
    val sep = nodeJsPath.sep
    check(sep.length == 1)
    sep[0]
}

public actual fun Path(path: String): Path {
    return Path(path, null)
}

internal class FileSource(private val path: Path) : RawSource {
    private var buffer: kotlinx.io.node.Buffer? = null
    private var closed = false
    private var offset = 0
    private val fd = open(path)

    private fun open(path: Path): Int {
        if (!fs.existsSync(path.path)) {
            throw FileNotFoundException("File does not exist: ${path.path}")
        }
        var fd: Int = -1
        withCaughtException {
            fd = fs.openSync(path.path, "r")
        }?.also {
            throw IOException("Failed to open a file ${path.path}.", it)
        }
        if (fd < 0) throw IOException("Failed to open a file ${path.path}.")
        return fd
    }

    override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
        check(!closed) { "Source is closed." }
        if (byteCount == 0L) {
            return 0
        }
        if (buffer === null) {
            withCaughtException {
                buffer = fs.readFileSync(fd, null)
            }?.also {
                throw IOException("Failed to read data from ${path.path}", it)
            }
        }
        val len: Int = buffer!!.length
        if (offset >= len) {
            return -1L
        }
        val bytesToRead = minOf(byteCount, (len - offset))
        for (i in 0 until bytesToRead) {
            sink.writeByte(buffer!!.readInt8(offset++))
        }

        return bytesToRead
    }

    override fun close() {
        if (!closed) {
            closed = true
            fs.closeSync(fd)
        }
    }
}

internal class FileSink(path: Path, append: Boolean) : RawSink {
    private var closed = false
    private val fd = open(path, append)

    private fun open(path: Path, append: Boolean): Int {
        val flags = if (append) "a" else "w"
        var fd = -1
        withCaughtException {
            fd = fs.openSync(path.path, flags)
        }?.also {
            throw IOException("Failed to open a file ${path.path}.", it)
        }
        if (fd < 0) throw IOException("Failed to open a file ${path.path}.")
        return fd
    }

    @OptIn(UnsafeIoApi::class)
    override fun write(source: Buffer, byteCount: Long) {
        check(!closed) { "Sink is closed." }
        if (byteCount == 0L) {
            return
        }

        var remainingBytes = minOf(byteCount, source.size)
        while (remainingBytes > 0) {
            var segmentBytes = 0
            UnsafeBufferOperations.readFromHead(source) { headData, headPos, headLimit ->
                segmentBytes = headLimit - headPos
                val buf = buffer.Buffer.allocUnsafe(segmentBytes)
                for (offset in 0 until segmentBytes) {
                    buf.writeInt8(headData[headPos + offset], offset)
                }
                withCaughtException {
                    fs.writeFileSync(fd, buf)
                }?.also {
                    throw IOException("Write failed", it)
                }
                segmentBytes
            }
            remainingBytes -= segmentBytes
        }
    }

    override fun flush() = Unit

    override fun close() {
        if (!closed) {
            closed = true
            fs.closeSync(fd)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy