From dce48e72c3102420afdc1a0ef46f751eaa605357 Mon Sep 17 00:00:00 2001 From: soywiz Date: Wed, 17 Feb 2021 19:25:19 +0100 Subject: [PATCH] Sync from next --- gradle.properties | 5 +- korio/build.gradle.kts | 2 + .../concurrent/atomic/KorAtomicAndroid.kt | 30 - .../soywiz/korio/async/ObservableProperty.kt | 27 +- .../korio/concurrent/atomic/KorAtomic.kt | 13 +- .../kotlin/com/soywiz/korio/file/Vfs.kt | 2 +- .../kotlin/com/soywiz/korio/file/VfsFile.kt | 1 + .../kotlin/com/soywiz/korio/lang/Charset.kt | 35 +- .../kotlin/com/soywiz/korio/lang/StringExt.kt | 2 +- .../soywiz/korio/serialization/json/Json.kt | 2 +- .../kotlin/com/soywiz/korio/time/TraceTime.kt | 19 + .../kotlin/com/soywiz/korio/util/BuildList.kt | 4 +- .../com/soywiz/korio/util/RedirectField.kt | 1 + .../kotlin/com/soywiz/korio/util/StrReader.kt | 726 +++++++++--------- .../com/soywiz/korio/concurrent/AtomicTest.kt | 15 +- .../korio/concurrent/atomic/KorAtomicJs.kt | 12 +- .../kotlin/com/soywiz/korio/file/FileExt.kt | 59 ++ .../korio/concurrent/atomic/KorAtomicJvm.kt | 8 +- .../com/soywiz/korio/file/std/LocalVfsJvm.kt | 1 + .../concurrent/atomic/KorAtomicNative.kt | 8 +- 20 files changed, 550 insertions(+), 422 deletions(-) delete mode 100644 korio/src/androidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicAndroid.kt create mode 100644 korio/src/commonMain/kotlin/com/soywiz/korio/time/TraceTime.kt create mode 100644 korio/src/jsMain/kotlin/com/soywiz/korio/file/FileExt.kt rename korio/src/{jvmMain => jvmAndroidMain}/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt (91%) diff --git a/gradle.properties b/gradle.properties index 293dc331..53528b1c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,9 +13,10 @@ coroutinesVersion=1.4.2 # korlibs klockVersion=2.0.6 -kdsVersion=2.0.6 -kmemVersion=2.0.6 +kdsVersion=2.0.7 +kmemVersion=2.0.7 kryptoVersion=2.0.6 +kloggerVersion=2.0.7 # bintray location project.bintray.org=korlibs diff --git a/korio/build.gradle.kts b/korio/build.gradle.kts index ca63a428..b657b9c6 100644 --- a/korio/build.gradle.kts +++ b/korio/build.gradle.kts @@ -6,6 +6,7 @@ val klockVersion: String by project val kdsVersion: String by project val kmemVersion: String by project val kryptoVersion: String by project +val kloggerVersion: String by project val coroutinesVersion: String by project dependencies { @@ -13,6 +14,7 @@ dependencies { add("commonMainApi", "com.soywiz.korlibs.kds:kds:$kdsVersion") add("commonMainApi", "com.soywiz.korlibs.kmem:kmem:$kmemVersion") add("commonMainApi", "com.soywiz.korlibs.krypto:krypto:$kryptoVersion") + add("commonMainApi", "com.soywiz.korlibs.klogger:klogger:$kloggerVersion") add("commonMainApi", "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") afterEvaluate { diff --git a/korio/src/androidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicAndroid.kt b/korio/src/androidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicAndroid.kt deleted file mode 100644 index 927840bf..00000000 --- a/korio/src/androidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicAndroid.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.soywiz.korio.concurrent.atomic - -import java.util.concurrent.atomic.* - -actual fun korAtomic(initial: T): KorAtomicRef = object : KorAtomicRef(initial) { - val ref = AtomicReference(initial) - override var value: T get() = ref.get(); set(value) = ref.set(value) - override fun compareAndSet(expect: T, update: T): Boolean = ref.compareAndSet(expect, update) -} - -actual fun korAtomic(initial: Boolean): KorAtomicBoolean = object : KorAtomicBoolean(initial) { - val ref = AtomicBoolean(initial) - override var value: Boolean get() = ref.get(); set(value) = ref.set(value) - - override fun compareAndSet(expect: Boolean, update: Boolean): Boolean = ref.compareAndSet(expect, update) -} - -actual fun korAtomic(initial: Int): KorAtomicInt = object : KorAtomicInt(initial) { - val ref = AtomicInteger(initial) - override var value: Int get() = ref.get(); set(value) = ref.set(value) - override fun compareAndSet(expect: Int, update: Int): Boolean = ref.compareAndSet(expect, update) - override fun addAndGet(delta: Int): Int = ref.addAndGet(delta) -} - -actual fun korAtomic(initial: Long): KorAtomicLong = object : KorAtomicLong(initial) { - val ref = AtomicLong(initial) - override var value: Long get() = ref.get(); set(value) = ref.set(value) - override fun compareAndSet(expect: Long, update: Long): Boolean = ref.compareAndSet(expect, update) - override fun addAndGet(delta: Long): Long = ref.addAndGet(delta) -} diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/async/ObservableProperty.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/async/ObservableProperty.kt index 3276e36f..bb2d21c7 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/async/ObservableProperty.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/async/ObservableProperty.kt @@ -6,17 +6,34 @@ import kotlin.reflect.* class ObservableProperty(initial: T) { private var observers = ArrayList<(T) -> Unit>() - var value: T = initial; private set + private var _value: T = initial + + var value: T + get() = _value + set(v) { + update(v) + } val observerCount: Int get() = observers.size fun clear() = observers.clear() - fun observe(handler: (T) -> Unit) { + fun observe(handler: (T) -> Unit): ObservableProperty { observers.add(handler) + return this + } + fun observeStart(handler: (T) -> Unit): ObservableProperty { + observe(handler) + handler(value) + return this } operator fun invoke(handler: (T) -> Unit) = observe(handler) + fun bind(prop: KMutableProperty0) { + observe { prop.set(value) } + prop.set(value) + } + fun update(value: T) { - this.value = value + this._value = value observers.fastForEach { it(value) } } operator fun invoke(value: T) = update(value) @@ -36,3 +53,7 @@ class ObservableProperty(initial: T) { } } } + +fun ObservableProperty(prop: KMutableProperty0): ObservableProperty { + return ObservableProperty(prop.get()).observeStart { prop.set(it) } +} diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomic.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomic.kt index ffd9777e..66858425 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomic.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomic.kt @@ -7,6 +7,11 @@ expect fun korAtomic(initial: Boolean): KorAtomicBoolean expect fun korAtomic(initial: Int): KorAtomicInt expect fun korAtomic(initial: Long): KorAtomicLong +fun KorAtomicRef(initial: T): KorAtomicRef = korAtomic(initial) +fun KorAtomicBoolean(initial: Boolean): KorAtomicBoolean = korAtomic(initial) +fun KorAtomicInt(initial: Int): KorAtomicInt = korAtomic(initial) +fun KorAtomicLong(initial: Long): KorAtomicLong = korAtomic(initial) + //fun korAtomic(initial: T): KorAtomicRef = KorAtomicRef(initial) //fun korAtomic(initial: Boolean): KorAtomicBoolean = KorAtomicBoolean(initial) //fun korAtomic(initial: Int): KorAtomicInt = KorAtomicInt(initial) @@ -21,7 +26,7 @@ interface KorAtomicNumber : KorAtomicBase { fun addAndGet(delta: T): T } -open class KorAtomicRef(initial: T) : KorAtomicBase { +open class KorAtomicRef internal constructor(initial: T, dummy: Boolean) : KorAtomicBase { override var value: T = initial override fun compareAndSet(expect: T, update: T): Boolean { @@ -34,7 +39,7 @@ open class KorAtomicRef(initial: T) : KorAtomicBase { } } -open class KorAtomicBoolean(initial: Boolean) : KorAtomicBase { +open class KorAtomicBoolean internal constructor(initial: Boolean, dummy: Boolean) : KorAtomicBase { override var value: Boolean = initial override fun compareAndSet(expect: Boolean, update: Boolean): Boolean { @@ -47,7 +52,7 @@ open class KorAtomicBoolean(initial: Boolean) : KorAtomicBase { } } -open class KorAtomicInt(initial: Int) : KorAtomicNumber { +open class KorAtomicInt internal constructor(initial: Int, dummy: Boolean) : KorAtomicNumber { override var value: Int = initial override fun compareAndSet(expect: Int, update: Int): Boolean { @@ -65,7 +70,7 @@ open class KorAtomicInt(initial: Int) : KorAtomicNumber { } } -open class KorAtomicLong(initial: Long) : KorAtomicNumber { +open class KorAtomicLong internal constructor(initial: Long, dummy: Boolean) : KorAtomicNumber { override var value: Long = initial override fun compareAndSet(expect: Long, update: Long): Boolean { diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt index 6926a230..3cc8836f 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/file/Vfs.kt @@ -60,7 +60,7 @@ abstract class Vfs : AsyncCloseable { handler: VfsProcessHandler = VfsProcessHandler() ): Int = unsupported() - open suspend fun open(path: String, mode: VfsOpenMode): AsyncStream = throw UnsupportedOperationException() + open suspend fun open(path: String, mode: VfsOpenMode): AsyncStream = unsupported() open suspend fun openInputStream(path: String): AsyncInputStream = open( path, diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/file/VfsFile.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/file/VfsFile.kt index 839a71aa..130e3afa 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/file/VfsFile.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/file/VfsFile.kt @@ -235,6 +235,7 @@ data class VfsFile( } fun jail(): VfsFile = JailVfs(this) + fun jailParent(): VfsFile = JailVfs(parent)[this.baseName] suspend fun getUnderlyingUnscapedFile(): FinalVfsFile = vfs.getUnderlyingUnscapedFile(this.path) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt index 4cddfedf..d93d373a 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/Charset.kt @@ -6,6 +6,10 @@ import com.soywiz.korio.internal.* import kotlin.native.concurrent.SharedImmutable abstract class Charset(val name: String) { + // Just an estimation, might not be accurate, but hopefully will help setting StringBuilder and ByteArrayBuilder to a better initial capacity + open fun estimateNumberOfCharactersForBytes(nbytes: Int): Int = nbytes * 2 + open fun estimateNumberOfBytesForCharacters(nchars: Int): Int = nchars * 2 + abstract fun encode(out: ByteArrayBuilder, src: CharSequence, start: Int = 0, end: Int = src.length) abstract fun decode(out: StringBuilder, src: ByteArray, start: Int = 0, end: Int = src.size) @@ -14,7 +18,7 @@ abstract class Charset(val name: String) { return UTF8 } - fun StringBuilder.appendCodePoint(codePoint: Int) { + fun StringBuilder.appendCodePointV(codePoint: Int) { if (codePoint in 0xD800..0xDFFF || codePoint > 0xFFFF) { val U0 = codePoint - 0x10000 val hs = U0.extract(10, 10) @@ -51,7 +55,10 @@ abstract class Charset(val name: String) { } open class UTC8CharsetBase(name: String) : Charset(name) { - private fun createByte(codePoint: Int, shift: Int): Int = codePoint shr shift and 0x3F or 0x80 + override fun estimateNumberOfCharactersForBytes(nbytes: Int): Int = nbytes * 2 + override fun estimateNumberOfBytesForCharacters(nchars: Int): Int = nchars * 2 + + private fun createByte(codePoint: Int, shift: Int): Int = codePoint shr shift and 0x3F or 0x80 override fun encode(out: ByteArrayBuilder, src: CharSequence, start: Int, end: Int) { decodeCodePoints(src, start, end) { codePoint -> @@ -85,22 +92,22 @@ open class UTC8CharsetBase(name: String) : Charset(name) { when (c shr 4) { in 0b0000..0b0111 -> { // 0xxxxxxx - out.appendCodePoint(c) + out.appendCodePointV(c) i += 1 } in 0b1100..0b1101 -> { // 110x xxxx 10xx xxxx - out.appendCodePoint((c and 0x1F shl 6 or (src[i + 1].toInt() and 0x3F))) + out.appendCodePointV((c and 0x1F shl 6 or (src[i + 1].toInt() and 0x3F))) i += 2 } 0b1110 -> { // 1110 xxxx 10xx xxxx 10xx xxxx - out.appendCodePoint((c and 0x0F shl 12 or (src[i + 1].toInt() and 0x3F shl 6) or (src[i + 2].toInt() and 0x3F))) + out.appendCodePointV((c and 0x0F shl 12 or (src[i + 1].toInt() and 0x3F shl 6) or (src[i + 2].toInt() and 0x3F))) i += 3 } 0b1111 -> { // 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx - out.appendCodePoint(0 + out.appendCodePointV(0 .insert(src[i + 0].toInt().extract(0, 3), 18, 3) .insert(src[i + 1].toInt().extract(0, 6), 12, 6) .insert(src[i + 2].toInt().extract(0, 6), 6, 6) @@ -119,7 +126,12 @@ open class UTC8CharsetBase(name: String) : Charset(name) { } } -open class SingleByteCharset(name: String, val conv: String) : Charset(name) { +abstract class BaseSingleByteCharset(name: String) : Charset(name) { + override fun estimateNumberOfCharactersForBytes(nbytes: Int): Int = nbytes + override fun estimateNumberOfBytesForCharacters(nchars: Int): Int = nchars +} + +open class SingleByteCharset(name: String, val conv: String) : BaseSingleByteCharset(name) { val v: IntIntMap = IntIntMap().apply { for (n in 0 until conv.length) this[conv[n].toInt()] = n } @@ -144,6 +156,9 @@ object ISO_8859_1 : SingleByteCharset("ISO-8859-1", buildString { for (n in 0 un expect val UTF8: Charset class UTF16Charset(val le: Boolean) : Charset("UTF-16-" + (if (le) "LE" else "BE")) { + override fun estimateNumberOfCharactersForBytes(nbytes: Int): Int = nbytes * 2 + override fun estimateNumberOfBytesForCharacters(nchars: Int): Int = nchars * 2 + override fun decode(out: StringBuilder, src: ByteArray, start: Int, end: Int) { for (n in start until end step 2) out.append(src.readS16(n, le).toChar()) } @@ -157,7 +172,7 @@ class UTF16Charset(val le: Boolean) : Charset("UTF-16-" + (if (le) "LE" else "BE } } -object ASCII : Charset("ASCII") { +object ASCII : BaseSingleByteCharset("ASCII") { override fun encode(out: ByteArrayBuilder, src: CharSequence, start: Int, end: Int) { for (n in start until end) out.append(src[n].toByte()) } @@ -182,13 +197,13 @@ object Charsets { } fun String.toByteArray(charset: Charset = UTF8): ByteArray { - val out = ByteArrayBuilder() + val out = ByteArrayBuilder(charset.estimateNumberOfBytesForCharacters(this.length)) charset.encode(out, this) return out.toByteArray() } fun ByteArray.toString(charset: Charset): String { - val out = StringBuilder() + val out = StringBuilder(charset.estimateNumberOfCharactersForBytes(this.size)) charset.decode(out, this) return out.toString() } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt index 6a333a13..6f8ca819 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/lang/StringExt.kt @@ -14,7 +14,7 @@ operator fun String.Companion.invoke(arrays: IntArray, offset: Int = 0, size: In } fun String_fromIntArray(arrays: IntArray, offset: Int = 0, size: Int = arrays.size - offset): String = String(arrays, offset, size) -fun String_fromCharArray(arrays: CharArray, offset: Int = 0, size: Int = arrays.size - offset): String = String(arrays, offset, size) +fun String_fromCharArray(arrays: CharArray, offset: Int = 0, size: Int = arrays.size - offset): String = arrays.concatToString(offset, offset + size) //////////////////////////////////// //////////////////////////////////// diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/serialization/json/Json.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/serialization/json/Json.kt index fffc1ef8..08d7975e 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/serialization/json/Json.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/serialization/json/Json.kt @@ -30,7 +30,7 @@ object Json { '}' -> break@obj; ',' -> continue@obj; else -> s.unread() } val key = parse(s, context) as String - s.skipSpaces().expect(':') + s.skipSpaces().skipExpect(':') val value = parse(s, context) this[key] = value } diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/time/TraceTime.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/time/TraceTime.kt new file mode 100644 index 00000000..549d03bf --- /dev/null +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/time/TraceTime.kt @@ -0,0 +1,19 @@ +package com.soywiz.korio.time + +import com.soywiz.klock.* +import com.soywiz.klogger.* +import com.soywiz.korio.lang.* + +@PublishedApi +internal val traceTimes by lazy { Environment["TRACE_TIMES"] == "true" } + +inline fun traceTime(name: String, block: () -> T): T { + lateinit var result: T + val time = measureTime { + result = block() + } + if (traceTimes) { + Console.info("$name loaded in $time") + } + return result +} diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/util/BuildList.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/util/BuildList.kt index d43cbb7f..42fccd36 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/util/BuildList.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/util/BuildList.kt @@ -1,3 +1,5 @@ package com.soywiz.korio.util -inline fun buildList(callback: ArrayList.() -> Unit): List = arrayListOf().apply(callback) +import com.soywiz.kds.* + +inline fun buildList(callback: FastArrayList.() -> Unit): List = FastArrayList().apply(callback) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/util/RedirectField.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/util/RedirectField.kt index 1e51d8b2..8c2b5a31 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/util/RedirectField.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/util/RedirectField.kt @@ -11,6 +11,7 @@ class RedirectMutableField(val redirect: KMutableProperty0) { inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: V) = redirect.set(value) } +// @TODO: Shouldn't be required anymore fun redirect(prop: KMutableProperty0) = RedirectMutableField(prop) fun redirect(prop: KProperty0) = RedirectField(prop) diff --git a/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt b/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt index e597d94e..a4c1e472 100644 --- a/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt +++ b/korio/src/commonMain/kotlin/com/soywiz/korio/util/StrReader.kt @@ -9,358 +9,380 @@ import com.soywiz.korio.internal.* import com.soywiz.korio.internal.max2 class StrReader(val str: String, val file: String = "file", var pos: Int = 0) { - companion object { - fun literals(vararg lits: String): Literals = Literals.fromList(lits.toList().toTypedArray()) - } - - val length: Int = this.str.length - val available: Int get() = length - this.pos - val eof: Boolean get() = (this.pos >= this.str.length) - val hasMore: Boolean get() = (this.pos < this.str.length) - - fun reset() = run { this.pos = 0 } - fun createRange(range: IntRange): TRange = createRange(range.start, range.endInclusive + 1) - fun createRange(start: Int = this.pos, end: Int = this.pos): TRange = TRange(start, end, this) - - fun readRange(length: Int): TRange { - val range = TRange(this.pos, this.pos + length, this) - this.pos += length - return range - } - - inline fun slice(action: () -> Unit): String? { - val start = this.pos - action() - val end = this.pos - return if (end > start) this.slice(start, end) else null - } - - fun slice(start: Int, end: Int): String = this.str.substring(start, end) - fun peek(count: Int): String = substr(this.pos, count) - fun peek(): Char = if (hasMore) this.str[this.pos] else '\u0000' - fun peekChar(): Char = if (hasMore) this.str[this.pos] else '\u0000' - fun read(count: Int): String = this.peek(count).apply { skip(count) } - fun skipUntil(char: Char) { - val skipPos = this.str.indexOf(char, pos) - pos = (if (skipPos >= 0) skipPos else length) - } - fun skipUntilIncluded(char: Char) { - skipUntil(char) - if (hasMore && peekChar() == char) skip(1) - } - //inline fun skipWhile(check: (Char) -> Boolean) = run { while (check(this.peekChar())) this.skip(1) } - inline fun skipWhile(filter: (Char) -> Boolean) { - while (hasMore && filter(this.peekChar())) { - this.readChar() - } - } - - inline fun skipUntil(filter: (Char) -> Boolean) = - run { while (hasMore && !filter(this.peekChar())) this.readChar() } - - inline fun matchWhile(check: (Char) -> Boolean): String? = slice { skipWhile(check) } - - fun readUntil(char: Char) = this.slice { skipUntil(char) } - fun readUntilIncluded(char: Char) = this.slice { skipUntilIncluded(char) } - inline fun readWhile(filter: (Char) -> Boolean) = this.slice { skipWhile(filter) } ?: "" - inline fun readUntil(filter: (Char) -> Boolean) = this.slice { skipUntil(filter) } ?: "" - fun unread(count: Int = 1) = this.apply { this.pos -= count; } - fun readChar(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' - fun read(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' - // @TODO: https://youtrack.jetbrains.com/issue/KT-29577 - private fun posSkip(count: Int): Int { - val out = this.pos - this.pos += count - return out - } - - fun readRemaining(): String = read(available) - - fun readExpect(expected: String): String { - val readed = this.read(expected.length) - if (readed != expected) throw IllegalArgumentException("Expected '$expected' but found '$readed' at $pos") - return readed - } - - fun skipExpect(expected: Char) { - val readed = this.readChar() - if (readed != expected) throw IllegalArgumentException("Expected '$expected' but found '$readed' at $pos") - } - - fun expect(expected: Char) = readExpect("$expected") - fun skip(count: Int = 1) = this.apply { this.pos += count; } - private fun substr(pos: Int, length: Int): String { - return this.str.substring(min2(pos, this.length), min2(pos + length, this.length)) - } - - fun tryLit(lit: String): String? { - if (substr(this.pos, lit.length) != lit) return null - this.pos += lit.length - return lit - } - - fun tryLitRange(lit: String): TRange? = - if (substr(this.pos, lit.length) == lit) this.readRange(lit.length) else null - - fun matchLit(lit: String): String? = tryLit(lit) - fun matchLitRange(lit: String): TRange? = tryLitRange(lit) - - fun matchLitListRange(lits: Literals): TRange? { - lits.lengths.fastForEach { len -> - if (lits.contains(substr(this.pos, len))) return this.readRange(len) - } - return null - } - - fun skipSpaces() = this.apply { this.skipWhile { it.isWhitespaceFast() } } - - fun matchIdentifier() = matchWhile { it.isLetterDigitOrUnderscore() || it == '-' || it == '~' || it == ':' } - fun matchSingleOrDoubleQuoteString(): String? { - return when (this.peekChar()) { - '\'', '"' -> { - this.slice { - val quoteType = this.readChar() - this.readUntil(quoteType) - this.readChar() - } - } - else -> null - } - } - - fun tryRegex(v: Regex): String? { - val result = v.find(this.str.substring(this.pos)) ?: return null - val m = result.groups[0]!!.value - this.pos += m.length - return m - } - - fun tryRegexRange(v: Regex): TRange? { - val result = v.find(this.str.substring(this.pos)) ?: return null - return this.readRange(result.groups[0]!!.value.length) - } - - fun matchStartEnd(start: String, end: String): String? { - if (substr(this.pos, start.length) != start) return null - val startIndex = this.pos - val index = this.str.indexOf(end, this.pos) - if (index < 0) return null - //trace(index); - this.pos = index + end.length - return this.slice(startIndex, this.pos) - } - - fun clone(): StrReader = StrReader(str, file, pos) - - fun tryRead(str: String): Boolean { - if (peek(str.length) == str) { - skip(str.length) - return true - } - return false - } - - class Literals( - private val lits: Array, - private val map: MutableMap, - val lengths: Array - ) { - companion object { - fun invoke(vararg lits: String): Literals = - fromList(lits.toCollection(arrayListOf()).toTypedArray()) - - //fun invoke(lits:Array): Literals = fromList(lits) - fun fromList(lits: Array): Literals { - val lengths = lits.map { it.length }.sorted().reversed().distinct().toTypedArray() - val map = linkedMapOf() - lits.fastForEach { lit -> - map[lit] = true - } - return Literals(lits, map, lengths) - } - } - - fun contains(lit: String) = map.containsKey(lit) - - fun matchAt(str: String, offset: Int): String? { - lengths.fastForEach { len -> - val id = str.substr(offset, len) - if (contains(id)) return id - } - return null - } - - override fun toString() = "Literals(${lits.joinToString(" ")})" - } - - class TRange(val min: Int, val max: Int, val reader: StrReader) { - companion object { - fun combine(a: TRange, b: TRange): TRange { - return TRange(min2(a.min, b.min), max2(a.max, b.max), a.reader) - } - - fun combineList(list: List): TRange? { - if (list.isEmpty()) return null - val first = list[0] - var min = first.min - var max = first.max - list.fastForEach { i -> - min = min2(min, i.min) - max = max2(max, i.max) - } - return TRange(min, max, first.reader) - } - - fun createDummy() = TRange(0, 0, StrReader("")) - } - - fun contains(index: Int): Boolean = index >= this.min && index <= this.max - override fun toString() = "$min:$max" - - val file: String get() = this.reader.file - val text: String get () = this.reader.slice(this.min, this.max) - - fun startEmptyRange(): TRange = TRange(this.min, this.min, this.reader) - fun endEmptyRange(): TRange = TRange(this.max, this.max, this.reader) - fun displace(offset: Int): TRange = TRange(this.min + offset, this.max + offset, this.reader) - } - - fun readStringLit(reportErrors: Boolean = true): String { - val out = StringBuilder() - val quotec = read() - when (quotec) { - '"', '\'' -> Unit - else -> invalidOp("Invalid string literal") - } - var closed = false - while (hasMore) { - val c = read() - if (c == '\\') { - val cc = read() - out.append( - when (cc) { - '\\' -> '\\'; '/' -> '/'; '\'' -> '\''; '"' -> '"' - 'b' -> '\b'; 'f' -> '\u000c'; 'n' -> '\n'; 'r' -> '\r'; 't' -> '\t' - 'u' -> read(4).toInt(0x10).toChar() - else -> throw IOException("Invalid char '$cc'") - } - ) - } else if (c == quotec) { - closed = true - break - } else { - out.append(c) - } - } - if (!closed && reportErrors) { - throw RuntimeException("String literal not closed! '${this.str}'") - } - return out.toString() - } - - - fun tryReadInt(default: Int): Int { - var digitCount = 0 - var integral = 0 - var mult = 1 - loop@ while (!eof) { - when (val c = peek()) { - '-' -> { - skip(1) - mult *= -1 - } - in '0'..'9' -> { - val digit = c - '0' - skip(1) - digitCount++ - integral *= 10 - integral += digit - } - else -> { - break@loop - } - } - } - return if (digitCount == 0) default else integral - } - - fun tryReadNumber(default: Double = Double.NaN): Double { - val start = pos - skipWhile { - @Suppress("ConvertTwoComparisonsToRangeCheck") - (it >= '0' && it <= '9') || (it == '+') || (it == '-') || (it == 'e') || (it == 'E') || (it == '.') - } - val end = pos - if (end == start) return default - return NumberParser.parseDouble(this.str, start, end) - } - - fun tryExpect(str: String): Boolean { - for (n in 0 until str.length) { - if (this.peekOffset(n) != str[n]) return false - } - skip(str.length) - return true - } - - fun tryExpect(str: Char): Boolean { - if (peekChar() != str) return false - skip(1) - return true - } - - fun peekOffset(offset: Int = 0): Char = this.str.getOrElse(pos + offset) { '\u0000' } - - fun readFloats(list: FloatArrayList = FloatArrayList(7)): FloatArrayList { - while (!eof) { - val pos0 = pos - val float = skipSpaces().tryReadNumber().toFloat() - skipSpaces() - val pos1 = pos - if (pos1 == pos0) error("Invalid number at $pos0 in '$str'") - list.add(float) - //println("float: $float, ${reader.pos}/${reader.length}") - } - return list - } - - fun readIds(list: ArrayList = ArrayList(7)): ArrayList { - while (!eof) { - val pos0 = pos - val id = skipSpaces().tryReadId() ?: "" - skipSpaces() - val pos1 = pos - if (pos1 == pos0) error("Invalid identifier at $pos0 in '$str'") - list.add(id) - //println("float: $float, ${reader.pos}/${reader.length}") - } - return list - } - - fun readInts(list: IntArrayList = IntArrayList(7)): IntArrayList { - while (!eof) { - val pos0 = pos - val v = skipSpaces().tryReadInt(0) - skipSpaces() - val pos1 = pos - if (pos1 == pos0) error("Invalid int at $pos0 in '$str'") - list.add(v) - //println("float: $float, ${reader.pos}/${reader.length}") - } - return list - } - - - fun tryReadId(): String? { - val start = pos - skipWhile { - @Suppress("ConvertTwoComparisonsToRangeCheck") - (it >= '0' && it <= '9') || (it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z') || (it == '_') || (it == '.') - } - val end = pos - if (end == start) return null - return this.str.substring(start, end) - } + private val tempCharArray = CharArray(str.length) + + companion object { + fun literals(vararg lits: String): Literals = Literals.fromList(lits.toList().toTypedArray()) + } + + val length: Int = this.str.length + val available: Int get() = length - this.pos + val eof: Boolean get() = (this.pos >= this.str.length) + val hasMore: Boolean get() = (this.pos < this.str.length) + + fun reset() = run { this.pos = 0 } + fun createRange(range: IntRange): TRange = createRange(range.start, range.endInclusive + 1) + fun createRange(start: Int = this.pos, end: Int = this.pos): TRange = TRange(start, end, this) + + fun readRange(length: Int): TRange { + val range = TRange(this.pos, this.pos + length, this) + this.pos += length + return range + } + + inline fun slice(action: () -> Unit): String? { + val start = this.pos + action() + val end = this.pos + return if (end > start) this.slice(start, end) else null + } + + fun slice(start: Int, end: Int): String = this.str.substring(start, end) + fun peek(count: Int): String = substr(this.pos, count) + fun peek(): Char = if (hasMore) this.str[this.pos] else '\u0000' + fun peekChar(): Char = if (hasMore) this.str[this.pos] else '\u0000' + fun read(count: Int): String = this.peek(count).apply { skip(count) } + fun skipUntil(char: Char) { + val skipPos = this.str.indexOf(char, pos) + pos = (if (skipPos >= 0) skipPos else length) + } + fun skipUntilIncluded(char: Char) { + skipUntil(char) + if (hasMore && peekChar() == char) skip(1) + } + //inline fun skipWhile(check: (Char) -> Boolean) = run { while (check(this.peekChar())) this.skip(1) } + inline fun skipWhile(filter: (Char) -> Boolean) { + while (hasMore && filter(this.peekChar())) { + this.readChar() + } + } + + inline fun skipUntil(filter: (Char) -> Boolean) = + run { while (hasMore && !filter(this.peekChar())) this.readChar() } + + inline fun matchWhile(check: (Char) -> Boolean): String? = slice { skipWhile(check) } + + fun readUntil(char: Char) = this.slice { skipUntil(char) } + fun readUntilIncluded(char: Char) = this.slice { skipUntilIncluded(char) } + inline fun readWhile(filter: (Char) -> Boolean) = this.slice { skipWhile(filter) } ?: "" + inline fun readUntil(filter: (Char) -> Boolean) = this.slice { skipUntil(filter) } ?: "" + fun unread(count: Int = 1): StrReader { + this.pos -= count + return this + } + fun readChar(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' + fun read(): Char = if (hasMore) this.str[posSkip(1)] else '\u0000' + // @TODO: https://youtrack.jetbrains.com/issue/KT-29577 + private fun posSkip(count: Int): Int { + val out = this.pos + this.pos += count + return out + } + + fun readRemaining(): String = read(available) + + fun readExpect(expected: String): String { + val readed = this.read(expected.length) + if (readed != expected) throw IllegalArgumentException("Expected '$expected' but found '$readed' at $pos") + return readed + } + + fun skipExpect(expected: Char) { + val readed = this.readChar() + if (readed != expected) throw IllegalArgumentException("Expected '$expected' but found '$readed' at $pos") + } + + @Deprecated("This overload is slow") + fun expect(expected: Char): String { + skipExpect(expected) + this.unread() + return this.read(1) + } + + fun skip(count: Int = 1): StrReader { + this.pos += count + return this + } + + private fun substr(pos: Int, length: Int): String { + return this.str.substring(min2(pos, this.length), min2(pos + length, this.length)) + } + + fun tryLit(lit: String): String? { + if (substr(this.pos, lit.length) != lit) return null + this.pos += lit.length + return lit + } + + fun tryLitRange(lit: String): TRange? = + if (substr(this.pos, lit.length) == lit) this.readRange(lit.length) else null + + fun matchLit(lit: String): String? = tryLit(lit) + fun matchLitRange(lit: String): TRange? = tryLitRange(lit) + + fun matchLitListRange(lits: Literals): TRange? { + lits.lengths.fastForEach { len -> + if (lits.contains(substr(this.pos, len))) return this.readRange(len) + } + return null + } + + fun skipSpaces() = this.apply { this.skipWhile { it.isWhitespaceFast() } } + + fun matchIdentifier() = matchWhile { it.isLetterDigitOrUnderscore() || it == '-' || it == '~' || it == ':' } + fun matchSingleOrDoubleQuoteString(): String? { + return when (this.peekChar()) { + '\'', '"' -> { + this.slice { + val quoteType = this.readChar() + this.readUntil(quoteType) + this.readChar() + } + } + else -> null + } + } + + fun tryRegex(v: Regex): String? { + val result = v.find(this.str.substring(this.pos)) ?: return null + val m = result.groups[0]!!.value + this.pos += m.length + return m + } + + fun tryRegexRange(v: Regex): TRange? { + val result = v.find(this.str.substring(this.pos)) ?: return null + return this.readRange(result.groups[0]!!.value.length) + } + + fun matchStartEnd(start: String, end: String): String? { + if (substr(this.pos, start.length) != start) return null + val startIndex = this.pos + val index = this.str.indexOf(end, this.pos) + if (index < 0) return null + //trace(index); + this.pos = index + end.length + return this.slice(startIndex, this.pos) + } + + fun clone(): StrReader = StrReader(str, file, pos) + + fun tryRead(str: String): Boolean { + if (peek(str.length) == str) { + skip(str.length) + return true + } + return false + } + + class Literals( + private val lits: Array, + private val map: MutableMap, + val lengths: Array + ) { + companion object { + fun invoke(vararg lits: String): Literals = + fromList(lits.toCollection(arrayListOf()).toTypedArray()) + + //fun invoke(lits:Array): Literals = fromList(lits) + fun fromList(lits: Array): Literals { + val lengths = lits.map { it.length }.sorted().reversed().distinct().toTypedArray() + val map = linkedMapOf() + lits.fastForEach { lit -> + map[lit] = true + } + return Literals(lits, map, lengths) + } + } + + fun contains(lit: String) = map.containsKey(lit) + + fun matchAt(str: String, offset: Int): String? { + lengths.fastForEach { len -> + val id = str.substr(offset, len) + if (contains(id)) return id + } + return null + } + + override fun toString() = "Literals(${lits.joinToString(" ")})" + } + + class TRange(val min: Int, val max: Int, val reader: StrReader) { + companion object { + fun combine(a: TRange, b: TRange): TRange { + return TRange(min2(a.min, b.min), max2(a.max, b.max), a.reader) + } + + fun combineList(list: List): TRange? { + if (list.isEmpty()) return null + val first = list[0] + var min = first.min + var max = first.max + list.fastForEach { i -> + min = min2(min, i.min) + max = max2(max, i.max) + } + return TRange(min, max, first.reader) + } + + fun createDummy() = TRange(0, 0, StrReader("")) + } + + fun contains(index: Int): Boolean = index >= this.min && index <= this.max + override fun toString() = "$min:$max" + + val file: String get() = this.reader.file + val text: String get () = this.reader.slice(this.min, this.max) + + fun startEmptyRange(): TRange = TRange(this.min, this.min, this.reader) + fun endEmptyRange(): TRange = TRange(this.max, this.max, this.reader) + fun displace(offset: Int): TRange = TRange(this.min + offset, this.max + offset, this.reader) + } + + fun readFixedSizeInt(count: Int, radix: Int = 10): Int { + val readCount = min2(available, count) + skip(readCount) + return NumberParser.parseInt(str, pos - readCount, pos, radix) + } + + fun readStringLit(reportErrors: Boolean = true): String { + val out = tempCharArray + var outp = 0 + val quotec = read() + when (quotec) { + '"', '\'' -> Unit + else -> invalidOp("Invalid string literal") + } + var closed = false + loop@ while (hasMore) { + when (val c = read()) { + '\\' -> { + val cc = read() + out[outp++] = when (cc) { + '\\' -> '\\'; '/' -> '/'; '\'' -> '\''; '"' -> '"' + 'b' -> '\b'; 'f' -> '\u000c'; 'n' -> '\n'; 'r' -> '\r'; 't' -> '\t' + 'u' -> readFixedSizeInt(4, 16).toChar() + else -> throw IOException("Invalid char '$cc'") + } + } + quotec -> { + closed = true + break@loop + } + else -> { + out[outp++] = c + } + } + } + if (!closed && reportErrors) { + throw RuntimeException("String literal not closed! '${this.str}'") + } + return String_fromCharArray(out, 0, outp) + } + + + fun tryReadInt(default: Int): Int { + var digitCount = 0 + var integral = 0 + var mult = 1 + loop@ while (!eof) { + when (val c = peek()) { + '-' -> { + skip(1) + mult *= -1 + } + in '0'..'9' -> { + val digit = c - '0' + skip(1) + digitCount++ + integral *= 10 + integral += digit + } + else -> { + break@loop + } + } + } + return if (digitCount == 0) default else integral + } + + fun tryReadNumber(default: Double = Double.NaN): Double { + val start = pos + skipWhile { + @Suppress("ConvertTwoComparisonsToRangeCheck") + (it >= '0' && it <= '9') || (it == '+') || (it == '-') || (it == 'e') || (it == 'E') || (it == '.') + } + val end = pos + if (end == start) return default + return NumberParser.parseDouble(this.str, start, end) + } + + fun tryExpect(str: String): Boolean { + for (n in 0 until str.length) { + if (this.peekOffset(n) != str[n]) return false + } + skip(str.length) + return true + } + + fun tryExpect(str: Char): Boolean { + if (peekChar() != str) return false + skip(1) + return true + } + + fun peekOffset(offset: Int = 0): Char = this.str.getOrElse(pos + offset) { '\u0000' } + + fun readFloats(list: FloatArrayList = FloatArrayList(7)): FloatArrayList { + while (!eof) { + val pos0 = pos + val float = skipSpaces().tryReadNumber().toFloat() + skipSpaces() + val pos1 = pos + if (pos1 == pos0) error("Invalid number at $pos0 in '$str'") + list.add(float) + //println("float: $float, ${reader.pos}/${reader.length}") + } + return list + } + + fun readIds(list: ArrayList = ArrayList(7)): ArrayList { + while (!eof) { + val pos0 = pos + val id = skipSpaces().tryReadId() ?: "" + skipSpaces() + val pos1 = pos + if (pos1 == pos0) error("Invalid identifier at $pos0 in '$str'") + list.add(id) + //println("float: $float, ${reader.pos}/${reader.length}") + } + return list + } + + fun readInts(list: IntArrayList = IntArrayList(7)): IntArrayList { + while (!eof) { + val pos0 = pos + val v = skipSpaces().tryReadInt(0) + skipSpaces() + val pos1 = pos + if (pos1 == pos0) error("Invalid int at $pos0 in '$str'") + list.add(v) + //println("float: $float, ${reader.pos}/${reader.length}") + } + return list + } + + fun tryReadId(): String? { + val start = pos + skipWhile { + @Suppress("ConvertTwoComparisonsToRangeCheck") + (it >= '0' && it <= '9') || (it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z') || (it == '_') || (it == '.') + } + val end = pos + if (end == start) return null + return this.str.substring(start, end) + } } fun String.reader(file: String = "file", pos: Int = 0): StrReader = StrReader(this, file, pos) diff --git a/korio/src/commonTest/kotlin/com/soywiz/korio/concurrent/AtomicTest.kt b/korio/src/commonTest/kotlin/com/soywiz/korio/concurrent/AtomicTest.kt index e6fcb0c0..c0daf8b0 100644 --- a/korio/src/commonTest/kotlin/com/soywiz/korio/concurrent/AtomicTest.kt +++ b/korio/src/commonTest/kotlin/com/soywiz/korio/concurrent/AtomicTest.kt @@ -12,4 +12,17 @@ class AtomicTest { assertEquals(2, value.value) assertEquals(3, ++value.value) } -} \ No newline at end of file + + @Test + fun testSingleton() { + val value = singleton + assertEquals(1, value.incrementAndGet()) + assertEquals(1, value.value++) + assertEquals(2, value.value) + assertEquals(3, ++value.value) + } + + companion object { + val singleton = KorAtomicInt(0) + } +} diff --git a/korio/src/jsMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJs.kt b/korio/src/jsMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJs.kt index e9a65775..212b5343 100644 --- a/korio/src/jsMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJs.kt +++ b/korio/src/jsMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJs.kt @@ -1,10 +1,6 @@ package com.soywiz.korio.concurrent.atomic -actual fun korAtomic(initial: T): KorAtomicRef = - KorAtomicRef(initial) -actual fun korAtomic(initial: Boolean): KorAtomicBoolean = - KorAtomicBoolean(initial) -actual fun korAtomic(initial: Int): KorAtomicInt = - KorAtomicInt(initial) -actual fun korAtomic(initial: Long): KorAtomicLong = - KorAtomicLong(initial) +actual fun korAtomic(initial: T): KorAtomicRef = KorAtomicRef(initial, true) +actual fun korAtomic(initial: Boolean): KorAtomicBoolean = KorAtomicBoolean(initial, true) +actual fun korAtomic(initial: Int): KorAtomicInt = KorAtomicInt(initial, true) +actual fun korAtomic(initial: Long): KorAtomicLong = KorAtomicLong(initial, true) diff --git a/korio/src/jsMain/kotlin/com/soywiz/korio/file/FileExt.kt b/korio/src/jsMain/kotlin/com/soywiz/korio/file/FileExt.kt new file mode 100644 index 00000000..765ddf43 --- /dev/null +++ b/korio/src/jsMain/kotlin/com/soywiz/korio/file/FileExt.kt @@ -0,0 +1,59 @@ +package com.soywiz.korio.file + +import com.soywiz.kmem.* +import com.soywiz.korio.async.* +import com.soywiz.korio.file.std.* +import com.soywiz.korio.internal.* +import com.soywiz.korio.lang.* +import com.soywiz.korio.stream.* +import com.soywiz.korio.util.* +import kotlinx.coroutines.* +import org.khronos.webgl.* +import org.w3c.files.* + +fun Blob.openAsync(): AsyncStream = BlobAsyncBaseStream(this).toAsyncStream().buffered(0x10_000, 0x10) + +external class BlobExt { + fun slice(start: Number = definedExternally, end: Number = definedExternally, contentType: String = definedExternally): Blob +} + +class BlobAsyncBaseStream(val blob: Blob) : AsyncStreamBase() { + override suspend fun close() { + } + + override suspend fun read(position: Long, buffer: ByteArray, offset: Int, len: Int): Int { + val deferred = CompletableDeferred() + val reader = FileReader() + reader.onload = { + val ab = reader.result.unsafeCast() + val minLen = min2(ab.size, len) + arraycopy(Int8Array(ab).toByteArray(), 0, buffer, offset, minLen) + deferred.complete(minLen) + } + reader.onerror = { + deferred.completeExceptionally(Throwable("${reader.error}")) + } + reader.readAsArrayBuffer(blob.unsafeCast().slice(position.toDouble(), position.toDouble() + len.toDouble())) + return deferred.await() + } + + override suspend fun getLength(): Long { + return blob.size.toLong() + } +} + +fun File.toVfs(): VfsFile { + val file = this + return object : Vfs() { + override val absolutePath: String = file.name + // @TODO: Check path + override suspend fun open(path: String, mode: VfsOpenMode): AsyncStream = file.openAsync() + override suspend fun listSimple(path: String): List { + return if (path == "/" || path == "") { + listOf(this[file.name]) + } else { + listOf() + } + } + }[file.name] +} diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt similarity index 91% rename from korio/src/jvmMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt rename to korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt index 927840bf..be449bda 100644 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt +++ b/korio/src/jvmAndroidMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicJvm.kt @@ -2,27 +2,27 @@ package com.soywiz.korio.concurrent.atomic import java.util.concurrent.atomic.* -actual fun korAtomic(initial: T): KorAtomicRef = object : KorAtomicRef(initial) { +actual fun korAtomic(initial: T): KorAtomicRef = object : KorAtomicRef(initial, true) { val ref = AtomicReference(initial) override var value: T get() = ref.get(); set(value) = ref.set(value) override fun compareAndSet(expect: T, update: T): Boolean = ref.compareAndSet(expect, update) } -actual fun korAtomic(initial: Boolean): KorAtomicBoolean = object : KorAtomicBoolean(initial) { +actual fun korAtomic(initial: Boolean): KorAtomicBoolean = object : KorAtomicBoolean(initial, true) { val ref = AtomicBoolean(initial) override var value: Boolean get() = ref.get(); set(value) = ref.set(value) override fun compareAndSet(expect: Boolean, update: Boolean): Boolean = ref.compareAndSet(expect, update) } -actual fun korAtomic(initial: Int): KorAtomicInt = object : KorAtomicInt(initial) { +actual fun korAtomic(initial: Int): KorAtomicInt = object : KorAtomicInt(initial, true) { val ref = AtomicInteger(initial) override var value: Int get() = ref.get(); set(value) = ref.set(value) override fun compareAndSet(expect: Int, update: Int): Boolean = ref.compareAndSet(expect, update) override fun addAndGet(delta: Int): Int = ref.addAndGet(delta) } -actual fun korAtomic(initial: Long): KorAtomicLong = object : KorAtomicLong(initial) { +actual fun korAtomic(initial: Long): KorAtomicLong = object : KorAtomicLong(initial, true) { val ref = AtomicLong(initial) override var value: Long get() = ref.get(); set(value) = ref.set(value) override fun compareAndSet(expect: Long, update: Long): Boolean = ref.compareAndSet(expect, update) diff --git a/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt b/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt index f79140f1..2d4b9450 100644 --- a/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt +++ b/korio/src/jvmMain/kotlin/com/soywiz/korio/file/std/LocalVfsJvm.kt @@ -43,6 +43,7 @@ fun localVfs(base: File): VfsFile = localVfs(base.absolutePath) fun jailedLocalVfs(base: File): VfsFile = localVfs(base.absolutePath).jail() suspend fun File.open(mode: VfsOpenMode) = localVfs(this).open(mode) fun File.toVfs() = localVfs(this) +fun File.toJailedVfs() = jailedLocalVfs(this.parentFile)[this.name] fun UrlVfs(url: URL): VfsFile = UrlVfs(url.toString()) operator fun File.get(path: String) = File(this, path) diff --git a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicNative.kt b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicNative.kt index eece12d0..aade3ca6 100644 --- a/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicNative.kt +++ b/korio/src/nativeCommonMain/kotlin/com/soywiz/korio/concurrent/atomic/KorAtomicNative.kt @@ -3,27 +3,27 @@ package com.soywiz.korio.concurrent.atomic import com.soywiz.kmem.* import kotlin.native.concurrent.* -actual fun korAtomic(initial: T): KorAtomicRef = object : KorAtomicRef(initial) { +actual fun korAtomic(initial: T): KorAtomicRef = object : KorAtomicRef(initial, true) { val ref = AtomicReference(initial) override var value: T get() = ref.value; set(value) = run { ref.value = value } override fun compareAndSet(expect: T, update: T): Boolean = ref.compareAndSet(expect, update) } -actual fun korAtomic(initial: Boolean): KorAtomicBoolean = object : KorAtomicBoolean(initial) { +actual fun korAtomic(initial: Boolean): KorAtomicBoolean = object : KorAtomicBoolean(initial, true) { val ref = AtomicInt(initial.toInt()) override var value: Boolean get() = ref.value != 0; set(value) = run { ref.value = value.toInt() } override fun compareAndSet(expect: Boolean, update: Boolean): Boolean = ref.compareAndSet(expect.toInt(), update.toInt()) } -actual fun korAtomic(initial: Int): KorAtomicInt = object : KorAtomicInt(initial) { +actual fun korAtomic(initial: Int): KorAtomicInt = object : KorAtomicInt(initial, true) { val ref = AtomicInt(initial) override var value: Int get() = ref.value; set(value) = run { ref.value = value } override fun compareAndSet(expect: Int, update: Int): Boolean = ref.compareAndSet(expect, update) override fun addAndGet(delta: Int): Int = ref.addAndGet(delta) } -actual fun korAtomic(initial: Long): KorAtomicLong = object : KorAtomicLong(initial) { +actual fun korAtomic(initial: Long): KorAtomicLong = object : KorAtomicLong(initial, true) { val ref = AtomicLong(initial) override var value: Long get() = ref.value; set(value) = run { ref.value = value } override fun compareAndSet(expect: Long, update: Long): Boolean = ref.compareAndSet(expect, update)