Skip to content

Instantly share code, notes, and snippets.

@iseki0
Last active October 12, 2025 19:36
Show Gist options
  • Select an option

  • Save iseki0/eb4495e72d65fd10c0dce408241d0fae to your computer and use it in GitHub Desktop.

Select an option

Save iseki0/eb4495e72d65fd10c0dce408241d0fae to your computer and use it in GitHub Desktop.
@file:OptIn(ExperimentalForeignApi::class)
package path
import kotlinx.cinterop.ExperimentalForeignApi
import platform.windows.GetLogicalDrives
internal object WindowsFileSystem : FileSystem {
override val separator: String get() = "\\"
override val roots: List<Path>
get() = run {
var mask = GetLogicalDrives()
List(mask.countOneBits()) {
val ch = 'A' + mask.takeLowestOneBit().countTrailingZeroBits()
mask = mask and mask.takeLowestOneBit().inv()
WindowsPath.of("""$ch:\""")
}
}
}
@file:OptIn(ExperimentalForeignApi::class)
package path
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import platform.windows.CloseHandle
import platform.windows.CreateFileW
import platform.windows.FILE_FLAG_BACKUP_SEMANTICS
import platform.windows.FILE_READ_ATTRIBUTES
import platform.windows.FILE_SHARE_DELETE
import platform.windows.FILE_SHARE_READ
import platform.windows.FILE_SHARE_WRITE
import platform.windows.GetFinalPathNameByHandleW
import platform.windows.GetFullPathNameW
import platform.windows.INVALID_HANDLE_VALUE
import platform.windows.MAX_PATH
import platform.windows.OPEN_EXISTING
import platform.windows.PathIsRelativeW
import platform.windows.TRUE
import platform.windows.WCHARVar
internal class WindowsPath : Path {
internal val value: String
val filenameIdx: Int
private constructor(value: String) {
this.value = value
filenameIdx = WindowsPathUtil.getFilenameIndex(value)
}
companion object {
internal fun ofNormalized(path: String) = WindowsPath(path)
internal fun of(path: String) = ofNormalized(WindowsPathUtil.normalizePath(path, collapse = false))
}
override val fileSystem: FileSystem
get() = WindowsFileSystem
override val isAbsolute: Boolean get() = PathIsRelativeW(value) != TRUE
override val parent: Path?
get() = if (filenameIdx == -1) null else of(value.substring(0, filenameIdx))
override val filename: String?
get() = if (filenameIdx != -1) value.substring(filenameIdx) else null
override fun join(vararg other: String): Path = other.fold(value) { base, other ->
WindowsPathUtil.joinPath(base, WindowsPathUtil.normalizePath(other, collapse = false))
}.let(::of)
override fun normalization(): Path = ofNormalized(WindowsPathUtil.normalizePath(value))
override fun toAbsolute(): Path = ofNormalized(getFullPath(value))
override fun evalSymlink(): Path = of(evalSymlink(value))
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as WindowsPath
return value == other.value
}
override fun hashCode(): Int {
return value.hashCode()
}
override fun toString(): String {
return value
}
}
private fun getFullPath(s: String): String {
memScoped {
var buf = allocArray<WCHARVar>(MAX_PATH)
var n = GetFullPathNameW(s, MAX_PATH.convert(), buf, null)
if (n >= MAX_PATH.convert()) {
buf = allocArray<WCHARVar>(n.convert())
n = GetFullPathNameW(s, n, buf, null)
}
if (n == 0u) {
throw InvalidPathException(s, formatErrorCode())
}
return buf.toKString()
}
}
private fun evalSymlink(s: String): String {
memScoped {
val h = CreateFileW(
s, FILE_READ_ATTRIBUTES.toUInt(), (FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE).toUInt(),
lpSecurityAttributes = null,
dwCreationDisposition = OPEN_EXISTING.toUInt(),
dwFlagsAndAttributes = FILE_FLAG_BACKUP_SEMANTICS.toUInt(),
hTemplateFile = null,
)
if (h == INVALID_HANDLE_VALUE) {
throw translateIOError(file = s)
}
try {
var buf = allocArray<WCHARVar>(MAX_PATH)
var n = GetFinalPathNameByHandleW(h, buf, MAX_PATH.convert(), 0u)
if (n >= MAX_PATH.convert()) {
buf = allocArray<WCHARVar>(n.convert())
n = GetFinalPathNameByHandleW(h, buf, n, 0u)
}
if (n == 0u) {
error("GetFinalPathNameByHandleW failed: ${formatErrorCode()}")
}
return buf.toKString()
} finally {
CloseHandle(h)
}
}
}
package path
import kotlin.jvm.JvmInline
internal data object WindowsPathUtil {
private fun invalidPath(
input: CharSequence, reason: String,
) = InvalidPathException(input.toString(), reason)
private fun Char.isSeparator() = this == '\\' || this == '/'
private fun Char.isWinDriveLetter() = this in 'A'..'Z' || this in 'a'..'z'
private fun Char.isPathForbidden(): Boolean = when (this) {
'<', '>', ':', '"', '/', '\\', '|', '?', '*' -> true
else -> this.code in 0x00..0x1F
}
private fun CharSequence.endWithDoubleDotSegment(index: Int): Boolean =
this[index] == '.' && index - 2 > 0 && this[index - 1] == '.' && this[index - 2].isPathForbidden()
private fun CharSequence.endWithSingleDotSegment(index: Int): Boolean =
this[index] == '.' && index - 1 > 0 && this[index - 1].isPathForbidden()
private fun CharSequence.findLastSeparatorInPath(end: Int, start: Int): Int {
for (i in end downTo start) {
if (this[i].isSeparator()) return i
if (this[i].isPathForbidden()) throwForbiddenCharInPath(i)
}
return -1
}
private fun CharSequence.throwForbiddenCharInPath(index: Int): Nothing {
throw invalidPath(this, "Filename contains forbidden character at $index: ${this[index]}")
}
private fun CharSequence.findPathStartOfUnc(isLong: Boolean): Int {
// Determine the prefix length: \\?\UNC\ (8) for long UNC paths, \\ (2) for normal UNC paths
val startIndex = if (isLong) 8 else 2
if (length <= startIndex) throw invalidPath(this, "UNC path is missing hostname")
var i = startIndex
// Scans a single UNC segment (e.g., server name or share name) until '\' or end of string.
// Also checks for any forbidden characters.
fun scanSegment(start: Int): Int {
var pos = start
while (pos < length) {
val ch = this[pos]
if (ch == '\\') break
if (ch.isPathForbidden()) {
throwForbiddenCharInPath(pos)
}
pos++
}
return pos
}
// Step 1: Scan the server name segment
i = scanSegment(i)
if (i == startIndex) {
throw invalidPath(this, "The UNC path is missing a server name.")
}
// Step 2: There must be a '\' after the server segment
if (i >= length || this[i] != '\\') return -1
i++ // Skip '\'
// Step 3: Scan the share name segment
val shareStart = i
i = scanSegment(i)
if (i == shareStart) {
throw invalidPath(this, "The UNC path is missing a share name.")
}
return i
}
@JvmInline
internal value class PathProp private constructor(private val v: ULong) {
companion object {
const val IS_LONG_OFF = 40
const val IS_UNC_OFF = 41
const val HAS_DOS_PREFIX = 42
const val IS_ABS_OFF = 43
const val IS_NO_PATH_OFF = 44
private inline val Boolean.l: ULong get() = if (this) 1uL else 0uL
fun analyze(input: CharSequence): PathProp {
val isLongPath: Boolean
val isUncPath: Boolean
var start = 0
when {
input.startsWith("""\\?\UNC\""") -> {
start = input.findPathStartOfUnc(isLong = true)
isLongPath = true
isUncPath = true
}
input.startsWith("""\\?\""") -> {
start = 4
isLongPath = true
isUncPath = false
}
input.length >= 2 && input[0].isSeparator() && input[1].isSeparator() -> {
start = input.findPathStartOfUnc(isLong = false)
isLongPath = false
isUncPath = true
}
else -> {
isLongPath = false
isUncPath = false
}
}
val hasDosPrefix: Boolean
if (!isUncPath && input.length >= start + 2 && input[start].isWinDriveLetter() && input[start + 1] == ':') {
start += 2
hasDosPrefix = true
} else {
hasDosPrefix = false
}
val isAbsolute = start <= input.lastIndex && input[start].isSeparator()
val isNoPath = start > input.lastIndex
var v = start.toUInt().toULong()
v = v or (isLongPath.l shl IS_LONG_OFF)
v = v or (isUncPath.l shl IS_UNC_OFF)
v = v or (hasDosPrefix.l shl HAS_DOS_PREFIX)
v = v or (isAbsolute.l shl IS_ABS_OFF)
v = v or (isNoPath.l shl IS_NO_PATH_OFF)
return PathProp(v)
}
}
private fun testb(off: Int) = (v shr off and 1uL) != 0uL
/**
* Path has long path prefix (e.g., `\\?\`).
*/
val isLong: Boolean get() = testb(IS_LONG_OFF)
/**
* Path is a UNC path (e.g., `\\server\share`).
*/
val isUNC: Boolean get() = testb(IS_UNC_OFF)
/**
* Path has DOS drive letter prefix (e.g., `C:`).
*/
val hasDosPrefix: Boolean get() = testb(HAS_DOS_PREFIX)
/**
* Path is absolute (e.g., `C:\folder` or `\\server\share`).
*/
val isAbs: Boolean get() = testb(IS_ABS_OFF)
/**
* Path contains no segments (e.g., `C:` or `\\server\share`).
*/
val isNoPath: Boolean get() = testb(IS_NO_PATH_OFF)
/**
* The index of the first character after the prefix (e.g., after `C:` or `\\server\share`).
*/
val start: Int get() = v.toUInt().toInt()
}
fun normalizePath(input: CharSequence, collapse: Boolean = true): String {
if (input.isEmpty()) return ""
val buf = CharArray(input.length + 1)
val props = PathProp.analyze(input)
val start = props.start
val isLongPath = props.isLong
val isUncPath = props.isUNC
val hasDosPrefix = props.hasDosPrefix
val isAbsolute = props.isAbs
val isNoPath = props.isNoPath
if (!isAbsolute && isUncPath && !isNoPath) {
throw invalidPath(input, "UNC path must be absolute")
}
if (!isAbsolute && isLongPath) {
throw invalidPath(input, "Long path prefix is only supported for absolute paths")
}
var isSep = true
var p = buf.lastIndex
var i = input.lastIndex
var skipLevel = 0
while (i >= start) {
val ch = input[i]
if (ch.isSeparator() && isSep) {
i--
continue
}
if (ch.isSeparator()) {
buf[p--] = '\\'
i--
isSep = true
continue
}
if (ch.isPathForbidden()) {
input.throwForbiddenCharInPath(i)
}
if (ch == '.' && collapse) {
if (input.endWithDoubleDotSegment(i)) {
skipLevel++
i -= 3 // skip \..
continue
}
if (input.endWithSingleDotSegment(i)) {
i -= 2 // skip \.
continue
}
}
if (skipLevel > 0) {
val ii = input.findLastSeparatorInPath(i, start)
i = if (ii == -1) {
// cannot go back anymore
start - 1
} else {
ii - 1
}
skipLevel--
continue
}
isSep = ch.isSeparator()
buf[p--] = ch
i--
}
if (!isAbsolute) {
repeat(skipLevel) {
buf[p--] = '.'
buf[p--] = '.'
if (it != skipLevel - 1) {
buf[p--] = '\\'
}
}
}
// handle the prefix \, check is abs
val alreadyWriteSlash = p + 1 <= buf.lastIndex && buf[p + 1] == '\\'
if (isAbsolute && !alreadyWriteSlash || isNoPath) {
buf[p--] = '\\'
} else if (!isAbsolute && alreadyWriteSlash) {
buf[p++] = 0.toChar()
}
// copy the prefix
for (i in start - 1 downTo 0) {
buf[p--] = input[i]
}
if (hasDosPrefix && buf[p + start - 1].isLowerCase()) {
buf[p + start - 1] = buf[p + start - 1].uppercaseChar()
}
val offset = p + 1
return buf.concatToString(offset).let(::normalizePathForInMaxPathConvention)
}
private fun normalizePathForInMaxPathConvention(input: CharSequence): String {
if (input.isEmpty()) return ""
val props = PathProp.analyze(input)
if (!props.isAbs) return input.toString()
if (props.isLong) {
if (props.isUNC) {
if (input.length < 264) {
return '\\' + input.substring(7)
}
return input.toString()
}
if (input.length < 264) {
return input.substring(4)
}
return input.toString()
}
if (input.length >= 260) {
if (props.isUNC) {
return """\\?\UNC\""" + input.substring(2)
}
return """\\?\$input"""
}
return input.toString()
}
fun getFilenameIndex(path: CharSequence): Int {
val props = PathProp.analyze(path)
if (props.isNoPath) return -1
val i = path.lastIndexOf('\\')
if (props.start == path.lastIndex && path[props.start] == '\\') return -1
if (i < props.start) {
return props.start
}
return i + 1
}
fun joinPath(base: CharSequence, other: CharSequence): String {
if (other.isEmpty()) {
return base.toString()
}
if (base.isEmpty()) {
return other.toString()
}
var base = base
var other = other
val otherProps = PathProp.analyze(other)
val baseProps = PathProp.analyze(base)
val otherDriver = otherProps.start.let { other.take(it) }
val baseDriver = baseProps.start.let { base.take(it) }
if (otherProps.isAbs) {
if (otherDriver != "" || baseDriver == "") {
return other.toString()
}
base = baseDriver
} else {
if (otherDriver == baseDriver) {
other = other.drop(otherProps.start)
} else if (otherDriver != "") {
return other.toString()
}
}
return when {
base.endsWith('\\') && other.startsWith('\\') -> "$base${other.substring(1)}"
base.endsWith('\\') || other.startsWith('\\') -> "$base$other"
else -> "$base\\$other"
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment