zerodds-endpoint-d 1.0 — Spec Coverage

Source: docs/specs/zerodds-endpoint-d-1.0.md — ZeroDDS D endpoint SDK spec (§1 XRCE framing, §2 sync Client, §3 async Reader/Writer, §4 reliable stream via reliable-endpoint-1.0). Covers the D endpoint SDK endpoints/d/: zerodds.d (XRCE framing, sync Client, async AsyncReader via std.concurrency) and the self-contained reliable.d (state machine + wire codec + lock-free SpscRing), plus the live Ping-Pong E2E. Complements the codegen coverage zerodds-xcdr2-d.

Implementation: endpoints/d/zerodds.d, endpoints/d/reliable.d, endpoints/d/reliable_app.d, endpoints/d/example_reliable.d, endpoints/d/reliable_test.d, endpoints/d/reliable_bench.d; E2E peer crates/endpoint-e2e/tests/d.rs + d_reliable.rs.

§1 XRCE Framing

Spec: zerodds-endpoint-d-1.0 §1 — 8-byte WRITE_DATA header (session, stream, seq LE, submsg id 0x07, flags 0x03, len LE) + body; best-effort stream 0x01.

Repo: zerodds.d — writeFrame/readFrame (line 136–151), constants SessionNoKey = 0x80, StreamBestEffort = 0x01 (line 133–134).

Tests: exercised indirectly via the Ping-Pong E2E (§4) — d_endpoint_sync and d_endpoint_async frame/deframe every sample through this code path.

Status: done.

§2 Sync Client

Spec: zerodds-endpoint-d-1.0 §2 — A blocking-poll Client over a swappable Transport (deliver/receive delegates); the sequence number wraps modulo 0x10000.

Repo: zerodds.d — class Client (line 162–182), struct Transport (line 155–158), memTransport() as an in-memory FIFO for tests/examples.

Tests: (Linux) d_endpoint_sync (Ping-Pong E2E, §4).

Status: done.

§3 Async Reader

Spec: zerodds-endpoint-d-1.0 §3 — A background actor via std.concurrency (spawn/send/receive, Tid message passing) — the idiomatic D concurrency primitive, analogous to Ada’s protected object/task and Rust’s channel. At this layer only an async Reader exists; a decoupled async Writer is part of the reliable stream (SpscRing, §5) — the sync Client sends inline.

Repo: zerodds.d — class AsyncReader (line 214–222), readerLoop (line 201–212).

Tests: (Linux) d_endpoint_async (Ping-Pong E2E, §4).

Status: done.

§4 Ping-Pong E2E (live)

Spec: no dedicated section in zerodds-endpoint-d-1.0 — repo-internal live proof for §1–§3 together: three live UDP tests against the shared Rust XRCE peer: generated codegen over bare UDP (no XRCE frame), full stack via the sync Client (§2), full stack via the async AsyncReader (§3).

Repo: crates/endpoint-e2e/tests/d.rs.

Tests: (Linux) d_raw_udp + d_endpoint_sync + d_endpoint_async — 3/3 passed.

Status: done.

§5 Reliable Stream — State Machine + async-decoupled Writer

Spec: zerodds-endpoint-d-1.0 §4, referencing reliable-endpoint-1.0 §3.1/§3.2/§3.3 — XRCE reliable stream (stream_id >= 128, §8.4.10/§8.4.11). Sender submit/pendingHeartbeat/recvAcknack/getInFlight; Receiver recvData/drainInOrder/pendingAcknack/reset. Window 16, receiver buffer 64, heartbeat 500 ms, payload ≤ 65535, RFC-1982 16-bit sequence numbers. Self-contained wire codec (no Endian/Writer name clash with zerodds.d). Plus SpscRing — a wait-free single-producer/single-consumer ring (CAP = 1024) as the async-decoupled writer: the producer’s enqueue is just a slot store plus one release-store of head, no lock, no syscall; a separate drain thread owns the socket and the sender state.

Repo: endpoints/d/reliable.d (Sender/Receiver/SpscRing/wire codec), endpoints/d/reliable_app.d (E2E sender app: initial burst + heartbeat/ ACKNACK-driven retransmit loop), endpoints/d/example_reliable.d (in-process demo), endpoints/d/reliable_test.d (unit + byte-golden suite).

Tests: (Linux) - d_reliable_unit_and_golden — reliable_test.d asserts monotone seq, payload-too-large, window-full, heartbeat first/silenced-<500ms/ after-500ms/empty, ACKNACK clear/full-clear, in-order drain, reorder, duplicate-drop, buffer-full, pending-ACKNACK bitmap, reset, plus 2 hardcoded byte-goldens (HEARTBEAT/ACKNACK) — and, when the Rust goldens are generated, an additional byte-identical check against golden_heartbeat_le.bin/golden_acknack_le.bin. Prints “ALL OK”. - d_reliable_loss_recovery — peer drops every 3rd datagram (12 samples); reliable_app.d retransmits on ACKNACK; all 12 delivered gap-free in order. - d_reliable_no_loss — same app, lossless baseline, 12/12. - d_reliable_example — example_reliable.d, in-process Sender/Receiver loss-recovery demo, N=12, prints “RELIABLE OK”.

4/4 passed (d_reliable_latency_bench covered separately in §6; all 5 tests in d_reliable.rs together 5/5, see Audit Status).

Status: done.

§6 Latency — producer enqueue vs. inline sendto

Spec: reliable-endpoint-1.0 §5 item 4 (latency bench), referenced from zerodds-endpoint-d-1.0 §4 — micro-bench comparing an inline UDP sendto per sample against the wait-free SpscRing enqueue — the syscall the decoupled writer removes from the producer’s hot path.

Repo: endpoints/d/reliable_bench.d — ITERS = 20000, MonoTime timestamps around sendto and ring.enqueue, sorted, median taken.

Tests: (Linux) d_reliable_latency_bench — inline-sendto median 3600 ns; ring-enqueue median 0 ns.

HONEST NOTE: the ring enqueue falls below this machine’s MonoTime resolution — the median reads 0 ns as a bench-granularity limit, not a claim of “infinitely fast”. What is measurable and real: the 3600 ns syscall is removed from the producer’s path once decoupled via the ring.

Status: done — measured to the resolution limit of the bench setup. The limit belongs to the instrument, not to the implementation.

Audit Status

6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).

Test run (Linux, verified): cargo test -p zerodds-endpoint-e2e --test d 3/3 (d_raw_udp, d_endpoint_sync, d_endpoint_async); --test d_reliable 5/5 (d_reliable_loss_recovery, d_reliable_no_loss, d_reliable_unit_and_golden, d_reliable_example, d_reliable_latency_bench); latency bench inline-sendto 3600 ns / ring-enqueue 0 ns (bench-granularity limit, see §6).

Open items: none.

zerodds-endpoint-d 1.0 — Spec-Coverage

Quelle: docs/specs/zerodds-endpoint-d-1.0.md — ZeroDDS D-Endpoint-SDK-Spec (§1 XRCE-Framing, §2 sync Client, §3 async Reader/Writer, §4 reliable Stream via reliable-endpoint-1.0). Deckt das D-Endpoint-SDK endpoints/d/ ab: zerodds.d (XRCE-Framing, sync Client, async AsyncReader via std.concurrency) und das selbstständige reliable.d (State-Machine + Wire-Codec + lock-freier SpscRing), plus das Live-Ping-Pong- E2E. Ergänzt die Codegen-Coverage zerodds-xcdr2-d.

Implementation: endpoints/d/zerodds.d, endpoints/d/reliable.d, endpoints/d/reliable_app.d, endpoints/d/example_reliable.d, endpoints/d/reliable_test.d, endpoints/d/reliable_bench.d; E2E-Peer crates/endpoint-e2e/tests/d.rs + d_reliable.rs.

§1 XRCE-Framing

Spec: zerodds-endpoint-d-1.0 §1 — 8-Byte-WRITE_DATA-Header (session, stream, seq LE, submsg id 0x07, flags 0x03, len LE) + Body; best-effort Stream 0x01.

Repo: zerodds.d — writeFrame/readFrame (Zeile 136–151), Konstanten SessionNoKey = 0x80, StreamBestEffort = 0x01 (Zeile 133–134).

Tests: indirekt über das Ping-Pong-E2E (§4) — d_endpoint_sync + d_endpoint_async framen/deframen jedes Sample über diesen Codepfad.

Status: done.

§2 Sync-Client

Spec: zerodds-endpoint-d-1.0 §2 — Ein blockierend gepollter Client über eine austauschbare Transport (deliver/receive-Delegates); Sequenznummer wrapt modulo 0x10000.

Repo: zerodds.d — class Client (Zeile 162–182), struct Transport (Zeile 155–158), memTransport() als In-Memory-FIFO für Tests/Beispiele.

Tests: (Linux) d_endpoint_sync (Ping-Pong-E2E, §4).

Status: done.

§3 Async Reader

Spec: zerodds-endpoint-d-1.0 §3 — Ein Hintergrund-Actor via std.concurrency (spawn/send/receive, Tid-Message-Passing) — die idiomatische D-Concurrency-Primitive, analog zu Adas protected object/task und Rusts Channel. Auf dieser Ebene existiert nur ein async Reader; ein entkoppelter async Writer ist Teil des reliable Streams (SpscRing, §5) — der Sync-Client sendet inline.

Repo: zerodds.d — class AsyncReader (Zeile 214–222), readerLoop (Zeile 201–212).

Tests: (Linux) d_endpoint_async (Ping-Pong-E2E, §4).

Status: done.

§4 Ping-Pong-E2E (live)

Spec: kein eigener Abschnitt in zerodds-endpoint-d-1.0 — repo-interner Live-Beweis für §1–§3 zusammen: drei Live-UDP-Tests gegen den geteilten Rust-XRCE-Peer: generierter Codegen über nacktes UDP (kein XRCE-Frame), Voll-Stack über den sync Client (§2), Voll-Stack über den async AsyncReader (§3).

Repo: crates/endpoint-e2e/tests/d.rs.

Tests: (Linux) d_raw_udp + d_endpoint_sync + d_endpoint_async — 3/3 passed.

Status: done.

§5 Reliable Stream — State-Machine + async-entkoppelter Writer

Spec: zerodds-endpoint-d-1.0 §4, referenziert reliable-endpoint-1.0 §3.1/§3.2/§3.3 — XRCE reliable Stream (stream_id >= 128, §8.4.10/§8.4.11). Sender submit/pendingHeartbeat/recvAcknack/getInFlight; Receiver recvData/drainInOrder/pendingAcknack/reset. Window 16, Receiver-Puffer 64, Heartbeat 500 ms, Payload ≤ 65535, RFC-1982-16-Bit-Sequenznummern. Selbstständiger Wire-Codec (kein Endian/Writer-Namenskonflikt mit zerodds.d). Dazu SpscRing — ein wait-freier Single-Producer/ Single-Consumer-Ring (CAP = 1024) als async-entkoppelter Writer: der Producer macht nur einen Slot-Store + einen Release-Store auf head, kein Lock, kein Syscall; ein separater Drain-Thread besitzt Socket + Sender-State.

Repo: endpoints/d/reliable.d (Sender/Receiver/SpscRing/Wire-Codec), endpoints/d/reliable_app.d (E2E-Sender-App: initialer Burst + Heartbeat/ ACKNACK-getriebene Retransmit-Loop), endpoints/d/example_reliable.d (In-Prozess-Demo), endpoints/d/reliable_test.d (Unit- + Byte-Golden-Suite).

Tests: (Linux) - d_reliable_unit_and_golden — reliable_test.d prüft monotone Seq, Payload-zu-groß, Window-full, Heartbeat first/silenced-<500ms/ after-500ms/empty, ACKNACK clear/full-clear, in-order Drain, Reorder, Duplicate-Drop, Buffer-full, Pending-ACKNACK-Bitmap, Reset, plus 2 hardcodierte Byte-Goldens (HEARTBEAT/ACKNACK) — und, wenn die Rust-Goldens generiert wurden, zusätzlich byte-identisch gegen golden_heartbeat_le.bin/golden_acknack_le.bin. Ausgabe „ALL OK”. - d_reliable_loss_recovery — Peer dropt jedes 3. Datagramm (12 Samples); reliable_app.d retransmittet auf ACKNACK; alle 12 lückenlos in-order geliefert. - d_reliable_no_loss — dieselbe App, lossless Baseline, 12/12. - d_reliable_example — example_reliable.d, In-Prozess-Sender/Receiver- Loss-Recovery-Demo, N=12, Ausgabe „RELIABLE OK”.

4/4 passed (d_reliable_latency_bench separat in §6; alle 5 Tests aus d_reliable.rs zusammen 5/5, siehe Audit-Status).

Status: done.

§6 Latenz — Producer-Enqueue vs. inline sendto

Spec: reliable-endpoint-1.0 §5 Punkt 4 (Latenz-Bench), referenziert von zerodds-endpoint-d-1.0 §4 — Micro-Bench vergleicht ein inline UDP-sendto pro Sample gegen den wait-freien SpscRing-Enqueue — der Syscall, den der entkoppelte Writer aus dem Hot-Path des Producers entfernt.

Repo: endpoints/d/reliable_bench.d — ITERS = 20000, MonoTime-Zeitstempel um sendto bzw. ring.enqueue, sortiert, Median genommen.

Tests: (Linux) d_reliable_latency_bench — inline-sendto-Median 3600 ns; ring-enqueue-Median 0 ns.

HONEST NOTE: Der Ring-Enqueue liegt unterhalb der MonoTime-Auflösung dieser Maschine — der Median liest sich als 0 ns als Bench-Granularitätslimit, nicht als Behauptung „unendlich schnell”. Messbar und real ist: die 3600-ns-Syscall wird durch die Entkopplung über den Ring aus dem Producer-Pfad entfernt.

Status: done — gemessen bis an die Auflösungsgrenze des Bench-Aufbaus. Die Grenze gehört zum Messmittel, nicht zur Implementierung.

Audit-Status

6 done / 0 partial / 0 open / 0 blocked / 0 n/a (informative).

Test-Lauf: Linux, verifiziert — cargo test -p zerodds-endpoint-e2e --test d 3/3 (d_raw_udp, d_endpoint_sync, d_endpoint_async); --test d_reliable 5/5 (d_reliable_loss_recovery, d_reliable_no_loss, d_reliable_unit_and_golden, d_reliable_example, d_reliable_latency_bench); Latenz-Bench inline-sendto 3600 ns / ring-enqueue 0 ns (Bench-Granularitätslimit, siehe §6).

Offene Punkte: keine.