Skip to content

Commit

Permalink
Add LinuxLocalTimeFormatter, LinuxTimeZoneInfoProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
illarionov committed Aug 26, 2024
1 parent 0b6d63b commit ec9510d
Show file tree
Hide file tree
Showing 17 changed files with 285 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import ru.pixnews.wasm.sqlite.driver.test.base.tests.AbstractTimeFunctionsTest
import ru.pixnews.wasm.sqlite.test.utils.TempFolder
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Ignore
import kotlin.test.Test

class ChasmTimeFunctionsTest : AbstractTimeFunctionsTest<WasmSQLiteDriver<*>>(
Expand All @@ -32,7 +31,6 @@ class ChasmTimeFunctionsTest : AbstractTimeFunctionsTest<WasmSQLiteDriver<*>>(
override fun fileInTempDir(databaseName: String): String = tempDir.resolve(databaseName)

@Test
@Ignore // TODO
override fun localtime_modifier_should_work() {
super.localtime_modifier_should_work()
}
Expand Down
47 changes: 47 additions & 0 deletions sqlite-tests/sqlite-test-utils/src/linuxMain/kotlin/TimeZoneExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.test.utils

import kotlinx.cinterop.toKStringFromUtf8
import platform.posix.errno
import platform.posix.getenv
import platform.posix.setenv
import platform.posix.tzset
import platform.posix.unsetenv

public fun <R : Any> withTimeZone(
timeZone: String,
block: () -> R,
) {
val oldTz = getenv("TZ")?.toKStringFromUtf8()
setEnvOrThrow("TZ", timeZone)
tzset()
try {
block()
} finally {
if (oldTz == null) {
val unsetEnvResult = unsetenv("TZ")
if (unsetEnvResult < 0) {
error("Can not unset TZ. Errno: $errno")
}
} else {
setEnvOrThrow("TZ", oldTz)
}
tzset()
}
}

private fun setEnvOrThrow(
name: String,
value: String,
replace: Boolean = true,
) {
val setEnvResult = setenv(name, value, if (replace) 1 else 0)
if (setEnvResult < 0) {
error("Can not set $name to `$value`. Errno: $errno")
}
}
12 changes: 6 additions & 6 deletions wasi-emscripten-host/api/wasi-emscripten-host.api
Original file line number Diff line number Diff line change
Expand Up @@ -691,10 +691,10 @@ public final class ru/pixnews/wasm/sqlite/open/helper/host/include/StructTimespe
}

public final class ru/pixnews/wasm/sqlite/open/helper/host/include/StructTm {
public fun <init> (IIIIIIIIIILjava/lang/String;)V
public synthetic fun <init> (IIIIIIIIIILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (IIIIIIIIIJLjava/lang/String;)V
public synthetic fun <init> (IIIIIIIIIJLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()I
public final fun component10 ()I
public final fun component10 ()J
public final fun component11 ()Ljava/lang/String;
public final fun component2 ()I
public final fun component3 ()I
Expand All @@ -704,10 +704,10 @@ public final class ru/pixnews/wasm/sqlite/open/helper/host/include/StructTm {
public final fun component7 ()I
public final fun component8 ()I
public final fun component9 ()I
public final fun copy (IIIIIIIIIILjava/lang/String;)Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;
public static synthetic fun copy$default (Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;IIIIIIIIIILjava/lang/String;ILjava/lang/Object;)Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;
public final fun copy (IIIIIIIIIJLjava/lang/String;)Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;
public static synthetic fun copy$default (Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;IIIIIIIIIJLjava/lang/String;ILjava/lang/Object;)Lru/pixnews/wasm/sqlite/open/helper/host/include/StructTm;
public fun equals (Ljava/lang/Object;)Z
public final fun getTm_gmtoff ()I
public final fun getTm_gmtoff ()J
public final fun getTm_hour ()I
public final fun getTm_isdst ()I
public final fun getTm_mday ()I
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public data class StructTm(
val tm_wday: Int,
val tm_yday: Int,
val tm_isdst: Int,
val tm_gmtoff: Int,
val tm_gmtoff: Long,
val tm_zone: String? = null,
) {
val isDstFlag: IsDstFlag = when {
Expand Down Expand Up @@ -82,7 +82,7 @@ public fun StructTm.packTo(sink: Sink): Unit = sink.run {
writeIntLe(tm_wday) // 24
writeIntLe(tm_yday) // 28
writeIntLe(tm_isdst) // 32
writeIntLe(tm_gmtoff) // 36
writeIntLe(tm_gmtoff.toInt()) // 36
}

public fun StructTm.pack(): Buffer = Buffer().also {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.internal

import ru.pixnews.wasm.sqlite.open.helper.host.EmbedderHost

internal object EmptyCommandArgsProvider : EmbedderHost.CommandArgsProvider {
override fun getCommandArgs(): List<String> = emptyList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class JvmLocalTimeFormatter(
localTimeZoneProvider(),
)
val zone = date.zone
@Suppress("COMPLEX_EXPRESSION")
return StructTm(
tm_sec = date.second,
tm_min = date.minute,
Expand All @@ -38,7 +39,7 @@ public class JvmLocalTimeFormatter(
} else {
StructTm.IsDstFlag.NOT_IN_EFFECT
}.asTmIsdstValue(),
tm_gmtoff = zone.rules.getOffset(date.toInstant()).totalSeconds,
tm_gmtoff = zone.rules.getOffset(date.toInstant()).totalSeconds.toLong(),
tm_zone = zone.id,
)
}
Expand Down
12 changes: 6 additions & 6 deletions wasi-emscripten-host/src/linuxMain/kotlin/EmbedderHost.linux.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import ru.pixnews.wasm.sqlite.open.helper.host.ext.DefaultFileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.LinuxFileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.internal.CommonClock
import ru.pixnews.wasm.sqlite.open.helper.host.internal.CommonMonotonicClock
import ru.pixnews.wasm.sqlite.open.helper.host.internal.EmptyCommandArgsProvider
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxEmbedderHost
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxEmbedderHost.NativeCommandArgsProvider
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxEmbedderHost.NativeSystemEnvProvider
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxEntropySource
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxLocalTimeFormatter
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxSystemEnvProvider
import ru.pixnews.wasm.sqlite.open.helper.host.linux.LinuxTimeZoneInfoProvider

internal actual fun createDefaultEmbedderHost(builder: Builder): EmbedderHost = LinuxEmbedderHost(
rootLogger = builder.rootLogger,
systemEnvProvider = builder.systemEnvProvider ?: NativeSystemEnvProvider,
commandArgsProvider = builder.commandArgsProvider ?: NativeCommandArgsProvider,
systemEnvProvider = builder.systemEnvProvider ?: LinuxSystemEnvProvider,
commandArgsProvider = builder.commandArgsProvider ?: EmptyCommandArgsProvider,
fileSystem = builder.fileSystem ?: DefaultFileSystem(LinuxFileSystem, builder.rootLogger.withTag("FSlnx")),
monotonicClock = builder.monotonicClock ?: CommonMonotonicClock(),
clock = builder.clock ?: CommonClock(),
localTimeFormatter = builder.localTimeFormatter ?: LinuxLocalTimeFormatter(),
timeZoneInfo = builder.timeZoneInfo ?: LinuxTimeZoneInfoProvider(),
localTimeFormatter = builder.localTimeFormatter ?: LinuxLocalTimeFormatter,
timeZoneInfo = builder.timeZoneInfo ?: LinuxTimeZoneInfoProvider,
entropySource = builder.entropySource ?: LinuxEntropySource(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,16 @@ import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.FileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.filesystem.LinuxFileSystem
import ru.pixnews.wasm.sqlite.open.helper.host.internal.CommonClock
import ru.pixnews.wasm.sqlite.open.helper.host.internal.CommonMonotonicClock
import ru.pixnews.wasm.sqlite.open.helper.host.internal.EmptyCommandArgsProvider

public class LinuxEmbedderHost(
override val rootLogger: Logger,
override val systemEnvProvider: SystemEnvProvider = NativeSystemEnvProvider,
override val commandArgsProvider: CommandArgsProvider = NativeCommandArgsProvider,
override val systemEnvProvider: SystemEnvProvider = LinuxSystemEnvProvider,
override val commandArgsProvider: CommandArgsProvider = EmptyCommandArgsProvider,
override val fileSystem: FileSystem = DefaultFileSystem(LinuxFileSystem, rootLogger.withTag("FSlnx")),
override val monotonicClock: MonotonicClock = CommonMonotonicClock(),
override val clock: Clock = CommonClock(),
override val localTimeFormatter: LocalTimeFormatter = LinuxLocalTimeFormatter(),
override val timeZoneInfo: TimeZoneInfoProvider = LinuxTimeZoneInfoProvider(),
override val localTimeFormatter: LocalTimeFormatter = LinuxLocalTimeFormatter,
override val timeZoneInfo: TimeZoneInfoProvider = LinuxTimeZoneInfoProvider,
override val entropySource: EntropySource = LinuxEntropySource(),
) : EmbedderHost {
internal object NativeSystemEnvProvider : SystemEnvProvider {
override fun getSystemEnv(): Map<String, String> = emptyMap() // TODO:
}

internal object NativeCommandArgsProvider : CommandArgsProvider {
override fun getCommandArgs(): List<String> = emptyList()
}
}
) : EmbedderHost
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,41 @@

package ru.pixnews.wasm.sqlite.open.helper.host.linux

import kotlinx.cinterop.CPointer
import kotlinx.cinterop.LongVarOf
import kotlinx.cinterop.alloc
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.pointed
import kotlinx.cinterop.ptr
import kotlinx.cinterop.toKStringFromUtf8
import kotlinx.cinterop.value
import platform.posix.localtime_r
import platform.posix.time_t
import platform.posix.tm
import ru.pixnews.wasm.sqlite.open.helper.host.EmbedderHost.LocalTimeFormatter
import ru.pixnews.wasm.sqlite.open.helper.host.include.StructTm

internal class LinuxLocalTimeFormatter : LocalTimeFormatter {
override fun format(epochSeconds: Long): StructTm {
TODO("Not yet implemented")
internal object LinuxLocalTimeFormatter : LocalTimeFormatter {
override fun format(epochSeconds: Long): StructTm = memScoped {
val epochRef: LongVarOf<time_t> = alloc<LongVarOf<time_t>>().apply {
value = epochSeconds
}
val resultBuf: tm = alloc()

val result: CPointer<tm> = localtime_r(epochRef.ptr, resultBuf.ptr) ?: error("localtime_r() failed")
val tm = result.pointed
StructTm(
tm_sec = tm.tm_sec,
tm_min = tm.tm_min,
tm_hour = tm.tm_hour,
tm_mday = tm.tm_mday,
tm_mon = tm.tm_mon,
tm_year = tm.tm_year,
tm_wday = tm.tm_wday,
tm_yday = tm.tm_yday,
tm_isdst = tm.tm_isdst,
tm_gmtoff = tm.tm_gmtoff,
tm_zone = tm.tm_zone?.toKStringFromUtf8(),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.linux

import kotlinx.cinterop.ByteVarOf
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.CPointerVarOf
import kotlinx.cinterop.get
import kotlinx.cinterop.toKStringFromUtf8
import platform.posix.__environ
import ru.pixnews.wasm.sqlite.open.helper.host.EmbedderHost.SystemEnvProvider
import ru.pixnews.wasm.sqlite.open.helper.host.native.parsePosixEnvironToEnvMap

internal object LinuxSystemEnvProvider : SystemEnvProvider {
override fun getSystemEnv(): Map<String, String> {
val envVariables: MutableList<String> = mutableListOf()
val env: CPointer<CPointerVarOf<CPointer<ByteVarOf<Byte>>>> = __environ ?: return emptyMap()
var index = 0
while (true) {
val current = env[index] ?: break
envVariables += current.toKStringFromUtf8()
index += 1
}
return parsePosixEnvironToEnvMap(envVariables)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@

package ru.pixnews.wasm.sqlite.open.helper.host.linux

import kotlinx.cinterop.get
import kotlinx.cinterop.toKStringFromUtf8
import platform.posix.tzname
import ru.pixnews.wasm.sqlite.open.helper.host.EmbedderHost.TimeZoneInfoProvider
import ru.pixnews.wasm.sqlite.open.helper.host.include.TimeZoneInfo

internal class LinuxTimeZoneInfoProvider : TimeZoneInfoProvider {
internal object LinuxTimeZoneInfoProvider : TimeZoneInfoProvider {
override fun getTimeZoneInfo(): TimeZoneInfo {
error("Not implemented")
return TimeZoneInfo(
timeZone = platform.posix.timezone_,
daylight = platform.posix.daylight,
stdName = tzname[0]?.toKStringFromUtf8() ?: "unk",
dstName = tzname[1]?.toKStringFromUtf8() ?: "unk",
)
}
}
10 changes: 10 additions & 0 deletions wasi-emscripten-host/src/linuxTest/kotlin/Dummy.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

@file:Suppress("FILE_CONTAINS_ONLY_COMMENTS", "FILE_NO_BLANK_LINE_BETWEEN_BLOCKS")

// Workaround for https://youtrack.jetbrains.com/issue/KTIJ-15797
package ru.pixnews.wasm.sqlite.open.helper.host
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.linux

import assertk.assertThat
import assertk.assertions.isEqualTo
import ru.pixnews.wasm.sqlite.open.helper.host.include.StructTm
import ru.pixnews.wasm.sqlite.test.utils.withTimeZone
import kotlin.test.Test

class LinuxLocalTimeFormatterTest {
@Test
@Suppress("MagicNumber")
fun formatter_should_work() = withTimeZone("Asia/Novosibirsk") {
val tm: StructTm = LinuxLocalTimeFormatter.format(1_724_702_567)
assertThat(tm).isEqualTo(
StructTm(
tm_sec = 47,
tm_min = 2,
tm_hour = 3,
tm_mday = 27,
tm_mon = 7,
tm_year = 124,
tm_wday = 2,
tm_yday = 239,
tm_isdst = 0,
tm_gmtoff = 7 * 60 * 60,
tm_zone = "+07",
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024, the wasm-sqlite-open-helper project authors and contributors. Please see the AUTHORS file
* for details. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
* SPDX-License-Identifier: Apache-2.0
*/

package ru.pixnews.wasm.sqlite.open.helper.host.linux

import assertk.all
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.isTrue
import platform.posix.setenv
import platform.posix.unsetenv
import kotlin.test.Test

class LinuxSystemEnvProviderTest {
@Test
fun env_provider_should_return_values() {
val testEnvVarKey = "TESTENVVARKEY"
val testEnvVarValue = " test = value"
setenv(testEnvVarKey, testEnvVarValue, 1)
try {
val env = LinuxSystemEnvProvider.getSystemEnv()
assertThat(env).all {
contains(testEnvVarKey, testEnvVarValue)
transform { it.containsKey("USER") }.isTrue()
}
} finally {
unsetenv(testEnvVarKey)
}
}
}
Loading

0 comments on commit ec9510d

Please sign in to comment.