# HPACK decoder This document records the implementation constraints of Flash's RFC 7541 decoder. It is contributor documentation, not application API documentation. ## Representation model `HpackDecoder` accepts all RFC 7541 field representations: indexed fields, literals with incremental indexing, literals without indexing, never-indexed literals, and dynamic-table size updates. Prefix integers are bounded against overflow, Huffman padding and EOS are validated, and decoded string lengths are checked while bytes are produced. The static table is stored as 61 immutable name/value byte pairs. Encoder-oriented reverse lookup uses fixed open-addressed integer tables built during class initialization; lookup never converts header bytes to `String` and never calls `HashMap` on the hot path. The dynamic table owns a bounded byte arena and a ring of primitive entry descriptors. Entry size is `name length + value length + 32`, and eviction is oldest-first as required by RFC 7541 §4.1. When the arena tail is too short, live entries are compacted into a contiguous prefix. This avoids segmented views in every consumer and keeps indexed fields cheap to copy. ## Ownership and eviction safety Views emitted by `HeaderSink` are callback-scoped. Production decoding targets a reusable `HpackHeaderBlock` owned by the stream, which copies each name and value into its own arena. The copy is required for correctness. Consider stream A referencing a dynamic-table entry while its handler is running. The connection thread can then decode stream B, evict that entry, and reuse its bytes. If stream A retained the dynamic-table view, its headers would silently change. Per-stream storage removes that race without reference counting or synchronization. The precise copy model is: - HTTP/1.1 copies nothing per request but scans header bytes in the connection buffer. - HTTP/2 copies decoded request headers into stream-owned storage because multiplexed handlers outlive subsequent HPACK mutations. - Novel incrementally-indexed fields are also copied once into the connection's dynamic table. `HpackEvictionRaceTest` contains both the unsafe borrowed-view demonstration and the stable stream-owned result. ## Header-list rejection The decoder counts RFC header-list size cumulatively. Once the configured limit is crossed it stops emitting fields, but continues parsing the entire block and applying dynamic-table updates. Only after the block ends does it throw `HeaderListSizeException`. The stream layer can reject the request while the connection's compression state remains synchronized. ## CONTINUATION assembly `ContinuationAssembler` copies HEADERS and CONTINUATION fragments into one bounded connection buffer. It rejects interleaving, stream-id changes, excessive continuation count, and blocks that exceed the configured capacity. `SegmentedByteView` is intentionally not used here: RFC 9113 §6.10 requires a contiguous, non-interleaved continuation sequence, and one bounded copy makes the HPACK decoder and all downstream views simpler. ## Verification - RFC 7541 Appendix C.1–C.6 vectors, including dynamic-table state after every sequence. - Invalid integer, Huffman, index, size-update, and header-list inputs. - Ten million deterministic random blocks; only typed protocol rejections may escape. - JMH `-prof gc`: `decodeStaticRequest` measured 102.725 ns/op and 0.001 B/op on JDK 21.0.11. The latter is the profiler's sampling noise floor; no garbage collections occurred.