# SBE scalar and idle UDP review, 2026-10-18

Session `codex-review-sbe-1018`, thread `01a0fcb1-4c4d-7380-bb51-0d6ed4a2b4d6`,
on BEELINK2. The user requested continued deep correctness and latency review,
parallel agents, fixes and regular publication. Work used a dedicated worktree and
external build root, a shared host CPU lease, sequential build/test stages and at
most 16 workers.

## Findings

Four generator defects and one .NET receive-path allocation defect were found.

1. Optional SBE float/double null setters wrote zero, while null predicates treated
   both signs of zero as absent and treated external NaNs as present. Root, group
   and inline-composite fields were affected in C++, Java, C# and Rust. Optional
   fields now use quiet NaN and the language's NaN predicate. The established zero
   fallback for required fields is preserved. This follows the
   [FIX SBE floating-point null contract](https://github.com/FIXTradingCommunity/fix-simple-binary-encoding/blob/master/v1-0-STANDARD/doc/02FieldEncoding.md#float-encoding).
2. Omitted field/ref presence defaulted to required even when its scalar alias was
   optional, losing the null setter/predicate API and optional-float fallback.
   Message fields, group fields and composite references now inherit optionality,
   with explicit required overrides retained. This follows SBE's
   [inherited attributes](https://github.com/FIXTradingCommunity/fix-simple-binary-encoding/blob/master/v1-0-STANDARD/doc/02FieldEncoding.md#inherited-attributes).
3. Valid custom uint64 null values above Java's signed-long range generated invalid
   decimal long literals. Java now receives a valid signed-long literal with the
   identical 64-bit wire pattern. Regressions cover 2^63, 2^64-1, adjacent values,
   absent fields, and exact little-endian bytes.
4. .NET's connected UDP and multicast receive wrappers threw/caught a SocketException
   on every empty nonblocking poll. Linux/Windows now use the nonthrowing span
   receive overload and distinguish an empty poll from a received empty datagram.
   Truncated datagrams still fail; a zero-length datagram still fails framing; a
   disposed socket still throws. The fallback path on other systems is preserved.

5. Custom integer null values outside their primitive range were accepted, allowing
   generated casts to wrap the sentinel or generated literals to fail compilation.
   Audit and all four generators now share range validation for signed and unsigned
   8-, 16-, 32- and 64-bit values. Boundary regressions exercise 16 invalid and
   16 valid schemas across all four backends; valid uint64 extrema remain supported.
   The existing char encoding policy is unchanged.

## Qualification

Tests were first committed at `5541b517c112255e91299d5062498837982d4974`.
The original generator reproduced missing inherited optional APIs in C++, C# and
Rust, and oversized Java integer literals. The alias/literal fix at
`e467d7fb8b5f949dfaf7ff8c555a05266c94ed30` then allowed runtime regressions to expose
the float failures in all four languages: C++ and Java runners failed, Rust's
scalar subset had 3 passes/6 failures, and .NET had 20 passes/24 failures across
44 tests (22 scalar failures plus both idle-allocation cases). Its native guard
passed, and no test was skipped.

The fixed source was qualified at
`f3857a3a3204ac5cbd9767dc52541a8e18d2136c`:

| Check | Development edition | Open edition |
| --- | --- | --- |
| C++ fixture binaries | 9 passed | 9 passed |
| Java fixture runners | 8 passed | 8 passed |
| Rust fixture tests | 33 passed, 0 ignored | 33 passed, 0 ignored |
| .NET selected tests | 90 passed, 0 skipped | 90 passed, 0 skipped |

Both .NET runs include the native-library-required guard, all 37 scalar-alias
cases and all six UDP idle/empty/closed-socket cases. The native libraries were
built at their canonical edition-specific paths. The full generator self-test,
eight sample-code drift targets, whitespace check and export scan passed.
The range regression separately failed on the pre-range candidate and passed
afterward: 16 invalid and 16 valid schema audits plus 64 rejected and 64 accepted
backend generations. No unrelated generated sample or golden file changed.

The scalar fixture covers direct/aliased float and double fields at root, group,
and inline-composite positions, positive/negative zero, finite values,
external signed quiet NaN payloads, null setters, old schema versions, short
blocks, explicit required overrides, and uint64 sentinel bit preservation. Rust
additionally checks all root boundaries, short group widths and its documented
absent-current-entry handling. C++/Java tests respect their valid group-iteration
precondition. The fixed-fixture Make target now executes scalar-alias runtime
checks in C++ and Java, alongside the existing Rust fixture crate; C# fixtures run
in the ordinary .NET test project.

Idle polling is measured with thread-local allocated-byte counters after warmup,
10,000 empty polls through each wrapper after 20,000 warmup polls. On Linux, both
wrappers went from 6,400,000 bytes (640 bytes/poll) to zero bytes in each fixed
edition run. This is an
allocation result; no end-to-end latency or throughput figure is claimed.

After qualification, the fixes were rebased onto `0638fc3fd`.
The rebased implementation at `7ead66acc` has the identical complete
`engine` tree; `tools`, `bench`, `sbe` and `publish` also have no differences.
Only documentation and an unrelated internal benchmark script changed.

## Review boundaries and evidence

Parallel handwritten SBE framing, shared-memory ring and transport audits found
no additional confirmed defect requiring a change in this batch. Live ring
recreation remains the previously documented API-policy question; this review
does not change its overwrite semantics. FIX session, configuration and store
code is unchanged. This is targeted qualification, not a full repository suite.

Raw evidence is retained outside Git under
`~/archive/libhft/reviews/2026-10-18-codex-review-sbe-1018`, with checksums, staged
source identities, commands, compiler/runtime logs, .NET reports, reviewed patches
and source-equivalence proof. No raw logs or build artifacts are committed.
