# .NET TLS read completion and nullable FAST boundaries

Two reproduced .NET defects are corrected: completed TLS read faults and
cancellations now consume their result, and nullable FAST integers support
their signed and unsigned 64-bit maxima without wrapping. Both corrections
preserve the existing polling, fail-closed and allocation-free codec contracts.
No locks or native transport changes were added.

## Completed TLS reads

`ManagedSession.Peer.ReceiveReadyTls` previously disconnected on a faulted or
canceled completed `ValueTask<int>` without calling `GetResult`. A pooled
`IValueTaskSource<int>` releases its asynchronous state when that result is
consumed, not merely when completion status is observed. The controlled
reproduction verifies the real peer polling path while replacing only its
`SslStream`; failed reads demonstrably skipped result consumption.

After `IsCompleted`, the peer now consumes the result exactly once inside a
try/catch and clears the stored task. Faults and cancellation still disconnect;
EOF still disconnects; data still advances the receive count and timestamp.
An incomplete read remains outstanding rather than issuing another read.
Twelve regression cases cover immediate and pending data, EOF, faults and
cancellation, completion after a pending status observation, and a synchronous
`ReadAsync` exception. This verifies completed operations; it is not a claim
about arbitrary teardown while a read remains incomplete.

## Nullable FAST maxima

A present nullable integer reserves the zero wire value for null. Its largest
positive value therefore needs one more wire bit than the target integer:
`2^64` for unsigned maximum and `2^63` for signed maximum. The .NET encoder
refused both values; its decoders also refused their valid ten-group forms.
C++, Java and Rust already handle these boundaries.

The .NET primitives now recognize and emit only the exact maximum forms:
first byte `02` for unsigned or `01` for signed, eight zero bytes, then `80`.
Mandatory decoders still refuse those out-of-range mandatory values. Other
overflowing forms remain rejected without advancing the cursor, and failed
encodes preserve the destination. Tests cover maxima at a nonzero offset,
short buffers, invalid offsets, every truncation and perturbed overflowing
forms. The existing exhaustive signed-prefix test now distinguishes the one
valid nullable maximum. Another test compares 10,000 signed/unsigned pairs
with an independent `Int128`/`UInt128` wire oracle, including both maxima and
the signed minimum. Production code introduces no wider-integer dependency.

## Verification and remaining failures

Before changing production code, the focused baseline run reported 45 tests:
37 passed, eight failed and none skipped. Six failures demonstrated unconsumed
read faults/cancellations and two demonstrated FAST boundaries. After the
corrections, all 106 selected TLS, send-batch, FAST and required-native tests
passed without skips. The same 106 tests also passed against the open edition,
with a separately built open native library. The standalone read reproduction
changed from four passes/four failures to eight passes/no failures. Export
scans of all four changed source/test files found nothing.

The complete development suite reported 1,692 tests: 1,687 passed, two failed
and three skipped. It is not an all-green result. The two failures are in
unchanged file-store tests already reported on main: the QuickFIX settings
fixture lacks mandatory sender/target IDs, and the native refusal test expects
`ArgumentException` rather than the actual `InvalidOperationException` carrying
`InvalidArg`. In the latter case the managed validation advertises a file
StorePath as valid for SyncBeforeSend, while hftnet refuses it. That support/
validation mismatch should be resolved, not hidden by simply changing the
asserted exception. These files are reserved by another session and were not
edited.

One skip requires `LIBHFT_ACCEPTOR_THREADS=2`; rerunning that native-pool
broadcast test with the requested setting passed without skipping. The two
remaining skipped cases require multicast loopback and an explicit bypass NIC
interface. The bypass NICs exist in network namespaces, but this run did not
stage or execute the new binaries through the privileged NIC runners. A
separate first-use timestamp allocation check also passed. These are functional
checks under an isolated CPU lease, not hardware latency measurements.

## Source pins and evidence

`~/archive/libhft/dotnet-read-fast-review-20261003/` retains baseline sources,
the source head `b80005257ff6a08625cd5db2918da4f34a3a72b6`, the exact verified
patch, source hashes, build logs, all TRX files, JSON counts and each failure or
skip reason. That source head plus the archived patch identifies the tested
production code; a later rebase is not silently substituted for it.

The freshly built development native library, with TCPDirect and SocketXtreme
support, has SHA-256
`0a024f7a24eb031266869ca40a30c6f177d231d4b91b303bf204c661ba9c995f`.
The separately built open library, without those private transports, has
SHA-256 `6a5c7bfeec51bf1ccd4df00a6d671b70b76a0d30b7b2330c3833b3fc5a09cbf3`.
Native loading was required in both sets of focused tests; absent native code
was not accepted as a passing run.

After merging through `5f5791224`, including new native FIXP liveness methods,
both native libraries were rebuilt again. The 106 development checks plus all
selected native FIXP suites passed 146 tests without skips; the 62 open-edition
read/write-completion, FAST and required-native checks also passed without
skips. Their separate source pin, native hashes, logs and TRX files are retained
as post-merge evidence. The earlier complete-suite counts are not claimed for
that later source revision.
