diff --git a/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt index 9b1db026..55d56f11 100644 --- a/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Date.kt @@ -16,6 +16,8 @@ expect class Date(millisSinceEpoch: Long) { val today: Date + val nanoSecondsSinceEpoch: Long + } diff --git a/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Stopwatch.kt b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Stopwatch.kt new file mode 100644 index 00000000..e7eaf3ec --- /dev/null +++ b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/Stopwatch.kt @@ -0,0 +1,170 @@ +package net.dankito.utils.multiplatform + +import net.dankito.utils.multiplatform.log.Logger +import net.dankito.utils.multiplatform.log.LoggerFactory + + +open class Stopwatch(createStarted: Boolean = true) { + + companion object { + private val log = LoggerFactory.getLogger(Stopwatch::class) + + fun measureDuration(task: () -> Unit): Long { + return measureDuration(Stopwatch(), task) + } + + private fun measureDuration(stopwatch: Stopwatch, task: () -> Unit): Long { + task() + + return stopwatch.stop() + } + + fun logDuration(loggedAction: String, task: () -> T): T { + return logDuration(loggedAction, log, task) + } + + fun logDuration(loggedAction: String, logger: Logger, task: () -> T): T { + val stopwatch = Stopwatch() + + val result = task() + + stopwatch.stopAndLog(loggedAction, logger) + + return result + } + + + suspend fun measureDurationSuspendable(task: suspend () -> Unit): Long { + val stopwatch = Stopwatch() + + task() + + return stopwatch.stop() + } + + suspend fun logDurationSuspendable(loggedAction: String, task: suspend () -> T): T { + return logDurationSuspendable(loggedAction, log, task) + } + + suspend fun logDurationSuspendable(loggedAction: String, logger: Logger, task: suspend () -> T): T { + val stopwatch = Stopwatch() + + val result = task() + + stopwatch.stopAndLog(loggedAction, logger) + + return result + } + } + + + constructor() : this(true) + + + var isRunning = false + protected set + + var startedAt = 0L + protected set + + var elapsedNanos = 0L + protected set + + + init { + if (createStarted) { + start() + } + } + + + open fun getCurrentTimeNanoSeconds(): Long { + return Date.nanoSecondsSinceEpoch + } + + open fun start() { + if (isRunning == false) { + startedAt = getCurrentTimeNanoSeconds() + + isRunning = true + } + } + + open fun stop(): Long { + if (isRunning) { + val stoppedAt = getCurrentTimeNanoSeconds() + + isRunning = false + elapsedNanos = stoppedAt - startedAt + } + + return elapsedNanos + } + + open fun stopAndPrint(): String { + stop() + + return formatElapsedTime() + } + + open fun stopAndLog(loggedAction: String): Long { + return stopAndLog(loggedAction, log) + } + + open fun stopAndLog(loggedAction: String, logger: Logger): Long { + stop() + + logElapsedTime(loggedAction, logger) + + return elapsedNanos + } + + +// open fun elapsed(desiredUnit: TimeUnit): Long { +// return desiredUnit.convert(elapsedNanos, TimeUnit.NANOSECONDS) +// } + + + open fun formatElapsedTime(): String { + return formatElapsedTime(elapsedNanos) + } + + protected open fun formatElapsedTime(elapsedNanos: Long): String { + val durationMicroseconds = elapsedNanos / 1000 + + val durationMillis = durationMicroseconds / 1000 + val millis = durationMillis % 1000 + + val durationSeconds = durationMillis / 1000 + val seconds = durationSeconds % 60 + + val minutes = durationSeconds / 60 + + return if (minutes > 0) { + StringHelper.format("%02d:%02d.%03d min", minutes, seconds, millis) + } + else if (seconds > 0) { + StringHelper.format("%02d.%03d s", seconds, millis) + } + else { + StringHelper.format("%02d.%03d ms", millis, (durationMicroseconds % 1000)) + } + } + + open fun logElapsedTime(loggedAction: String, logger: Logger) { + val formattedElapsedTime = formatElapsedTime() + + logger.info("$loggedAction took $formattedElapsedTime") + } + + + override fun toString(): String { + if (isRunning) { + val elapsedNanos = getCurrentTimeNanoSeconds() - startedAt + return "Running, ${formatElapsedTime(elapsedNanos)} elapsed" + } + + return "Stopped, ${formatElapsedTime()} elapsed" + } + +} \ No newline at end of file diff --git a/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt new file mode 100644 index 00000000..cc4424ea --- /dev/null +++ b/common/src/commonMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt @@ -0,0 +1,14 @@ +package net.dankito.utils.multiplatform + +import kotlin.String + + +expect class StringHelper { + + companion object { + + fun format(format: String, vararg args: Any?): String + + } + +} \ No newline at end of file diff --git a/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt index 3ca108c2..7fce5964 100644 --- a/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ b/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/Date.kt @@ -1,5 +1,6 @@ package net.dankito.utils.multiplatform +import platform.CoreFoundation.* import platform.Foundation.* @@ -43,6 +44,9 @@ actual class Date(val date: NSDate) { // cannot subclass NSDate as it's a class return Date(from(now.year(), now.monthInt(), now.day())) } + actual val nanoSecondsSinceEpoch: Long + get() = CFAbsoluteTimeGetCurrent().toLong() * 1000 + } diff --git a/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt b/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt new file mode 100644 index 00000000..619ff309 --- /dev/null +++ b/common/src/iosMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt @@ -0,0 +1,40 @@ +package net.dankito.utils.multiplatform + +import kotlin.String +import platform.Foundation.* + + +actual class StringHelper { + + actual companion object { + + actual fun format(format: String, vararg args: Any?): String { + val adjustedFormat = format.replace("%s", "%@") // Objective-C uses %@ for strings // TODO: also replace %$1s, ... + +// return NSString.stringWithFormat("%@ %@ %d", "один" as NSString, "two" as NSString, 3) + //return NSString.stringWithFormat(adjustedFormat, args.firstOrNull()) // spread operator is not supported for varadic functions, seehttps://github.com/JetBrains/kotlin-native/issues/1834 + + // as spread operator is not supported for varadic functions tried to pass arguments one by one, but didn't work either + return when (args.size) { + 0 -> format + 1 -> format(adjustedFormat, args.first()) + 2 -> format(adjustedFormat, args.first(), args.get(2)) + else -> format(adjustedFormat, args.first(), args.get(2), args.get(3)) + } + } + + fun format(format: String, arg1: Any): String { + return NSString.stringWithFormat(format, arg1 as? NSString ?: arg1) + } + + fun format(format: String, arg1: Any, arg2: Any): String { + return NSString.stringWithFormat(format, arg1 as? NSString ?: arg1, arg2 as? NSString ?: arg2) + } + + fun format(format: String, arg1: Any, arg2: Any, arg3: Any): String { + return NSString.stringWithFormat(format, arg1 as? NSString ?: arg1, arg2 as? NSString ?: arg2, arg3 as? NSString ?: arg3) + } + + } + +} \ No newline at end of file diff --git a/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt b/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt index 87cf4444..1204ad20 100644 --- a/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt +++ b/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/Date.kt @@ -23,6 +23,9 @@ actual class Date actual constructor(millisSinceEpoch: Long) : java.util.Date(mi return Date(today.time.time) } + actual val nanoSecondsSinceEpoch: Long + get() = System.nanoTime() + } diff --git a/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt b/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt new file mode 100644 index 00000000..a9df9b58 --- /dev/null +++ b/common/src/jvmMain/kotlin/net/dankito/utils/multiplatform/StringHelper.kt @@ -0,0 +1,16 @@ +package net.dankito.utils.multiplatform + +import kotlin.String + + +actual class StringHelper { + + actual companion object { + + actual fun format(format: String, vararg args: Any?): String { + return String.format(format, *args) + } + + } + +} \ No newline at end of file