Skip to content
This repository has been archived by the owner on Jul 8, 2022. It is now read-only.

Commit

Permalink
Proper implementation of CatalogVfs (#582)
Browse files Browse the repository at this point in the history
Fixes #486
  • Loading branch information
soywiz authored Apr 11, 2022
1 parent f8ba6e1 commit cd02eb9
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 27 deletions.
17 changes: 13 additions & 4 deletions korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package com.soywiz.korio.file

import com.soywiz.klock.*
import com.soywiz.korio.async.*
import com.soywiz.korio.experimental.*
import com.soywiz.korio.file.std.*
import com.soywiz.korio.lang.*
import com.soywiz.korio.stream.*
Expand Down Expand Up @@ -31,14 +32,17 @@ abstract class Vfs : AsyncCloseable {
fun createExistsStat(
path: String, isDirectory: Boolean, size: Long, device: Long = -1, inode: Long = -1, mode: Int = 511,
owner: String = "nobody", group: String = "nobody", createTime: DateTime = DateTime.EPOCH, modifiedTime: DateTime = DateTime.EPOCH,
lastAccessTime: DateTime = modifiedTime, extraInfo: Any? = null, id: String? = null
lastAccessTime: DateTime = modifiedTime, extraInfo: Any? = null, id: String? = null,
cache: Boolean = false
) = VfsStat(
file = file(path), exists = true, isDirectory = isDirectory, size = size, device = device, inode = inode,
mode = mode, owner = owner, group = group, createTime = createTime, modifiedTime = modifiedTime,
lastAccessTime = lastAccessTime, extraInfo = extraInfo, id = id
)
).also {
if (cache) it.file.cachedStat = it
}

fun createNonExistsStat(path: String, extraInfo: Any? = null) = VfsStat(
fun createNonExistsStat(path: String, extraInfo: Any? = null, cache: Boolean = false) = VfsStat(
file = file(path), exists = false, isDirectory = false, size = 0L,
device = -1L, inode = -1L, mode = 511, owner = "nobody", group = "nobody",
createTime = DateTime.EPOCH, modifiedTime = DateTime.EPOCH, lastAccessTime = DateTime.EPOCH, extraInfo = extraInfo
Expand Down Expand Up @@ -277,6 +281,7 @@ open class VfsProcessHandler {

class VfsProcessException(message: String) : IOException(message)

@OptIn(KorioExperimentalApi::class)
data class VfsStat(
val file: VfsFile,
val exists: Boolean,
Expand All @@ -294,7 +299,11 @@ data class VfsStat(
val kind: Vfs.FileKind? = null,
val id: String? = null
) : Path by file {
val enrichedFile get() = file.copy().also { it.cachedStat = this }
val enrichedFile: VfsFile get() = file.copy().also { it.cachedStat = this }

//@Deprecated("Use file instead")
//val enrichedFile: VfsFile get() = file
//init { file.cachedStat = this }

fun toString(showFile: Boolean): String = "VfsStat(" + ArrayList<String>(16).also { al ->
if (showFile) al.add("file=$file") else al.add("file=${file.absolutePath}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.soywiz.korio.file.std

import com.soywiz.kds.*
import com.soywiz.klock.*
import com.soywiz.korio.dynamic.*
import com.soywiz.korio.file.*
import com.soywiz.korio.serialization.json.*
import kotlinx.coroutines.flow.*

fun VfsFile.withCatalog(): VfsFile = CatalogVfs(this).root
fun VfsFile.withCatalogJail(): VfsFile = CatalogVfs(this.jail()).root

open class CatalogVfs(val parent: VfsFile) : Vfs.Proxy() {

override suspend fun access(path: String): VfsFile = parent[path]

override suspend fun stat(path: String): VfsStat {
val normalizedPath = PathInfo(path).normalize()
if (normalizedPath == "/" || normalizedPath == "") {
return createExistsStat("/", isDirectory = true, size = 0L, cache = true)
}
val baseName = PathInfo(normalizedPath).baseName
val info = cachedListSimpleStats(PathInfo(normalizedPath).parent.fullPath)
return info[baseName] ?: createNonExistsStat(normalizedPath, cache = true)
}

override suspend fun listFlow(path: String): Flow<VfsFile> = listSimple(path).asFlow()

override suspend fun listSimple(path: String): List<VfsFile> =
cachedListSimpleStats(path).map { it.value.enrichedFile }

private val catalogCache = FastStringMap<Map<String, VfsStat>>()

suspend fun cachedListSimpleStats(path: String): Map<String, VfsStat> {
val key = PathInfo(path).normalize()
return catalogCache.getOrPut(key) { listSimpleStats(key) }
}

suspend fun listSimpleStats(path: String): Map<String, VfsStat> {
val catalogJsonString = parent[path]["\$catalog.json"].readString()
val data = Json.parse(catalogJsonString).dyn

return data.list.map {
val localName = PathInfo(it["name"].str).baseName
createExistsStat(
path = "$path/$localName",
isDirectory = it["isDirectory"].bool,
size = it["size"].long,
createTime = DateTime.fromUnix(it["createTime"].long),
modifiedTime = DateTime.fromUnix(it["modifiedTime"].long),
cache = true
)
}.associateBy { it.baseName }
}
}
24 changes: 5 additions & 19 deletions korio/src/commonMain/kotlin/com/soywiz/korio/file/std/UrlVfs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import com.soywiz.korio.serialization.json.*
import com.soywiz.korio.stream.*
import com.soywiz.korio.util.*

fun UrlVfs(url: String, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile = UrlVfs(URL(url), client, failFromStatus)
fun UrlVfs(url: String, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile =
UrlVfs(URL(url), client, failFromStatus)

fun UrlVfs(url: URL, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile =
UrlVfs(url.copy(path = "", query = null).fullUrl, Unit, client, failFromStatus)[url.path]

fun UrlVfsJailed(url: String, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile = UrlVfsJailed(URL(url), client, failFromStatus)
fun UrlVfsJailed(url: String, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile =
UrlVfsJailed(URL(url), client, failFromStatus)

fun UrlVfsJailed(url: URL, client: HttpClient = createHttpClient(), failFromStatus: Boolean = true): VfsFile =
UrlVfs(url.fullUrl, Unit, client, failFromStatus)[url.path]
Expand Down Expand Up @@ -161,23 +163,7 @@ class UrlVfs(
}

override suspend fun listSimple(path: String): List<VfsFile> {
return listSimpleStats(path).map { it.file }
}

suspend fun listSimpleStats(path: String): List<VfsStat> {
val catalogJsonString = this[path]["\$catalog.json"].readString()
val data = Json.parse(catalogJsonString).dyn

return data.list.map {
val localName = PathInfo(it["name"].str).baseName
createExistsStat(
path = "$path/$localName",
isDirectory = it["isDirectory"].bool,
size = it["size"].long,
createTime = DateTime.fromUnix(it["createTime"].long),
modifiedTime = DateTime.fromUnix(it["modifiedTime"].long),
)
}
TODO()
}

override fun toString(): String = "UrlVfs"
Expand Down
34 changes: 34 additions & 0 deletions korio/src/commonTest/kotlin/com/soywiz/korio/vfs/CatalogVfsTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.soywiz.korio.vfs

import com.soywiz.korio.async.*
import com.soywiz.korio.file.*
import com.soywiz.korio.file.std.*
import kotlinx.coroutines.flow.*
import kotlin.test.*

class CatalogVfsTest {
@Test
fun test() = suspendTest {
val vfs = MemoryVfsMix(
"/\$catalog.json" to """[
{"name": "demo", "size": 96, "modifiedTime": 0, "createTime": 0, "isDirectory": true},
{"name": "korge.png", "size": 14015, "modifiedTime": 1, "createTime": 1, "isDirectory": false},
{"name": "test.txt", "size": 11, "modifiedTime": 2, "createTime": 2, "isDirectory": false},
]""",
"/demo/\$catalog.json" to """[
{"name": "test.txt", "size": 12, "modifiedTime": 2, "createTime": 2, "isDirectory": false},
]""",
).withCatalog()

assertEquals(
"/demo,/demo/test.txt,/korge.png,/test.txt",
vfs.listRecursive().toList().joinToString(",") { it.fullPathNormalized }
)

assertEquals(0L, vfs["/"].size())
assertEquals(96L, vfs["/demo"].size())
assertEquals(14015L, vfs["/korge.png"].size())
assertEquals(11L, vfs["/test.txt"].size())
assertEquals(12L, vfs["/demo/test.txt"].size())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package com.soywiz.korio.runtime.browser

import com.soywiz.korio.file.SimpleStorage
import com.soywiz.korio.file.VfsFile
import com.soywiz.korio.file.std.MapLikeStorageVfs
import com.soywiz.korio.file.std.MemoryVfs
import com.soywiz.korio.file.std.UrlVfs
import com.soywiz.korio.file.std.*
import com.soywiz.korio.net.QueryString
import com.soywiz.korio.net.http.Http
import com.soywiz.korio.net.http.HttpClient
Expand Down Expand Up @@ -44,7 +42,7 @@ object JsRuntimeBrowser : JsRuntime() {

override fun langs(): List<String> = window.navigator.languages.asList()
override fun openVfs(path: String): VfsFile {
return UrlVfs(currentDir())[path].also {
return UrlVfs(currentDir())[path].withCatalogJail().root.also {
println("BROWSER openVfs: currentDir=${currentDir()}, path=$path, urlVfs=$it")
}
}
Expand Down

0 comments on commit cd02eb9

Please sign in to comment.