# Slice H-c2 — borrowed packet decode ## Result The production receive path no longer materializes an owned `Packet`, `PacketHeaderOptional`, `BodyBytes`, fragment list, or single-fragment payload array for every accepted UDP datagram. It now decodes one borrowed view over the right-sized pooled datagram: ```text pooled datagram -> borrowed packet/header/optional views -> allocation-free fragment enumeration -> single-fragment message borrows the same storage -> synchronous message/event parse and dispatch -> pooled datagram returned ``` Only a multi-fragment logical message copies its fragment payloads. That copy is required because those bytes must survive beyond the current datagram while the remaining fragments arrive. ## Lifetime contract `BorrowedPacket`, `BorrowedOptionalHeader`, and `BorrowedMessageFragment` are internal types. Their memory is valid only while the caller retains the datagram owner. `WorldSession.ProcessDatagram` performs parsing and event dispatch synchronously before returning that owner. `GameEventEnvelope.TryParseBorrowed` is likewise internal. Registered event handlers must parse or copy durable state during `Dispatch`; they must not retain the envelope or payload. The existing public byte-array parser remains available for independently owned fixtures and callers. Multi-fragment assembly copies on first acceptance, not on completion, so returning either source datagram cannot invalidate the partial message. Conflicting `Count` or `Queue` values for a reused sequence cannot corrupt an accepted partial. Duplicate fragments remain idempotent. ## Behavior preserved - optional-header field order exactly matches `PacketHeaderOptional.Parse`; - unencrypted and ISAAC checksum verification consume the same bytes and key; - packet ACK and last-heard placement in `WorldSession` are unchanged; - fragment grouping remains keyed by message `Sequence`; - complete messages are delivered in the same fragment-index order; - game-event handlers remain synchronous on the update/render thread; - malformed zero-count and out-of-range-index fragments are rejected before assembly rather than indexing invalid storage. Named-retail `CNetLayerPacket` remains the packet-size reference. ACE's packet parser and Holtburger's inbound fragment path are the wire/order cross-references; this slice changes .NET storage ownership only. ## Deterministic evidence The differential suite feeds the owned and borrowed decoders identical datagrams and compares: - all optional fields in their retail wire order; - retransmit sequence order; - login payload coverage; - one and multiple fragments; - body, optional, fragment-header, and fragment-payload bytes; - matching seeded ISAAC checksum consumption; - every malformed-packet error category touched by this slice. Fragment tests prove that a single-fragment result aliases its source storage, while a multi-fragment result survives mutations of both source datagrams. The warmed single-fragment decode/enumeration unit test measures zero current-thread allocation over 1,000 iterations. A Release microbenchmark over 500,000 valid 48-byte, single-fragment packets measured: | Decoder | Time | Throughput cost | Allocated | |---|---:|---:|---:| | owned | 246.372 ms | 492.7 ns/packet | 200,000,040 bytes | | borrowed | 28.431 ms | 56.9 ns/packet | 40 bytes | That is an 8.666x decoder microbenchmark speedup and removes the warmed per-packet allocation (the reported 40 bytes are the benchmark timer). ## Remaining H-c work H-c3 removes outbound packet/fragment intermediate arrays by writing the exact wire framing directly into caller-provided storage. H-c closeout then runs the connected lifecycle, reconnect, portal, interaction, and soak gates.