diff --git a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosAgent.kt b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosAgent.kt index 40c9699..6b271cc 100644 --- a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosAgent.kt +++ b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosAgent.kt @@ -49,8 +49,8 @@ internal object IosAgent { private fun handlePause(conn: TcpConnection, id: Long) { val snapshots = IosPauser.pauseAndSnapshot { - val snap = synchronized(SanderlingIos.extractors) { SanderlingIos.extractors.toMap() } - buildMap { for ((name, fn) in snap) put(name, runCatching { fn() }.getOrNull()) } + val snap = SanderlingIos.extractors.toMap() + buildMap { for ((name, extractor) in snap) put(name, runCatching { extractor() }.getOrNull()) } } val snapshotsJson = snapshots.entries.joinToString(",") { (k, v) -> "${jsonString(k)}:${jsonValue(v)}" } conn.writeFrame("""{"type":"STATE","id":$id,"snapshots":{$snapshotsJson}}""".encodeToByteArray()) @@ -84,7 +84,7 @@ internal fun jsonValue(value: Any?): String = when (value) { is Float -> value.toString() is Double -> value.toString() is String -> jsonString(value) - is Map<*, *> -> "{${(value as Map<*, *>).entries.joinToString(",") { (k, v) -> "${jsonString(k.toString())}:${jsonValue(v)}" }}}" - is List<*> -> "[${(value as List<*>).joinToString(",") { jsonValue(it) }}]" + is Map<*, *> -> "{${value.entries.joinToString(",") { (k, v) -> "${jsonString(k.toString())}:${jsonValue(v)}" }}}" + is List<*> -> "[${value.joinToString(",") { jsonValue(it) }}]" else -> jsonString(value.toString()) } diff --git a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosPauser.kt b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosPauser.kt index ed5fa92..e680427 100644 --- a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosPauser.kt +++ b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/IosPauser.kt @@ -7,7 +7,7 @@ import platform.darwin.* internal object IosPauser { private val snapshotReady = dispatch_semaphore_create(0) private val resumeGate = dispatch_semaphore_create(0) - @Volatile private var capturedSnapshot: Map = emptyMap() + private var capturedSnapshot: Map = emptyMap() fun pauseAndSnapshot(extractors: () -> Map): Map { dispatch_async(dispatch_get_main_queue()) { diff --git a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/TcpConnection.kt b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/TcpConnection.kt index ee5891b..d821388 100644 --- a/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/TcpConnection.kt +++ b/examples/folio/composeApp/src/iosMain/kotlin/app/folio/sanderling/TcpConnection.kt @@ -12,16 +12,27 @@ internal class TcpConnection private constructor(private val fd: Int) { memScoped { val addr = alloc() addr.sin_family = AF_INET.convert() - addr.sin_port = htons(port.convert()) - inet_pton(AF_INET, host, addr.sin_addr.ptr) + addr.sin_port = networkShort(port) + addr.sin_addr.s_addr = networkAddress(host) val result = platform.posix.connect(sock, addr.ptr.reinterpret(), sizeOf().convert()) if (result < 0) { close(sock) - error("connect() failed: errno=$errno") + error("connect() to $host:$port failed: errno=$errno") } } return TcpConnection(sock) } + + // Convert port to network byte order (big-endian bytes in memory on little-endian iOS). + private fun networkShort(value: Int): UShort = + (((value ushr 8) and 0xFF) or ((value and 0xFF) shl 8)).toUShort() + + // Parse "a.b.c.d" → UInt with bytes [a,b,c,d] in memory (little-endian storage). + private fun networkAddress(host: String): UInt { + val parts = host.split(".").map { it.toInt() } + check(parts.size == 4) { "expected IPv4 address, got: $host" } + return (parts[0] or (parts[1] shl 8) or (parts[2] shl 16) or (parts[3] shl 24)).toUInt() + } } fun writeFrame(data: ByteArray) {