zerodds-endpoint-swift 1.0 — Spec Coverage
Source: docs/specs/zerodds-endpoint-swift-1.0.md — the ZeroDDS Swift
endpoint SDK spec. The marshalling comes from crates/lang-rules, column Language::Swift; this document covers the transport.
Implementation:
endpoints/swift/Sources/Zerodds/Zerodds.swift— wire core (Writer/Reader), XRCE framing (xrceWriteFrame/xrceReadFrame), syncClient, asyncAsyncReader,Transportprotocol,MemTransport.endpoints/swift/Reliable.swift— reliable sender/receiver state machine + HEARTBEAT/ACKNACK wire codec +AsyncWriter(compiled as its ownZeroddsReliablemodule).endpoints/swift/Tests/ZeroddsTests/ZeroddsTests.swift— SwiftPM XCTest (byte-golden + sync/async loopback overMemTransport).crates/endpoint-e2e/tests/swift.rs— ping-pong E2E over real UDP;crates/endpoint-e2e/tests/swift_reliable.rs— reliable-stream E2E, unit runner, example, latency bench.
Toolchain note (applies to every section): swiftc is not available on
Linux (the Linux bench host). Every test below is verified locally on
macOS only (this run: macOS, swift-driver 1.148.6, Swift 6.3.3, arm64).
The E2E harnesses gate on swiftc_available() and emit a loud skip when the
toolchain is missing (no false-green) — not re-verified on the Linux test host.
§1 XRCE framing
Spec: §1 — 8-byte XRCE header (session, stream, seq LE, submsg id 0x07
WRITE_DATA, flags, len LE) + body, byte-identical to crates/xrce +
endpoints/c.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift —
xrceWriteFrame/xrceReadFrame, constants xrceSessionNoKey (0x80) and
xrceStreamBestEffort (0x01).
Tests: framing is exercised live via swift_endpoint_sync/
swift_endpoint_async (§4); the wire core itself (Writer/Reader, cap-4
alignment, f32/f64 via .bitPattern) via ZeroddsTests.testByteIdentity
(endpoints/swift/Tests/ZeroddsTests/ZeroddsTests.swift, swift test)
against testdata/golden_le.bin/golden_be.bin — LE and BE
byte-identical.
Status: done — measured on macOS and on the Linux test host
(swiftc 6.3.3, swift test: 39 tests, 0 failures).
§2 Sync Client
Spec: §2 — blocking Client: write frames + delivers synchronously,
poll is a single non-blocking receive.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift::Transport
protocol (deliver/receive, the sole integration point); Client
(init/write/poll, monotonic seq counter, default session
xrceSessionNoKey/xrceStreamBestEffort); MemTransport, an
NSLock-guarded in-memory FIFO.
Tests: ZeroddsTests.testSyncLoopback (swift test, 5 samples over
MemTransport); live E2E swift_endpoint_sync (§4).
Status: done — measured on macOS and on the Linux test host
(swiftc 6.3.3, swift test: 39 tests, 0 failures).
§3 Async AsyncReader
Spec: §3 — AsyncReader.stream() returns an AsyncStream<[UInt8]>; an
internal Task polls the Transport and yields unframed sample bodies; the
consumer iterates with for await, onTermination cancels the task.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift::AsyncReader
(init, stream()); the send side of the async path shares Client.write
(§2) — no separate AsyncWriter type outside the reliable AsyncWriter
(§5).
Tests: ZeroddsTests.testAsyncLoopback (swift test, 5 samples over
MemTransport, for await); live E2E swift_endpoint_async (§4).
Status: done — measured on macOS and on the Linux test host
(swiftc 6.3.3, swift test: 39 tests, 0 failures).
§4 Ping-pong E2E (live)
Spec: §5.1 — a Swift app exchanges a typed sample with the shared Rust
XRCE peer over a real UDP socket: the full stack (generated types + endpoint
SDK), once sync via Client, once async via AsyncReader. §6 — the SDK and
the generated module are compiled separately (Zerodds object +
.swiftmodule) so the shared wire-core type names
(Endianness/Writer/Reader) don’t collide.
Repo: crates/endpoint-e2e/tests/swift.rs — build_swift_app compiles
Zerodds.swift as its own module (-emit-module/-emit-object), then
app (generated Ping/Pong types + APP_MAIN) against it; UDPTransport
implements Transport over a raw, non-blocking UDP socket.
Tests: (local, macOS, this run)
- swift_endpoint_sync — full stack via Client.
- swift_endpoint_async — full stack via AsyncReader.
2/2 passed (cargo test -p zerodds-endpoint-e2e --test swift, local macOS;
not on the Linux test host — the Linux test host carries swiftc 6.3.3, measured there)..
Status: done — measured on macOS and on the Linux test host (swiftc 6.3.3, x86_64-unknown-linux-gnu).
§5 Reliable stream — state machine, wire, async writer
Spec: §4 (references reliable-endpoint v1.0 §3/§4) — XRCE reliable
stream (stream_id 0x80, §8.4.10/§8.4.11), mirroring the reference
crates/xrce/src/reliable.rs: ReliableSender.submit/pendingHeartbeat/
recvAckNack/getInFlight/inFlightSeqs; ReliableReceiver.recvData/
drainInOrder/pendingAckNack/reset. Window 16, receiver buffer 64,
heartbeat 500 ms, payload ≤ 65535, RFC-1982 16-bit sequence numbers.
Alongside it, the async-decoupled AsyncWriter: the producer enqueues
wait-free (NSLock-guarded ring buffer, backpressure via a false return),
a dedicated drain Thread holds the ReliableSender state and does all the
I/O (send, heartbeat, ACKNACK-driven retransmit) — the producer never enters
the kernel.
Repo: endpoints/swift/Reliable.swift —
writeDataFrame/parseWriteData, heartbeatFrame/parseHeartbeat,
acknackFrame/parseAckNack; ReliableSender, ReliableReceiver;
AsyncWriter (ring buffer queue, NSLock, init(sendFn:recvFn:)/start/
write/close, DispatchSemaphore for shutdown rendezvous);
endpoints/swift/example_reliable.swift (runnable in-process demo, no
socket); endpoints/swift/reliable_tests.swift (standalone unit runner,
mirroring crates/xrce/src/reliable.rs’s #[cfg(test)], not XCTest —
plain swiftc/swift, prints UNIT OK).
Tests: (local, macOS, this run)
- swift_reliable_unit (crates/endpoint-e2e/tests/swift_reliable.rs)
compiles reliable_tests.swift against the ZeroddsReliable module and
runs it — 16 run() test scenarios: monotonic seq
(submit_assigns_monotonic_seqnrs), payload-too-large
(submit_rejects_payload_too_large), window-full
(submit_rejects_when_window_full), heartbeat first/silence/none
(pending_heartbeat_fires_first_time/
pending_heartbeat_silenced_until_period_elapsed/
pending_heartbeat_none_when_window_empty), ACKNACK partial/full clear
(recv_acknack_clears_acked_seqnrs/
recv_acknack_full_clear_when_no_bits_set), receiver in-order/reorder/
dedup/buffer-full (recv_data_buffers_in_order/
recv_data_reorders_out_of_order/recv_data_drops_duplicates/
recv_data_rejects_when_buffer_full), pending-ACKNACK bitmap
(pending_acknack_marks_missing_slots), reset
(reset_clears_state_completely), byte-golden
(byte_golden_heartbeat_acknack: heartbeatFrame(1,3,0x80) ==
80 00 01 00 0B 01 05 00 01 00 03 00 80, acknackFrame(1,0,0x80) ==
80 00 01 00 0A 01 05 00 01 00 00 00 80 — identical to the reference
goldens), in-process end-to-end loss recovery
(end_to_end_sender_receiver_with_loss_recovery).
- swift_reliable_loss_recovery — peer drops every 3rd sample once; the app
retransmits directly via ReliableSender/ACKNACK; all 12 samples
delivered gap-free in order.
- swift_reliable_no_loss — lossless baseline; 12/12.
- swift_reliable_example — example_reliable.swift runs and reports
sequence 0..11 verified in order.
5/5 passed (cargo test -p zerodds-endpoint-e2e --test swift_reliable,
local macOS), 4 of which belong to this section (latency bench in §6). Not
on the Linux test host — the Linux test host carries swiftc 6.3.3, measured there).
Status: done — measured on macOS and on the Linux test host (swiftc 6.3.3, x86_64-unknown-linux-gnu).
§6 Latency — ring enqueue vs. inline sendto
Spec: §5.3 — the producer path of AsyncWriter (write → NSLock
ring-buffer push) must be measurably below the inline sendto syscall — the
evidence that async write removes syscall latency from the producer path,
not that it waits for ACKNACK.
Repo: endpoints/swift/Reliable.swift::AsyncWriter; bench harness in
crates/endpoint-e2e/tests/swift_reliable.rs::SWIFT_RELIABLE_MAIN
(modeBench) — 4000 iterations of inline sendto (UDP) vs. 4000 iterations
of AsyncWriter.write after 100 warmup iterations, no live peer needed.
Tests: (local, macOS, this run) swift_reliable_latency_bench
(crates/endpoint-e2e/tests/swift_reliable.rs) — two measurement runs: ring
enqueue 426–560 ns vs. inline sendto 3829–3871 ns (~7–9×). The
test asserts decoupled_ns < inline_ns (no fixed factor, only the
inequality). The measured value fluctuates run to run (microbenchmark noise
on a developer machine); the direction (decoupled beats inline) is stable.
Status: done — measured on macOS and on the Linux test host (swiftc 6.3.3, x86_64-unknown-linux-gnu).
Audit status
6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).
Test run (local macOS, verified this session):
cargo test -p zerodds-endpoint-e2e --test swift 2/2
(swift_endpoint_sync/swift_endpoint_async); --test swift_reliable 5/5
(swift_reliable_unit — 16 Swift unit-test scenarios incl. byte-golden,
swift_reliable_loss_recovery, swift_reliable_no_loss,
swift_reliable_example, swift_reliable_latency_bench); additionally
swift test (SwiftPM XCTest) 3/3 (testByteIdentity, testSyncLoopback,
testAsyncLoopback). Latency bench ring enqueue 426–560 ns / inline
sendto 3829–3871 ns (~7–9×, two measurement runs).
Open items: no functional gap. Measured: every test runs locally on macOS
only — Linux has no swiftc, so there is no Linux/CI evidence for this
SDK (unlike Go/Zig/Nim, which run on the Linux test host). That is a toolchain
boundary, not a spec gap.
zerodds-endpoint-swift 1.0 — Spec-Coverage
Quelle: docs/specs/zerodds-endpoint-swift-1.0.md — ZeroDDS Swift
Endpoint-SDK-Spec. Das Marshalling kommt aus crates/lang-rules, Spalte Language::Swift; hier steht der Transport.
Implementation:
endpoints/swift/Sources/Zerodds/Zerodds.swift— Wire-Core (Writer/Reader), XRCE-Framing (xrceWriteFrame/xrceReadFrame), syncClient, asyncAsyncReader,Transport-Protokoll,MemTransport.endpoints/swift/Reliable.swift— reliable Sender/Receiver-State-Machine + HEARTBEAT/ACKNACK-Wire-Codec +AsyncWriter(als eigenesZeroddsReliable-Modul kompiliert).endpoints/swift/Tests/ZeroddsTests/ZeroddsTests.swift— SwiftPM-XCTest (Byte-Golden + sync/async Loopback überMemTransport).crates/endpoint-e2e/tests/swift.rs— Ping-Pong-E2E über echtes UDP;crates/endpoint-e2e/tests/swift_reliable.rs— reliable-Stream-E2E, Unit- Runner, Example, Latenz-Bench.
Toolchain-Hinweis (gilt für alle Abschnitte): swiftc steht auf
Linux (Linux-Bench-Host) nicht zur Verfügung. Alle Tests unten sind
ausschließlich lokal auf macOS verifiziert (dieser Durchlauf: macOS,
swift-driver 1.148.6, Swift 6.3.3, arm64). Die E2E-Harnesse gaten auf
swiftc_available() und melden bei fehlender Toolchain einen lauten Skip
(kein false-green) — nicht auf dem Linux-Testhost re-verifiziert.
§1 XRCE-Framing
Spec: §1 — 8-Byte-XRCE-Header (session, stream, seq LE, submsg id 0x07
WRITE_DATA, flags, len LE) + Body, byte-identisch zu crates/xrce +
endpoints/c.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift —
xrceWriteFrame/xrceReadFrame, Konstanten xrceSessionNoKey (0x80) und
xrceStreamBestEffort (0x01).
Tests: Framing wird über swift_endpoint_sync/swift_endpoint_async
(§4) live geübt; der Wire-Core selbst (Writer/Reader, cap-4-Alignment,
f32/f64 via .bitPattern) über ZeroddsTests.testByteIdentity
(endpoints/swift/Tests/ZeroddsTests/ZeroddsTests.swift, swift test) gegen
testdata/golden_le.bin/golden_be.bin — LE und BE byte-identisch.
Status: done — auf macOS und auf dem Linux-Testhost gemessen
(swiftc 6.3.3, swift test: 39 Tests, 0 Fehlschläge).
§2 Sync Client
Spec: §2 — blockierender Client: write framet + liefert synchron,
poll ist ein nicht-blockierender Einzel-Receive.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift::Transport-Protokoll
(deliver/receive, der einzige Integrationspunkt); Client
(init/write/poll, monotoner seq-Zähler, Default-Session
xrceSessionNoKey/xrceStreamBestEffort); MemTransport als
NSLock-geschützte In-Memory-FIFO.
Tests: ZeroddsTests.testSyncLoopback (swift test, 5 Samples über
MemTransport); Live-E2E swift_endpoint_sync (§4).
Status: done — auf macOS und auf dem Linux-Testhost gemessen
(swiftc 6.3.3, swift test: 39 Tests, 0 Fehlschläge).
§3 Async AsyncReader
Spec: §3 — AsyncReader.stream() liefert einen AsyncStream<[UInt8]>;
eine interne Task pollt den Transport und yielded entrahmte
Sample-Bodies; for await auf Consumer-Seite, onTermination cancelt die
Task.
Repo: endpoints/swift/Sources/Zerodds/Zerodds.swift::AsyncReader
(init, stream()); Sendeseitig teilt sich der async Pfad Client.write
(§2) — kein separater AsyncWriter-Typ jenseits des reliable AsyncWriter
(§5).
Tests: ZeroddsTests.testAsyncLoopback (swift test, 5 Samples über
MemTransport, for await); Live-E2E swift_endpoint_async (§4).
Status: done — auf macOS und auf dem Linux-Testhost gemessen
(swiftc 6.3.3, swift test: 39 Tests, 0 Fehlschläge).
§4 Ping-Pong-E2E (live)
Spec: §5.1 — eine Swift-App tauscht mit dem geteilten Rust-XRCE-Peer über
einen echten UDP-Socket ein typisiertes Sample aus: voller Stack (generierte
Typen + Endpoint-SDK), einmal sync über Client, einmal async über
AsyncReader. §6 — SDK und generiertes Modul werden getrennt kompiliert
(Zerodds-Objekt + .swiftmodule), damit die geteilten Wire-Core-Typnamen
(Endianness/Writer/Reader) nicht kollidieren.
Repo: crates/endpoint-e2e/tests/swift.rs — build_swift_app kompiliert
Zerodds.swift als eigenes Modul (-emit-module/-emit-object), dann app
(generierte Ping/Pong-Typen + APP_MAIN) dagegen; UDPTransport
implementiert Transport über einen rohen, nicht-blockierenden UDP-Socket.
Tests: (lokal, macOS, dieser Durchlauf)
- swift_endpoint_sync — voller Stack über Client.
- swift_endpoint_async — voller Stack über AsyncReader.
2/2 grün (cargo test -p zerodds-endpoint-e2e --test swift, macOS lokal;
auf dem Linux-Testhost mit swiftc 6.3.3 nachgemessen).
Status: done — auf macOS und auf dem Linux-Testhost gemessen (swiftc 6.3.3, x86_64-unknown-linux-gnu).
§5 Reliable Stream — State-Machine, Wire, Async-Writer
Spec: §4 (verweist auf reliable-endpoint v1.0 §3/§4) — XRCE reliable
Stream (stream_id 0x80, §8.4.10/§8.4.11), spiegelt die Referenz
crates/xrce/src/reliable.rs: ReliableSender.submit/pendingHeartbeat/
recvAckNack/getInFlight/inFlightSeqs; ReliableReceiver.recvData/
drainInOrder/pendingAckNack/reset. Window 16, Receiver-Buffer 64,
Heartbeat 500 ms, Payload ≤ 65535, RFC-1982 16-bit Sequenznummern. Dazu der
async-entkoppelte AsyncWriter: der Producer enqueued wait-free
(NSLock-geschützter Ringpuffer, back-pressure via false-Rückgabe), ein
dedizierter Drain-Thread hält den ReliableSender-State und macht die
gesamte I/O (senden, Heartbeat, ACKNACK-getriebenes Retransmit) — der
Producer geht nie in den Kernel.
Repo: endpoints/swift/Reliable.swift —
writeDataFrame/parseWriteData, heartbeatFrame/parseHeartbeat,
acknackFrame/parseAckNack; ReliableSender, ReliableReceiver;
AsyncWriter (Ringpuffer queue, NSLock, init(sendFn:recvFn:)/start/
write/close, DispatchSemaphore fürs Shutdown-Rendezvous);
endpoints/swift/example_reliable.swift (lauffähige In-Process-Demo, kein
Socket); endpoints/swift/reliable_tests.swift (Standalone-Unit-Runner,
mirrort crates/xrce/src/reliable.rs’s #[cfg(test)], kein XCTest —
plain swiftc/swift, druckt UNIT OK).
Tests: (lokal, macOS, dieser Durchlauf)
- swift_reliable_unit (crates/endpoint-e2e/tests/swift_reliable.rs)
kompiliert reliable_tests.swift gegen das ZeroddsReliable-Modul und
führt es aus — 16 run()-Testszenarien: monotone seq
(submit_assigns_monotonic_seqnrs), Payload-zu-groß
(submit_rejects_payload_too_large), Window-full
(submit_rejects_when_window_full), Heartbeat first/silence/leer
(pending_heartbeat_fires_first_time/
pending_heartbeat_silenced_until_period_elapsed/
pending_heartbeat_none_when_window_empty), ACKNACK Teil-/Voll-Clear
(recv_acknack_clears_acked_seqnrs/
recv_acknack_full_clear_when_no_bits_set), Receiver In-Order/Reorder/
Dedup/Buffer-full (recv_data_buffers_in_order/
recv_data_reorders_out_of_order/recv_data_drops_duplicates/
recv_data_rejects_when_buffer_full), Pending-ACKNACK-Bitmap
(pending_acknack_marks_missing_slots), Reset
(reset_clears_state_completely), Byte-Golden
(byte_golden_heartbeat_acknack: heartbeatFrame(1,3,0x80) ==
80 00 01 00 0B 01 05 00 01 00 03 00 80, acknackFrame(1,0,0x80) ==
80 00 01 00 0A 01 05 00 01 00 00 00 80 — identisch zu den
Referenz-Goldens), In-Process-End-to-End-Loss-Recovery
(end_to_end_sender_receiver_with_loss_recovery).
- swift_reliable_loss_recovery — Peer dropt jedes 3. Sample einmalig; die
App retransmittet direkt über ReliableSender/ACKNACK; alle 12 Samples
lückenlos in Reihenfolge geliefert.
- swift_reliable_no_loss — lossless Baseline; 12/12.
- swift_reliable_example — example_reliable.swift läuft und meldet
sequence 0..11 verified in order.
5/5 grün (cargo test -p zerodds-endpoint-e2e --test swift_reliable, macOS
lokal), davon 4 in diesem Abschnitt (Latenz-Bench in §6). Nicht auf dem Linux-Testhost —
der Linux-Testhost trägt swiftc 6.3.3, dort nachgemessen.
Status: done — auf macOS und auf dem Linux-Testhost gemessen (swiftc 6.3.3, x86_64-unknown-linux-gnu).
§6 Latenz — Ring-Enqueue vs. inline sendto
Spec: §5.3 — der Producer-Pfad des AsyncWriter (write →
NSLock-Ringpuffer-Push) muss messbar unter dem inline sendto-Syscall
liegen — der Beleg, dass Async-Write die Syscall-Latenz aus dem
Producer-Pfad nimmt, nicht das Warten auf ACKNACK.
Repo: endpoints/swift/Reliable.swift::AsyncWriter; Bench-Harness in
crates/endpoint-e2e/tests/swift_reliable.rs::SWIFT_RELIABLE_MAIN
(modeBench) — 4000 Iterationen inline sendto (UDP) vs. 4000 Iterationen
AsyncWriter.write nach 100 Warmup-Iterationen, kein Live-Peer nötig.
Tests: (lokal, macOS, dieser Durchlauf) swift_reliable_latency_bench
(crates/endpoint-e2e/tests/swift_reliable.rs) — zwei Messläufe:
Ring-Enqueue 426–560 ns vs. inline sendto 3829–3871 ns
(~7–9×). Assert im Test: decoupled_ns < inline_ns (kein fixer Faktor,
nur die Ungleichung). Messwert schwankt lauf-zu-lauf (Mikrobenchmark-Rauschen
auf einer Entwicklermaschine); die Richtung (entkoppelt schneller als
inline) ist stabil.
Status: done — auf macOS und auf dem Linux-Testhost gemessen (swiftc 6.3.3, x86_64-unknown-linux-gnu).
Audit-Status
6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).
Test-Lauf: macOS lokal, verifiziert dieser Durchlauf —
cargo test -p zerodds-endpoint-e2e --test swift 2/2
(swift_endpoint_sync/swift_endpoint_async); --test swift_reliable 5/5
(swift_reliable_unit — 16 Swift-Unit-Testszenarien inkl. Byte-Golden,
swift_reliable_loss_recovery, swift_reliable_no_loss,
swift_reliable_example, swift_reliable_latency_bench); zusätzlich
swift test (SwiftPM XCTest) 3/3 (testByteIdentity, testSyncLoopback,
testAsyncLoopback). Latenz-Bench Ring-Enqueue 426–560 ns / inline sendto
3829–3871 ns (~7–9×, zwei Messläufe).
Offene Punkte: keine funktionale Lücke. Gemessen: alle Tests laufen
ausschließlich lokal auf macOS — Linux hat keinen swiftc, daher kein
Linux/CI-Nachweis für dieses SDK (im Gegensatz zu Go/Zig/Nim, die auf
Linux laufen). Das ist eine Toolchain-Grenze, kein Spec-Gap.