diff --git a/.duvet/config.toml b/.duvet/config.toml index 8482911b..283d3881 100644 --- a/.duvet/config.toml +++ b/.duvet/config.toml @@ -6,6 +6,9 @@ pattern = "h3/**/*.rs" [[specification]] source = "https://www.rfc-editor.org/rfc/rfc9114" +[[specification]] +source = "https://www.rfc-editor.org/rfc/rfc9204.html" + [report.html] enabled = true issue-link = "https://github.com/hyperium/h3/issues" diff --git a/.duvet/exceptions/rfc9204/4.2.toml b/.duvet/exceptions/rfc9204/4.2.toml new file mode 100644 index 00000000..9e31fe9c --- /dev/null +++ b/.duvet/exceptions/rfc9204/4.2.toml @@ -0,0 +1,24 @@ +[[exception]] +target = "https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2" +quote = ''' +An endpoint MAY avoid creating an encoder stream if it will not be +used (for example, if its encoder does not wish to use the dynamic +table or if the maximum size of the dynamic table permitted by the +peer is zero). +''' +reason = ''' +H3 creates the streams even if they are not used. +See https://github.com/hyperium/h3/pull/242 +''' + + +[[exception]] +target = "https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2" +quote = ''' +An endpoint MAY avoid creating a decoder stream if its decoder sets +the maximum capacity of the dynamic table to zero. +''' +reason = ''' +H3 creates the streams even if they are not used. +See https://github.com/hyperium/h3/pull/242 +''' diff --git a/.duvet/snapshot.txt b/.duvet/snapshot.txt index 9f069074..5a257e08 100644 --- a/.duvet/snapshot.txt +++ b/.duvet/snapshot.txt @@ -865,3 +865,182 @@ SPECIFICATION: https://www.rfc-editor.org/rfc/rfc9114 TEXT[!MUST,exception]: values of N (that is, 0x21, 0x40, ..., through 0x3ffffffffffffffe) TEXT[!MUST,exception]: MUST NOT be assigned by IANA and MUST NOT appear in the listing of TEXT[!MUST,exception]: assigned values. + +SPECIFICATION: https://www.rfc-editor.org/rfc/rfc9204.html + SECTION: [Encoder](#section-2.1) + TEXT[!MAY]: An encoder MAY insert any entry in the dynamic table it chooses; it + TEXT[!MAY]: is not limited to field lines it is compressing. + TEXT[!MUST]: An encoder MUST emit field representations in the order + TEXT[!MUST]: they appear in the input field section. + + SECTION: [Limits on Dynamic Table Insertions](#section-2.1.1) + TEXT[!MUST]: If the dynamic table does not contain enough room for a new entry + TEXT[!MUST]: without evicting other entries, and the entries that would be evicted + TEXT[!MUST]: are not evictable, the encoder MUST NOT insert that entry into the + TEXT[!MUST]: dynamic table (including duplicates of existing entries). + + SECTION: [Blocked Streams](#section-2.1.2) + TEXT[!MUST]: An encoder MUST limit the number of streams that could + TEXT[!MUST]: become blocked to the value of SETTINGS_QPACK_BLOCKED_STREAMS at all + TEXT[!MUST]: times. + TEXT[!MUST]: If a decoder encounters more blocked streams than it promised + TEXT[!MUST]: to support, it MUST treat this as a connection error of type + TEXT[!MUST]: QPACK_DECOMPRESSION_FAILED. + + SECTION: [Avoiding Flow-Control Deadlocks](#section-2.1.3) + TEXT[!SHOULD]: To avoid these deadlocks, an encoder SHOULD NOT write an instruction + TEXT[!SHOULD]: unless sufficient stream and connection flow-control credit is + TEXT[!SHOULD]: available for the entire instruction. + + SECTION: [Decoder](#section-2.2) + TEXT[!MUST]: The decoder MUST emit field lines in the order their representations + TEXT[!MUST]: appear in the encoded field section. + + SECTION: [Blocked Decoding](#section-2.2.1) + TEXT[!SHOULD]: While blocked, encoded field section data SHOULD remain in the + TEXT[!SHOULD]: blocked stream's flow-control window. + TEXT[!MUST]: If it encounters a Required Insert + TEXT[!MUST]: Count smaller than expected, it MUST treat this as a connection error + TEXT[!MUST]: of type QPACK_DECOMPRESSION_FAILED; see Section 2.2.3. + TEXT[!MAY]: If it + TEXT[!MAY]: encounters a Required Insert Count larger than expected, it MAY treat + TEXT[!MAY]: this as a connection error of type QPACK_DECOMPRESSION_FAILED. + + SECTION: [Completed Processing of a Field Section](#section-2.2.2.1) + TEXT[!MUST]: After the decoder finishes decoding a field section encoded using + TEXT[!MUST]: representations containing dynamic table references, it MUST emit a + TEXT[!MUST]: Section Acknowledgment instruction (Section 4.4.1). + + SECTION: [Abandonment of a Stream](#section-2.2.2.2) + TEXT[!MAY]: A decoder with a maximum dynamic table + TEXT[!MAY]: capacity (Section 3.2.3) equal to zero MAY omit sending Stream + TEXT[!MAY]: Cancellations, because the encoder cannot have any dynamic table + TEXT[!MAY]: references. + + SECTION: [Invalid References](#section-2.2.3) + TEXT[!MUST]: If the decoder encounters a reference in a field line representation + TEXT[!MUST]: to a dynamic table entry that has already been evicted or that has an + TEXT[!MUST]: absolute index greater than or equal to the declared Required Insert + TEXT[!MUST]: Count (Section 4.5.1), it MUST treat this as a connection error of + TEXT[!MUST]: type QPACK_DECOMPRESSION_FAILED. + TEXT[!MUST]: If the decoder encounters a reference in an encoder instruction to a + TEXT[!MUST]: dynamic table entry that has already been evicted, it MUST treat this + TEXT[!MUST]: as a connection error of type QPACK_ENCODER_STREAM_ERROR. + + SECTION: [Static Table](#section-3.1) + TEXT[!MUST]: When the decoder encounters an invalid static table index in a field + TEXT[!MUST]: line representation, it MUST treat this as a connection error of type + TEXT[!MUST]: QPACK_DECOMPRESSION_FAILED. + TEXT[!MUST]: If this index is received on the encoder + TEXT[!MUST]: stream, this MUST be treated as a connection error of type + TEXT[!MUST]: QPACK_ENCODER_STREAM_ERROR. + + SECTION: [Dynamic Table](#section-3.2) + TEXT[!MUST]: Therefore, duplicate entries MUST NOT + TEXT[!MUST]: be treated as an error by the decoder. + + SECTION: [Dynamic Table Capacity and Eviction](#section-3.2.2) + TEXT[!MUST]: The + TEXT[!MUST]: encoder MUST NOT cause a dynamic table entry to be evicted unless + TEXT[!MUST]: that entry is evictable; see Section 2.1.1. + TEXT[!MUST]: It is an error if the encoder attempts to add an + TEXT[!MUST]: entry that is larger than the dynamic table capacity; the decoder + TEXT[!MUST]: MUST treat this as a connection error of type + TEXT[!MUST]: QPACK_ENCODER_STREAM_ERROR. + + SECTION: [Maximum Dynamic Table Capacity](#section-3.2.3) + TEXT[!MUST]: The encoder MUST NOT set a dynamic table capacity that exceeds this + TEXT[!MUST]: maximum, but it can choose to use a lower dynamic table capacity; see + TEXT[!MUST]: Section 4.3.1. + TEXT[!MAY]: When the client's 0-RTT value of the + TEXT[!MAY]: SETTING is zero, the server MAY set it to a non-zero value in its + TEXT[!MAY]: SETTINGS frame. + TEXT[!MUST]: If the remembered value is non-zero, the server MUST + TEXT[!MUST]: send the same non-zero value in its SETTINGS frame. + TEXT[!MUST]: When the maximum table capacity is zero, the encoder MUST NOT insert + TEXT[!MUST]: entries into the dynamic table and MUST NOT send any encoder + TEXT[!MUST]: instructions on the encoder stream. + + SECTION: [Prefixed Integers](#section-4.1.1) + TEXT[!MUST,implementation,test]: QPACK implementations MUST be able to decode integers up to and + TEXT[!MUST,implementation,test]: including 62 bits long. + + SECTION: [Encoder and Decoder Streams](#section-4.2) + TEXT[!MUST,implementation]: Each endpoint + TEXT[!MUST,implementation]: MUST initiate, at most, one encoder stream and, at most, one decoder + TEXT[!MUST,implementation]: stream. + TEXT[!MUST,implementation]: Receipt of a second instance of either stream type MUST be + TEXT[!MUST,implementation]: treated as a connection error of type H3_STREAM_CREATION_ERROR. + TEXT[!MUST]: The sender MUST NOT close either of these streams, and the receiver + TEXT[!MUST]: MUST NOT request that the sender close either of these streams. + TEXT[!MUST]: Closure of either unidirectional stream type MUST be treated as a + TEXT[!MUST]: connection error of type H3_CLOSED_CRITICAL_STREAM. + TEXT[!MAY,exception]: An endpoint MAY avoid creating an encoder stream if it will not be + TEXT[!MAY,exception]: used (for example, if its encoder does not wish to use the dynamic + TEXT[!MAY,exception]: table or if the maximum size of the dynamic table permitted by the + TEXT[!MAY,exception]: peer is zero). + TEXT[!MAY,exception]: An endpoint MAY avoid creating a decoder stream if its decoder sets + TEXT[!MAY,exception]: the maximum capacity of the dynamic table to zero. + TEXT[!MUST,implementation]: An endpoint MUST allow its peer to create an encoder stream and a + TEXT[!MUST,implementation]: decoder stream even if the connection's settings prevent their use. + + SECTION: [Set Dynamic Table Capacity](#section-4.3.1) + TEXT[!MUST]: The new capacity MUST be lower than or equal to the limit described + TEXT[!MUST]: in Section 3.2.3. + TEXT[!MUST]: The decoder MUST treat a new dynamic table capacity + TEXT[!MUST]: value that exceeds this limit as a connection error of type + TEXT[!MUST]: QPACK_ENCODER_STREAM_ERROR. + TEXT[!MUST]: This MUST NOT cause the eviction of entries that + TEXT[!MUST]: are not evictable; see Section 2.1.1. + + SECTION: [Section Acknowledgment](#section-4.4.1) + TEXT[!MUST]: If an encoder receives a Section Acknowledgment instruction referring + TEXT[!MUST]: to a stream on which every encoded field section with a non-zero + TEXT[!MUST]: Required Insert Count has already been acknowledged, this MUST be + TEXT[!MUST]: treated as a connection error of type QPACK_DECODER_STREAM_ERROR. + + SECTION: [Insert Count Increment](#section-4.4.3) + TEXT[!MUST]: An encoder that receives an Increment field equal to zero, or one + TEXT[!MUST]: that increases the Known Received Count beyond what the encoder has + TEXT[!MUST]: sent, MUST treat this as a connection error of type + TEXT[!MUST]: QPACK_DECODER_STREAM_ERROR. + + SECTION: [Required Insert Count](#section-4.5.1.1) + TEXT[!MUST]: If the decoder encounters a value + TEXT[!MUST]: of EncodedInsertCount that could not have been produced by a + TEXT[!MUST]: conformant encoder, it MUST treat this as a connection error of type + TEXT[!MUST]: QPACK_DECOMPRESSION_FAILED. + + SECTION: [Base](#section-4.5.1.2) + TEXT[!MUST]: The value of Base MUST NOT be negative. + TEXT[!MUST]: An endpoint MUST treat a field block + TEXT[!MUST]: with a Sign bit of 1 as invalid if the value of Required Insert Count + TEXT[!MUST]: is less than or equal to the value of Delta Base. + + SECTION: [Literal Field Line with Name Reference](#section-4.5.4) + TEXT[!MUST]: When + TEXT[!MUST]: the 'N' bit is set, the encoded field line MUST always be encoded + TEXT[!MUST]: with a literal representation. + TEXT[!MUST]: In particular, when a peer sends a + TEXT[!MUST]: field line that it received represented as a literal field line with + TEXT[!MUST]: the 'N' bit set, it MUST use a literal representation to forward this + TEXT[!MUST]: field line. + + SECTION: [Never-Indexed Literals](#section-7.1.3) + TEXT[!MUST]: An intermediary MUST NOT re-encode a value that uses a literal + TEXT[!MUST]: representation with the 'N' bit set with another representation that + TEXT[!MUST]: would index it. + TEXT[!MUST]: If QPACK is used for re-encoding, a literal + TEXT[!MUST]: representation with the 'N' bit set MUST be used. + TEXT[!MUST]: If HPACK is used + TEXT[!MUST]: for re-encoding, the never-indexed literal representation (see + TEXT[!MUST]: Section 6.2.3 of [RFC7541]) MUST be used. + + SECTION: [Implementation Limits](#section-7.4) + TEXT[!SHOULD]: These limits SHOULD be large + TEXT[!SHOULD]: enough to process the largest individual field the HTTP + TEXT[!SHOULD]: implementation can be configured to accept. + TEXT[!MUST]: If an implementation encounters a value larger than it is able to + TEXT[!MUST]: decode, this MUST be treated as a stream error of type + TEXT[!MUST]: QPACK_DECOMPRESSION_FAILED if on a request stream or a connection + TEXT[!MUST]: error of the appropriate type if on the encoder or decoder stream. diff --git a/.duvet/specifications/www.rfc-editor.org/rfc/rfc9204.txt b/.duvet/specifications/www.rfc-editor.org/rfc/rfc9204.txt new file mode 100644 index 00000000..d9aa0f68 --- /dev/null +++ b/.duvet/specifications/www.rfc-editor.org/rfc/rfc9204.txt @@ -0,0 +1,2133 @@ + + + + +Internet Engineering Task Force (IETF) C. Krasic +Request for Comments: 9204 +Category: Standards Track M. Bishop +ISSN: 2070-1721 Akamai Technologies + A. Frindell, Ed. + Facebook + June 2022 + + + QPACK: Field Compression for HTTP/3 + +Abstract + + This specification defines QPACK: a compression format for + efficiently representing HTTP fields that is to be used in HTTP/3. + This is a variation of HPACK compression that seeks to reduce head- + of-line blocking. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 7841. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + https://www.rfc-editor.org/info/rfc9204. + +Copyright Notice + + Copyright (c) 2022 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Revised BSD License text as described in Section 4.e of the + Trust Legal Provisions and are provided without warranty as described + in the Revised BSD License. + +Table of Contents + + 1. Introduction + 1.1. Conventions and Definitions + 1.2. Notational Conventions + 2. Compression Process Overview + 2.1. Encoder + 2.1.1. Limits on Dynamic Table Insertions + 2.1.2. Blocked Streams + 2.1.3. Avoiding Flow-Control Deadlocks + 2.1.4. Known Received Count + 2.2. Decoder + 2.2.1. Blocked Decoding + 2.2.2. State Synchronization + 2.2.3. Invalid References + 3. Reference Tables + 3.1. Static Table + 3.2. Dynamic Table + 3.2.1. Dynamic Table Size + 3.2.2. Dynamic Table Capacity and Eviction + 3.2.3. Maximum Dynamic Table Capacity + 3.2.4. Absolute Indexing + 3.2.5. Relative Indexing + 3.2.6. Post-Base Indexing + 4. Wire Format + 4.1. Primitives + 4.1.1. Prefixed Integers + 4.1.2. String Literals + 4.2. Encoder and Decoder Streams + 4.3. Encoder Instructions + 4.3.1. Set Dynamic Table Capacity + 4.3.2. Insert with Name Reference + 4.3.3. Insert with Literal Name + 4.3.4. Duplicate + 4.4. Decoder Instructions + 4.4.1. Section Acknowledgment + 4.4.2. Stream Cancellation + 4.4.3. Insert Count Increment + 4.5. Field Line Representations + 4.5.1. Encoded Field Section Prefix + 4.5.2. Indexed Field Line + 4.5.3. Indexed Field Line with Post-Base Index + 4.5.4. Literal Field Line with Name Reference + 4.5.5. Literal Field Line with Post-Base Name Reference + 4.5.6. Literal Field Line with Literal Name + 5. Configuration + 6. Error Handling + 7. Security Considerations + 7.1. Probing Dynamic Table State + 7.1.1. Applicability to QPACK and HTTP + 7.1.2. Mitigation + 7.1.3. Never-Indexed Literals + 7.2. Static Huffman Encoding + 7.3. Memory Consumption + 7.4. Implementation Limits + 8. IANA Considerations + 8.1. Settings Registration + 8.2. Stream Type Registration + 8.3. Error Code Registration + 9. References + 9.1. Normative References + 9.2. Informative References + Appendix A. Static Table + Appendix B. Encoding and Decoding Examples + B.1. Literal Field Line with Name Reference + B.2. Dynamic Table + B.3. Speculative Insert + B.4. Duplicate Instruction, Stream Cancellation + B.5. Dynamic Table Insert, Eviction + Appendix C. Sample Single-Pass Encoding Algorithm + Acknowledgments + Authors' Addresses + +1. Introduction + + The QUIC transport protocol ([QUIC-TRANSPORT]) is designed to support + HTTP semantics, and its design subsumes many of the features of + HTTP/2 ([HTTP/2]). HTTP/2 uses HPACK ([RFC7541]) for compression of + the header and trailer sections. If HPACK were used for HTTP/3 + ([HTTP/3]), it would induce head-of-line blocking for field sections + due to built-in assumptions of a total ordering across frames on all + streams. + + QPACK reuses core concepts from HPACK, but is redesigned to allow + correctness in the presence of out-of-order delivery, with + flexibility for implementations to balance between resilience against + head-of-line blocking and optimal compression ratio. The design + goals are to closely approach the compression ratio of HPACK with + substantially less head-of-line blocking under the same loss + conditions. + +1.1. Conventions and Definitions + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and + "OPTIONAL" in this document are to be interpreted as described in + BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all + capitals, as shown here. + + The following terms are used in this document: + + HTTP fields: Metadata sent as part of an HTTP message. The term + encompasses both header and trailer fields. Colloquially, the + term "headers" has often been used to refer to HTTP header fields + and trailer fields; this document uses "fields" for generality. + + HTTP field line: A name-value pair sent as part of an HTTP field + section. See Sections 6.3 and 6.5 of [HTTP]. + + HTTP field value: Data associated with a field name, composed from + all field line values with that field name in that section, + concatenated together with comma separators. + + Field section: An ordered collection of HTTP field lines associated + with an HTTP message. A field section can contain multiple field + lines with the same name. It can also contain duplicate field + lines. An HTTP message can include both header and trailer + sections. + + Representation: An instruction that represents a field line, + possibly by reference to the dynamic and static tables. + + Encoder: An implementation that encodes field sections. + + Decoder: An implementation that decodes encoded field sections. + + Absolute Index: A unique index for each entry in the dynamic table. + + Base: A reference point for relative and post-Base indices. + Representations that reference dynamic table entries are relative + to a Base. + + Insert Count: The total number of entries inserted in the dynamic + table. + + Note that QPACK is a name, not an abbreviation. + +1.2. Notational Conventions + + Diagrams in this document use the format described in Section 3.1 of + [RFC2360], with the following additional conventions: + + x (A) Indicates that x is A bits long. + + x (A+) Indicates that x uses the prefixed integer encoding defined + in Section 4.1.1, beginning with an A-bit prefix. + + x ... Indicates that x is variable length and extends to the end of + the region. + +2. Compression Process Overview + + Like HPACK, QPACK uses two tables for associating field lines + ("headers") to indices. The static table (Section 3.1) is predefined + and contains common header field lines (some of them with an empty + value). The dynamic table (Section 3.2) is built up over the course + of the connection and can be used by the encoder to index both header + and trailer field lines in the encoded field sections. + + QPACK defines unidirectional streams for sending instructions from + encoder to decoder and vice versa. + +2.1. Encoder + + An encoder converts a header or trailer section into a series of + representations by emitting either an indexed or a literal + representation for each field line in the list; see Section 4.5. + Indexed representations achieve high compression by replacing the + literal name and possibly the value with an index to either the + static or dynamic table. References to the static table and literal + representations do not require any dynamic state and never risk head- + of-line blocking. References to the dynamic table risk head-of-line + blocking if the encoder has not received an acknowledgment indicating + the entry is available at the decoder. + + An encoder MAY insert any entry in the dynamic table it chooses; it + is not limited to field lines it is compressing. + + QPACK preserves the ordering of field lines within each field + section. An encoder MUST emit field representations in the order + they appear in the input field section. + + QPACK is designed to place the burden of optional state tracking on + the encoder, resulting in relatively simple decoders. + +2.1.1. Limits on Dynamic Table Insertions + + Inserting entries into the dynamic table might not be possible if the + table contains entries that cannot be evicted. + + A dynamic table entry cannot be evicted immediately after insertion, + even if it has never been referenced. Once the insertion of a + dynamic table entry has been acknowledged and there are no + outstanding references to the entry in unacknowledged + representations, the entry becomes evictable. Note that references + on the encoder stream never preclude the eviction of an entry, + because those references are guaranteed to be processed before the + instruction evicting the entry. + + If the dynamic table does not contain enough room for a new entry + without evicting other entries, and the entries that would be evicted + are not evictable, the encoder MUST NOT insert that entry into the + dynamic table (including duplicates of existing entries). In order + to avoid this, an encoder that uses the dynamic table has to keep + track of each dynamic table entry referenced by each field section + until those representations are acknowledged by the decoder; see + Section 4.4.1. + +2.1.1.1. Avoiding Prohibited Insertions + + To ensure that the encoder is not prevented from adding new entries, + the encoder can avoid referencing entries that are close to eviction. + Rather than reference such an entry, the encoder can emit a Duplicate + instruction (Section 4.3.4) and reference the duplicate instead. + + Determining which entries are too close to eviction to reference is + an encoder preference. One heuristic is to target a fixed amount of + available space in the dynamic table: either unused space or space + that can be reclaimed by evicting non-blocking entries. To achieve + this, the encoder can maintain a draining index, which is the + smallest absolute index (Section 3.2.4) in the dynamic table that it + will emit a reference for. As new entries are inserted, the encoder + increases the draining index to maintain the section of the table + that it will not reference. If the encoder does not create new + references to entries with an absolute index lower than the draining + index, the number of unacknowledged references to those entries will + eventually become zero, allowing them to be evicted. + + <-- Newer Entries Older Entries --> + (Larger Indices) (Smaller Indices) + +--------+---------------------------------+----------+ + | Unused | Referenceable | Draining | + | Space | Entries | Entries | + +--------+---------------------------------+----------+ + ^ ^ ^ + | | | + Insertion Point Draining Index Dropping + Point + + Figure 1: Draining Dynamic Table Entries + +2.1.2. Blocked Streams + + Because QUIC does not guarantee order between data on different + streams, a decoder might encounter a representation that references a + dynamic table entry that it has not yet received. + + Each encoded field section contains a Required Insert Count + (Section 4.5.1), the lowest possible value for the Insert Count with + which the field section can be decoded. For a field section encoded + using references to the dynamic table, the Required Insert Count is + one larger than the largest absolute index of all referenced dynamic + table entries. For a field section encoded with no references to the + dynamic table, the Required Insert Count is zero. + + When the decoder receives an encoded field section with a Required + Insert Count greater than its own Insert Count, the stream cannot be + processed immediately and is considered "blocked"; see Section 2.2.1. + + The decoder specifies an upper bound on the number of streams that + can be blocked using the SETTINGS_QPACK_BLOCKED_STREAMS setting; see + Section 5. An encoder MUST limit the number of streams that could + become blocked to the value of SETTINGS_QPACK_BLOCKED_STREAMS at all + times. If a decoder encounters more blocked streams than it promised + to support, it MUST treat this as a connection error of type + QPACK_DECOMPRESSION_FAILED. + + Note that the decoder might not become blocked on every stream that + risks becoming blocked. + + An encoder can decide whether to risk having a stream become blocked. + If permitted by the value of SETTINGS_QPACK_BLOCKED_STREAMS, + compression efficiency can often be improved by referencing dynamic + table entries that are still in transit, but if there is loss or + reordering, the stream can become blocked at the decoder. An encoder + can avoid the risk of blocking by only referencing dynamic table + entries that have been acknowledged, but this could mean using + literals. Since literals make the encoded field section larger, this + can result in the encoder becoming blocked on congestion or flow- + control limits. + +2.1.3. Avoiding Flow-Control Deadlocks + + Writing instructions on streams that are limited by flow control can + produce deadlocks. + + A decoder might stop issuing flow-control credit on the stream that + carries an encoded field section until the necessary updates are + received on the encoder stream. If the granting of flow-control + credit on the encoder stream (or the connection as a whole) depends + on the consumption and release of data on the stream carrying the + encoded field section, a deadlock might result. + + More generally, a stream containing a large instruction can become + deadlocked if the decoder withholds flow-control credit until the + instruction is completely received. + + To avoid these deadlocks, an encoder SHOULD NOT write an instruction + unless sufficient stream and connection flow-control credit is + available for the entire instruction. + +2.1.4. Known Received Count + + The Known Received Count is the total number of dynamic table + insertions and duplications acknowledged by the decoder. The encoder + tracks the Known Received Count in order to identify which dynamic + table entries can be referenced without potentially blocking a + stream. The decoder tracks the Known Received Count in order to be + able to send Insert Count Increment instructions. + + A Section Acknowledgment instruction (Section 4.4.1) implies that the + decoder has received all dynamic table state necessary to decode the + field section. If the Required Insert Count of the acknowledged + field section is greater than the current Known Received Count, the + Known Received Count is updated to that Required Insert Count value. + + An Insert Count Increment instruction (Section 4.4.3) increases the + Known Received Count by its Increment parameter. See Section 2.2.2.3 + for guidance. + +2.2. Decoder + + As in HPACK, the decoder processes a series of representations and + emits the corresponding field sections. It also processes + instructions received on the encoder stream that modify the dynamic + table. Note that encoded field sections and encoder stream + instructions arrive on separate streams. This is unlike HPACK, where + encoded field sections (header blocks) can contain instructions that + modify the dynamic table, and there is no dedicated stream of HPACK + instructions. + + The decoder MUST emit field lines in the order their representations + appear in the encoded field section. + +2.2.1. Blocked Decoding + + Upon receipt of an encoded field section, the decoder examines the + Required Insert Count. When the Required Insert Count is less than + or equal to the decoder's Insert Count, the field section can be + processed immediately. Otherwise, the stream on which the field + section was received becomes blocked. + + While blocked, encoded field section data SHOULD remain in the + blocked stream's flow-control window. This data is unusable until + the stream becomes unblocked, and releasing the flow control + prematurely makes the decoder vulnerable to memory exhaustion + attacks. A stream becomes unblocked when the Insert Count becomes + greater than or equal to the Required Insert Count for all encoded + field sections the decoder has started reading from the stream. + + When processing encoded field sections, the decoder expects the + Required Insert Count to equal the lowest possible value for the + Insert Count with which the field section can be decoded, as + prescribed in Section 2.1.2. If it encounters a Required Insert + Count smaller than expected, it MUST treat this as a connection error + of type QPACK_DECOMPRESSION_FAILED; see Section 2.2.3. If it + encounters a Required Insert Count larger than expected, it MAY treat + this as a connection error of type QPACK_DECOMPRESSION_FAILED. + +2.2.2. State Synchronization + + The decoder signals the following events by emitting decoder + instructions (Section 4.4) on the decoder stream. + +2.2.2.1. Completed Processing of a Field Section + + After the decoder finishes decoding a field section encoded using + representations containing dynamic table references, it MUST emit a + Section Acknowledgment instruction (Section 4.4.1). A stream may + carry multiple field sections in the case of intermediate responses, + trailers, and pushed requests. The encoder interprets each + Section Acknowledgment instruction as acknowledging the earliest + unacknowledged field section containing dynamic table references sent + on the given stream. + +2.2.2.2. Abandonment of a Stream + + When an endpoint receives a stream reset before the end of a stream + or before all encoded field sections are processed on that stream, or + when it abandons reading of a stream, it generates a Stream + Cancellation instruction; see Section 4.4.2. This signals to the + encoder that all references to the dynamic table on that stream are + no longer outstanding. A decoder with a maximum dynamic table + capacity (Section 3.2.3) equal to zero MAY omit sending Stream + Cancellations, because the encoder cannot have any dynamic table + references. An encoder cannot infer from this instruction that any + updates to the dynamic table have been received. + + The Section Acknowledgment and Stream Cancellation instructions + permit the encoder to remove references to entries in the dynamic + table. When an entry with an absolute index lower than the Known + Received Count has zero references, then it is considered evictable; + see Section 2.1.1. + +2.2.2.3. New Table Entries + + After receiving new table entries on the encoder stream, the decoder + chooses when to emit Insert Count Increment instructions; see + Section 4.4.3. Emitting this instruction after adding each new + dynamic table entry will provide the timeliest feedback to the + encoder, but could be redundant with other decoder feedback. By + delaying an Insert Count Increment instruction, the decoder might be + able to coalesce multiple Insert Count Increment instructions or + replace them entirely with Section Acknowledgments; see + Section 4.4.1. However, delaying too long may lead to compression + inefficiencies if the encoder waits for an entry to be acknowledged + before using it. + +2.2.3. Invalid References + + If the decoder encounters a reference in a field line representation + to a dynamic table entry that has already been evicted or that has an + absolute index greater than or equal to the declared Required Insert + Count (Section 4.5.1), it MUST treat this as a connection error of + type QPACK_DECOMPRESSION_FAILED. + + If the decoder encounters a reference in an encoder instruction to a + dynamic table entry that has already been evicted, it MUST treat this + as a connection error of type QPACK_ENCODER_STREAM_ERROR. + +3. Reference Tables + + Unlike in HPACK, entries in the QPACK static and dynamic tables are + addressed separately. The following sections describe how entries in + each table are addressed. + +3.1. Static Table + + The static table consists of a predefined list of field lines, each + of which has a fixed index over time. Its entries are defined in + Appendix A. + + All entries in the static table have a name and a value. However, + values can be empty (that is, have a length of 0). Each entry is + identified by a unique index. + + Note that the QPACK static table is indexed from 0, whereas the HPACK + static table is indexed from 1. + + When the decoder encounters an invalid static table index in a field + line representation, it MUST treat this as a connection error of type + QPACK_DECOMPRESSION_FAILED. If this index is received on the encoder + stream, this MUST be treated as a connection error of type + QPACK_ENCODER_STREAM_ERROR. + +3.2. Dynamic Table + + The dynamic table consists of a list of field lines maintained in + first-in, first-out order. A QPACK encoder and decoder share a + dynamic table that is initially empty. The encoder adds entries to + the dynamic table and sends them to the decoder via instructions on + the encoder stream; see Section 4.3. + + The dynamic table can contain duplicate entries (i.e., entries with + the same name and same value). Therefore, duplicate entries MUST NOT + be treated as an error by the decoder. + + Dynamic table entries can have empty values. + +3.2.1. Dynamic Table Size + + The size of the dynamic table is the sum of the size of its entries. + + The size of an entry is the sum of its name's length in bytes, its + value's length in bytes, and 32 additional bytes. The size of an + entry is calculated using the length of its name and value without + Huffman encoding applied. + +3.2.2. Dynamic Table Capacity and Eviction + + The encoder sets the capacity of the dynamic table, which serves as + the upper limit on its size. The initial capacity of the dynamic + table is zero. The encoder sends a Set Dynamic Table Capacity + instruction (Section 4.3.1) with a non-zero capacity to begin using + the dynamic table. + + Before a new entry is added to the dynamic table, entries are evicted + from the end of the dynamic table until the size of the dynamic table + is less than or equal to (table capacity - size of new entry). The + encoder MUST NOT cause a dynamic table entry to be evicted unless + that entry is evictable; see Section 2.1.1. The new entry is then + added to the table. It is an error if the encoder attempts to add an + entry that is larger than the dynamic table capacity; the decoder + MUST treat this as a connection error of type + QPACK_ENCODER_STREAM_ERROR. + + A new entry can reference an entry in the dynamic table that will be + evicted when adding this new entry into the dynamic table. + Implementations are cautioned to avoid deleting the referenced name + or value if the referenced entry is evicted from the dynamic table + prior to inserting the new entry. + + Whenever the dynamic table capacity is reduced by the encoder + (Section 4.3.1), entries are evicted from the end of the dynamic + table until the size of the dynamic table is less than or equal to + the new table capacity. This mechanism can be used to completely + clear entries from the dynamic table by setting a capacity of 0, + which can subsequently be restored. + +3.2.3. Maximum Dynamic Table Capacity + + To bound the memory requirements of the decoder, the decoder limits + the maximum value the encoder is permitted to set for the dynamic + table capacity. In HTTP/3, this limit is determined by the value of + SETTINGS_QPACK_MAX_TABLE_CAPACITY sent by the decoder; see Section 5. + The encoder MUST NOT set a dynamic table capacity that exceeds this + maximum, but it can choose to use a lower dynamic table capacity; see + Section 4.3.1. + + For clients using 0-RTT data in HTTP/3, the server's maximum table + capacity is the remembered value of the setting or zero if the value + was not previously sent. When the client's 0-RTT value of the + SETTING is zero, the server MAY set it to a non-zero value in its + SETTINGS frame. If the remembered value is non-zero, the server MUST + send the same non-zero value in its SETTINGS frame. If it specifies + any other value, or omits SETTINGS_QPACK_MAX_TABLE_CAPACITY from + SETTINGS, the encoder must treat this as a connection error of type + QPACK_DECODER_STREAM_ERROR. + + For clients not using 0-RTT data (whether 0-RTT is not attempted or + is rejected) and for all HTTP/3 servers, the maximum table capacity + is 0 until the encoder processes a SETTINGS frame with a non-zero + value of SETTINGS_QPACK_MAX_TABLE_CAPACITY. + + When the maximum table capacity is zero, the encoder MUST NOT insert + entries into the dynamic table and MUST NOT send any encoder + instructions on the encoder stream. + +3.2.4. Absolute Indexing + + Each entry possesses an absolute index that is fixed for the lifetime + of that entry. The first entry inserted has an absolute index of 0; + indices increase by one with each insertion. + +3.2.5. Relative Indexing + + Relative indices begin at zero and increase in the opposite direction + from the absolute index. Determining which entry has a relative + index of 0 depends on the context of the reference. + + In encoder instructions (Section 4.3), a relative index of 0 refers + to the most recently inserted value in the dynamic table. Note that + this means the entry referenced by a given relative index will change + while interpreting instructions on the encoder stream. + + +-----+---------------+-------+ + | n-1 | ... | d | Absolute Index + + - - +---------------+ - - - + + | 0 | ... | n-d-1 | Relative Index + +-----+---------------+-------+ + ^ | + | V + Insertion Point Dropping Point + + n = count of entries inserted + d = count of entries dropped + + Figure 2: Example Dynamic Table Indexing - Encoder Stream + + Unlike in encoder instructions, relative indices in field line + representations are relative to the Base at the beginning of the + encoded field section; see Section 4.5.1. This ensures that + references are stable even if encoded field sections and dynamic + table updates are processed out of order. + + In a field line representation, a relative index of 0 refers to the + entry with absolute index equal to Base - 1. + + Base + | + V + +-----+-----+-----+-----+-------+ + | n-1 | n-2 | n-3 | ... | d | Absolute Index + +-----+-----+ - +-----+ - + + | 0 | ... | n-d-3 | Relative Index + +-----+-----+-------+ + + n = count of entries inserted + d = count of entries dropped + In this example, Base = n - 2 + + Figure 3: Example Dynamic Table Indexing - Relative Index in + Representation + +3.2.6. Post-Base Indexing + + Post-Base indices are used in field line representations for entries + with absolute indices greater than or equal to Base, starting at 0 + for the entry with absolute index equal to Base and increasing in the + same direction as the absolute index. + + Post-Base indices allow an encoder to process a field section in a + single pass and include references to entries added while processing + this (or other) field sections. + + Base + | + V + +-----+-----+-----+-----+-----+ + | n-1 | n-2 | n-3 | ... | d | Absolute Index + +-----+-----+-----+-----+-----+ + | 1 | 0 | Post-Base Index + +-----+-----+ + + n = count of entries inserted + d = count of entries dropped + In this example, Base = n - 2 + + Figure 4: Example Dynamic Table Indexing - Post-Base Index in + Representation + +4. Wire Format + +4.1. Primitives + +4.1.1. Prefixed Integers + + The prefixed integer from Section 5.1 of [RFC7541] is used heavily + throughout this document. The format from [RFC7541] is used + unmodified. Note, however, that QPACK uses some prefix sizes not + actually used in HPACK. + + QPACK implementations MUST be able to decode integers up to and + including 62 bits long. + +4.1.2. String Literals + + The string literal defined by Section 5.2 of [RFC7541] is also used + throughout. This string format includes optional Huffman encoding. + + HPACK defines string literals to begin on a byte boundary. They + begin with a single bit flag, denoted as 'H' in this document + (indicating whether the string is Huffman encoded), followed by the + string length encoded as a 7-bit prefix integer, and finally the + indicated number of bytes of data. When Huffman encoding is enabled, + the Huffman table from Appendix B of [RFC7541] is used without + modification and the indicated length is the size of the string after + encoding. + + This document expands the definition of string literals by permitting + them to begin other than on a byte boundary. An "N-bit prefix string + literal" begins mid-byte, with the first (8-N) bits allocated to a + previous field. The string uses one bit for the Huffman flag, + followed by the length of the encoded string as a (N-1)-bit prefix + integer. The prefix size, N, can have a value between 2 and 8, + inclusive. The remainder of the string literal is unmodified. + + A string literal without a prefix length noted is an 8-bit prefix + string literal and follows the definitions in [RFC7541] without + modification. + +4.2. Encoder and Decoder Streams + + QPACK defines two unidirectional stream types: + + * An encoder stream is a unidirectional stream of type 0x02. It + carries an unframed sequence of encoder instructions from encoder + to decoder. + + * A decoder stream is a unidirectional stream of type 0x03. It + carries an unframed sequence of decoder instructions from decoder + to encoder. + + HTTP/3 endpoints contain a QPACK encoder and decoder. Each endpoint + MUST initiate, at most, one encoder stream and, at most, one decoder + stream. Receipt of a second instance of either stream type MUST be + treated as a connection error of type H3_STREAM_CREATION_ERROR. + + The sender MUST NOT close either of these streams, and the receiver + MUST NOT request that the sender close either of these streams. + Closure of either unidirectional stream type MUST be treated as a + connection error of type H3_CLOSED_CRITICAL_STREAM. + + An endpoint MAY avoid creating an encoder stream if it will not be + used (for example, if its encoder does not wish to use the dynamic + table or if the maximum size of the dynamic table permitted by the + peer is zero). + + An endpoint MAY avoid creating a decoder stream if its decoder sets + the maximum capacity of the dynamic table to zero. + + An endpoint MUST allow its peer to create an encoder stream and a + decoder stream even if the connection's settings prevent their use. + +4.3. Encoder Instructions + + An encoder sends encoder instructions on the encoder stream to set + the capacity of the dynamic table and add dynamic table entries. + Instructions adding table entries can use existing entries to avoid + transmitting redundant information. The name can be transmitted as a + reference to an existing entry in the static or the dynamic table or + as a string literal. For entries that already exist in the dynamic + table, the full entry can also be used by reference, creating a + duplicate entry. + +4.3.1. Set Dynamic Table Capacity + + An encoder informs the decoder of a change to the dynamic table + capacity using an instruction that starts with the '001' 3-bit + pattern. This is followed by the new dynamic table capacity + represented as an integer with a 5-bit prefix; see Section 4.1.1. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 1 | Capacity (5+) | + +---+---+---+-------------------+ + + Figure 5: Set Dynamic Table Capacity + + The new capacity MUST be lower than or equal to the limit described + in Section 3.2.3. In HTTP/3, this limit is the value of the + SETTINGS_QPACK_MAX_TABLE_CAPACITY parameter (Section 5) received from + the decoder. The decoder MUST treat a new dynamic table capacity + value that exceeds this limit as a connection error of type + QPACK_ENCODER_STREAM_ERROR. + + Reducing the dynamic table capacity can cause entries to be evicted; + see Section 3.2.2. This MUST NOT cause the eviction of entries that + are not evictable; see Section 2.1.1. Changing the capacity of the + dynamic table is not acknowledged as this instruction does not insert + an entry. + +4.3.2. Insert with Name Reference + + An encoder adds an entry to the dynamic table where the field name + matches the field name of an entry stored in the static or the + dynamic table using an instruction that starts with the '1' 1-bit + pattern. The second ('T') bit indicates whether the reference is to + the static or dynamic table. The 6-bit prefix integer + (Section 4.1.1) that follows is used to locate the table entry for + the field name. When T=1, the number represents the static table + index; when T=0, the number is the relative index of the entry in the + dynamic table. + + The field name reference is followed by the field value represented + as a string literal; see Section 4.1.2. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 1 | T | Name Index (6+) | + +---+---+-----------------------+ + | H | Value Length (7+) | + +---+---------------------------+ + | Value String (Length bytes) | + +-------------------------------+ + + Figure 6: Insert Field Line -- Indexed Name + +4.3.3. Insert with Literal Name + + An encoder adds an entry to the dynamic table where both the field + name and the field value are represented as string literals using an + instruction that starts with the '01' 2-bit pattern. + + This is followed by the name represented as a 6-bit prefix string + literal and the value represented as an 8-bit prefix string literal; + see Section 4.1.2. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 1 | H | Name Length (5+) | + +---+---+---+-------------------+ + | Name String (Length bytes) | + +---+---------------------------+ + | H | Value Length (7+) | + +---+---------------------------+ + | Value String (Length bytes) | + +-------------------------------+ + + Figure 7: Insert Field Line -- New Name + +4.3.4. Duplicate + + An encoder duplicates an existing entry in the dynamic table using an + instruction that starts with the '000' 3-bit pattern. This is + followed by the relative index of the existing entry represented as + an integer with a 5-bit prefix; see Section 4.1.1. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 0 | Index (5+) | + +---+---+---+-------------------+ + + Figure 8: Duplicate + + The existing entry is reinserted into the dynamic table without + resending either the name or the value. This is useful to avoid + adding a reference to an older entry, which might block inserting new + entries. + +4.4. Decoder Instructions + + A decoder sends decoder instructions on the decoder stream to inform + the encoder about the processing of field sections and table updates + to ensure consistency of the dynamic table. + +4.4.1. Section Acknowledgment + + After processing an encoded field section whose declared Required + Insert Count is not zero, the decoder emits a Section Acknowledgment + instruction. The instruction starts with the '1' 1-bit pattern, + followed by the field section's associated stream ID encoded as a + 7-bit prefix integer; see Section 4.1.1. + + This instruction is used as described in Sections 2.1.4 and 2.2.2. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 1 | Stream ID (7+) | + +---+---------------------------+ + + Figure 9: Section Acknowledgment + + If an encoder receives a Section Acknowledgment instruction referring + to a stream on which every encoded field section with a non-zero + Required Insert Count has already been acknowledged, this MUST be + treated as a connection error of type QPACK_DECODER_STREAM_ERROR. + + The Section Acknowledgment instruction might increase the Known + Received Count; see Section 2.1.4. + +4.4.2. Stream Cancellation + + When a stream is reset or reading is abandoned, the decoder emits a + Stream Cancellation instruction. The instruction starts with the + '01' 2-bit pattern, followed by the stream ID of the affected stream + encoded as a 6-bit prefix integer. + + This instruction is used as described in Section 2.2.2. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 1 | Stream ID (6+) | + +---+---+-----------------------+ + + Figure 10: Stream Cancellation + +4.4.3. Insert Count Increment + + The Insert Count Increment instruction starts with the '00' 2-bit + pattern, followed by the Increment encoded as a 6-bit prefix integer. + This instruction increases the Known Received Count (Section 2.1.4) + by the value of the Increment parameter. The decoder should send an + Increment value that increases the Known Received Count to the total + number of dynamic table insertions and duplications processed so far. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | Increment (6+) | + +---+---+-----------------------+ + + Figure 11: Insert Count Increment + + An encoder that receives an Increment field equal to zero, or one + that increases the Known Received Count beyond what the encoder has + sent, MUST treat this as a connection error of type + QPACK_DECODER_STREAM_ERROR. + +4.5. Field Line Representations + + An encoded field section consists of a prefix and a possibly empty + sequence of representations defined in this section. Each + representation corresponds to a single field line. These + representations reference the static table or the dynamic table in a + particular state, but they do not modify that state. + + Encoded field sections are carried in frames on streams defined by + the enclosing protocol. + +4.5.1. Encoded Field Section Prefix + + Each encoded field section is prefixed with two integers. The + Required Insert Count is encoded as an integer with an 8-bit prefix + using the encoding described in Section 4.5.1.1. The Base is encoded + as a Sign bit ('S') and a Delta Base value with a 7-bit prefix; see + Section 4.5.1.2. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | Required Insert Count (8+) | + +---+---------------------------+ + | S | Delta Base (7+) | + +---+---------------------------+ + | Encoded Field Lines ... + +-------------------------------+ + + Figure 12: Encoded Field Section + +4.5.1.1. Required Insert Count + + Required Insert Count identifies the state of the dynamic table + needed to process the encoded field section. Blocking decoders use + the Required Insert Count to determine when it is safe to process the + rest of the field section. + + The encoder transforms the Required Insert Count as follows before + encoding: + + if ReqInsertCount == 0: + EncInsertCount = 0 + else: + EncInsertCount = (ReqInsertCount mod (2 * MaxEntries)) + 1 + + Here MaxEntries is the maximum number of entries that the dynamic + table can have. The smallest entry has empty name and value strings + and has the size of 32. Hence, MaxEntries is calculated as: + + MaxEntries = floor( MaxTableCapacity / 32 ) + + MaxTableCapacity is the maximum capacity of the dynamic table as + specified by the decoder; see Section 3.2.3. + + This encoding limits the length of the prefix on long-lived + connections. + + The decoder can reconstruct the Required Insert Count using an + algorithm such as the following. If the decoder encounters a value + of EncodedInsertCount that could not have been produced by a + conformant encoder, it MUST treat this as a connection error of type + QPACK_DECOMPRESSION_FAILED. + + TotalNumberOfInserts is the total number of inserts into the + decoder's dynamic table. + + FullRange = 2 * MaxEntries + if EncodedInsertCount == 0: + ReqInsertCount = 0 + else: + if EncodedInsertCount > FullRange: + Error + MaxValue = TotalNumberOfInserts + MaxEntries + + # MaxWrapped is the largest possible value of + # ReqInsertCount that is 0 mod 2 * MaxEntries + MaxWrapped = floor(MaxValue / FullRange) * FullRange + ReqInsertCount = MaxWrapped + EncodedInsertCount - 1 + + # If ReqInsertCount exceeds MaxValue, the Encoder's value + # must have wrapped one fewer time + if ReqInsertCount > MaxValue: + if ReqInsertCount <= FullRange: + Error + ReqInsertCount -= FullRange + + # Value of 0 must be encoded as 0. + if ReqInsertCount == 0: + Error + + For example, if the dynamic table is 100 bytes, then the Required + Insert Count will be encoded modulo 6. If a decoder has received 10 + inserts, then an encoded value of 4 indicates that the Required + Insert Count is 9 for the field section. + +4.5.1.2. Base + + The Base is used to resolve references in the dynamic table as + described in Section 3.2.5. + + To save space, the Base is encoded relative to the Required Insert + Count using a one-bit Sign ('S' in Figure 12) and the Delta Base + value. A Sign bit of 0 indicates that the Base is greater than or + equal to the value of the Required Insert Count; the decoder adds the + value of Delta Base to the Required Insert Count to determine the + value of the Base. A Sign bit of 1 indicates that the Base is less + than the Required Insert Count; the decoder subtracts the value of + Delta Base from the Required Insert Count and also subtracts one to + determine the value of the Base. That is: + + if Sign == 0: + Base = ReqInsertCount + DeltaBase + else: + Base = ReqInsertCount - DeltaBase - 1 + + A single-pass encoder determines the Base before encoding a field + section. If the encoder inserted entries in the dynamic table while + encoding the field section and is referencing them, Required Insert + Count will be greater than the Base, so the encoded difference is + negative and the Sign bit is set to 1. If the field section was not + encoded using representations that reference the most recent entry in + the table and did not insert any new entries, the Base will be + greater than the Required Insert Count, so the encoded difference + will be positive and the Sign bit is set to 0. + + The value of Base MUST NOT be negative. Though the protocol might + operate correctly with a negative Base using post-Base indexing, it + is unnecessary and inefficient. An endpoint MUST treat a field block + with a Sign bit of 1 as invalid if the value of Required Insert Count + is less than or equal to the value of Delta Base. + + An encoder that produces table updates before encoding a field + section might set Base to the value of Required Insert Count. In + such a case, both the Sign bit and the Delta Base will be set to + zero. + + A field section that was encoded without references to the dynamic + table can use any value for the Base; setting Delta Base to zero is + one of the most efficient encodings. + + For example, with a Required Insert Count of 9, a decoder receives a + Sign bit of 1 and a Delta Base of 2. This sets the Base to 6 and + enables post-Base indexing for three entries. In this example, a + relative index of 1 refers to the fifth entry that was added to the + table; a post-Base index of 1 refers to the eighth entry. + +4.5.2. Indexed Field Line + + An indexed field line representation identifies an entry in the + static table or an entry in the dynamic table with an absolute index + less than the value of the Base. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 1 | T | Index (6+) | + +---+---+-----------------------+ + + Figure 13: Indexed Field Line + + This representation starts with the '1' 1-bit pattern, followed by + the 'T' bit, indicating whether the reference is into the static or + dynamic table. The 6-bit prefix integer (Section 4.1.1) that follows + is used to locate the table entry for the field line. When T=1, the + number represents the static table index; when T=0, the number is the + relative index of the entry in the dynamic table. + +4.5.3. Indexed Field Line with Post-Base Index + + An indexed field line with post-Base index representation identifies + an entry in the dynamic table with an absolute index greater than or + equal to the value of the Base. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 0 | 1 | Index (4+) | + +---+---+---+---+---------------+ + + Figure 14: Indexed Field Line with Post-Base Index + + This representation starts with the '0001' 4-bit pattern. This is + followed by the post-Base index (Section 3.2.6) of the matching field + line, represented as an integer with a 4-bit prefix; see + Section 4.1.1. + +4.5.4. Literal Field Line with Name Reference + + A literal field line with name reference representation encodes a + field line where the field name matches the field name of an entry in + the static table or the field name of an entry in the dynamic table + with an absolute index less than the value of the Base. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 1 | N | T |Name Index (4+)| + +---+---+---+---+---------------+ + | H | Value Length (7+) | + +---+---------------------------+ + | Value String (Length bytes) | + +-------------------------------+ + + Figure 15: Literal Field Line with Name Reference + + This representation starts with the '01' 2-bit pattern. The + following bit, 'N', indicates whether an intermediary is permitted to + add this field line to the dynamic table on subsequent hops. When + the 'N' bit is set, the encoded field line MUST always be encoded + with a literal representation. In particular, when a peer sends a + field line that it received represented as a literal field line with + the 'N' bit set, it MUST use a literal representation to forward this + field line. This bit is intended for protecting field values that + are not to be put at risk by compressing them; see Section 7.1 for + more details. + + The fourth ('T') bit indicates whether the reference is to the static + or dynamic table. The 4-bit prefix integer (Section 4.1.1) that + follows is used to locate the table entry for the field name. When + T=1, the number represents the static table index; when T=0, the + number is the relative index of the entry in the dynamic table. + + Only the field name is taken from the dynamic table entry; the field + value is encoded as an 8-bit prefix string literal; see + Section 4.1.2. + +4.5.5. Literal Field Line with Post-Base Name Reference + + A literal field line with post-Base name reference representation + encodes a field line where the field name matches the field name of a + dynamic table entry with an absolute index greater than or equal to + the value of the Base. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 0 | 0 | N |NameIdx(3+)| + +---+---+---+---+---+-----------+ + | H | Value Length (7+) | + +---+---------------------------+ + | Value String (Length bytes) | + +-------------------------------+ + + Figure 16: Literal Field Line with Post-Base Name Reference + + This representation starts with the '0000' 4-bit pattern. The fifth + bit is the 'N' bit as described in Section 4.5.4. This is followed + by a post-Base index of the dynamic table entry (Section 3.2.6) + encoded as an integer with a 3-bit prefix; see Section 4.1.1. + + Only the field name is taken from the dynamic table entry; the field + value is encoded as an 8-bit prefix string literal; see + Section 4.1.2. + +4.5.6. Literal Field Line with Literal Name + + The literal field line with literal name representation encodes a + field name and a field value as string literals. + + 0 1 2 3 4 5 6 7 + +---+---+---+---+---+---+---+---+ + | 0 | 0 | 1 | N | H |NameLen(3+)| + +---+---+---+---+---+-----------+ + | Name String (Length bytes) | + +---+---------------------------+ + | H | Value Length (7+) | + +---+---------------------------+ + | Value String (Length bytes) | + +-------------------------------+ + + Figure 17: Literal Field Line with Literal Name + + This representation starts with the '001' 3-bit pattern. The fourth + bit is the 'N' bit as described in Section 4.5.4. The name follows, + represented as a 4-bit prefix string literal, then the value, + represented as an 8-bit prefix string literal; see Section 4.1.2. + +5. Configuration + + QPACK defines two settings for the HTTP/3 SETTINGS frame: + + SETTINGS_QPACK_MAX_TABLE_CAPACITY (0x01): The default value is zero. + See Section 3.2 for usage. This is the equivalent of the + SETTINGS_HEADER_TABLE_SIZE from HTTP/2. + + SETTINGS_QPACK_BLOCKED_STREAMS (0x07): The default value is zero. + See Section 2.1.2. + +6. Error Handling + + The following error codes are defined for HTTP/3 to indicate failures + of QPACK that prevent the stream or connection from continuing: + + QPACK_DECOMPRESSION_FAILED (0x0200): The decoder failed to interpret + an encoded field section and is not able to continue decoding that + field section. + + QPACK_ENCODER_STREAM_ERROR (0x0201): The decoder failed to interpret + an encoder instruction received on the encoder stream. + + QPACK_DECODER_STREAM_ERROR (0x0202): The encoder failed to interpret + a decoder instruction received on the decoder stream. + +7. Security Considerations + + This section describes potential areas of security concern with + QPACK: + + * Use of compression as a length-based oracle for verifying guesses + about secrets that are compressed into a shared compression + context. + + * Denial of service resulting from exhausting processing or memory + capacity at a decoder. + +7.1. Probing Dynamic Table State + + QPACK reduces the encoded size of field sections by exploiting the + redundancy inherent in protocols like HTTP. The ultimate goal of + this is to reduce the amount of data that is required to send HTTP + requests or responses. + + The compression context used to encode header and trailer fields can + be probed by an attacker who can both define fields to be encoded and + transmitted and observe the length of those fields once they are + encoded. When an attacker can do both, they can adaptively modify + requests in order to confirm guesses about the dynamic table state. + If a guess is compressed into a shorter length, the attacker can + observe the encoded length and infer that the guess was correct. + + This is possible even over the Transport Layer Security Protocol + ([TLS]) and the QUIC Transport Protocol ([QUIC-TRANSPORT]), because + while TLS and QUIC provide confidentiality protection for content, + they only provide a limited amount of protection for the length of + that content. + + | Note: Padding schemes only provide limited protection against + | an attacker with these capabilities, potentially only forcing + | an increased number of guesses to learn the length associated + | with a given guess. Padding schemes also work directly against + | compression by increasing the number of bits that are + | transmitted. + + Attacks like CRIME ([CRIME]) demonstrated the existence of these + general attacker capabilities. The specific attack exploited the + fact that DEFLATE ([RFC1951]) removes redundancy based on prefix + matching. This permitted the attacker to confirm guesses a character + at a time, reducing an exponential-time attack into a linear-time + attack. + +7.1.1. Applicability to QPACK and HTTP + + QPACK mitigates, but does not completely prevent, attacks modeled on + CRIME ([CRIME]) by forcing a guess to match an entire field line + rather than individual characters. An attacker can only learn + whether a guess is correct or not, so the attacker is reduced to a + brute-force guess for the field values associated with a given field + name. + + Therefore, the viability of recovering specific field values depends + on the entropy of values. As a result, values with high entropy are + unlikely to be recovered successfully. However, values with low + entropy remain vulnerable. + + Attacks of this nature are possible any time that two mutually + distrustful entities control requests or responses that are placed + onto a single HTTP/3 connection. If the shared QPACK compressor + permits one entity to add entries to the dynamic table, and the other + to refer to those entries while encoding chosen field lines, then the + attacker (the second entity) can learn the state of the table by + observing the length of the encoded output. + + For example, requests or responses from mutually distrustful entities + can occur when an intermediary either: + + * sends requests from multiple clients on a single connection toward + an origin server, or + + * takes responses from multiple origin servers and places them on a + shared connection toward a client. + + Web browsers also need to assume that requests made on the same + connection by different web origins ([RFC6454]) are made by mutually + distrustful entities. Other scenarios involving mutually distrustful + entities are also possible. + +7.1.2. Mitigation + + Users of HTTP that require confidentiality for header or trailer + fields can use values with entropy sufficient to make guessing + infeasible. However, this is impractical as a general solution + because it forces all users of HTTP to take steps to mitigate + attacks. It would impose new constraints on how HTTP is used. + + Rather than impose constraints on users of HTTP, an implementation of + QPACK can instead constrain how compression is applied in order to + limit the potential for dynamic table probing. + + An ideal solution segregates access to the dynamic table based on the + entity that is constructing the message. Field values that are added + to the table are attributed to an entity, and only the entity that + created a particular value can extract that value. + + To improve compression performance of this option, certain entries + might be tagged as being public. For example, a web browser might + make the values of the Accept-Encoding header field available in all + requests. + + An encoder without good knowledge of the provenance of field values + might instead introduce a penalty for many field lines with the same + field name and different values. This penalty could cause a large + number of attempts to guess a field value to result in the field not + being compared to the dynamic table entries in future messages, + effectively preventing further guesses. + + This response might be made inversely proportional to the length of + the field value. Disabling access to the dynamic table for a given + field name might occur for shorter values more quickly or with higher + probability than for longer values. + + This mitigation is most effective between two endpoints. If messages + are re-encoded by an intermediary without knowledge of which entity + constructed a given message, the intermediary could inadvertently + merge compression contexts that the original encoder had specifically + kept separate. + + | Note: Simply removing entries corresponding to the field from + | the dynamic table can be ineffectual if the attacker has a + | reliable way of causing values to be reinstalled. For example, + | a request to load an image in a web browser typically includes + | the Cookie header field (a potentially highly valued target for + | this sort of attack), and websites can easily force an image to + | be loaded, thereby refreshing the entry in the dynamic table. + +7.1.3. Never-Indexed Literals + + Implementations can also choose to protect sensitive fields by not + compressing them and instead encoding their value as literals. + + Refusing to insert a field line into the dynamic table is only + effective if doing so is avoided on all hops. The never-indexed + literal bit (see Section 4.5.4) can be used to signal to + intermediaries that a particular value was intentionally sent as a + literal. + + An intermediary MUST NOT re-encode a value that uses a literal + representation with the 'N' bit set with another representation that + would index it. If QPACK is used for re-encoding, a literal + representation with the 'N' bit set MUST be used. If HPACK is used + for re-encoding, the never-indexed literal representation (see + Section 6.2.3 of [RFC7541]) MUST be used. + + The choice to mark that a field value should never be indexed depends + on several factors. Since QPACK does not protect against guessing an + entire field value, short or low-entropy values are more readily + recovered by an adversary. Therefore, an encoder might choose not to + index values with low entropy. + + An encoder might also choose not to index values for fields that are + considered to be highly valuable or sensitive to recovery, such as + the Cookie or Authorization header fields. + + On the contrary, an encoder might prefer indexing values for fields + that have little or no value if they were exposed. For instance, a + User-Agent header field does not commonly vary between requests and + is sent to any server. In that case, confirmation that a particular + User-Agent value has been used provides little value. + + Note that these criteria for deciding to use a never-indexed literal + representation will evolve over time as new attacks are discovered. + +7.2. Static Huffman Encoding + + There is no currently known attack against a static Huffman encoding. + A study has shown that using a static Huffman encoding table created + an information leakage; however, this same study concluded that an + attacker could not take advantage of this information leakage to + recover any meaningful amount of information (see [PETAL]). + +7.3. Memory Consumption + + An attacker can try to cause an endpoint to exhaust its memory. + QPACK is designed to limit both the peak and stable amounts of memory + allocated by an endpoint. + + QPACK uses the definition of the maximum size of the dynamic table + and the maximum number of blocking streams to limit the amount of + memory the encoder can cause the decoder to consume. In HTTP/3, + these values are controlled by the decoder through the settings + parameters SETTINGS_QPACK_MAX_TABLE_CAPACITY and + SETTINGS_QPACK_BLOCKED_STREAMS, respectively (see Section 3.2.3 and + Section 2.1.2). The limit on the size of the dynamic table takes + into account the size of the data stored in the dynamic table, plus a + small allowance for overhead. The limit on the number of blocked + streams is only a proxy for the maximum amount of memory required by + the decoder. The actual maximum amount of memory will depend on how + much memory the decoder uses to track each blocked stream. + + A decoder can limit the amount of state memory used for the dynamic + table by setting an appropriate value for the maximum size of the + dynamic table. In HTTP/3, this is realized by setting an appropriate + value for the SETTINGS_QPACK_MAX_TABLE_CAPACITY parameter. An + encoder can limit the amount of state memory it uses by choosing a + smaller dynamic table size than the decoder allows and signaling this + to the decoder (see Section 4.3.1). + + A decoder can limit the amount of state memory used for blocked + streams by setting an appropriate value for the maximum number of + blocked streams. In HTTP/3, this is realized by setting an + appropriate value for the SETTINGS_QPACK_BLOCKED_STREAMS parameter. + Streams that risk becoming blocked consume no additional state memory + on the encoder. + + An encoder allocates memory to track all dynamic table references in + unacknowledged field sections. An implementation can directly limit + the amount of state memory by only using as many references to the + dynamic table as it wishes to track; no signaling to the decoder is + required. However, limiting references to the dynamic table will + reduce compression effectiveness. + + The amount of temporary memory consumed by an encoder or decoder can + be limited by processing field lines sequentially. A decoder + implementation does not need to retain a complete list of field lines + while decoding a field section. An encoder implementation does not + need to retain a complete list of field lines while encoding a field + section if it is using a single-pass algorithm. Note that it might + be necessary for an application to retain a complete list of field + lines for other reasons; even if QPACK does not force this to occur, + application constraints might make this necessary. + + While the negotiated limit on the dynamic table size accounts for + much of the memory that can be consumed by a QPACK implementation, + data that cannot be immediately sent due to flow control is not + affected by this limit. Implementations should limit the size of + unsent data, especially on the decoder stream where flexibility to + choose what to send is limited. Possible responses to an excess of + unsent data might include limiting the ability of the peer to open + new streams, reading only from the encoder stream, or closing the + connection. + +7.4. Implementation Limits + + An implementation of QPACK needs to ensure that large values for + integers, long encoding for integers, or long string literals do not + create security weaknesses. + + An implementation has to set a limit for the values it accepts for + integers, as well as for the encoded length; see Section 4.1.1. In + the same way, it has to set a limit to the length it accepts for + string literals; see Section 4.1.2. These limits SHOULD be large + enough to process the largest individual field the HTTP + implementation can be configured to accept. + + If an implementation encounters a value larger than it is able to + decode, this MUST be treated as a stream error of type + QPACK_DECOMPRESSION_FAILED if on a request stream or a connection + error of the appropriate type if on the encoder or decoder stream. + +8. IANA Considerations + + This document makes multiple registrations in the registries defined + by [HTTP/3]. The allocations created by this document are all + assigned permanent status and list a change controller of the IETF + and a contact of the HTTP working group (ietf-http-wg@w3.org). + +8.1. Settings Registration + + This document specifies two settings. The entries in the following + table are registered in the "HTTP/3 Settings" registry established in + [HTTP/3]. + + +==========================+======+===============+=========+ + | Setting Name | Code | Specification | Default | + +==========================+======+===============+=========+ + | QPACK_MAX_TABLE_CAPACITY | 0x01 | Section 5 | 0 | + +--------------------------+------+---------------+---------+ + | QPACK_BLOCKED_STREAMS | 0x07 | Section 5 | 0 | + +--------------------------+------+---------------+---------+ + + Table 1: Additions to the HTTP/3 Settings Registry + + For formatting reasons, the setting names here are abbreviated by + removing the 'SETTINGS_' prefix. + +8.2. Stream Type Registration + + This document specifies two stream types. The entries in the + following table are registered in the "HTTP/3 Stream Types" registry + established in [HTTP/3]. + + +======================+======+===============+========+ + | Stream Type | Code | Specification | Sender | + +======================+======+===============+========+ + | QPACK Encoder Stream | 0x02 | Section 4.2 | Both | + +----------------------+------+---------------+--------+ + | QPACK Decoder Stream | 0x03 | Section 4.2 | Both | + +----------------------+------+---------------+--------+ + + Table 2: Additions to the HTTP/3 Stream Types Registry + +8.3. Error Code Registration + + This document specifies three error codes. The entries in the + following table are registered in the "HTTP/3 Error Codes" registry + established in [HTTP/3]. + + +============================+========+=============+===============+ + | Name | Code |Description | Specification | + +============================+========+=============+===============+ + | QPACK_DECOMPRESSION_FAILED | 0x0200 |Decoding of a| Section 6 | + | | |field section| | + | | |failed | | + +----------------------------+--------+-------------+---------------+ + | QPACK_ENCODER_STREAM_ERROR | 0x0201 |Error on the | Section 6 | + | | |encoder | | + | | |stream | | + +----------------------------+--------+-------------+---------------+ + | QPACK_DECODER_STREAM_ERROR | 0x0202 |Error on the | Section 6 | + | | |decoder | | + | | |stream | | + +----------------------------+--------+-------------+---------------+ + + Table 3: Additions to the HTTP/3 Error Codes Registry + +9. References + +9.1. Normative References + + [HTTP] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, + Ed., "HTTP Semantics", STD 97, RFC 9110, + DOI 10.17487/RFC9110, June 2022, + . + + [HTTP/3] Bishop, M., Ed., "HTTP/3", RFC 9114, DOI 10.17487/RFC9114, + June 2022, . + + [QUIC-TRANSPORT] + Iyengar, J., Ed. and M. Thomson, Ed., "QUIC: A UDP-Based + Multiplexed and Secure Transport", RFC 9000, + DOI 10.17487/RFC9000, May 2021, + . + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, + DOI 10.17487/RFC2119, March 1997, + . + + [RFC2360] Scott, G., "Guide for Internet Standards Writers", BCP 22, + RFC 2360, DOI 10.17487/RFC2360, June 1998, + . + + [RFC7541] Peon, R. and H. Ruellan, "HPACK: Header Compression for + HTTP/2", RFC 7541, DOI 10.17487/RFC7541, May 2015, + . + + [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC + 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, + May 2017, . + +9.2. Informative References + + [CRIME] Wikipedia, "CRIME", May 2015, . + + [HTTP/2] Thomson, M., Ed. and C. Benfield, Ed., "HTTP/2", RFC 9113, + DOI 10.17487/RFC9113, June 2022, + . + + [PETAL] Tan, J. and J. Nahata, "PETAL: Preset Encoding + Table Information Leakage", April 2013, + . + + [RFC1951] Deutsch, P., "DEFLATE Compressed Data Format Specification + version 1.3", RFC 1951, DOI 10.17487/RFC1951, May 1996, + . + + [RFC6454] Barth, A., "The Web Origin Concept", RFC 6454, + DOI 10.17487/RFC6454, December 2011, + . + + [TLS] Rescorla, E., "The Transport Layer Security (TLS) Protocol + Version 1.3", RFC 8446, DOI 10.17487/RFC8446, August 2018, + . + +Appendix A. Static Table + + This table was generated by analyzing actual Internet traffic in 2018 + and including the most common header fields, after filtering out some + unsupported and non-standard values. Due to this methodology, some + of the entries may be inconsistent or appear multiple times with + similar but not identical values. The order of the entries is + optimized to encode the most common header fields with the smallest + number of bytes. + + +=======+==================================+=======================+ + | Index | Name | Value | + +=======+==================================+=======================+ + | 0 | :authority | | + +-------+----------------------------------+-----------------------+ + | 1 | :path | / | + +-------+----------------------------------+-----------------------+ + | 2 | age | 0 | + +-------+----------------------------------+-----------------------+ + | 3 | content-disposition | | + +-------+----------------------------------+-----------------------+ + | 4 | content-length | 0 | + +-------+----------------------------------+-----------------------+ + | 5 | cookie | | + +-------+----------------------------------+-----------------------+ + | 6 | date | | + +-------+----------------------------------+-----------------------+ + | 7 | etag | | + +-------+----------------------------------+-----------------------+ + | 8 | if-modified-since | | + +-------+----------------------------------+-----------------------+ + | 9 | if-none-match | | + +-------+----------------------------------+-----------------------+ + | 10 | last-modified | | + +-------+----------------------------------+-----------------------+ + | 11 | link | | + +-------+----------------------------------+-----------------------+ + | 12 | location | | + +-------+----------------------------------+-----------------------+ + | 13 | referer | | + +-------+----------------------------------+-----------------------+ + | 14 | set-cookie | | + +-------+----------------------------------+-----------------------+ + | 15 | :method | CONNECT | + +-------+----------------------------------+-----------------------+ + | 16 | :method | DELETE | + +-------+----------------------------------+-----------------------+ + | 17 | :method | GET | + +-------+----------------------------------+-----------------------+ + | 18 | :method | HEAD | + +-------+----------------------------------+-----------------------+ + | 19 | :method | OPTIONS | + +-------+----------------------------------+-----------------------+ + | 20 | :method | POST | + +-------+----------------------------------+-----------------------+ + | 21 | :method | PUT | + +-------+----------------------------------+-----------------------+ + | 22 | :scheme | http | + +-------+----------------------------------+-----------------------+ + | 23 | :scheme | https | + +-------+----------------------------------+-----------------------+ + | 24 | :status | 103 | + +-------+----------------------------------+-----------------------+ + | 25 | :status | 200 | + +-------+----------------------------------+-----------------------+ + | 26 | :status | 304 | + +-------+----------------------------------+-----------------------+ + | 27 | :status | 404 | + +-------+----------------------------------+-----------------------+ + | 28 | :status | 503 | + +-------+----------------------------------+-----------------------+ + | 29 | accept | */* | + +-------+----------------------------------+-----------------------+ + | 30 | accept | application/dns- | + | | | message | + +-------+----------------------------------+-----------------------+ + | 31 | accept-encoding | gzip, deflate, br | + +-------+----------------------------------+-----------------------+ + | 32 | accept-ranges | bytes | + +-------+----------------------------------+-----------------------+ + | 33 | access-control-allow-headers | cache-control | + +-------+----------------------------------+-----------------------+ + | 34 | access-control-allow-headers | content-type | + +-------+----------------------------------+-----------------------+ + | 35 | access-control-allow-origin | * | + +-------+----------------------------------+-----------------------+ + | 36 | cache-control | max-age=0 | + +-------+----------------------------------+-----------------------+ + | 37 | cache-control | max-age=2592000 | + +-------+----------------------------------+-----------------------+ + | 38 | cache-control | max-age=604800 | + +-------+----------------------------------+-----------------------+ + | 39 | cache-control | no-cache | + +-------+----------------------------------+-----------------------+ + | 40 | cache-control | no-store | + +-------+----------------------------------+-----------------------+ + | 41 | cache-control | public, max- | + | | | age=31536000 | + +-------+----------------------------------+-----------------------+ + | 42 | content-encoding | br | + +-------+----------------------------------+-----------------------+ + | 43 | content-encoding | gzip | + +-------+----------------------------------+-----------------------+ + | 44 | content-type | application/dns- | + | | | message | + +-------+----------------------------------+-----------------------+ + | 45 | content-type | application/ | + | | | javascript | + +-------+----------------------------------+-----------------------+ + | 46 | content-type | application/json | + +-------+----------------------------------+-----------------------+ + | 47 | content-type | application/x-www- | + | | | form-urlencoded | + +-------+----------------------------------+-----------------------+ + | 48 | content-type | image/gif | + +-------+----------------------------------+-----------------------+ + | 49 | content-type | image/jpeg | + +-------+----------------------------------+-----------------------+ + | 50 | content-type | image/png | + +-------+----------------------------------+-----------------------+ + | 51 | content-type | text/css | + +-------+----------------------------------+-----------------------+ + | 52 | content-type | text/html; | + | | | charset=utf-8 | + +-------+----------------------------------+-----------------------+ + | 53 | content-type | text/plain | + +-------+----------------------------------+-----------------------+ + | 54 | content-type | text/ | + | | | plain;charset=utf-8 | + +-------+----------------------------------+-----------------------+ + | 55 | range | bytes=0- | + +-------+----------------------------------+-----------------------+ + | 56 | strict-transport-security | max-age=31536000 | + +-------+----------------------------------+-----------------------+ + | 57 | strict-transport-security | max-age=31536000; | + | | | includesubdomains | + +-------+----------------------------------+-----------------------+ + | 58 | strict-transport-security | max-age=31536000; | + | | | includesubdomains; | + | | | preload | + +-------+----------------------------------+-----------------------+ + | 59 | vary | accept-encoding | + +-------+----------------------------------+-----------------------+ + | 60 | vary | origin | + +-------+----------------------------------+-----------------------+ + | 61 | x-content-type-options | nosniff | + +-------+----------------------------------+-----------------------+ + | 62 | x-xss-protection | 1; mode=block | + +-------+----------------------------------+-----------------------+ + | 63 | :status | 100 | + +-------+----------------------------------+-----------------------+ + | 64 | :status | 204 | + +-------+----------------------------------+-----------------------+ + | 65 | :status | 206 | + +-------+----------------------------------+-----------------------+ + | 66 | :status | 302 | + +-------+----------------------------------+-----------------------+ + | 67 | :status | 400 | + +-------+----------------------------------+-----------------------+ + | 68 | :status | 403 | + +-------+----------------------------------+-----------------------+ + | 69 | :status | 421 | + +-------+----------------------------------+-----------------------+ + | 70 | :status | 425 | + +-------+----------------------------------+-----------------------+ + | 71 | :status | 500 | + +-------+----------------------------------+-----------------------+ + | 72 | accept-language | | + +-------+----------------------------------+-----------------------+ + | 73 | access-control-allow-credentials | FALSE | + +-------+----------------------------------+-----------------------+ + | 74 | access-control-allow-credentials | TRUE | + +-------+----------------------------------+-----------------------+ + | 75 | access-control-allow-headers | * | + +-------+----------------------------------+-----------------------+ + | 76 | access-control-allow-methods | get | + +-------+----------------------------------+-----------------------+ + | 77 | access-control-allow-methods | get, post, options | + +-------+----------------------------------+-----------------------+ + | 78 | access-control-allow-methods | options | + +-------+----------------------------------+-----------------------+ + | 79 | access-control-expose-headers | content-length | + +-------+----------------------------------+-----------------------+ + | 80 | access-control-request-headers | content-type | + +-------+----------------------------------+-----------------------+ + | 81 | access-control-request-method | get | + +-------+----------------------------------+-----------------------+ + | 82 | access-control-request-method | post | + +-------+----------------------------------+-----------------------+ + | 83 | alt-svc | clear | + +-------+----------------------------------+-----------------------+ + | 84 | authorization | | + +-------+----------------------------------+-----------------------+ + | 85 | content-security-policy | script-src 'none'; | + | | | object-src 'none'; | + | | | base-uri 'none' | + +-------+----------------------------------+-----------------------+ + | 86 | early-data | 1 | + +-------+----------------------------------+-----------------------+ + | 87 | expect-ct | | + +-------+----------------------------------+-----------------------+ + | 88 | forwarded | | + +-------+----------------------------------+-----------------------+ + | 89 | if-range | | + +-------+----------------------------------+-----------------------+ + | 90 | origin | | + +-------+----------------------------------+-----------------------+ + | 91 | purpose | prefetch | + +-------+----------------------------------+-----------------------+ + | 92 | server | | + +-------+----------------------------------+-----------------------+ + | 93 | timing-allow-origin | * | + +-------+----------------------------------+-----------------------+ + | 94 | upgrade-insecure-requests | 1 | + +-------+----------------------------------+-----------------------+ + | 95 | user-agent | | + +-------+----------------------------------+-----------------------+ + | 96 | x-forwarded-for | | + +-------+----------------------------------+-----------------------+ + | 97 | x-frame-options | deny | + +-------+----------------------------------+-----------------------+ + | 98 | x-frame-options | sameorigin | + +-------+----------------------------------+-----------------------+ + + Table 4: Static Table + + Any line breaks that appear within field names or values are due to + formatting. + +Appendix B. Encoding and Decoding Examples + + The following examples represent a series of exchanges between an + encoder and a decoder. The exchanges are designed to exercise most + QPACK instructions and highlight potentially common patterns and + their impact on dynamic table state. The encoder sends three encoded + field sections containing one field line each, as well as two + speculative inserts that are not referenced. + + The state of the encoder's dynamic table is shown, along with its + current size. Each entry is shown with the Absolute Index of the + entry (Abs), the current number of outstanding encoded field sections + with references to that entry (Ref), along with the name and value. + Entries above the 'acknowledged' line have been acknowledged by the + decoder. + +B.1. Literal Field Line with Name Reference + + The encoder sends an encoded field section containing a literal + representation of a field with a static name reference. + + Data | Interpretation + | Encoder's Dynamic Table + + Stream: 0 + 0000 | Required Insert Count = 0, Base = 0 + 510b 2f69 6e64 6578 | Literal Field Line with Name Reference + 2e68 746d 6c | Static Table, Index=1 + | (:path=/index.html) + + Abs Ref Name Value + ^-- acknowledged --^ + Size=0 + +B.2. Dynamic Table + + The encoder sets the dynamic table capacity, inserts a header with a + dynamic name reference, then sends a potentially blocking, encoded + field section referencing this new entry. The decoder acknowledges + processing the encoded field section, which implicitly acknowledges + all dynamic table insertions up to the Required Insert Count. + + Stream: Encoder + 3fbd01 | Set Dynamic Table Capacity=220 + c00f 7777 772e 6578 | Insert With Name Reference + 616d 706c 652e 636f | Static Table, Index=0 + 6d | (:authority=www.example.com) + c10c 2f73 616d 706c | Insert With Name Reference + 652f 7061 7468 | Static Table, Index=1 + | (:path=/sample/path) + + Abs Ref Name Value + ^-- acknowledged --^ + 0 0 :authority www.example.com + 1 0 :path /sample/path + Size=106 + + Stream: 4 + 0381 | Required Insert Count = 2, Base = 0 + 10 | Indexed Field Line With Post-Base Index + | Absolute Index = Base(0) + Index(0) = 0 + | (:authority=www.example.com) + 11 | Indexed Field Line With Post-Base Index + | Absolute Index = Base(0) + Index(1) = 1 + | (:path=/sample/path) + + Abs Ref Name Value + ^-- acknowledged --^ + 0 1 :authority www.example.com + 1 1 :path /sample/path + Size=106 + + Stream: Decoder + 84 | Section Acknowledgment (stream=4) + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + ^-- acknowledged --^ + Size=106 + +B.3. Speculative Insert + + The encoder inserts a header into the dynamic table with a literal + name. The decoder acknowledges receipt of the entry. The encoder + does not send any encoded field sections. + + Stream: Encoder + 4a63 7573 746f 6d2d | Insert With Literal Name + 6b65 790c 6375 7374 | (custom-key=custom-value) + 6f6d 2d76 616c 7565 | + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + ^-- acknowledged --^ + 2 0 custom-key custom-value + Size=160 + + Stream: Decoder + 01 | Insert Count Increment (1) + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + 2 0 custom-key custom-value + ^-- acknowledged --^ + Size=160 + +B.4. Duplicate Instruction, Stream Cancellation + + The encoder duplicates an existing entry in the dynamic table, then + sends an encoded field section referencing the dynamic table entries + including the duplicated entry. The packet containing the encoder + stream data is delayed. Before the packet arrives, the decoder + cancels the stream and notifies the encoder that the encoded field + section was not processed. + + Stream: Encoder + 02 | Duplicate (Relative Index = 2) + | Absolute Index = + | Insert Count(3) - Index(2) - 1 = 0 + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + 2 0 custom-key custom-value + ^-- acknowledged --^ + 3 0 :authority www.example.com + Size=217 + + Stream: 8 + 0500 | Required Insert Count = 4, Base = 4 + 80 | Indexed Field Line, Dynamic Table + | Absolute Index = Base(4) - Index(0) - 1 = 3 + | (:authority=www.example.com) + c1 | Indexed Field Line, Static Table Index = 1 + | (:path=/) + 81 | Indexed Field Line, Dynamic Table + | Absolute Index = Base(4) - Index(1) - 1 = 2 + | (custom-key=custom-value) + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + 2 1 custom-key custom-value + ^-- acknowledged --^ + 3 1 :authority www.example.com + Size=217 + + Stream: Decoder + 48 | Stream Cancellation (Stream=8) + + Abs Ref Name Value + 0 0 :authority www.example.com + 1 0 :path /sample/path + 2 0 custom-key custom-value + ^-- acknowledged --^ + 3 0 :authority www.example.com + Size=217 + +B.5. Dynamic Table Insert, Eviction + + The encoder inserts another header into the dynamic table, which + evicts the oldest entry. The encoder does not send any encoded field + sections. + + Stream: Encoder + 810d 6375 7374 6f6d | Insert With Name Reference + 2d76 616c 7565 32 | Dynamic Table, Relative Index = 1 + | Absolute Index = + | Insert Count(4) - Index(1) - 1 = 2 + | (custom-key=custom-value2) + + Abs Ref Name Value + 1 0 :path /sample/path + 2 0 custom-key custom-value + ^-- acknowledged --^ + 3 0 :authority www.example.com + 4 0 custom-key custom-value2 + Size=215 + +Appendix C. Sample Single-Pass Encoding Algorithm + + Pseudocode for single-pass encoding, excluding handling of + duplicates, non-blocking mode, available encoder stream flow control + and reference tracking. + + # Helper functions: + # ==== + # Encode an integer with the specified prefix and length + encodeInteger(buffer, prefix, value, prefixLength) + + # Encode a dynamic table insert instruction with optional static + # or dynamic name index (but not both) + encodeInsert(buffer, staticNameIndex, dynamicNameIndex, fieldLine) + + # Encode a static index reference + encodeStaticIndexReference(buffer, staticIndex) + + # Encode a dynamic index reference relative to Base + encodeDynamicIndexReference(buffer, dynamicIndex, base) + + # Encode a literal with an optional static name index + encodeLiteral(buffer, staticNameIndex, fieldLine) + + # Encode a literal with a dynamic name index relative to Base + encodeDynamicLiteral(buffer, dynamicNameIndex, base, fieldLine) + + # Encoding Algorithm + # ==== + base = dynamicTable.getInsertCount() + requiredInsertCount = 0 + for line in fieldLines: + staticIndex = staticTable.findIndex(line) + if staticIndex is not None: + encodeStaticIndexReference(streamBuffer, staticIndex) + continue + + dynamicIndex = dynamicTable.findIndex(line) + if dynamicIndex is None: + # No matching entry. Either insert+index or encode literal + staticNameIndex = staticTable.findName(line.name) + if staticNameIndex is None: + dynamicNameIndex = dynamicTable.findName(line.name) + + if shouldIndex(line) and dynamicTable.canIndex(line): + encodeInsert(encoderBuffer, staticNameIndex, + dynamicNameIndex, line) + dynamicIndex = dynamicTable.add(line) + + if dynamicIndex is None: + # Could not index it, literal + if dynamicNameIndex is not None: + # Encode literal with dynamic name, possibly above Base + encodeDynamicLiteral(streamBuffer, dynamicNameIndex, + base, line) + requiredInsertCount = max(requiredInsertCount, + dynamicNameIndex) + else: + # Encodes a literal with a static name or literal name + encodeLiteral(streamBuffer, staticNameIndex, line) + else: + # Dynamic index reference + assert(dynamicIndex is not None) + requiredInsertCount = max(requiredInsertCount, dynamicIndex) + # Encode dynamicIndex, possibly above Base + encodeDynamicIndexReference(streamBuffer, dynamicIndex, base) + + # encode the prefix + if requiredInsertCount == 0: + encodeInteger(prefixBuffer, 0x00, 0, 8) + encodeInteger(prefixBuffer, 0x00, 0, 7) + else: + wireRIC = ( + requiredInsertCount + % (2 * getMaxEntries(maxTableCapacity)) + ) + 1; + encodeInteger(prefixBuffer, 0x00, wireRIC, 8) + if base >= requiredInsertCount: + encodeInteger(prefixBuffer, 0x00, + base - requiredInsertCount, 7) + else: + encodeInteger(prefixBuffer, 0x80, + requiredInsertCount - base - 1, 7) + + return encoderBuffer, prefixBuffer + streamBuffer + +Acknowledgments + + The IETF QUIC Working Group received an enormous amount of support + from many people. + + The compression design team did substantial work exploring the + problem space and influencing the initial draft version of this + document. The contributions of design team members Roberto Peon, + Martin Thomson, and Dmitri Tikhonov are gratefully acknowledged. + + The following people also provided substantial contributions to this + document: + + * Bence Beky + * Alessandro Ghedini + * Ryan Hamilton + * Robin Marx + * Patrick McManus + * 奥 一穂 (Kazuho Oku) + * Lucas Pardue + * Biren Roy + * Ian Swett + + This document draws heavily on the text of [RFC7541]. The indirect + input of those authors is also gratefully acknowledged. + + Buck Krasic's contribution was supported by Google during his + employment there. + + A portion of Mike Bishop's contribution was supported by Microsoft + during his employment there. + +Authors' Addresses + + Charles 'Buck' Krasic + Email: krasic@acm.org + + + Mike Bishop + Akamai Technologies + Email: mbishop@evequefou.be + + + Alan Frindell (editor) + Facebook + Email: afrind@fb.com diff --git a/examples/server.rs b/examples/server.rs index a38c873a..78b6b5ee 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,4 +1,4 @@ -use std::{net::SocketAddr, path::PathBuf, sync::Arc}; +use std::{future::Future, net::SocketAddr, path::PathBuf, sync::Arc}; use bytes::{Bytes, BytesMut}; use http::StatusCode; diff --git a/h3/Cargo.toml b/h3/Cargo.toml index 6684e7e6..e15df1ef 100644 --- a/h3/Cargo.toml +++ b/h3/Cargo.toml @@ -31,6 +31,7 @@ tokio = { version = "1", features = ["sync"] } pin-project-lite = { version = "0.2", default-features = false } tracing = {version = "0.1.40", optional = true} fastrand = "2.0.1" +thiserror = "2" [dev-dependencies] assert_matches = "1.5.0" diff --git a/h3/src/connection.rs b/h3/src/connection.rs index 275e4c98..ea4239c5 100644 --- a/h3/src/connection.rs +++ b/h3/src/connection.rs @@ -81,6 +81,10 @@ where control_send: C::SendStream, control_recv: Option>, qpack_streams: QpackStreams, + + /// QPack decoder stream handler + decoder: crate::qpack2::decoder2::decoder_stream_handler::DecoderStreamHandler, + /// Buffers incoming uni/recv streams which have yet to be claimed. /// /// This is opposed to discarding them by returning in `poll_accept_recv`, which may cause them to be missed by something else polling. @@ -193,6 +197,11 @@ where let mut decoder_send = Option::take(&mut self.qpack_streams.decoder_send); let mut encoder_send = Option::take(&mut self.qpack_streams.encoder_send); + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# Each endpoint + //# MUST initiate, at most, one encoder stream and, at most, one decoder + //# stream. + let (control, ..) = future::join3( stream::write( &mut self.control_send, @@ -307,6 +316,7 @@ where control_send: control_send, control_recv: None, qpack_streams, + decoder: todo!(), handled_connection_error: None, pending_recv_streams: Vec::with_capacity(3), got_peer_settings: false, @@ -402,6 +412,9 @@ where loop { match self .conn + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# An endpoint MUST allow its peer to create an encoder stream and a + //# decoder stream even if the connection's settings prevent their use. .poll_accept_recv(cx) .map_err(|e| self.handle_connection_error(e))? { @@ -450,6 +463,14 @@ where } enc @ AcceptedRecvStream::Encoder(_) => { if let Some(_prev) = self.qpack_streams.encoder_recv.replace(enc) { + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# Receipt of a second instance of either stream type MUST be + //# treated as a connection error of type H3_STREAM_CREATION_ERROR. + + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# An endpoint MUST allow its peer to create an encoder stream and a + //# decoder stream even if the connection's settings prevent their use. + return Err(self.handle_connection_error(InternalConnectionError::new( Code::H3_STREAM_CREATION_ERROR, "got two encoder streams".to_string(), @@ -458,6 +479,14 @@ where } dec @ AcceptedRecvStream::Decoder(_) => { if let Some(_prev) = self.qpack_streams.decoder_recv.replace(dec) { + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# Receipt of a second instance of either stream type MUST be + //# treated as a connection error of type H3_STREAM_CREATION_ERROR. + + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.2 + //# An endpoint MUST allow its peer to create an encoder stream and a + //# decoder stream even if the connection's settings prevent their use. + return Err(self.handle_connection_error(InternalConnectionError::new( Code::H3_STREAM_CREATION_ERROR, "got two decoder streams".to_string(), diff --git a/h3/src/lib.rs b/h3/src/lib.rs index 1e4855fc..b668e564 100644 --- a/h3/src/lib.rs +++ b/h3/src/lib.rs @@ -55,6 +55,8 @@ mod stream; #[cfg(not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))] mod webtransport; +#[allow(dead_code)] +mod qpack2; #[cfg(test)] mod tests; #[cfg(test)] diff --git a/h3/src/qpack2/block.rs b/h3/src/qpack2/block.rs new file mode 100644 index 00000000..1e570b7a --- /dev/null +++ b/h3/src/qpack2/block.rs @@ -0,0 +1,499 @@ +use bytes::{Buf, BufMut}; + +use super::{parse_error::ParseError, prefix_int, prefix_string}; + +// 4.5. Field Line Representations +// Single header field line. These representations reference the static table or +// the dynamic table in a particular state, but do not modify that state. +pub enum HeaderBlockField { + // 4.5.2. Indexed Field Line + // Entry in the static table, or in the dynamic table with an absolute index + // less than the value of the Base. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 1 | T | Index (6+) | + // +---+---+-----------------------+ + Indexed, + // 4.5.3. Indexed Field Line With Post-Base Index + // Entry in the dynamic table with an absolute index greater than or equal + // to the value of the Base. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 1 | Index (4+) | + // +---+---+---+---+---------------+ + IndexedWithPostBase, + // 4.5.4. Literal Field Line With Name Reference + // Entry in the dynamic table with an absolute index greater than or equal + // to the value of the Base. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | N | T |Name Index (4+)| + // +---+---+---+---+---------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + LiteralWithNameRef, + // 4.5.5. Literal Field Line With Post-Base Name Reference + // The field name matches a name of an entry in the static table, or in the + // dynamic table with an absolute index less than the value of the Base. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | 0 | N |NameIdx(3+)| + // +---+---+---+---+---+-----------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + LiteralWithPostBaseNameRef, + // 4.5.6. Literal Field Line With Literal Name + // Field name and field value are encoded as string literals. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 1 | N | H |NameLen(3+)| + // +---+---+---+---+---+-----------+ + // | Name String (Length bytes) | + // +---+---------------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + Literal, + Unknown, +} + +impl HeaderBlockField { + // Check how the next field is encoded according its first byte + pub fn decode(first: u8) -> Self { + if first & 0b1000_0000 != 0 { + HeaderBlockField::Indexed + } else if first & 0b1111_0000 == 0b0001_0000 { + HeaderBlockField::IndexedWithPostBase + } else if first & 0b1100_0000 == 0b0100_0000 { + HeaderBlockField::LiteralWithNameRef + } else if first & 0b1111_0000 == 0 { + HeaderBlockField::LiteralWithPostBaseNameRef + } else if first & 0b1110_0000 == 0b0010_0000 { + HeaderBlockField::Literal + } else { + HeaderBlockField::Unknown + } + } +} + +// 4.5.1. Encoded Field Section Prefix +#[derive(Debug, PartialEq)] +pub struct HeaderPrefix { + encoded_insert_count: usize, + sign_negative: bool, + delta_base: usize, +} + +impl HeaderPrefix { + pub fn new(required: usize, base: usize, total_inserted: usize, max_table_size: usize) -> Self { + if max_table_size == 0 { + return Self { + encoded_insert_count: 0, + sign_negative: false, + delta_base: 0, + }; + } + + if required == 0 { + return Self { + encoded_insert_count: 0, + delta_base: 0, + sign_negative: false, + }; + } + + assert!(required <= total_inserted); + let (sign_negative, delta_base) = if required > base { + (true, required - base - 1) + } else { + (false, base - required) + }; + + let max_entries = max_table_size / 32; + + Self { + encoded_insert_count: required % (2 * max_entries) + 1, + sign_negative, + delta_base, + } + } + + pub fn get( + self, + total_inserted: usize, + max_table_size: usize, + ) -> Result<(usize, usize), ParseError> { + if max_table_size == 0 { + return Ok((0, 0)); + } + + // 4.5.1.1. Required Insert Count + let required = if self.encoded_insert_count == 0 { + 0 + } else { + let mut insert_count = self.encoded_insert_count - 1; + let max_entries = max_table_size / 32; + let mut wrapped = total_inserted % (2 * max_entries); + + if wrapped >= insert_count + max_entries { + insert_count += 2 * max_entries; + } else if wrapped + max_entries < insert_count { + wrapped += 2 * max_entries; + } + + insert_count + total_inserted - wrapped + }; + + let base = if required == 0 { + 0 + } else if !self.sign_negative { + required + self.delta_base + } else { + if self.delta_base + 1 > required { + return Err(ParseError::InvalidBase( + required as isize - self.delta_base as isize - 1, + )); + } + required - self.delta_base - 1 + }; + + Ok((required, base)) + } + + // 4.5.1. Encoded Field Section Prefix + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | Required Insert Count (8+) | + // +---+---------------------------+ + // | S | Delta Base (7+) | + // +---+---------------------------+ + // | Encoded Field Lines ... + // +-------------------------------+ + pub fn decode(buf: &mut R) -> Result { + let (_, encoded_insert_count) = prefix_int::decode(8, buf)?; + let (sign_negative, delta_base) = prefix_int::decode(7, buf)?; + + if encoded_insert_count > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + if delta_base > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(Self { + encoded_insert_count: encoded_insert_count as usize, + delta_base: delta_base as usize, + sign_negative: sign_negative == 1, + }) + } + + pub fn encode(&self, buf: &mut W) { + let sign_bit = if self.sign_negative { 1 } else { 0 }; + prefix_int::encode(8, 0, self.encoded_insert_count as u64, buf); + prefix_int::encode(7, sign_bit, self.delta_base as u64, buf); + } +} + +#[derive(Debug, PartialEq)] +pub enum Indexed { + Static(usize), + Dynamic(usize), +} + +impl Indexed { + pub fn decode(buf: &mut R) -> Result { + match prefix_int::decode(6, buf)? { + (0b11, i) => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(Indexed::Static(i as usize)) + } + (0b10, i) => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(Indexed::Dynamic(i as usize)) + } + (f, _) => Err(ParseError::InvalidPrefix(f)), + } + } + + pub fn encode(&self, buf: &mut W) { + match self { + Indexed::Static(i) => prefix_int::encode(6, 0b11, *i as u64, buf), + Indexed::Dynamic(i) => prefix_int::encode(6, 0b10, *i as u64, buf), + } + } +} + +#[derive(Debug, PartialEq)] +pub struct IndexedWithPostBase(pub usize); + +impl IndexedWithPostBase { + pub fn decode(buf: &mut R) -> Result { + match prefix_int::decode(4, buf)? { + (0b0001, i) => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(IndexedWithPostBase(i as usize)) + } + (f, _) => Err(ParseError::InvalidPrefix(f)), + } + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(4, 0b0001, self.0 as u64, buf) + } +} + +#[derive(Debug, PartialEq)] +pub enum LiteralWithNameRef { + Static { index: usize, value: Vec }, + Dynamic { index: usize, value: Vec }, +} + +impl LiteralWithNameRef { + pub fn new_static>>(index: usize, value: T) -> Self { + LiteralWithNameRef::Static { + index, + value: value.into(), + } + } + + pub fn new_dynamic>>(index: usize, value: T) -> Self { + LiteralWithNameRef::Dynamic { + index, + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result { + match prefix_int::decode(4, buf)? { + (f, i) if f & 0b0101 == 0b0101 => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(LiteralWithNameRef::new_static( + i as usize, + prefix_string::decode(8, buf)?, + )) + } + (f, i) if f & 0b0101 == 0b0100 => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(LiteralWithNameRef::new_dynamic( + i as usize, + prefix_string::decode(8, buf)?, + )) + } + (f, _) => Err(ParseError::InvalidPrefix(f)), + } + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + match self { + LiteralWithNameRef::Static { index, value } => { + prefix_int::encode(4, 0b0101, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + LiteralWithNameRef::Dynamic { index, value } => { + prefix_int::encode(4, 0b0100, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + } + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct LiteralWithPostBaseNameRef { + pub index: usize, + pub value: Vec, +} + +impl LiteralWithPostBaseNameRef { + pub fn new>>(index: usize, value: T) -> Self { + LiteralWithPostBaseNameRef { + index, + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result { + match prefix_int::decode(3, buf)? { + (f, i) if f & 0b1111_0000 == 0 => { + if i > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + + Ok(LiteralWithPostBaseNameRef::new( + i as usize, + prefix_string::decode(8, buf)?, + )) + } + (f, _) => Err(ParseError::InvalidPrefix(f)), + } + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + prefix_int::encode(3, 0b0000, self.index as u64, buf); + prefix_string::encode(8, 0, &self.value, buf)?; + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct Literal { + pub name: Vec, + pub value: Vec, +} + +impl Literal { + pub fn new>>(name: T, value: T) -> Self { + Literal { + name: name.into(), + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result { + if buf.remaining() < 1 { + return Err(ParseError::Integer(prefix_int::Error::UnexpectedEnd)); + } else if buf.chunk()[0] & 0b1110_0000 != 0b0010_0000 { + return Err(ParseError::InvalidPrefix(buf.chunk()[0])); + } + Ok(Literal::new( + prefix_string::decode(4, buf)?, + prefix_string::decode(8, buf)?, + )) + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + prefix_string::encode(4, 0b0010, &self.name, buf)?; + prefix_string::encode(8, 0, &self.value, buf)?; + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::convert::TryInto; + use std::io::Cursor; + + const TABLE_SIZE: usize = 4096; + + #[test] + fn indexed_static() { + let field = Indexed::Static(42); + let mut buf = vec![]; + field.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(Indexed::decode(&mut read), Ok(field)); + } + + #[test] + fn indexed_dynamic() { + let field = Indexed::Dynamic(42); + let mut buf = vec![]; + field.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(Indexed::decode(&mut read), Ok(field)); + } + + #[test] + fn indexed_with_postbase() { + let field = IndexedWithPostBase(42); + let mut buf = vec![]; + field.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(IndexedWithPostBase::decode(&mut read), Ok(field)); + } + + #[test] + fn literal_with_name_ref() { + let field = LiteralWithNameRef::new_static(42, "foo"); + let mut buf = vec![]; + field.encode(&mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!(LiteralWithNameRef::decode(&mut read), Ok(field)); + } + + #[test] + fn literal_with_post_base_name_ref() { + let field = LiteralWithPostBaseNameRef::new(42, "foo"); + let mut buf = vec![]; + field.encode(&mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!(LiteralWithPostBaseNameRef::decode(&mut read), Ok(field)); + } + + #[test] + fn literal() { + let field = Literal::new("foo", "bar"); + let mut buf = vec![]; + field.encode(&mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!(Literal::decode(&mut read), Ok(field)); + } + + #[test] + fn header_prefix() { + let prefix = HeaderPrefix::new(10, 5, 12, TABLE_SIZE); + let mut buf = vec![]; + prefix.encode(&mut buf); + let mut read = Cursor::new(&buf); + let decoded = HeaderPrefix::decode(&mut read); + assert_eq!(decoded, Ok(prefix)); + assert_eq!(decoded.unwrap().get(13, 3332).unwrap(), (10, 5)); + } + + #[test] + fn header_prefix_table_size_0() { + HeaderPrefix::new(10, 5, 12, 0).get(1, 0).unwrap(); + } + + #[test] + fn base_index_too_small() { + let mut buf = vec![]; + let encoded_largest_ref: u64 = ((2 % (2 * TABLE_SIZE / 32)) + 1).try_into().unwrap(); + prefix_int::encode(8, 0, encoded_largest_ref, &mut buf); + prefix_int::encode(7, 1, 2, &mut buf); // base index negative = 0 + + let mut read = Cursor::new(&buf); + assert_eq!( + HeaderPrefix::decode(&mut read).unwrap().get(2, TABLE_SIZE), + Err(ParseError::InvalidBase(-1)) + ); + } +} diff --git a/h3/src/qpack2/decoder.rs b/h3/src/qpack2/decoder.rs new file mode 100644 index 00000000..bdbd5be0 --- /dev/null +++ b/h3/src/qpack2/decoder.rs @@ -0,0 +1,722 @@ +use bytes::{Buf, BufMut}; +use std::{convert::TryInto, fmt, io::Cursor, num::TryFromIntError}; + +#[cfg(feature = "tracing")] +use tracing::trace; + +use super::{ + dynamic::{DynamicTable, DynamicTableDecoder, Error as DynamicTableError}, + field::HeaderField, + static_::{Error as StaticError, StaticTable}, + vas, +}; + +use super::{ + block::{ + HeaderBlockField, HeaderPrefix, Indexed, IndexedWithPostBase, Literal, LiteralWithNameRef, + LiteralWithPostBaseNameRef, + }, + parse_error::ParseError, + stream::{ + Duplicate, DynamicTableSizeUpdate, EncoderInstruction, HeaderAck, InsertCountIncrement, + InsertWithNameRef, InsertWithoutNameRef, StreamCancel, + }, +}; + +use super::{prefix_int, prefix_string}; + +#[derive(Debug, PartialEq)] +pub enum DecoderError { + InvalidInteger(prefix_int::Error), + InvalidString(prefix_string::PrefixStringError), + InvalidIndex(vas::Error), + DynamicTable(DynamicTableError), + InvalidStaticIndex(usize), + UnknownPrefix(u8), + MissingRefs(usize), + BadBaseIndex(isize), + UnexpectedEnd, + HeaderTooLong(u64), + BufSize(TryFromIntError), +} + +impl std::error::Error for DecoderError {} + +impl std::fmt::Display for DecoderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + match self { + DecoderError::InvalidInteger(e) => write!(f, "invalid integer: {}", e), + DecoderError::InvalidString(e) => write!(f, "invalid string: {:?}", e), + DecoderError::InvalidIndex(e) => write!(f, "invalid dynamic index: {:?}", e), + DecoderError::DynamicTable(e) => write!(f, "dynamic table error: {:?}", e), + DecoderError::InvalidStaticIndex(i) => write!(f, "unknown static index: {}", i), + DecoderError::UnknownPrefix(p) => write!(f, "unknown instruction code: 0x{}", p), + DecoderError::MissingRefs(n) => write!(f, "missing {} refs to decode bloc", n), + DecoderError::BadBaseIndex(i) => write!(f, "out of bounds base index: {}", i), + DecoderError::UnexpectedEnd => write!(f, "unexpected end"), + DecoderError::HeaderTooLong(_) => write!(f, "header too long"), + DecoderError::BufSize(_) => write!(f, "number in buffer wrong size"), + } + } +} + +pub fn ack_header(stream_id: u64, decoder: &mut W) { + HeaderAck(stream_id).encode(decoder); +} + +pub fn stream_canceled(stream_id: u64, decoder: &mut W) { + StreamCancel(stream_id).encode(decoder); +} + +#[derive(PartialEq, Debug)] +pub struct Decoded { + /// The decoded fields + pub fields: Vec, + /// Whether one or more encoded fields were referencing the dynamic table + pub dyn_ref: bool, + /// Decoded size, calculated as stated in "4.1.1.3. Header Size Constraints" + pub mem_size: u64, +} + +pub struct Decoder { + table: DynamicTable, +} + +impl Decoder { + // Decode field lines received on Request of Push stream. + // https://www.rfc-editor.org/rfc/rfc9204.html#name-field-line-representations + pub fn decode_header(&self, buf: &mut T) -> Result { + let (required_ref, base) = HeaderPrefix::decode(buf)? + .get(self.table.total_inserted(), self.table.max_mem_size())?; + + if required_ref > self.table.total_inserted() { + return Err(DecoderError::MissingRefs(required_ref)); + } + + let decoder_table = self.table.decoder(base); + + let mut mem_size = 0; + let mut fields = Vec::new(); + while buf.has_remaining() { + let field = Self::parse_header_field(&decoder_table, buf)?; + mem_size += field.mem_size() as u64; + fields.push(field); + } + + Ok(Decoded { + fields, + mem_size, + dyn_ref: required_ref > 0, + }) + } + + // The receiving side of encoder stream + pub fn on_encoder_recv( + &mut self, + read: &mut R, + write: &mut W, + ) -> Result { + let inserted_on_start = self.table.total_inserted(); + todo!() + /*while let Some(instruction) = self.parse_instruction(read)? { + #[cfg(feature = "tracing")] + trace!("instruction {:?}", instruction); + + match instruction { + Instruction::Insert(field) => self.table.put(field)?, + Instruction::TableSizeUpdate(size) => { + self.table.set_max_size(size)?; + } + } + } + + if self.table.total_inserted() != inserted_on_start { + InsertCountIncrement((self.table.total_inserted() - inserted_on_start).try_into()?) + .encode(write); + } + + Ok(self.table.total_inserted())*/ + } + + fn parse_header_field( + table: &DynamicTableDecoder, + buf: &mut R, + ) -> Result { + let first = buf.chunk()[0]; + let field = match HeaderBlockField::decode(first) { + HeaderBlockField::Indexed => match Indexed::decode(buf)? { + Indexed::Static(index) => StaticTable::get(index)?.clone(), + Indexed::Dynamic(index) => table.get_relative(index)?.clone(), + }, + HeaderBlockField::IndexedWithPostBase => { + let index = IndexedWithPostBase::decode(buf)?.0; + table.get_postbase(index)?.clone() + } + HeaderBlockField::LiteralWithNameRef => match LiteralWithNameRef::decode(buf)? { + LiteralWithNameRef::Static { index, value } => { + StaticTable::get(index)?.with_value(value) + } + LiteralWithNameRef::Dynamic { index, value } => { + table.get_relative(index)?.with_value(value) + } + }, + HeaderBlockField::LiteralWithPostBaseNameRef => { + let literal = LiteralWithPostBaseNameRef::decode(buf)?; + table.get_postbase(literal.index)?.with_value(literal.value) + } + HeaderBlockField::Literal => { + let literal = Literal::decode(buf)?; + HeaderField::new(literal.name, literal.value) + } + _ => return Err(DecoderError::UnknownPrefix(first)), + }; + Ok(field) + } +} + +// Decode field lines received on Request or Push stream. +// https://www.rfc-editor.org/rfc/rfc9204.html#name-field-line-representations +pub fn decode_stateless(buf: &mut T, max_size: u64) -> Result { + let (required_ref, _base) = HeaderPrefix::decode(buf)?.get(0, 0)?; + + if required_ref > 0 { + return Err(DecoderError::MissingRefs(required_ref)); + } + + let mut mem_size = 0; + let mut fields = Vec::new(); + while buf.has_remaining() { + let field = match HeaderBlockField::decode(buf.chunk()[0]) { + HeaderBlockField::IndexedWithPostBase => return Err(DecoderError::MissingRefs(0)), + HeaderBlockField::LiteralWithPostBaseNameRef => { + return Err(DecoderError::MissingRefs(0)) + } + HeaderBlockField::Indexed => match Indexed::decode(buf)? { + Indexed::Static(index) => StaticTable::get(index)?.clone(), + Indexed::Dynamic(_) => return Err(DecoderError::MissingRefs(0)), + }, + HeaderBlockField::LiteralWithNameRef => match LiteralWithNameRef::decode(buf)? { + LiteralWithNameRef::Dynamic { .. } => return Err(DecoderError::MissingRefs(0)), + LiteralWithNameRef::Static { index, value } => { + StaticTable::get(index)?.with_value(value) + } + }, + HeaderBlockField::Literal => { + let literal = Literal::decode(buf)?; + HeaderField::new(literal.name, literal.value) + } + _ => return Err(DecoderError::UnknownPrefix(buf.chunk()[0])), + }; + mem_size += field.mem_size() as u64; + // Cancel decoding if the header is considered too big + if mem_size > max_size { + return Err(DecoderError::HeaderTooLong(mem_size)); + } + fields.push(field); + } + + Ok(Decoded { + fields, + mem_size, + dyn_ref: false, + }) +} + +#[cfg(test)] +impl From for Decoder { + fn from(table: DynamicTable) -> Self { + Self { table } + } +} + +#[derive(PartialEq)] +enum Instruction { + Insert(HeaderField), + TableSizeUpdate(usize), +} + +impl fmt::Debug for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Instruction::Insert(h) => write!(f, "Instruction::Insert {{ {} }}", h), + Instruction::TableSizeUpdate(n) => { + write!(f, "Instruction::TableSizeUpdate {{ {} }}", n) + } + } + } +} + +impl From for DecoderError { + fn from(e: prefix_int::Error) -> Self { + match e { + prefix_int::Error::UnexpectedEnd => DecoderError::UnexpectedEnd, + e => DecoderError::InvalidInteger(e), + } + } +} + +impl From for DecoderError { + fn from(e: prefix_string::PrefixStringError) -> Self { + match e { + prefix_string::PrefixStringError::UnexpectedEnd => DecoderError::UnexpectedEnd, + e => DecoderError::InvalidString(e), + } + } +} + +impl From for DecoderError { + fn from(e: vas::Error) -> Self { + DecoderError::InvalidIndex(e) + } +} + +impl From for DecoderError { + fn from(e: StaticError) -> Self { + match e { + StaticError::Unknown(i) => DecoderError::InvalidStaticIndex(i), + } + } +} + +impl From for DecoderError { + fn from(e: DynamicTableError) -> Self { + DecoderError::DynamicTable(e) + } +} + +impl From for DecoderError { + fn from(e: ParseError) -> Self { + match e { + ParseError::Integer(x) => DecoderError::InvalidInteger(x), + ParseError::String(x) => DecoderError::InvalidString(x), + ParseError::InvalidPrefix(p) => DecoderError::UnknownPrefix(p), + ParseError::InvalidBase(b) => DecoderError::BadBaseIndex(b), + } + } +} + +impl From for DecoderError { + fn from(error: TryFromIntError) -> Self { + DecoderError::BufSize(error) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::qpack::tests::helpers::{build_table_with_size, TABLE_SIZE}; + + #[test] + fn test_header_too_long() { + let mut trailers = http::HeaderMap::new(); + trailers.insert("trailer", "value".parse().unwrap()); + trailers.insert("trailer2", "value2".parse().unwrap()); + let mut buf = bytes::BytesMut::new(); + let _ = crate::qpack::encode_stateless( + &mut buf, + crate::proto::headers::Header::trailer(trailers), + ); + let result = decode_stateless(&mut buf, 2); + assert_eq!(result, Err(DecoderError::HeaderTooLong(44))); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-name-reference + * 4.3.2. Insert With Name Reference + */ + #[test] + fn test_insert_field_with_name_ref_into_dynamic_table() { + let mut buf = vec![]; + InsertWithNameRef::new_static(1, "serial value") + .encode(&mut buf) + .unwrap(); + let mut decoder = Decoder::from(build_table_with_size(0)); + let mut enc = Cursor::new(&buf); + let mut dec = vec![]; + assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok()); + + assert_eq!( + decoder.table.decoder(1).get_relative(0), + Ok(&StaticTable::get(1).unwrap().with_value("serial value")) + ); + + let mut dec_cursor = Cursor::new(&dec); + assert_eq!( + InsertCountIncrement::decode(&mut dec_cursor), + Ok(Some(InsertCountIncrement(1))) + ); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-name-reference + * 4.3.2. Insert With Name Reference + */ + #[test] + fn test_insert_field_with_wrong_name_index_from_static_table() { + let mut buf = vec![]; + InsertWithNameRef::new_static(3000, "") + .encode(&mut buf) + .unwrap(); + let mut enc = Cursor::new(&buf); + let mut decoder = Decoder::from(build_table_with_size(0)); + let res = decoder.on_encoder_recv(&mut enc, &mut vec![]); + assert_eq!(res, Err(DecoderError::InvalidStaticIndex(3000))); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-name-referencehtml + * 4.3.2. Insert With Name Reference + */ + #[test] + fn test_insert_field_with_wrong_name_index_from_dynamic_table() { + let mut buf = vec![]; + InsertWithNameRef::new_dynamic(3000, "") + .encode(&mut buf) + .unwrap(); + let mut enc = Cursor::new(&buf); + let mut dec = vec![]; + let mut decoder = Decoder::from(build_table_with_size(0)); + let res = decoder.on_encoder_recv(&mut enc, &mut dec); + assert_eq!( + res, + Err(DecoderError::DynamicTable( + DynamicTableError::BadRelativeIndex(3000) + )) + ); + + assert!(dec.is_empty()); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-insert-with-literal-name + * 4.3.3. Insert with Literal Name + */ + #[test] + fn test_insert_field_without_name_ref() { + let mut buf = vec![]; + InsertWithoutNameRef::new("key", "value") + .encode(&mut buf) + .unwrap(); + + let mut decoder = Decoder::from(build_table_with_size(0)); + let mut enc = Cursor::new(&buf); + let mut dec = vec![]; + assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok()); + + assert_eq!( + decoder.table.decoder(1).get_relative(0), + Ok(&HeaderField::new("key", "value")) + ); + + let mut dec_cursor = Cursor::new(&dec); + assert_eq!( + InsertCountIncrement::decode(&mut dec_cursor), + Ok(Some(InsertCountIncrement(1))) + ); + } + + fn insert_fields(table: &mut DynamicTable, fields: Vec) { + for field in fields { + table.put(field).unwrap(); + } + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-duplicate + * 4.3.4. Duplicate + */ + #[test] + fn test_duplicate_field() { + // let mut table = build_table_with_size(0); + let mut table = build_table_with_size(0); + insert_fields( + &mut table, + vec![HeaderField::new("", ""), HeaderField::new("", "")], + ); + let mut decoder = Decoder::from(table); + + let mut buf = vec![]; + Duplicate(1).encode(&mut buf); + + let mut enc = Cursor::new(&buf); + let mut dec = vec![]; + let res = decoder.on_encoder_recv(&mut enc, &mut dec); + assert_eq!(res, Ok(3)); + + let mut dec_cursor = Cursor::new(&dec); + assert_eq!( + InsertCountIncrement::decode(&mut dec_cursor), + Ok(Some(InsertCountIncrement(1))) + ); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-set-dynamic-table-capacity + * 4.3.1. Set Dynamic Table Capacity + */ + #[test] + fn test_dynamic_table_size_update() { + let mut buf = vec![]; + DynamicTableSizeUpdate(25).encode(&mut buf); + + let mut enc = Cursor::new(&buf); + let mut dec = vec![]; + let mut decoder = Decoder::from(build_table_with_size(0)); + let res = decoder.on_encoder_recv(&mut enc, &mut dec); + assert_eq!(res, Ok(0)); + + let actual_max_size = decoder.table.max_mem_size(); + assert_eq!(actual_max_size, 25); + assert!(dec.is_empty()); + } + + /* #[test] + fn enc_recv_buf_too_short() { + let decoder = Decoder::from(build_table_with_size(0)); + let mut buf = vec![]; + { + let mut enc = Cursor::new(&buf); + assert_eq!(decoder.parse_instruction(&mut enc), Ok(None)); + } + + buf.push(0b1000_0000); + let mut enc = Cursor::new(&buf); + assert_eq!(decoder.parse_instruction(&mut enc), Ok(None)); + } +*/ + #[test] + fn enc_recv_accepts_truncated_messages() { + let mut buf = vec![]; + InsertWithoutNameRef::new("keyfoobarbaz", "value") + .encode(&mut buf) + .unwrap(); + + let mut decoder = Decoder::from(build_table_with_size(0)); + // cut in middle of the first int + let mut enc = Cursor::new(&buf[..2]); + let mut dec = vec![]; + assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok()); + assert_eq!(enc.position(), 0); + + // cut the last byte of the 2nd string + let mut enc = Cursor::new(&buf[..buf.len() - 1]); + let mut dec = vec![]; + assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok()); + assert_eq!(enc.position(), 0); + + InsertWithoutNameRef::new("keyfoobarbaz2", "value") + .encode(&mut buf) + .unwrap(); + + // the first valid field is inserted and buf is left at the first byte of incomplete string + let mut enc = Cursor::new(&buf[..buf.len() - 1]); + let mut dec = vec![]; + assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok()); + assert_eq!(enc.position(), 15); + + let mut dec_cursor = Cursor::new(&dec); + assert_eq!( + InsertCountIncrement::decode(&mut dec_cursor), + Ok(Some(InsertCountIncrement(1))) + ); + } + + #[test] + fn largest_ref_too_big() { + let decoder = Decoder::from(build_table_with_size(0)); + let mut buf = vec![]; + HeaderPrefix::new(8, 8, 10, TABLE_SIZE).encode(&mut buf); + + let mut read = Cursor::new(&buf); + assert_eq!( + decoder.decode_header(&mut read), + Err(DecoderError::MissingRefs(8)) + ); + } + + fn field(n: usize) -> HeaderField { + HeaderField::new(format!("foo{}", n), "bar") + } + + // Largest Reference + // Base Index = 2 + // | + // foo2 foo1 + // +-----+-----+ + // | 2 | 1 | Absolute Index + // +-----+-----+ + // | 0 | 1 | Relative Index + // --+---+-----+ + + #[test] + fn decode_indexed_header_field() { + let mut buf = vec![]; + HeaderPrefix::new(2, 2, 2, TABLE_SIZE).encode(&mut buf); + Indexed::Dynamic(0).encode(&mut buf); + Indexed::Dynamic(1).encode(&mut buf); + Indexed::Static(18).encode(&mut buf); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(2)); + let Decoded { + fields, dyn_ref, .. + } = decoder.decode_header(&mut read).unwrap(); + assert!(dyn_ref); + assert_eq!( + fields, + &[field(2), field(1), StaticTable::get(18).unwrap().clone()] + ) + } + + // Largest Reference + // Base Index = 2 + // | + // foo4 foo3 foo2 foo1 + // +---+-----+-----+-----+ + // | 4 | 3 | 2 | 1 | Absolute Index + // +---+-----+-----+-----+ + // | 0 | 1 | Relative Index + // +-----+-----+---+-----+ + // | 1 | 0 | Post-Base Index + // +---+-----+ + + #[test] + fn decode_post_base_indexed() { + let mut buf = vec![]; + HeaderPrefix::new(4, 2, 4, TABLE_SIZE).encode(&mut buf); + Indexed::Dynamic(0).encode(&mut buf); + IndexedWithPostBase(0).encode(&mut buf); + IndexedWithPostBase(1).encode(&mut buf); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(4)); + let Decoded { + fields, dyn_ref, .. + } = decoder.decode_header(&mut read).unwrap(); + assert!(dyn_ref); + assert_eq!(fields, &[field(2), field(3), field(4)]) + } + + #[test] + fn decode_name_ref_header_field() { + let mut buf = vec![]; + HeaderPrefix::new(2, 2, 4, TABLE_SIZE).encode(&mut buf); + LiteralWithNameRef::new_dynamic(1, "new bar1") + .encode(&mut buf) + .unwrap(); + LiteralWithNameRef::new_static(18, "PUT") + .encode(&mut buf) + .unwrap(); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(4)); + let Decoded { + fields, dyn_ref, .. + } = decoder.decode_header(&mut read).unwrap(); + assert!(dyn_ref); + assert_eq!( + fields, + &[ + field(1).with_value("new bar1"), + StaticTable::get(18).unwrap().with_value("PUT") + ] + ) + } + + #[test] + fn decode_post_base_name_ref_header_field() { + let mut buf = vec![]; + HeaderPrefix::new(2, 2, 4, TABLE_SIZE).encode(&mut buf); + LiteralWithPostBaseNameRef::new(0, "new bar3") + .encode(&mut buf) + .unwrap(); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(4)); + let Decoded { fields, .. } = decoder.decode_header(&mut read).unwrap(); + assert_eq!(fields, &[field(3).with_value("new bar3")]); + } + + #[test] + fn decode_without_name_ref_header_field() { + let mut buf = vec![]; + HeaderPrefix::new(0, 0, 0, TABLE_SIZE).encode(&mut buf); + Literal::new("foo", "bar").encode(&mut buf).unwrap(); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(0)); + let Decoded { fields, .. } = decoder.decode_header(&mut read).unwrap(); + assert_eq!( + fields, + &[HeaderField::new(b"foo".to_vec(), b"bar".to_vec())] + ); + } + + // Largest Reference = 4 + // | Base Index = 0 + // | | + // foo4 foo3 foo2 foo1 + // +---+-----+-----+-----+ + // | 4 | 3 | 2 | 1 | Absolute Index + // +---+-----+-----+-----+ + // Relative Index + // +---+-----+-----+-----+ + // | 2 | 2 | 1 | 0 | Post-Base Index + // +---+-----+-----+-----+ + + #[test] + fn decode_single_pass_encoded() { + let mut buf = vec![]; + HeaderPrefix::new(4, 0, 4, TABLE_SIZE).encode(&mut buf); + IndexedWithPostBase(0).encode(&mut buf); + IndexedWithPostBase(1).encode(&mut buf); + IndexedWithPostBase(2).encode(&mut buf); + IndexedWithPostBase(3).encode(&mut buf); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(4)); + let Decoded { fields, .. } = decoder.decode_header(&mut read).unwrap(); + assert_eq!(fields, &[field(1), field(2), field(3), field(4)]); + } + + #[test] + fn largest_ref_greater_than_max_entries() { + let max_entries = TABLE_SIZE / 32; + // some fields evicted + let table = build_table_with_size(max_entries + 10); + let mut buf = vec![]; + + // Pre-base relative reference + HeaderPrefix::new( + max_entries + 5, + max_entries + 5, + max_entries + 10, + TABLE_SIZE, + ) + .encode(&mut buf); + Indexed::Dynamic(10).encode(&mut buf); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(build_table_with_size(max_entries + 10)); + let Decoded { fields, .. } = decoder.decode_header(&mut read).expect("decode"); + assert_eq!(fields, &[field(max_entries - 5)]); + + let mut buf = vec![]; + + // Post-base reference + HeaderPrefix::new( + max_entries + 10, + max_entries + 5, + max_entries + 10, + TABLE_SIZE, + ) + .encode(&mut buf); + IndexedWithPostBase(0).encode(&mut buf); + IndexedWithPostBase(4).encode(&mut buf); + + let mut read = Cursor::new(&buf); + let decoder = Decoder::from(table); + let Decoded { fields, .. } = decoder.decode_header(&mut read).unwrap(); + assert_eq!(fields, &[field(max_entries + 6), field(max_entries + 10)]); + } +} diff --git a/h3/src/qpack2/decoder2/decoder.rs b/h3/src/qpack2/decoder2/decoder.rs new file mode 100644 index 00000000..3e296703 --- /dev/null +++ b/h3/src/qpack2/decoder2/decoder.rs @@ -0,0 +1,34 @@ +//! This module is responsible for decoding Headers + +use std::{sync::Arc, task::Poll}; + +use tokio::sync::Notify; + +use super::dynamic_table::RequestStreamDynamicTableForDecoder; + +/// Decoder for QPACK. Each RequestStream has a instance of this decoder +#[derive(Debug, Clone)] +pub(crate) struct Decoder { + /// The dynamic table + state: RequestStreamDynamicTableForDecoder, +} + +impl Decoder { + /// Creates a new decoder + pub fn new(state: RequestStreamDynamicTableForDecoder) -> Self { + Self { state } + } + fn poll_new_read(&mut self, cx: &mut std::task::Context) -> Poll<()> { + // Poll the blocked stream + let x = self.state.inner.table.read(); + todo!() + } + /// Sets the notifier for the blocked stream + pub fn set_blocked(mut self) { + self.state + .inner + .blocked_streams + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + // self.blocked = Some(self.state.event.clone()); + } +} diff --git a/h3/src/qpack2/decoder2/decoder_stream_handler.rs b/h3/src/qpack2/decoder2/decoder_stream_handler.rs new file mode 100644 index 00000000..dc390cd0 --- /dev/null +++ b/h3/src/qpack2/decoder2/decoder_stream_handler.rs @@ -0,0 +1,161 @@ +//! This module is responsible for handling decoder_send and encoder_recv streams + +use std::task::{ready, Poll}; + +use bytes::Buf; +use tokio::sync::mpsc; + +use crate::{ + error::internal_error::InternalConnectionError, + quic::{self, RecvStream}, + stream::BufRecvStream, +}; + +use super::{decoder, dynamic_table::MainDynamicTableForDecoder}; + +pub(crate) struct DecoderStreamHandler +where + C: quic::Connection, + B: bytes::Buf, +{ + decoder_send: C::SendStream, + pub(crate) encoder_recv: BufRecvStream, + table: super::dynamic_table::MainDynamicTableForDecoder, + /// When decoder events are triggered, this channel is used to notify the decoder + decoder_event: mpsc::Receiver<()>, +} + +impl DecoderStreamHandler +where + C: quic::Connection, + B: bytes::Buf, +{ + /// Creates a new decoder stream handler + pub fn new(decoder_send: C::SendStream, encoder_recv: BufRecvStream) -> Self { + let (table, event) = super::dynamic_table::MainDynamicTableForDecoder::new(); + + Self { + decoder_send, + encoder_recv, + table: table, + decoder_event: event, + } + } + /// Returns a Decoder with a link to the same dynamic table + pub fn get_decoder(&self) -> super::decoder::Decoder { + super::decoder::Decoder::new(self.table.shared()) + } + + /// Function to handle the encoder streams + async fn handle_qpack_decoder(self) -> Result<(), InternalConnectionError> { + let Self { + decoder_send, + encoder_recv, + table, + decoder_event, + } = self; + + Err(tokio::select! { + err = recv_encoder_instruction(table, encoder_recv) => err, + err = send_decoder_instructions(decoder_send, decoder_event) => err, + }) + } +} + +/// Function to handle the encoder_recv stream +/// +/// This function is responsible for receiving the encoder instructions from the peer +/// The future resolves only in the event of an error +async fn recv_encoder_instruction( + table: MainDynamicTableForDecoder, + encoder_recv: S, +) -> InternalConnectionError +where + S: RecvStream, +{ + loop { + // Wait for encoder instructions from peer + + // Decode the instructions + + // Update the dynamic table + } +} + +/// Function to handle the decoder_send stream +/// +/// This function is responsible for sending the decoder instructions to the peer +/// The future resolves only in the event of an error +async fn send_decoder_instructions( + decoder_send: S, + decoder_event: mpsc::Receiver<()>, +) -> InternalConnectionError +where + S: quic::SendStream, + B: bytes::Buf, +{ + loop { + // Wait for decoder events + + // Send the decoder instructions + } +} + + +/*fn parse_instruction(read: &mut R) -> Result, InternalConnectionError> { + if read.remaining() < 1 { + return Ok(None); + } + + let mut buf = Cursor::new(read.chunk()); + let first = buf.chunk()[0]; + let instruction = match EncoderInstruction::decode(first) { + EncoderInstruction::Unknown => return Err(DecoderError::UnknownPrefix(first)), + EncoderInstruction::DynamicTableSizeUpdate => { + DynamicTableSizeUpdate::decode(&mut buf)?.map(|x| Instruction::TableSizeUpdate(x.0)) + } + EncoderInstruction::InsertWithoutNameRef => InsertWithoutNameRef::decode(&mut buf)? + .map(|x| Instruction::Insert(HeaderField::new(x.name, x.value))), + EncoderInstruction::Duplicate => match Duplicate::decode(&mut buf)? { + Some(Duplicate(index)) => { + Some(Instruction::Insert(self.table.get_relative(index)?.clone())) + } + None => None, + }, + EncoderInstruction::InsertWithNameRef => match InsertWithNameRef::decode(&mut buf)? { + Some(InsertWithNameRef::Static { index, value }) => Some(Instruction::Insert( + StaticTable::get(index)?.with_value(value), + )), + Some(InsertWithNameRef::Dynamic { index, value }) => Some(Instruction::Insert( + self.table.get_relative(index)?.with_value(value), + )), + None => None, + }, + }; + + if instruction.is_some() { + let pos = buf.position(); + read.advance(pos as usize); + } + + Ok(instruction) +} + + + +#[derive(PartialEq)] +enum Instruction { + Insert(HeaderField), + TableSizeUpdate(usize), +} + +impl fmt::Debug for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Instruction::Insert(h) => write!(f, "Instruction::Insert {{ {} }}", h), + Instruction::TableSizeUpdate(n) => { + write!(f, "Instruction::TableSizeUpdate {{ {} }}", n) + } + } + } +}*/ \ No newline at end of file diff --git a/h3/src/qpack2/decoder2/dynamic_table.rs b/h3/src/qpack2/decoder2/dynamic_table.rs new file mode 100644 index 00000000..21bfba6e --- /dev/null +++ b/h3/src/qpack2/decoder2/dynamic_table.rs @@ -0,0 +1,73 @@ +//! Dynamic Table for QPACK encoder +//! Instructions for this dynamic table are received from the peer encoder + +use std::{ + collections::BTreeMap, + sync::{atomic::AtomicUsize, Arc}, +}; + +use tokio::sync::{mpsc, Notify, RwLock}; + +use crate::qpack::HeaderField; + +/// Dynamic Table for QPACK decoder +/// This decoder holds information relevant to the request-streams in an RWLock +#[derive(Debug)] +pub(super) struct MainDynamicTableForDecoder { + /// The dynamic information relevant to the request-streams + shared: RequestStreamDynamicTableForDecoder, +} + +impl MainDynamicTableForDecoder { + /// Creates a new dynamic table for the decoder + pub fn new() -> (Self, mpsc::Receiver<()>) { + let (sender, receiver) = mpsc::channel(10); + + ( + Self { + shared: RequestStreamDynamicTableForDecoder { + inner: Arc::new(InnnerDecoder { + table: RwLock::new(SharedDynamicTableForDecoder { + // TODO: Try out if HashMap is faster + table: BTreeMap::new(), + }), + blocked_streams: AtomicUsize::new(0), + event: Notify::new(), + }), + decoder_event: sender, + }, + }, + receiver, + ) + } + /// Returns a reference to the shared dynamic table + pub fn shared(&self) -> RequestStreamDynamicTableForDecoder { + self.shared.clone() + } +} + +/// Struct, which holds the dynamic table for the request-streams +#[derive(Debug, Clone)] +pub(crate) struct RequestStreamDynamicTableForDecoder { + /// The dynamic table + pub(super) inner: Arc, + /// Send decoder events on the decoder stream + pub(super) decoder_event: mpsc::Sender<()>, +} + +#[derive(Debug)] +pub(crate) struct InnnerDecoder { + /// The dynamic table + pub(super) table: RwLock, + /// Blocked streams + pub(super) blocked_streams: AtomicUsize, + /// Event, which is triggered when the table is updated + pub(super) event: Notify, +} + +/// Shared dynamic table for the decoder +#[derive(Debug)] +pub(crate) struct SharedDynamicTableForDecoder { + /// The dynamic table + table: BTreeMap, +} diff --git a/h3/src/qpack2/decoder2/mod.rs b/h3/src/qpack2/decoder2/mod.rs new file mode 100644 index 00000000..98b55bc7 --- /dev/null +++ b/h3/src/qpack2/decoder2/mod.rs @@ -0,0 +1,6 @@ +//! Internal module for the QPACK decoder. + + +pub(crate) mod dynamic_table; +pub(crate) mod decoder_stream_handler; +pub(crate) mod decoder; \ No newline at end of file diff --git a/h3/src/qpack2/dynamic.rs b/h3/src/qpack2/dynamic.rs new file mode 100644 index 00000000..542e0ccf --- /dev/null +++ b/h3/src/qpack2/dynamic.rs @@ -0,0 +1,1439 @@ +use std::{ + borrow::Cow, + collections::{btree_map::Entry as BTEntry, hash_map::Entry, BTreeMap, HashMap, VecDeque}, +}; + +use super::{field::HeaderField, static_::StaticTable}; +use crate::qpack::vas::{self, VirtualAddressSpace}; + +/** + * https://www.rfc-editor.org/rfc/rfc9204.html#maximum-dynamic-table-capacity + */ +const SETTINGS_MAX_TABLE_CAPACITY_MAX: usize = 1_073_741_823; // 2^30 -1 +const SETTINGS_MAX_BLOCKED_STREAMS_MAX: usize = 65_535; // 2^16 - 1 + +#[derive(Debug, PartialEq)] +pub enum Error { + BadRelativeIndex(usize), + BadPostbaseIndex(usize), + BadIndex(usize), + MaxTableSizeReached, + MaximumTableSizeTooLarge, + MaxBlockedStreamsTooLarge, + UnknownStreamId(u64), + NoTrackingData, + InvalidTrackingCount, +} + +pub struct DynamicTableDecoder<'a> { + table: &'a DynamicTable, + base: usize, +} + +impl<'a> DynamicTableDecoder<'a> { + pub(super) fn get_relative(&self, index: usize) -> Result<&HeaderField, Error> { + let real_index = self.table.vas.relative_base(self.base, index)?; + self.table + .fields + .get(real_index) + .ok_or(Error::BadIndex(real_index)) + } + + pub(super) fn get_postbase(&self, index: usize) -> Result<&HeaderField, Error> { + let real_index = self.table.vas.post_base(self.base, index)?; + self.table + .fields + .get(real_index) + .ok_or(Error::BadIndex(real_index)) + } +} + +pub struct DynamicTableEncoder<'a> { + table: &'a mut DynamicTable, + base: usize, + commited: bool, + stream_id: u64, + block_refs: HashMap, +} + +impl<'a> Drop for DynamicTableEncoder<'a> { + fn drop(&mut self) { + if !self.commited { + // TODO maybe possible to replace and not clone here? + // HOW Err should be handled? + self.table + .track_cancel(self.block_refs.iter().map(|(x, y)| (*x, *y))) + .ok(); + } + } +} + +impl<'a> DynamicTableEncoder<'a> { + pub(super) fn max_size(&self) -> usize { + self.table.max_size + } + + pub(super) fn base(&self) -> usize { + self.base + } + + pub(super) fn total_inserted(&self) -> usize { + self.table.total_inserted() + } + + pub(super) fn commit(&mut self, largest_ref: usize) { + self.table + .track_block(self.stream_id, self.block_refs.clone()); + self.table.register_blocked(largest_ref); + self.commited = true; + } + + pub(super) fn find(&mut self, field: &HeaderField) -> DynamicLookupResult { + self.lookup_result(self.table.field_map.get(field).cloned()) + } + + fn lookup_result(&mut self, absolute: Option) -> DynamicLookupResult { + match absolute { + Some(absolute) if absolute <= self.base => { + self.track_ref(absolute); + DynamicLookupResult::Relative { + index: self.base - absolute, + absolute, + } + } + Some(absolute) if absolute > self.base => { + self.track_ref(absolute); + DynamicLookupResult::PostBase { + index: absolute - self.base - 1, + absolute, + } + } + _ => DynamicLookupResult::NotFound, + } + } + + pub(super) fn insert(&mut self, field: &HeaderField) -> Result { + if self.table.blocked_count >= self.table.blocked_max { + return Ok(DynamicInsertionResult::NotInserted( + self.find_name(&field.name), + )); + } + + let index = match self.table.insert(field.clone()) { + Ok(Some(index)) => index, + Err(Error::MaxTableSizeReached) | Ok(None) => { + return Ok(DynamicInsertionResult::NotInserted( + self.find_name(&field.name), + )); + } + Err(e) => return Err(e), + }; + self.track_ref(index); + + let field_index = match self.table.field_map.entry(field.clone()) { + Entry::Occupied(mut e) => { + let ref_index = e.insert(index); + self.table + .name_map + .entry(field.name.clone()) + .and_modify(|i| *i = index); + + Some(( + ref_index, + DynamicInsertionResult::Duplicated { + relative: index - ref_index - 1, + postbase: index - self.base - 1, + absolute: index, + }, + )) + } + Entry::Vacant(e) => { + e.insert(index); + None + } + }; + + if let Some((ref_index, result)) = field_index { + self.track_ref(ref_index); + return Ok(result); + } + + if let Some(static_idx) = StaticTable::find_name(&field.name) { + return Ok(DynamicInsertionResult::InsertedWithStaticNameRef { + postbase: index - self.base - 1, + index: static_idx, + absolute: index, + }); + } + + let result = match self.table.name_map.entry(field.name.clone()) { + Entry::Occupied(mut e) => { + let ref_index = e.insert(index); + self.track_ref(ref_index); + + DynamicInsertionResult::InsertedWithNameRef { + postbase: index - self.base - 1, + relative: index - ref_index - 1, + absolute: index, + } + } + Entry::Vacant(e) => { + e.insert(index); + DynamicInsertionResult::Inserted { + postbase: index - self.base - 1, + absolute: index, + } + } + }; + Ok(result) + } + + fn find_name(&mut self, name: &[u8]) -> DynamicLookupResult { + if let Some(index) = StaticTable::find_name(name) { + return DynamicLookupResult::Static(index); + } + + self.lookup_result(self.table.name_map.get(name).cloned()) + } + + fn track_ref(&mut self, reference: usize) { + self.block_refs + .entry(reference) + .and_modify(|c| *c += 1) + .or_insert(1); + self.table.track_ref(reference); + } +} + +#[derive(Debug, PartialEq)] +pub enum DynamicLookupResult { + Static(usize), + Relative { index: usize, absolute: usize }, + PostBase { index: usize, absolute: usize }, + NotFound, +} + +#[derive(Debug, PartialEq)] +pub enum DynamicInsertionResult { + Inserted { + postbase: usize, + absolute: usize, + }, + Duplicated { + relative: usize, + postbase: usize, + absolute: usize, + }, + InsertedWithNameRef { + postbase: usize, + relative: usize, + absolute: usize, + }, + InsertedWithStaticNameRef { + postbase: usize, + index: usize, + absolute: usize, + }, + NotInserted(DynamicLookupResult), +} + +#[derive(Default)] +pub struct DynamicTable { + fields: VecDeque, + curr_size: usize, + max_size: usize, + vas: VirtualAddressSpace, + field_map: HashMap, + name_map: HashMap, usize>, + track_map: BTreeMap, + track_blocks: HashMap>>, + largest_known_received: usize, + blocked_max: usize, + blocked_count: usize, + blocked_streams: BTreeMap, // +} + +impl DynamicTable { + pub fn new() -> DynamicTable { + DynamicTable::default() + } + + pub fn decoder(&self, base: usize) -> DynamicTableDecoder { + DynamicTableDecoder { table: self, base } + } + + pub fn encoder(&mut self, stream_id: u64) -> DynamicTableEncoder { + for (idx, field) in self.fields.iter().enumerate() { + self.name_map + .insert(field.name.clone(), self.vas.index(idx).unwrap()); + self.field_map + .insert(field.clone(), self.vas.index(idx).unwrap()); + } + + DynamicTableEncoder { + base: self.vas.largest_ref(), + table: self, + block_refs: HashMap::new(), + commited: false, + stream_id, + } + } + + pub fn set_max_blocked(&mut self, max: usize) -> Result<(), Error> { + // TODO handle existing data + if max >= SETTINGS_MAX_BLOCKED_STREAMS_MAX { + return Err(Error::MaxBlockedStreamsTooLarge); + } + self.blocked_max = max; + Ok(()) + } + + pub fn set_max_size(&mut self, size: usize) -> Result<(), Error> { + if size > SETTINGS_MAX_TABLE_CAPACITY_MAX { + return Err(Error::MaximumTableSizeTooLarge); + } + + if size >= self.max_size { + self.max_size = size; + return Ok(()); + } + + let required = self.max_size - size; + + if let Some(to_evict) = self.can_free(required)? { + self.evict(to_evict)?; + } + + self.max_size = size; + Ok(()) + } + + pub(super) fn put(&mut self, field: HeaderField) -> Result<(), Error> { + let index = match self.insert(field.clone())? { + Some(index) => index, + None => return Ok(()), + }; + + self.field_map + .entry(field.clone()) + .and_modify(|e| *e = index) + .or_insert(index); + + if StaticTable::find_name(&field.name).is_some() { + return Ok(()); + } + + self.name_map + .entry(field.name.clone()) + .and_modify(|e| *e = index) + .or_insert(index); + Ok(()) + } + + pub(super) fn get_relative(&self, index: usize) -> Result<&HeaderField, Error> { + let real_index = self.vas.relative(index)?; + self.fields + .get(real_index) + .ok_or(Error::BadIndex(real_index)) + } + + pub(super) fn total_inserted(&self) -> usize { + self.vas.total_inserted() + } + + pub(super) fn untrack_block(&mut self, stream_id: u64) -> Result<(), Error> { + let mut entry = self.track_blocks.entry(stream_id); + let block = match entry { + Entry::Occupied(ref mut blocks) if blocks.get().len() > 1 => { + blocks.get_mut().pop_front() + } + Entry::Occupied(blocks) => blocks.remove().pop_front(), + Entry::Vacant { .. } => return Err(Error::UnknownStreamId(stream_id)), + }; + + if let Some(b) = block { + self.track_cancel(b.iter().map(|(x, y)| (*x, *y)))?; + } + Ok(()) + } + + fn insert(&mut self, field: HeaderField) -> Result, Error> { + if self.max_size == 0 { + return Ok(None); + } + + match self.can_free(field.mem_size())? { + None => return Ok(None), + Some(to_evict) => { + self.evict(to_evict)?; + } + } + + self.curr_size += field.mem_size(); + self.fields.push_back(field); + let absolute = self.vas.add(); + + Ok(Some(absolute)) + } + + fn evict(&mut self, to_evict: usize) -> Result<(), Error> { + for _ in 0..to_evict { + let field = self.fields.pop_front().ok_or(Error::MaxTableSizeReached)?; //TODO better type + self.curr_size -= field.mem_size(); + + self.vas.drop(); + + if let Entry::Occupied(e) = self.name_map.entry(field.name.clone()) { + if self.vas.evicted(*e.get()) { + e.remove(); + } + } + + if let Entry::Occupied(e) = self.field_map.entry(field) { + if self.vas.evicted(*e.get()) { + e.remove(); + } + } + } + Ok(()) + } + + fn can_free(&mut self, required: usize) -> Result, Error> { + if required > self.max_size { + return Err(Error::MaxTableSizeReached); + } + + if self.max_size - self.curr_size >= required { + return Ok(Some(0)); + } + let lower_bound = self.max_size - required; + + let mut hypothetic_mem_size = self.curr_size; + let mut evictable = 0; + + for (idx, to_evict) in self.fields.iter().enumerate() { + if hypothetic_mem_size <= lower_bound { + break; + } + + if self.is_tracked(self.vas.index(idx).unwrap()) { + // TODO handle out of bounds error + break; + } + + evictable += 1; + hypothetic_mem_size -= to_evict.mem_size(); + } + + if required <= self.max_size - hypothetic_mem_size { + Ok(Some(evictable)) + } else { + Ok(None) + } + } + + fn track_ref(&mut self, reference: usize) { + self.track_map + .entry(reference) + .and_modify(|c| *c += 1) + .or_insert(1); + } + + fn is_tracked(&self, reference: usize) -> bool { + matches!(self.track_map.get(&reference), Some(count) if *count > 0) + } + + fn track_block(&mut self, stream_id: u64, refs: HashMap) { + match self.track_blocks.entry(stream_id) { + Entry::Occupied(mut e) => { + e.get_mut().push_back(refs); + } + Entry::Vacant(e) => { + let mut blocks = VecDeque::with_capacity(2); + blocks.push_back(refs); + e.insert(blocks); + } + } + } + + fn track_cancel(&mut self, refs: T) -> Result<(), Error> + where + T: IntoIterator, + { + for (reference, count) in refs { + match self.track_map.entry(reference) { + BTEntry::Occupied(mut e) => { + use std::cmp::Ordering; + match e.get().cmp(&count) { + Ordering::Less => { + return Err(Error::InvalidTrackingCount); + } + Ordering::Equal => { + e.remove(); // TODO just pu 0 ? + } + _ => *e.get_mut() -= count, + } + } + BTEntry::Vacant(_) => return Err(Error::InvalidTrackingCount), + } + } + Ok(()) + } + + fn register_blocked(&mut self, largest: usize) { + if largest <= self.largest_known_received { + return; + } + + self.blocked_count += 1; + + match self.blocked_streams.entry(largest) { + BTEntry::Occupied(mut e) => { + let entry = e.get_mut(); + *entry += 1; + } + BTEntry::Vacant(e) => { + e.insert(1); + } + } + } + + pub fn update_largest_received(&mut self, increment: usize) { + self.largest_known_received += increment; + + if self.blocked_count == 0 { + return; + } + + let blocked = self + .blocked_streams + .split_off(&(self.largest_known_received + 1)); + let acked = std::mem::replace(&mut self.blocked_streams, blocked); + + if !acked.is_empty() { + let total_acked = acked.iter().fold(0usize, |t, (_, v)| t + v); + self.blocked_count -= total_acked; + } + } + + pub(super) fn max_mem_size(&self) -> usize { + self.max_size + } +} + +impl From for Error { + fn from(e: vas::Error) -> Self { + match e { + vas::Error::RelativeIndex(e) => Error::BadRelativeIndex(e), + vas::Error::PostbaseIndex(e) => Error::BadPostbaseIndex(e), + vas::Error::Index(e) => Error::BadIndex(e), + } + } +} + +#[cfg(test)] +mod tests { + #![allow(clippy::identity_op)] + + use super::*; + use crate::qpack::{static_::StaticTable, tests::helpers::build_table}; + + const STREAM_ID: u64 = 0x4; + + // Test on table size + /** + * https://tools.ietf.org/html/rfc7541#section-4.1 + * "The size of the dynamic table is the sum of the size of its entries." + */ + #[test] + fn test_table_size_is_sum_of_its_entries() { + let mut table = build_table(); + + let fields: [(&'static str, &'static str); 2] = [ + ("Name", "Value"), + ("Another-Name", ""), // no value + ]; + let table_size = 4 + 5 + 12 + 0 + /* ESTIMATED_OVERHEAD_BYTES */ 32 * 2; + + for pair in fields.iter() { + let field = HeaderField::new(pair.0, pair.1); + table.insert(field).unwrap(); + } + + assert_eq!(table.curr_size, table_size); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-maximum-dynamic-table-capac + * "To bound the memory requirements of the decoder, the decoder + * limits the maximum value the encoder is permitted to set for the + * dynamic table capacity. In HTTP/3, this limit is determined by + * the value of SETTINGS_QPACK_MAX_TABLE_CAPACITY sent by the + * decoder; see Section 5." + */ + #[test] + fn test_try_set_too_large_maximum_table_size() { + let mut table = build_table(); + let invalid_size = SETTINGS_MAX_TABLE_CAPACITY_MAX + 10; + let res_change = table.set_max_size(invalid_size); + assert_eq!(res_change, Err(Error::MaximumTableSizeTooLarge)); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table-capacity-and- + * "This mechanism can be used to completely clear entries from the + * dynamic table by setting a maximum size of 0, which can subsequently + * be restored." + */ + #[test] + fn test_maximum_table_size_can_reach_zero() { + let mut table = build_table(); + let res_change = table.set_max_size(0); + assert!(res_change.is_ok()); + assert_eq!(table.max_mem_size(), 0); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-maximum-dynamic-table-capac + * "To bound the memory requirements of the decoder, the decoder + * limits the maximum value the encoder is permitted to set for the + * dynamic table capacity. In HTTP/3, this limit is determined by + * the value of SETTINGS_QPACK_MAX_TABLE_CAPACITY sent by the + * decoder; see Section 5." + */ + #[test] + fn test_maximum_table_size_can_reach_maximum() { + let mut table = build_table(); + let res_change = table.set_max_size(SETTINGS_MAX_TABLE_CAPACITY_MAX); + assert!(res_change.is_ok()); + assert_eq!(table.max_mem_size(), SETTINGS_MAX_TABLE_CAPACITY_MAX); + } + + // Test duplicated fields + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table + * "The dynamic table can contain duplicate entries (i.e., entries with + * the same name and same value). Therefore, duplicate entries MUST NOT + * be treated as an error by a decoder." + */ + #[test] + fn test_table_supports_duplicated_entries() { + let mut table = build_table(); + table.insert(HeaderField::new("Name", "Value")).unwrap(); + table.insert(HeaderField::new("Name", "Value")).unwrap(); + assert_eq!(table.fields.len(), 2); + } + + // Test adding fields + + /** functional test */ + #[test] + fn test_add_field_fitting_free_space() { + let mut table = build_table(); + + table.insert(HeaderField::new("Name", "Value")).unwrap(); + assert_eq!(table.fields.len(), 1); + } + + /** functional test */ + #[test] + fn test_add_field_reduce_free_space() { + let mut table = build_table(); + + let field = HeaderField::new("Name", "Value"); + table.insert(field.clone()).unwrap(); + assert_eq!(table.curr_size, field.mem_size()); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table-capacity-and- + * "Before a new entry is added to the dynamic table, entries are evicted + * from the end of the dynamic table until the size of the dynamic table + * is less than or equal to (maximum size - new entry size) or until the + * table is empty." + */ + #[test] + fn test_add_field_drop_older_fields_to_have_enough_space() { + let mut table = build_table(); + + table.insert(HeaderField::new("Name-A", "Value-A")).unwrap(); + table.insert(HeaderField::new("Name-B", "Value-B")).unwrap(); + let perfect_size = table.curr_size; + assert!(table.set_max_size(perfect_size).is_ok()); + + let field = HeaderField::new("Name-Large", "Value-Large"); + table.insert(field).unwrap(); + + assert_eq!(table.fields.len(), 1); + assert_eq!( + table.fields.front(), + Some(&HeaderField::new("Name-Large", "Value-Large")) + ); + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table-capacity-and- + * "It is an error if the encoder attempts to add an entry that is + * larger than the dynamic table capacity; the decoder MUST treat + * this as a connection error of type QPACK_ENCODER_STREAM_ERROR." + */ + #[test] + fn test_try_add_field_larger_than_maximum_size() { + let mut table = build_table(); + + table.insert(HeaderField::new("Name-A", "Value-A")).unwrap(); + let perfect_size = table.curr_size; + assert!(table.set_max_size(perfect_size).is_ok()); + + let field = HeaderField::new("Name-Large", "Value-Large"); + assert_eq!(table.insert(field), Err(Error::MaxTableSizeReached)); + } + + fn insert_fields(table: &mut DynamicTable, fields: Vec) { + for field in fields { + table.insert(field).unwrap(); + } + } + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-dynamic-table-capacity-and- + * "This mechanism can be used to completely clear entries from the + * dynamic table by setting a maximum size of 0, which can subsequently + * be restored." + */ + #[test] + fn test_set_maximum_table_size_to_zero_clear_entries() { + let mut table = build_table(); + insert_fields( + &mut table, + vec![ + HeaderField::new("Name", "Value"), + HeaderField::new("Name", "Value"), + ], + ); + assert_eq!(table.fields.len(), 2); + + table.set_max_size(0).unwrap(); + assert_eq!(table.fields.len(), 0); + } + + /** functional test */ + #[test] + fn test_eviction_is_fifo() { + let mut table = build_table(); + + insert_fields( + &mut table, + vec![ + HeaderField::new("Name-A", "Value-A"), + HeaderField::new("Name-B", "Value-B"), + ], + ); + let perfect_size = table.curr_size; + assert!(table.set_max_size(perfect_size).is_ok()); + + insert_fields(&mut table, vec![HeaderField::new("Name-C", "Value-C")]); + + assert_eq!( + table.fields.front(), + Some(&HeaderField::new("Name-B", "Value-B")) + ); + assert_eq!( + table.fields.get(1), + Some(&HeaderField::new("Name-C", "Value-C")) + ); + assert_eq!(table.fields.get(2), None); + } + + #[test] + fn encoder_build() { + let mut table = build_table(); + let field_a = HeaderField::new("Name-A", "Value-A"); + let field_b = HeaderField::new("Name-B", "Value-B"); + insert_fields(&mut table, vec![field_a.clone(), field_b.clone()]); + + let encoder = table.encoder(STREAM_ID); + assert_eq!(encoder.base, 2); + assert_eq!(encoder.table.name_map.len(), 2); + assert_eq!(encoder.table.field_map.len(), 2); + assert_eq!(encoder.table.name_map.get(&field_a.name).copied(), Some(1)); + assert_eq!(encoder.table.name_map.get(&field_b.name).copied(), Some(2)); + assert_eq!(encoder.table.field_map.get(&field_a).copied(), Some(1)); + assert_eq!(encoder.table.field_map.get(&field_b).copied(), Some(2)); + } + + #[test] + fn encoder_find_relative() { + let mut table = build_table(); + let field_a = HeaderField::new("Name-A", "Value-A"); + let field_b = HeaderField::new("Name-B", "Value-B"); + insert_fields(&mut table, vec![field_a.clone(), field_b.clone()]); + + let mut encoder = table.encoder(STREAM_ID); + assert_eq!( + encoder.find(&field_a), + DynamicLookupResult::Relative { + index: 1, + absolute: 1 + } + ); + assert_eq!( + encoder.find(&field_b), + DynamicLookupResult::Relative { + index: 0, + absolute: 2 + } + ); + assert_eq!( + encoder.find(&HeaderField::new("Name-C", "Value-C")), + DynamicLookupResult::NotFound + ); + assert_eq!( + encoder.find_name(&field_a.name), + DynamicLookupResult::Relative { + index: 1, + absolute: 1 + } + ); + assert_eq!( + encoder.find_name(&field_b.name), + DynamicLookupResult::Relative { + index: 0, + absolute: 2 + } + ); + assert_eq!( + encoder.find_name(&b"Name-C"[..]), + DynamicLookupResult::NotFound + ); + } + + #[test] + fn encoder_insert() { + let mut table = build_table(); + let field_a = HeaderField::new("Name-A", "Value-A"); + let field_b = HeaderField::new("Name-B", "Value-B"); + insert_fields(&mut table, vec![field_a.clone(), field_b.clone()]); + + let mut encoder = table.encoder(STREAM_ID); + assert_eq!( + encoder.insert(&field_a), + Ok(DynamicInsertionResult::Duplicated { + postbase: 0, + relative: 1, + absolute: 3 + }) + ); + assert_eq!( + encoder.insert(&field_b.with_value("New Value-B")), + Ok(DynamicInsertionResult::InsertedWithNameRef { + postbase: 1, + relative: 1, + absolute: 4, + }) + ); + assert_eq!( + encoder.insert(&field_b.with_value("Newer Value-B")), + Ok(DynamicInsertionResult::InsertedWithNameRef { + postbase: 2, + relative: 0, + absolute: 5, + }) + ); + + let field_c = HeaderField::new("Name-C", "Value-C"); + assert_eq!( + encoder.insert(&field_c), + Ok(DynamicInsertionResult::Inserted { + postbase: 3, + absolute: 6, + }) + ); + + assert_eq!(encoder.table.fields.len(), 6); + + assert_eq!( + encoder.table.fields, + &[ + field_a.clone(), + field_b.clone(), + field_a.clone(), + field_b.with_value("New Value-B"), + field_b.with_value("Newer Value-B"), + field_c + ] + ); + assert_eq!(encoder.table.name_map.get(&field_a.name).copied(), Some(3)); + assert_eq!(encoder.table.name_map.get(&field_b.name).copied(), Some(5)); + assert_eq!(encoder.table.field_map.get(&field_a).copied(), Some(3)); + assert_eq!(encoder.table.field_map.get(&field_b).copied(), Some(2)); + assert_eq!( + encoder + .table + .field_map + .get(&field_b.with_value("New Value-B")) + .copied(), + Some(4) + ); + assert_eq!( + encoder + .table + .field_map + .get(&field_b.with_value("Newer Value-B")) + .copied(), + Some(5) + ); + } + + #[test] + fn encode_insert_in_empty() { + let mut table = build_table(); + let field_a = HeaderField::new("Name-A", "Value-A"); + + let mut encoder = table.encoder(STREAM_ID); + assert_eq!( + encoder.insert(&field_a), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 1, + }) + ); + + assert_eq!(encoder.table.fields.len(), 1); + assert_eq!(encoder.table.fields, &[field_a.clone()]); + assert_eq!(encoder.table.name_map.get(&field_a.name).copied(), Some(1)); + assert_eq!(encoder.table.field_map.get(&field_a).copied(), Some(1)); + } + + #[test] + fn insert_static() { + let mut table = build_table(); + let field = HeaderField::new(":method", "Value-A"); + table.insert(field.clone()).unwrap(); + + assert_eq!(StaticTable::find_name(&field.name), Some(15)); + let mut encoder = table.encoder(STREAM_ID); + assert_eq!( + encoder.insert(&field), + Ok(DynamicInsertionResult::Duplicated { + relative: 0, + postbase: 0, + absolute: 2 + }) + ); + assert_eq!( + encoder.insert(&field.with_value("Value-B")), + Ok(DynamicInsertionResult::InsertedWithStaticNameRef { + postbase: 1, + index: 15, + absolute: 3 + }) + ); + assert_eq!( + encoder.insert(&HeaderField::new(":path", "/baz")), + Ok(DynamicInsertionResult::InsertedWithStaticNameRef { + postbase: 2, + index: 1, + absolute: 4, + }) + ); + assert_eq!(encoder.table.fields.len(), 4); + } + + #[test] + fn cannot_insert_field_greater_than_total_size() { + let mut table = build_table(); + table.set_max_size(33).unwrap(); + let mut encoder = table.encoder(4); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "bar")), + Ok(DynamicInsertionResult::NotInserted( + DynamicLookupResult::NotFound + )) + ); + } + + #[test] + fn encoder_maps_are_cleaned_on_eviction() { + let mut table = build_table(); + table.set_max_size(64).unwrap(); + + { + let mut encoder = table.encoder(4); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "bar")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 1 + }) + ); + encoder.commit(1); + } + table.untrack_block(4).unwrap(); + + { + let mut encoder = table.encoder(4); + assert_eq!( + encoder.insert(&HeaderField::new("foo2", "bar")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 2 + }) + ); + assert_eq!( + encoder.find(&HeaderField::new("foo", "bar")), + DynamicLookupResult::NotFound + ); + assert_eq!(encoder.find_name(b"foo"), DynamicLookupResult::NotFound); + encoder.commit(2); + } + } + + #[test] + fn encoder_can_evict_unreferenced() { + let mut table = build_table(); + table.set_max_size(63).unwrap(); + table.insert(HeaderField::new("foo", "bar")).unwrap(); + + assert_eq!(table.fields.len(), 1); + assert_eq!( + table.encoder(4).insert(&HeaderField::new("baz", "quxx")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 2, + }) + ); + assert_eq!(table.fields.len(), 1); + } + + #[test] + fn encoder_insertion_tracks_ref() { + let mut table = build_table(); + let mut encoder = table.encoder(4); + assert_eq!( + encoder.insert(&HeaderField::new("baz", "quxx")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 1, + }) + ); + assert_eq!(encoder.table.track_map.get(&1).copied(), Some(1)); + assert_eq!(encoder.block_refs.get(&1).copied(), Some(1)); + } + + #[test] + fn encoder_insertion_refs_commited() { + let mut table = build_table(); + let stream_id = 42; + { + let mut encoder = table.encoder(stream_id); + for idx in 1..4 { + encoder + .insert(&HeaderField::new(format!("foo{}", idx), "quxx")) + .unwrap(); + } + assert_eq!(encoder.block_refs.len(), 3); + encoder.commit(2); + } + + for idx in 1..4 { + assert!(table.is_tracked(idx)); + assert_eq!(table.track_map.get(&1), Some(&1)); + } + let track_blocks = table.track_blocks; + let block = track_blocks.get(&stream_id).unwrap().front().unwrap(); + assert_eq!(block.get(&1), Some(&1)); + assert_eq!(block.get(&2), Some(&1)); + assert_eq!(block.get(&3), Some(&1)); + } + + #[test] + fn encoder_insertion_refs_not_commited() { + let mut table = build_table(); + table.track_blocks = HashMap::new(); + let stream_id = 42; + { + let mut encoder = table.encoder(stream_id); + for idx in 1..4 { + encoder + .insert(&HeaderField::new(format!("foo{}", idx), "quxx")) + .unwrap(); + } + assert_eq!(encoder.block_refs.len(), 3); + } // dropped without ::commit() + + assert_eq!(table.track_map.len(), 0); + assert_eq!(table.track_blocks.len(), 0); + } + + #[test] + fn encoder_insertion_with_ref_tracks_both() { + let mut table = build_table(); + table.insert(HeaderField::new("foo", "bar")).unwrap(); + table.track_blocks = HashMap::new(); + + let stream_id = 42; + let mut encoder = table.encoder(stream_id); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "quxx")), + Ok(DynamicInsertionResult::InsertedWithNameRef { + postbase: 0, + relative: 0, + absolute: 2, + }) + ); + + assert_eq!(encoder.table.track_map.get(&1), Some(&1)); + assert_eq!(encoder.table.track_map.get(&2), Some(&1)); + assert_eq!(encoder.block_refs.get(&1), Some(&1)); + assert_eq!(encoder.block_refs.get(&2), Some(&1)); + } + + #[test] + fn encoder_ref_count_are_incremented() { + let mut table = build_table(); + table.insert(HeaderField::new("foo", "bar")).unwrap(); + table.track_blocks = HashMap::new(); + table.track_ref(1); + + let stream_id = 42; + { + let mut encoder = table.encoder(stream_id); + encoder.track_ref(1); + encoder.track_ref(2); + encoder.track_ref(2); + + assert_eq!(encoder.table.track_map.get(&1), Some(&2)); + assert_eq!(encoder.table.track_map.get(&2), Some(&2)); + assert_eq!(encoder.block_refs.get(&1), Some(&1)); + assert_eq!(encoder.block_refs.get(&2), Some(&2)); + } + + // check ref count is correctly decremented after uncommited drop() + assert_eq!(table.track_map.get(&1), Some(&1)); + assert_eq!(table.track_map.get(&2), None); + } + + #[test] + fn encoder_does_not_evict_referenced() { + let mut table = build_table(); + table.set_max_size(95).unwrap(); + table.insert(HeaderField::new("foo", "bar")).unwrap(); + + let stream_id = 42; + let mut encoder = table.encoder(stream_id); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "quxx")), + Ok(DynamicInsertionResult::InsertedWithNameRef { + postbase: 0, + relative: 0, + absolute: 2, + }) + ); + assert!(encoder.table.is_tracked(1)); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "baz")), + Ok(DynamicInsertionResult::NotInserted( + DynamicLookupResult::PostBase { + index: 0, + absolute: 2, + } + )) + ); + assert_eq!(encoder.table.fields.len(), 2); + } + + fn tracked_table(stream_id: u64) -> DynamicTable { + let mut table = build_table(); + table.track_blocks = HashMap::new(); + { + let mut encoder = table.encoder(stream_id); + for idx in 1..4 { + encoder + .insert(&HeaderField::new(format!("foo{}", idx), "quxx")) + .unwrap(); + } + assert_eq!(encoder.block_refs.len(), 3); + encoder.commit(3); + } + table + } + + #[test] + fn untrack_block() { + let mut table = tracked_table(42); + assert_eq!(table.track_map.len(), 3); + assert_eq!(table.track_blocks.len(), 1); + table.untrack_block(42).unwrap(); + assert_eq!(table.track_map.len(), 0); + assert_eq!(table.track_blocks.len(), 0); + } + + #[test] + fn untrack_block_not_in_map() { + let mut table = tracked_table(42); + table.track_map.remove(&2); + assert_eq!(table.untrack_block(42), Err(Error::InvalidTrackingCount)); + } + + #[test] + fn untrack_block_wrong_count() { + let mut table = tracked_table(42); + table.track_blocks.entry(42).and_modify(|x| { + x.get_mut(0).unwrap().entry(2).and_modify(|c| *c += 1); + }); + assert_eq!(table.untrack_block(42), Err(Error::InvalidTrackingCount)); + } + + #[test] + fn untrack_bloc_wrong_stream() { + let mut table = tracked_table(41); + assert_eq!(table.untrack_block(42), Err(Error::UnknownStreamId(42))); + } + + #[test] + fn untrack_trailers() { + const STREAM_ID: u64 = 42; + let mut table = tracked_table(STREAM_ID); + { + // encode trailers + let mut encoder = table.encoder(STREAM_ID); + for idx in 4..=9 { + encoder + .insert(&HeaderField::new(format!("foo{}", idx), "quxx")) + .unwrap(); + } + assert_eq!(encoder.block_refs.len(), 6); + encoder.commit(6); + } + assert_eq!(table.untrack_block(STREAM_ID), Ok(())); + assert!(!table.is_tracked(3)); + assert!(table.is_tracked(5)); + assert_eq!(table.untrack_block(STREAM_ID), Ok(())); + assert!(!table.is_tracked(6)); + assert_eq!( + table.untrack_block(STREAM_ID), + Err(Error::UnknownStreamId(STREAM_ID)) + ); + } + + #[test] + fn put_updates_maps() { + let mut table = tracked_table(42); + assert_eq!(table.name_map.len(), 3); + assert_eq!(table.field_map.len(), 3); + + table.put(HeaderField::new("foo", "bar")).unwrap(); + assert_eq!(table.name_map.len(), 4); + assert_eq!(table.field_map.len(), 4); + + let field = HeaderField::new("foo1", "quxx"); + table.put(field.clone()).unwrap(); + assert_eq!(table.name_map.len(), 4); + assert_eq!(table.field_map.len(), 4); + assert_eq!(table.name_map.get(&b"foo1"[..]), Some(&5usize)); + assert_eq!(table.field_map.get(&field), Some(&5usize)); + } + + #[test] + fn blocked_stream_registered() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + assert_eq!(table.blocked_count, 1); + assert_eq!(table.blocked_streams.get(&3), Some(&1usize)) + } + + #[test] + fn blocked_stream_not_registered() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + table + .encoder(44) + .insert(&HeaderField::new("foo", "bar")) + .unwrap(); + // encoder dropped without commit + + assert_eq!(table.blocked_count, 1); + assert_eq!(table.blocked_streams.get(&5), None); + } + + #[test] + fn blocked_stream_register_accumulate() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + { + let mut encoder = table.encoder(44); + + assert_eq!( + encoder.find(&HeaderField::new("foo3", "quxx")), + DynamicLookupResult::Relative { + index: 0, + absolute: 3, + } + ); + // the encoder inserts a reference to foo3 in a block (absolte index = 3) + encoder.commit(3); + } + + assert_eq!(table.blocked_count, 2); + assert_eq!(table.blocked_streams.get(&3), Some(&2)); + } + + #[test] + fn blocked_stream_register_put_smaller() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + { + let mut encoder = table.encoder(44); + encoder.commit(2); + } + + assert_eq!(table.blocked_count, 2); + assert_eq!(table.blocked_streams.get(&2), Some(&1)); + } + + #[test] + fn blocked_stream_register_put_larger() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + { + let mut encoder = table.encoder(44); + encoder.commit(5); + } + + assert_eq!(table.blocked_count, 2); + assert_eq!(table.blocked_streams.get(&5), Some(&1)); + } + + #[test] + fn unblock_stream_smaller() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + { + let mut encoder = table.encoder(44); + encoder.commit(2); + } + + assert_eq!(table.blocked_count, 2); + assert_eq!(table.blocked_streams.get(&2), Some(&1)); + + table.update_largest_received(2); + + assert_eq!(table.blocked_count, 1); + assert_eq!(table.blocked_streams.get(&2), None); + assert_eq!(table.blocked_streams.get(&3), Some(&1)); + } + + #[test] + fn unblock_stream_larger() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + table.encoder(44).commit(2); + table.encoder(46).commit(5); + + assert_eq!(table.blocked_count, 3); + assert_eq!(table.blocked_streams.get(&2), Some(&1)); + assert_eq!(table.blocked_streams.get(&3), Some(&1)); + + table.update_largest_received(5); + + assert_eq!(table.blocked_count, 0); + assert_eq!(table.blocked_streams.len(), 0); + } + + #[test] + fn unblock_stream_decrement() { + let mut table = tracked_table(42); + table.set_max_blocked(100).unwrap(); + + table.encoder(44).commit(3); + + assert_eq!(table.blocked_count, 2); + assert_eq!(table.blocked_streams.get(&3), Some(&2)); + + table.update_largest_received(5); + + assert_eq!(table.blocked_count, 0); + assert_eq!(table.blocked_streams.len(), 0); + } + + #[test] + fn no_insert_when_max_blocked_0() { + let mut table = tracked_table(42); + table.set_max_blocked(0).unwrap(); + + assert_eq!( + table.encoder(44).insert(&HeaderField::new("foo", "bar")), + Ok(DynamicInsertionResult::NotInserted( + DynamicLookupResult::NotFound + )) + ); + } + + #[test] + fn no_insert_after_max_blocked_reached() { + let mut table = tracked_table(42); + table.set_max_blocked(2).unwrap(); + + { + let mut encoder = table.encoder(44); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "bar")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 4 + }) + ); + encoder.commit(4); + } + + assert_eq!(table.blocked_count, 2); + + let mut encoder = table.encoder(46); + assert_eq!( + encoder.insert(&HeaderField::new("foo99", "bar")), + Ok(DynamicInsertionResult::NotInserted( + DynamicLookupResult::NotFound + )) + ); + } + + #[test] + fn insert_again_after_encoder_ack() { + let mut table = tracked_table(42); + table.set_max_blocked(1).unwrap(); + + assert_eq!(table.blocked_count, 1); + + { + let mut encoder = table.encoder(44); + assert_eq!( + encoder.insert(&HeaderField::new("foo99", "bar")), + Ok(DynamicInsertionResult::NotInserted( + DynamicLookupResult::NotFound + )) + ); + encoder.commit(0); + } + + table.update_largest_received(3); + assert_eq!(table.blocked_count, 0); + + let mut encoder = table.encoder(46); + assert_eq!( + encoder.insert(&HeaderField::new("foo", "bar")), + Ok(DynamicInsertionResult::Inserted { + postbase: 0, + absolute: 4 + }) + ); + } +} diff --git a/h3/src/qpack2/encoder.rs b/h3/src/qpack2/encoder.rs new file mode 100644 index 00000000..e78e0e6d --- /dev/null +++ b/h3/src/qpack2/encoder.rs @@ -0,0 +1,655 @@ +use std::{cmp, io::Cursor}; + +use bytes::{Buf, BufMut}; + +use super::{ + block::{ + HeaderPrefix, Indexed, IndexedWithPostBase, Literal, LiteralWithNameRef, + LiteralWithPostBaseNameRef, + }, + dynamic::{ + DynamicInsertionResult, DynamicLookupResult, DynamicTable, DynamicTableEncoder, + Error as DynamicTableError, + }, + parse_error::ParseError, + prefix_int::Error as IntError, + prefix_string::PrefixStringError as StringError, + static_::StaticTable, + stream::{ + DecoderInstruction, Duplicate, DynamicTableSizeUpdate, HeaderAck, InsertCountIncrement, + InsertWithNameRef, InsertWithoutNameRef, StreamCancel, + }, + HeaderField, +}; + +#[derive(Debug, PartialEq)] +pub enum EncoderError { + Insertion(DynamicTableError), + InvalidString(StringError), + InvalidInteger(IntError), + UnknownDecoderInstruction(u8), +} + +impl std::error::Error for EncoderError {} + +impl std::fmt::Display for EncoderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + EncoderError::Insertion(e) => write!(f, "dynamic table insertion: {:?}", e), + EncoderError::InvalidString(e) => write!(f, "could not parse string: {}", e), + EncoderError::InvalidInteger(e) => write!(f, "could not parse integer: {}", e), + EncoderError::UnknownDecoderInstruction(e) => { + write!(f, "got unkown decoder instruction: {}", e) + } + } + } +} + +pub struct Encoder { + table: DynamicTable, +} + +impl Encoder { + pub fn encode( + &mut self, + stream_id: u64, + block: &mut W, + encoder_buf: &mut W, + fields: T, + ) -> Result + where + W: BufMut, + T: IntoIterator, + H: AsRef, + { + let mut required_ref = 0; + let mut block_buf = Vec::new(); + let mut encoder = self.table.encoder(stream_id); + + for field in fields { + if let Some(reference) = + Self::encode_field(&mut encoder, &mut block_buf, encoder_buf, field.as_ref())? + { + required_ref = cmp::max(required_ref, reference); + } + } + + HeaderPrefix::new( + required_ref, + encoder.base(), + encoder.total_inserted(), + encoder.max_size(), + ) + .encode(block); + block.put(block_buf.as_slice()); + + encoder.commit(required_ref); + + Ok(required_ref) + } + + pub fn on_decoder_recv(&mut self, read: &mut R) -> Result<(), EncoderError> { + while let Some(instruction) = Action::parse(read)? { + match instruction { + Action::Untrack(stream_id) => self.table.untrack_block(stream_id)?, + Action::StreamCancel(stream_id) => { + // Untrack block twice, as this stream might have a trailer in addition to + // the header. Failures are ignored as blocks might have been acked before + // cancellation. + if self.table.untrack_block(stream_id).is_ok() { + let _ = self.table.untrack_block(stream_id); + } + } + Action::ReceivedRefIncrement(increment) => { + self.table.update_largest_received(increment) + } + } + } + Ok(()) + } + + fn encode_field( + table: &mut DynamicTableEncoder, + block: &mut Vec, + encoder: &mut W, + field: &HeaderField, + ) -> Result, EncoderError> { + if let Some(index) = StaticTable::find(field) { + Indexed::Static(index).encode(block); + return Ok(None); + } + + if let DynamicLookupResult::Relative { index, absolute } = table.find(field) { + Indexed::Dynamic(index).encode(block); + return Ok(Some(absolute)); + } + + let reference = match table.insert(field)? { + DynamicInsertionResult::Duplicated { + relative, + postbase, + absolute, + } => { + Duplicate(relative).encode(encoder); + IndexedWithPostBase(postbase).encode(block); + Some(absolute) + } + DynamicInsertionResult::Inserted { postbase, absolute } => { + InsertWithoutNameRef::new(field.name.clone(), field.value.clone()) + .encode(encoder)?; + IndexedWithPostBase(postbase).encode(block); + Some(absolute) + } + DynamicInsertionResult::InsertedWithStaticNameRef { + postbase, + index, + absolute, + } => { + InsertWithNameRef::new_static(index, field.value.clone()).encode(encoder)?; + IndexedWithPostBase(postbase).encode(block); + Some(absolute) + } + DynamicInsertionResult::InsertedWithNameRef { + postbase, + relative, + absolute, + } => { + InsertWithNameRef::new_dynamic(relative, field.value.clone()).encode(encoder)?; + IndexedWithPostBase(postbase).encode(block); + Some(absolute) + } + DynamicInsertionResult::NotInserted(lookup_result) => match lookup_result { + DynamicLookupResult::Static(index) => { + LiteralWithNameRef::new_static(index, field.value.clone()).encode(block)?; + None + } + DynamicLookupResult::Relative { index, absolute } => { + LiteralWithNameRef::new_dynamic(index, field.value.clone()).encode(block)?; + Some(absolute) + } + DynamicLookupResult::PostBase { index, absolute } => { + LiteralWithPostBaseNameRef::new(index, field.value.clone()).encode(block)?; + Some(absolute) + } + DynamicLookupResult::NotFound => { + Literal::new(field.name.clone(), field.value.clone()).encode(block)?; + None + } + }, + }; + Ok(reference) + } +} + +impl Default for Encoder { + fn default() -> Self { + Self { + table: DynamicTable::new(), + } + } +} + +pub fn encode_stateless(block: &mut W, fields: T) -> Result +where + W: BufMut, + T: IntoIterator, + H: AsRef, +{ + let mut size = 0; + + HeaderPrefix::new(0, 0, 0, 0).encode(block); + for field in fields { + let field = field.as_ref(); + + if let Some(index) = StaticTable::find(field) { + Indexed::Static(index).encode(block); + } else if let Some(index) = StaticTable::find_name(&field.name) { + LiteralWithNameRef::new_static(index, field.value.clone()).encode(block)?; + } else { + Literal::new(field.name.clone(), field.value.clone()).encode(block)?; + } + + size += field.mem_size() as u64; + } + Ok(size) +} + +#[cfg(test)] +impl From for Encoder { + fn from(table: DynamicTable) -> Encoder { + Encoder { table } + } +} + +// Action to apply to the encoder table, given an instruction received from the decoder. +#[derive(Debug, PartialEq)] +enum Action { + ReceivedRefIncrement(usize), + Untrack(u64), + StreamCancel(u64), +} + +impl Action { + fn parse(read: &mut R) -> Result, EncoderError> { + if read.remaining() < 1 { + return Ok(None); + } + + let mut buf = Cursor::new(read.chunk()); + let first = buf.chunk()[0]; + let instruction = match DecoderInstruction::decode(first) { + DecoderInstruction::Unknown => { + return Err(EncoderError::UnknownDecoderInstruction(first)) + } + DecoderInstruction::InsertCountIncrement => InsertCountIncrement::decode(&mut buf)? + .map(|x| Action::ReceivedRefIncrement(x.0 as usize)), + DecoderInstruction::HeaderAck => { + HeaderAck::decode(&mut buf)?.map(|x| Action::Untrack(x.0)) + } + DecoderInstruction::StreamCancel => { + StreamCancel::decode(&mut buf)?.map(|x| Action::StreamCancel(x.0)) + } + }; + + if instruction.is_some() { + let pos = buf.position(); + read.advance(pos as usize); + } + + Ok(instruction) + } +} + +pub fn set_dynamic_table_size( + table: &mut DynamicTable, + encoder: &mut W, + size: usize, +) -> Result<(), EncoderError> { + table.set_max_size(size)?; + DynamicTableSizeUpdate(size).encode(encoder); + Ok(()) +} + +impl From for EncoderError { + fn from(e: DynamicTableError) -> Self { + EncoderError::Insertion(e) + } +} + +impl From for EncoderError { + fn from(e: StringError) -> Self { + EncoderError::InvalidString(e) + } +} + +impl From for EncoderError { + fn from(e: ParseError) -> Self { + match e { + ParseError::Integer(x) => EncoderError::InvalidInteger(x), + ParseError::String(x) => EncoderError::InvalidString(x), + ParseError::InvalidPrefix(x) => EncoderError::UnknownDecoderInstruction(x), + _ => unreachable!(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::qpack::tests::helpers::{build_table, TABLE_SIZE}; + + #[allow(clippy::type_complexity)] + fn check_encode_field( + init_fields: &[HeaderField], + field: &[HeaderField], + check: &dyn Fn(&mut Cursor<&mut Vec>, &mut Cursor<&mut Vec>), + ) { + let mut table = build_table(); + table.set_max_size(TABLE_SIZE).unwrap(); + check_encode_field_table(&mut table, init_fields, field, 1, check); + } + + #[allow(clippy::type_complexity)] + fn check_encode_field_table( + table: &mut DynamicTable, + init_fields: &[HeaderField], + field: &[HeaderField], + stream_id: u64, + check: &dyn Fn(&mut Cursor<&mut Vec>, &mut Cursor<&mut Vec>), + ) { + for field in init_fields { + table.put(field.clone()).unwrap(); + } + + let mut encoder = Vec::new(); + let mut block = Vec::new(); + let mut enc_table = table.encoder(stream_id); + + for field in field { + Encoder::encode_field(&mut enc_table, &mut block, &mut encoder, field).unwrap(); + } + + enc_table.commit(field.len()); + + let mut read_block = Cursor::new(&mut block); + let mut read_encoder = Cursor::new(&mut encoder); + check(&mut read_block, &mut read_encoder); + } + + #[test] + fn encode_static() { + let field = HeaderField::new(":method", "GET"); + check_encode_field(&[], &[field], &|mut b, e| { + assert_eq!(Indexed::decode(&mut b), Ok(Indexed::Static(17))); + assert_eq!(e.get_ref().len(), 0); + }); + } + + #[test] + fn encode_static_nameref() { + let field = HeaderField::new("location", "/bar"); + check_encode_field(&[], &[field], &|mut b, mut e| { + assert_eq!( + IndexedWithPostBase::decode(&mut b), + Ok(IndexedWithPostBase(0)) + ); + assert_eq!( + InsertWithNameRef::decode(&mut e), + Ok(Some(InsertWithNameRef::new_static(12, "/bar"))) + ); + }); + } + + #[test] + fn encode_static_nameref_indexed_in_dynamic() { + let field = HeaderField::new("location", "/bar"); + check_encode_field(&[field.clone()], &[field], &|mut b, e| { + assert_eq!(Indexed::decode(&mut b), Ok(Indexed::Dynamic(0))); + assert_eq!(e.get_ref().len(), 0); + }); + } + + #[test] + fn encode_dynamic_insert() { + let field = HeaderField::new("foo", "bar"); + check_encode_field(&[], &[field], &|mut b, mut e| { + assert_eq!( + IndexedWithPostBase::decode(&mut b), + Ok(IndexedWithPostBase(0)) + ); + assert_eq!( + InsertWithoutNameRef::decode(&mut e), + Ok(Some(InsertWithoutNameRef::new("foo", "bar"))) + ); + }); + } + + #[test] + fn encode_dynamic_insert_nameref() { + let field = HeaderField::new("foo", "bar"); + check_encode_field( + &[field.clone(), HeaderField::new("baz", "bar")], + &[field.with_value("quxx")], + &|mut b, mut e| { + assert_eq!( + IndexedWithPostBase::decode(&mut b), + Ok(IndexedWithPostBase(0)) + ); + assert_eq!( + InsertWithNameRef::decode(&mut e), + Ok(Some(InsertWithNameRef::new_dynamic(1, "quxx"))) + ); + }, + ); + } + + #[test] + fn encode_literal() { + let mut table = build_table(); + table.set_max_size(0).unwrap(); + let field = HeaderField::new("foo", "bar"); + check_encode_field_table(&mut table, &[], &[field], 1, &|mut b, e| { + assert_eq!(Literal::decode(&mut b), Ok(Literal::new("foo", "bar"))); + assert_eq!(e.get_ref().len(), 0); + }); + } + + #[test] + fn encode_literal_nameref() { + let mut table = build_table(); + table.set_max_size(63).unwrap(); + let field = HeaderField::new("foo", "bar"); + + check_encode_field_table(&mut table, &[], &[field.clone()], 1, &|mut b, _| { + assert_eq!( + IndexedWithPostBase::decode(&mut b), + Ok(IndexedWithPostBase(0)) + ); + }); + check_encode_field_table( + &mut table, + &[field.clone()], + &[field.with_value("quxx")], + 2, + &|mut b, e| { + assert_eq!( + LiteralWithNameRef::decode(&mut b), + Ok(LiteralWithNameRef::new_dynamic(0, "quxx")) + ); + assert_eq!(e.get_ref().len(), 0); + }, + ); + } + + #[test] + fn encode_literal_postbase_nameref() { + let mut table = build_table(); + table.set_max_size(63).unwrap(); + let field = HeaderField::new("foo", "bar"); + check_encode_field_table( + &mut table, + &[], + &[field.clone(), field.with_value("quxx")], + 1, + &|mut b, mut e| { + assert_eq!( + IndexedWithPostBase::decode(&mut b), + Ok(IndexedWithPostBase(0)) + ); + assert_eq!( + LiteralWithPostBaseNameRef::decode(&mut b), + Ok(LiteralWithPostBaseNameRef::new(0, "quxx")) + ); + assert_eq!( + InsertWithoutNameRef::decode(&mut e), + Ok(Some(InsertWithoutNameRef::new("foo", "bar"))) + ); + }, + ); + } + + #[test] + fn encode_with_header_block() { + let mut table = build_table(); + + for idx in 1..5 { + table + .put(HeaderField::new( + format!("foo{}", idx), + format!("bar{}", idx), + )) + .unwrap(); + } + + let mut encoder_buf = Vec::new(); + let mut block = Vec::new(); + let mut encoder = Encoder::from(table); + + let fields = vec![ + HeaderField::new(":method", "GET"), + HeaderField::new("foo1", "bar1"), + HeaderField::new("foo3", "new bar3"), + HeaderField::new(":method", "staticnameref"), + HeaderField::new("newfoo", "newbar"), + ] + .into_iter(); + + assert_eq!( + encoder.encode(1, &mut block, &mut encoder_buf, fields), + Ok(7) + ); + + let mut read_block = Cursor::new(&mut block); + let mut read_encoder = Cursor::new(&mut encoder_buf); + + assert_eq!( + InsertWithNameRef::decode(&mut read_encoder), + Ok(Some(InsertWithNameRef::new_dynamic(1, "new bar3"))) + ); + assert_eq!( + InsertWithNameRef::decode(&mut read_encoder), + Ok(Some(InsertWithNameRef::new_static( + StaticTable::find_name(&b":method"[..]).unwrap(), + "staticnameref" + ))) + ); + assert_eq!( + InsertWithoutNameRef::decode(&mut read_encoder), + Ok(Some(InsertWithoutNameRef::new("newfoo", "newbar"))) + ); + + assert_eq!( + HeaderPrefix::decode(&mut read_block) + .unwrap() + .get(7, TABLE_SIZE), + Ok((7, 4)) + ); + assert_eq!(Indexed::decode(&mut read_block), Ok(Indexed::Static(17))); + assert_eq!(Indexed::decode(&mut read_block), Ok(Indexed::Dynamic(3))); + assert_eq!( + IndexedWithPostBase::decode(&mut read_block), + Ok(IndexedWithPostBase(0)) + ); + assert_eq!( + IndexedWithPostBase::decode(&mut read_block), + Ok(IndexedWithPostBase(1)) + ); + assert_eq!( + IndexedWithPostBase::decode(&mut read_block), + Ok(IndexedWithPostBase(2)) + ); + assert_eq!(read_block.get_ref().len() as u64, read_block.position()); + } + + #[test] + fn decoder_block_ack() { + let mut table = build_table(); + + let field = HeaderField::new("foo", "bar"); + check_encode_field_table( + &mut table, + &[], + &[field.clone(), field.with_value("quxx")], + 2, + &|_, _| {}, + ); + + let mut buf = vec![]; + let mut encoder = Encoder::from(table); + + HeaderAck(2).encode(&mut buf); + let mut cur = Cursor::new(&buf); + assert_eq!(Action::parse(&mut cur), Ok(Some(Action::Untrack(2)))); + + let mut cur = Cursor::new(&buf); + assert_eq!(encoder.on_decoder_recv(&mut cur), Ok(()),); + + let mut cur = Cursor::new(&buf); + assert_eq!( + encoder.on_decoder_recv(&mut cur), + Err(EncoderError::Insertion(DynamicTableError::UnknownStreamId( + 2 + ))) + ); + } + + #[test] + fn decoder_stream_cacnceled() { + let mut table = build_table(); + + let field = HeaderField::new("foo", "bar"); + check_encode_field_table( + &mut table, + &[], + &[field.clone(), field.with_value("quxx")], + 2, + &|_, _| {}, + ); + + let mut buf = vec![]; + + StreamCancel(2).encode(&mut buf); + let mut cur = Cursor::new(&buf); + assert_eq!(Action::parse(&mut cur), Ok(Some(Action::StreamCancel(2)))); + } + + #[test] + fn decoder_accept_truncated() { + let mut buf = vec![]; + StreamCancel(2321).encode(&mut buf); + + let mut cur = Cursor::new(&buf[..2]); // trucated prefix_int + assert_eq!(Action::parse(&mut cur), Ok(None)); + + let mut cur = Cursor::new(&buf); + assert_eq!( + Action::parse(&mut cur), + Ok(Some(Action::StreamCancel(2321))) + ); + } + + #[test] + fn decoder_unknown_stream() { + let mut table = build_table(); + + check_encode_field_table( + &mut table, + &[], + &[HeaderField::new("foo", "bar")], + 2, + &|_, _| {}, + ); + let mut encoder = Encoder::from(table); + + let mut buf = vec![]; + HeaderAck(4).encode(&mut buf); + + let mut cur = Cursor::new(&buf); + assert_eq!( + encoder.on_decoder_recv(&mut cur), + Err(EncoderError::Insertion(DynamicTableError::UnknownStreamId( + 4 + ))) + ); + } + + #[test] + fn insert_count() { + let mut buf = vec![]; + InsertCountIncrement(4).encode(&mut buf); + + let mut cur = Cursor::new(&buf); + assert_eq!( + Action::parse(&mut cur), + Ok(Some(Action::ReceivedRefIncrement(4))) + ); + + let mut encoder = Encoder { + table: build_table(), + }; + + let mut cur = Cursor::new(&buf); + assert_eq!(encoder.on_decoder_recv(&mut cur), Ok(())); + } +} diff --git a/h3/src/qpack2/encoder_instructions.rs b/h3/src/qpack2/encoder_instructions.rs new file mode 100644 index 00000000..fa98b02d --- /dev/null +++ b/h3/src/qpack2/encoder_instructions.rs @@ -0,0 +1,293 @@ +//! Types that can be sent and received in the encoder stream + +use bytes::{Buf, BufMut}; + +use crate::error::internal_error::InternalConnectionError; +use crate::error::Code; + +use super::prefix_int; +use super::prefix_int::Error as IntError; +use super::prefix_string; +use super::prefix_string::PrefixStringError as StringError; + +impl ParseEncoderInstructionError { + fn connection_error(message: String) -> Self { + ParseEncoderInstructionError::ConnectionError(InternalConnectionError::new( + Code::QPACK_ENCODER_STREAM_ERROR, + message, + )) + } +} + +// 4.3. Encoder Instructions +pub enum EncoderInstruction { + // 4.3.1. Set Dynamic Table Capacity + // An encoder informs the decoder of a change to the dynamic table capacity. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 1 | Capacity (5+) | + // +---+---+---+-------------------+ + DynamicTableSizeUpdate(DynamicTableSizeUpdate), + // 4.3.2. Insert With Name Reference + // An encoder adds an entry to the dynamic table where the field name + // matches the field name of an entry stored in the static or the dynamic + // table. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 1 | T | Name Index (6+) | + // +---+---+-----------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + InsertWithNameRef(InsertWithNameRef), + // 4.3.3. Insert With Literal Name + // An encoder adds an entry to the dynamic table where both the field name + // and the field value are represented as string literals. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | H | Name Length (5+) | + // +---+---+---+-------------------+ + // | Name String (Length bytes) | + // +---+---------------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + InsertWithoutNameRef(InsertWithoutNameRef), + // 4.3.4. Duplicate + // An encoder duplicates an existing entry in the dynamic table. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | Index (5+) | + // +---+---+---+-------------------+ + Duplicate(Duplicate), + Unknown, +} + +impl EncoderInstruction { + /*pub fn decode(first: u8) -> Self { + if first & 0b1000_0000 != 0 { + EncoderInstruction::InsertWithNameRef + } else if first & 0b0100_0000 == 0b0100_0000 { + EncoderInstruction::InsertWithoutNameRef + } else if first & 0b1110_0000 == 0 { + EncoderInstruction::Duplicate + } else if first & 0b0010_0000 == 0b0010_0000 { + EncoderInstruction::DynamicTableSizeUpdate + } else { + EncoderInstruction::Unknown + } + }*/ + + /* fn decode(read: &mut R) -> Result, InternalConnectionError> { + if read.remaining() < 1 { + return Ok(None); + } + + let mut buf = Cursor::new(read.chunk()); + let first = buf.chunk()[0]; + + let x = if first & 0b1000_0000 != 0 { + //EncoderInstruction::InsertWithNameRef + todo!() + } else if first & 0b0100_0000 == 0b0100_0000 { + //EncoderInstruction::InsertWithoutNameRef + todo!() + } else if first & 0b1110_0000 == 0 { + //EncoderInstruction::Duplicate + todo!() + } else if first & 0b0010_0000 == 0b0010_0000 { + //EncoderInstruction::DynamicTableSizeUpdate + let table_size_update= DynamicTableSizeUpdate::decode(&mut buf); + EncoderInstruction::DynamicTableSizeUpdate(DynamicTableSizeUpdate::decode(&mut buf)) + } else { + //EncoderInstruction::Unknown + // TODO: can this be unreachable!()? + return Err(InternalConnectionError::new( + Code::H3_INTERNAL_ERROR, + "Unknown EncoderInstruction".to_string(), + )); + }; + + let instruction = match EncoderInstruction::decode(first) { + EncoderInstruction::Unknown => return Err(DecoderError::UnknownPrefix(first)), + EncoderInstruction::DynamicTableSizeUpdate => { + DynamicTableSizeUpdate::decode(&mut buf)?.map(|x| Instruction::TableSizeUpdate(x.0)) + } + EncoderInstruction::InsertWithoutNameRef => InsertWithoutNameRef::decode(&mut buf)? + .map(|x| Instruction::Insert(HeaderField::new(x.name, x.value))), + EncoderInstruction::Duplicate => match Duplicate::decode(&mut buf)? { + Some(Duplicate(index)) => { + Some(Instruction::Insert(self.table.get_relative(index)?.clone())) + } + None => None, + }, + EncoderInstruction::InsertWithNameRef => match InsertWithNameRef::decode(&mut buf)? { + Some(InsertWithNameRef::Static { index, value }) => Some(Instruction::Insert( + StaticTable::get(index)?.with_value(value), + )), + Some(InsertWithNameRef::Dynamic { index, value }) => Some(Instruction::Insert( + self.table.get_relative(index)?.with_value(value), + )), + None => None, + }, + }; + + if instruction.is_some() { + let pos = buf.position(); + read.advance(pos as usize); + } + + Ok(instruction) + }*/ +} + +#[derive(Debug, PartialEq)] +pub enum InsertWithNameRef { + Static { index: usize, value: Vec }, + Dynamic { index: usize, value: Vec }, +} + +impl InsertWithNameRef { + pub fn new_static>>(index: usize, value: T) -> Self { + InsertWithNameRef::Static { + index, + value: value.into(), + } + } + + pub fn new_dynamic>>(index: usize, value: T) -> Self { + InsertWithNameRef::Dynamic { + index, + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result, InternalConnectionError> { + let (flags, index) = match prefix_int::decode(6, buf) { + Ok((f, x)) if f & 0b10 == 0b10 => (f, x), + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + let index: usize = index.try_into().map_err(|_e| { + ParseEncoderInstructionError::Integer(crate::qpack::prefix_int::Error::Overflow) + })?; + + let value = match prefix_string::decode(8, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + + if flags & 0b01 == 0b01 { + Ok(Some(InsertWithNameRef::new_static(index, value))) + } else { + Ok(Some(InsertWithNameRef::new_dynamic(index, value))) + } + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + match self { + InsertWithNameRef::Static { index, value } => { + prefix_int::encode(6, 0b11, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + InsertWithNameRef::Dynamic { index, value } => { + prefix_int::encode(6, 0b10, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + } + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct InsertWithoutNameRef { + pub name: Vec, + pub value: Vec, +} + +impl InsertWithoutNameRef { + pub fn new>>(name: T, value: T) -> Self { + Self { + name: name.into(), + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result, InternalConnectionError> { + let name = match prefix_string::decode(6, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + let value = match prefix_string::decode(8, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(Self::new(name, value))) + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + prefix_string::encode(6, 0b01, &self.name, buf)?; + prefix_string::encode(8, 0, &self.value, buf)?; + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct Duplicate(u64); + +impl Duplicate { + /// Call this function only if you are sure that the prefix is 0b000 + pub fn decode(buf: &mut R) -> Result, InternalConnectionError> { + let index = match prefix_int::decode(5, buf) { + Ok((0, x)) => x, + Ok(_) => { + unreachable!("This function must not be called when the prefix is other than 0b000") + } + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(IntError::Overflow) => { + return Err(InternalConnectionError { + code: Code::QPACK_ENCODER_STREAM_ERROR, + message: "Integer value to large".to_string(), + }) + } + }; + Ok(Some(Duplicate(index))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(5, 0, self.0 as u64, buf); + } +} + +#[derive(Debug, PartialEq)] +pub struct DynamicTableSizeUpdate(u64); + +impl DynamicTableSizeUpdate { + /// Call this function only if you are sure that the prefix is 0b001 + pub fn decode(buf: &mut R) -> Result, InternalConnectionError> { + let size = match prefix_int::decode(5, buf) { + Ok((0b001, x)) => x, + Ok(_) => { + unreachable!("This function must not be called when the prefix is other than 0b001") + } + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(IntError::Overflow) => { + return Err(InternalConnectionError { + code: Code::QPACK_ENCODER_STREAM_ERROR, + message: "Integer value to large".to_string(), + }) + } + }; + Ok(Some(DynamicTableSizeUpdate(size))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(5, 0b001, self.0 as u64, buf); + } +} diff --git a/h3/src/qpack2/field.rs b/h3/src/qpack2/field.rs new file mode 100644 index 00000000..22a9f6c7 --- /dev/null +++ b/h3/src/qpack2/field.rs @@ -0,0 +1,126 @@ +use std::{ + borrow::Cow, + fmt::{Display, Formatter}, +}; + +/** + * https://tools.ietf.org/html/rfc7541 + * 4.1. Calculating Table Size + */ +pub const ESTIMATED_OVERHEAD_BYTES: usize = 32; + +#[derive(Debug, PartialEq, Clone, Hash, Eq)] +pub struct HeaderField { + pub name: Cow<'static, [u8]>, + pub value: Cow<'static, [u8]>, +} + +impl HeaderField { + pub fn new(name: T, value: S) -> HeaderField + where + T: Into>, + S: Into>, + { + HeaderField { + name: Cow::Owned(name.into()), + value: Cow::Owned(value.into()), + } + } + + pub fn mem_size(&self) -> usize { + self.name.len() + self.value.len() + ESTIMATED_OVERHEAD_BYTES + } + + pub fn with_value(&self, value: T) -> Self + where + T: Into>, + { + Self { + name: self.name.clone(), + value: Cow::Owned(value.into()), + } + } + + pub fn into_inner(self) -> (Cow<'static, [u8]>, Cow<'static, [u8]>) { + (self.name, self.value) + } +} + +impl AsRef for HeaderField { + fn as_ref(&self) -> &Self { + self + } +} + +impl Display for HeaderField { + fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { + write!( + f, + "\"{}\": \"{}\"", + String::from_utf8_lossy(&self.name), + String::from_utf8_lossy(&self.value) + )?; + Ok(()) + } +} + +impl From for String { + fn from(field: HeaderField) -> String { + format!( + "{}\t{}", + String::from_utf8_lossy(&field.name), + String::from_utf8_lossy(&field.value) + ) + } +} + +impl From<(N, V)> for HeaderField +where + N: AsRef<[u8]>, + V: AsRef<[u8]>, +{ + fn from(header: (N, V)) -> Self { + let (name, value) = header; + Self { + // FIXME: could avoid allocation if HeaderField had a lifetime + name: Cow::Owned(Vec::from(name.as_ref())), + value: Cow::Owned(Vec::from(value.as_ref())), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /** + * https://tools.ietf.org/html/rfc7541#section-4.1 + * "The size of an entry is the sum of its name's length in octets (as + * defined in Section 5.2), its value's length in octets, and 32." + * "The size of an entry is calculated using the length of its name and + * value without any Huffman encoding applied." + */ + #[test] + fn test_field_size_is_offset_by_32() { + let field = HeaderField { + name: Cow::Borrowed(b"Name"), + value: Cow::Borrowed(b"Value"), + }; + assert_eq!(field.mem_size(), 4 + 5 + 32); + } + + #[test] + fn with_value() { + let field = HeaderField { + name: Cow::Borrowed(b"Name"), + value: Cow::Borrowed(b"Value"), + }; + assert_eq!( + field.with_value("New value"), + HeaderField { + name: Cow::Borrowed(b"Name"), + value: Cow::Borrowed(b"New value"), + } + ); + } +} diff --git a/h3/src/qpack2/mod.rs b/h3/src/qpack2/mod.rs new file mode 100644 index 00000000..685557df --- /dev/null +++ b/h3/src/qpack2/mod.rs @@ -0,0 +1,25 @@ +/*/ +mod block; +mod dynamic; +mod field; +mod parse_error; +mod static_; +mod stream; +mod vas; + +mod decoder; +mod encoder; + + +mod encoder_instructions; +*/ +mod prefix_int; +mod prefix_string; + +//#[cfg(test)] +//mod tests; + +pub(crate) mod decoder2; + +pub(crate) mod qpack_result; + diff --git a/h3/src/qpack2/parse_error.rs b/h3/src/qpack2/parse_error.rs new file mode 100644 index 00000000..e5bb7b12 --- /dev/null +++ b/h3/src/qpack2/parse_error.rs @@ -0,0 +1,21 @@ +use super::{prefix_int, prefix_string}; + +#[derive(Debug, PartialEq)] +pub enum ParseError { + Integer(prefix_int::Error), + String(prefix_string::PrefixStringError), + InvalidPrefix(u8), + InvalidBase(isize), +} + +impl From for ParseError { + fn from(e: prefix_int::Error) -> Self { + ParseError::Integer(e) + } +} + +impl From for ParseError { + fn from(e: prefix_string::PrefixStringError) -> Self { + ParseError::String(e) + } +} diff --git a/h3/src/qpack2/prefix_int.rs b/h3/src/qpack2/prefix_int.rs new file mode 100644 index 00000000..f17b9543 --- /dev/null +++ b/h3/src/qpack2/prefix_int.rs @@ -0,0 +1,235 @@ +use bytes::Buf; +use thiserror::Error; + +use crate::{ + proto::coding::BufExt, + qpack2::qpack_result::{ParseProgressResult, StatefulParser}, +}; + +#[derive(Debug, PartialEq, Error)] +pub enum PrefixIntParseError { + #[error("value overflow")] + Overflow, +} + +/// PrefixIntParser is a parser for prefix integers. +/// It is used to decode and encode prefix integers in the QPACK format. +#[derive(Debug)] +pub struct PrefixIntParser { + value: u64, + position: u8, + flags: Option, +} + +/// Creates a new PrefixIntParser with the given size. +/// The size is the number of bits used to encode the prefix integer. +/// The size must be between 1 and 8. +fn new_prefix_int_parser() -> PrefixIntParser { + // TODO: maybe add compile-time check for F2 + assert!(F2 <= 8); + + PrefixIntParser { + value: 0, + position: 0, + flags: None, + } +} + +impl StatefulParser for PrefixIntParser { + fn parse_progress( + mut self, + buf: &mut B, + ) -> ParseProgressResult { + if self.flags.is_none() { + // Get the first byte from the buffer + let first = match buf.get::() { + Ok(value) => value, + Err(_) => return ParseProgressResult::MoreData(self), + }; + // Get the flags from the first byte + let flags = (first as usize >> F) as u8; + // Get the value from the first byte + let value = first & Self::MASK; + // Check if the value is less than the mask + if value < Self::MASK { + // The whole value is in the first byte + return ParseProgressResult::Done((flags, value as u64)); + } else { + // The value is larger than the mask, so we need to read more bytes + self.flags = Some(flags); + self.value = Self::MASK as u64; + } + } + + loop { + // Get the next byte from the buffer + let byte = match buf.get::() { + Ok(value) => value, + Err(_) => return ParseProgressResult::MoreData(self), + } as u64; + + // Add the value of the byte to the prefix integer + self.value += (byte & 127) << self.position; + // Increment the position + self.position += 7; + + // Check if the byte is the last byte + if byte & 128 == 0 { + // The whole value is in the buffer + let flags = self.flags.take().expect("we must have flags here"); + return ParseProgressResult::Done((flags, self.value)); + } + + // Overflow check + if self.position >= MAX_POWER { + // The value is too large, so we need to return an error + return ParseProgressResult::Error(PrefixIntParseError::Overflow); + } + } + } +} + +impl PrefixIntParser { + const MASK: u8 = 0xFF >> (8 - F); +} + +//= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.1 +//# QPACK implementations MUST be able to decode integers up to and +//# including 62 bits long. +const MAX_POWER: u8 = 9 * 7; + +#[cfg(test)] +mod test { + use std::{io::Cursor, panic}; + + use crate::tests::test_all_chunking_combinations; + + use super::*; + use assert_matches::assert_matches; + + fn check_codec(flags: u8, value: u64, data: &[u8]) { + test_all_chunking_combinations( + &mut Cursor::new(data), + || new_prefix_int_parser::(), + ParseProgressResult::Done((flags, value)), + ); + } + + fn check_overflow(data: &[u8]) { + test_all_chunking_combinations( + &mut Cursor::new(data), + || new_prefix_int_parser::(), + ParseProgressResult::Error(PrefixIntParseError::Overflow), + ); + } + + #[test] + fn codec_5_bits() { + check_codec::<5>(0b101, 10, &[0b1010_1010]); + check_codec::<5>(0b101, 0, &[0b1010_0000]); + check_codec::<5>(0b010, 1337, &[0b0101_1111, 154, 10]); + check_codec::<5>(0b010, 31, &[0b0101_1111, 0]); + check_codec::<5>( + 0b010, + 0x80_00_00_00_00_00_00_1E, + &[95, 255, 255, 255, 255, 255, 255, 255, 255, 127], + ); + } + + #[test] + fn codec_8_bits() { + check_codec::<8>(0, 42, &[0b0010_1010]); + check_codec::<8>(0, 424_242, &[255, 179, 240, 25]); + check_codec::<8>( + 0, + 0x80_00_00_00_00_00_00_FE, + &[255, 255, 255, 255, 255, 255, 255, 255, 255, 127], + ); + } + + #[test] + #[should_panic] + fn size_too_big_of_size() { + let _ = new_prefix_int_parser::<9>(); + } + + #[test] + fn overflow_1() { + check_overflow::<1>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_2() { + check_overflow::<2>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_3() { + check_overflow::<3>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_4() { + check_overflow::<4>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_5() { + check_overflow::<5>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_6() { + check_overflow::<6>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_7() { + check_overflow::<7>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + #[test] + fn overflow_8() { + check_overflow::<8>(&[255, 128, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + + #[test] + fn number_never_ends_with_0x80() { + check_codec::<4>(0b0001, 143, &[31, 128, 1]); + } + + #[test] + fn test_prefix_int_parser() { + let mut buf = &[0b011_00010][..]; + let parser = new_prefix_int_parser::<5>(); + let result = parser.parse_progress(&mut buf); + assert_matches!(result, ParseProgressResult::Done((0b011, 0b00010))); + } + + #[test] + fn test_prefix_int_two_buffer() { + let mut buf = &[0b011_11111][..]; + let parser = new_prefix_int_parser::<5>(); + let parser = match parser.parse_progress(&mut buf) { + ParseProgressResult::MoreData(parser) => parser, + _ => panic!("Expected MoreData"), + }; + let mut buffer2 = &[0b00000001][..]; + let result = parser.parse_progress(&mut buffer2); + assert_matches!(result, ParseProgressResult::Done((0b011, 32))); + } + + #[test] + fn overflow_x() { + check_overflow::<5>(&[95, 225, 255, 255, 255, 255, 255, 255, 255, 255, 1]); + } + + #[test] + fn allow_62_bit() { + //= https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.1 + //= type=test + //# QPACK implementations MUST be able to decode integers up to and + //# including 62 bits long. + + // This is the maximum value that can be encoded in with a flag size of 7 bits + // The value is requires more than 62 bits so the spec is fulfilled + check_codec::<1>( + 1, + 9223372036854775808, + &[3, 255, 255, 255, 255, 255, 255, 255, 255, 127], + ); + } +} diff --git a/h3/src/qpack2/prefix_string/bitwin.rs b/h3/src/qpack2/prefix_string/bitwin.rs new file mode 100644 index 00000000..4090d07a --- /dev/null +++ b/h3/src/qpack2/prefix_string/bitwin.rs @@ -0,0 +1,29 @@ +#[derive(Debug, Default, PartialEq, Clone)] +pub struct BitWindow { + pub byte: u32, + pub bit: u32, + pub count: u32, +} + +impl BitWindow { + pub fn new() -> Self { + Self::default() + } + + pub fn forwards(&mut self, step: u32) { + self.bit += self.count; + + self.byte += self.bit / 8; + self.bit %= 8; + + self.count = step; + } + + pub fn opposite_bit_window(&self) -> BitWindow { + BitWindow { + byte: self.byte, + bit: self.bit, + count: 8 - (self.bit % 8), + } + } +} diff --git a/h3/src/qpack2/prefix_string/decode.rs b/h3/src/qpack2/prefix_string/decode.rs new file mode 100644 index 00000000..f7383869 --- /dev/null +++ b/h3/src/qpack2/prefix_string/decode.rs @@ -0,0 +1,1952 @@ +use bytes::{buf, Buf}; + +use crate::qpack2::qpack_result::{ParseProgressResult, StatefulParser}; + +use super::bitwin::BitWindow; + +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum HuffmanDecodingError { + #[error("missing bits while decoding")] + MissingBits(BitWindow), + #[error("unhandled decoding at {0:?} for value {1}")] + Unhandled(BitWindow, usize), +} + +#[derive(Debug, thiserror::Error, PartialEq)] +pub enum HuffmanDecodingError2 { + #[error("encountered eof value while decoding")] + EOS, + #[error("padding error while decoding")] + PaddingError, +} + +/// A stateful decoder for Huffman encoded strings. +#[derive(Debug)] +struct StatefulHuffmanDecoder { + /// The current byte being processed. + current_byte: Option, + /// The current bit position within the current byte. + current_bit_pos: u8, + /// The decoded string being built + decoded_string: Vec, + /// The current position in the Huffman decoding table. + current_decoder: &'static HuffmanDecoder, + /// count of bytes read + bytes_read: usize, + /// length of the encoded data + length: usize, +} + +enum GetNextByteResult { + // Some byte was read + Byte(u8), + // All bytes which belong to the string have been read + EndOfString, + // End of the buffer has been reached, but more data is needed + EndOfBuffer, + // Error while decoding + Error(HuffmanDecodingError2), +} + +impl StatefulParser> for StatefulHuffmanDecoder { + fn parse_progress( + mut self, + reader: &mut B, + ) -> ParseProgressResult> { + loop { + // Gets the current byte or tries to read the next byte + // There is no Option::get_or_try_insert_with + let current_byte = match self.current_byte { + Some(8) => { + // If the current byte is 8, it means we have read all bits + + // set bit position to 0 + self.current_bit_pos = 0; + match self.get_next_byte(reader) { + GetNextByteResult::Byte(value) => value, + GetNextByteResult::EndOfString => { + return ParseProgressResult::Done(self.decoded_string) + } + GetNextByteResult::EndOfBuffer => { + return ParseProgressResult::MoreData(self) + } + GetNextByteResult::Error(err) => return ParseProgressResult::Error(err), + } + } + Some(byte) => byte, + None => match self.get_next_byte(reader) { + // initial: read the first byte + GetNextByteResult::Byte(value) => value, + GetNextByteResult::EndOfString => { + return ParseProgressResult::Done(self.decoded_string) + } + GetNextByteResult::EndOfBuffer => return ParseProgressResult::MoreData(self), + GetNextByteResult::Error(err) => return ParseProgressResult::Error(err), + }, + }; + + // the bit position can only be between 0 and 7 + self.current_bit_pos %= 8; + + // Check if the remaining bits in the current byte are enough + let value = if self.current_decoder.lookup > (8 - self.current_bit_pos) { + // Not enough bits, so we need to read the next byte + let next_byte = match self.get_next_byte(reader) { + GetNextByteResult::Byte(value) => value as u16, + GetNextByteResult::EndOfString => { + return ParseProgressResult::Done(self.decoded_string) + } + GetNextByteResult::EndOfBuffer => return ParseProgressResult::MoreData(self), + GetNextByteResult::Error(err) => return ParseProgressResult::Error(err), + }; + // Combine the current byte and the next byte into a 16-bit value + let value = ((current_byte as u16) << 8) | next_byte; + + // Shift to left to remove the already read bits + let value = value << self.current_bit_pos; + + // allign bits to the right + let value = value >> (16 - self.current_decoder.lookup); + value as u8 + } else { + // Enough bits, so we can read the value directly + + // Remove the already read bits from the current byte + let current_byte = current_byte << self.current_bit_pos; + // allign bits to the right + current_byte >> (8 - self.current_decoder.lookup) + }; + + // Update the current bit position + self.current_bit_pos += self.current_decoder.lookup as u8; + + match self.current_decoder.table.get(value as usize) { + Some(DecodeValue::Sym(sym)) => { + // If the value is a symbol, we can add it to the decoded string + self.decoded_string.push(*sym); + // Reset to the initial decoder + self.current_decoder = &HPACK_STRING; + } + Some(DecodeValue::Partial(decoder)) => { + // If the value is a partial decoder, we need to update the current decoder + self.current_decoder = decoder; + } + None => return ParseProgressResult::Error(HuffmanDecodingError2::EOS), + } + } + } +} + +impl StatefulHuffmanDecoder { + /// Creates a new `StatefulDecoder` with the specified length. + fn new(length: usize) -> Self { + StatefulHuffmanDecoder { + current_byte: None, + current_bit_pos: 0, + // TODO: how much capacity should we initially reserve? The length of the encoded data? + decoded_string: Vec::with_capacity(length), + current_decoder: &HPACK_STRING, + bytes_read: 0, + length, + } + } + + /// Reads the next byte from the buffer and updates the current byte. + fn get_next_byte(&mut self, buf: &mut impl Buf) -> GetNextByteResult { + if self.bytes_read >= self.length { + // All bytes have been read + + // if the rest of the bits in the current byte are not 0, it means that the string is not properly padded + if let Some(byte) = self.current_byte { + if (byte & (0b1111_1111 >> self.current_bit_pos)) + != (0b1111_1111 >> self.current_bit_pos) + { + return GetNextByteResult::Error(HuffmanDecodingError2::PaddingError); + } + } + return GetNextByteResult::EndOfString; + } + let byte = match buf.try_get_u8().ok() { + Some(byte) => byte, + None => return GetNextByteResult::EndOfBuffer, + }; + self.bytes_read += 1; + self.current_byte = Some(byte); + GetNextByteResult::Byte(byte) + } +} + +#[derive(Clone, Debug)] +enum DecodeValue { + Partial(&'static HuffmanDecoder), + Sym(u8), +} + +#[derive(Clone, Debug)] +struct HuffmanDecoder { + lookup: u8, + table: &'static [DecodeValue], +} + +macro_rules! bits_decode { + // general way + ( + lookup: $count:expr, [ + $($sym:expr,)* + $(=> $sub:ident,)* ] + ) => { + HuffmanDecoder { + lookup: $count, + table: &[ + $( DecodeValue::Sym($sym as u8), )* + $( DecodeValue::Partial(&$sub), )* + ] + } + }; + // 2-final + ( $first:expr, $second:expr ) => { + HuffmanDecoder { + lookup: 1, + table: &[ + DecodeValue::Sym($first as u8), + DecodeValue::Sym($second as u8), + ] + } + }; + // 4-final + ( $first:expr, $second:expr, $third:expr, $fourth:expr ) => { + HuffmanDecoder { + lookup: 2, + table: &[ + DecodeValue::Sym($first as u8), + DecodeValue::Sym($second as u8), + DecodeValue::Sym($third as u8), + DecodeValue::Sym($fourth as u8), + ] + } + }; + // 2-final-partial + ( $first:expr, => $second:ident ) => { + HuffmanDecoder { + lookup: 1, + table: &[ + DecodeValue::Sym($first as u8), + DecodeValue::Partial(&$second), + ] + } + }; + // 2-partial + ( => $first:ident, => $second:ident ) => { + HuffmanDecoder { + lookup: 1, + table: &[ + DecodeValue::Partial(&$first), + DecodeValue::Partial(&$second), + ] + } + }; + // 4-partial + ( => $first:ident, => $second:ident, + => $third:ident, => $fourth:ident ) => { + HuffmanDecoder { + lookup: 2, + table: &[ + DecodeValue::Partial(&$first), + DecodeValue::Partial(&$second), + DecodeValue::Partial(&$third), + DecodeValue::Partial(&$fourth), + ] + } + }; + [ $( $name:ident => ( $($value:tt)* ), )* ] => { + $( const $name: HuffmanDecoder = bits_decode!( $( $value )* ); )* + }; +} + +#[rustfmt::skip] +bits_decode![ + HPACK_STRING => ( + lookup: 5, [ '0', '1', '2', 'a', 'c', 'e', 'i', 'o', 's', 't', + => END0_01010, => END0_01011, => END0_01100, => END0_01101, + => END0_01110, => END0_01111, => END0_10000, => END0_10001, + => END0_10010, => END0_10011, => END0_10100, => END0_10101, + => END0_10110, => END0_10111, => END0_11000, => END0_11001, + => END0_11010, => END0_11011, => END0_11100, => END0_11101, + => END0_11110, => END0_11111, + ]), + END0_01010 => ( 32, '%'), + END0_01011 => ('-', '.'), + END0_01100 => ('/', '3'), + END0_01101 => ('4', '5'), + END0_01110 => ('6', '7'), + END0_01111 => ('8', '9'), + END0_10000 => ('=', 'A'), + END0_10001 => ('_', 'b'), + END0_10010 => ('d', 'f'), + END0_10011 => ('g', 'h'), + END0_10100 => ('l', 'm'), + END0_10101 => ('n', 'p'), + END0_10110 => ('r', 'u'), + END0_10111 => (':', 'B', 'C', 'D'), + END0_11000 => ('E', 'F', 'G', 'H'), + END0_11001 => ('I', 'J', 'K', 'L'), + END0_11010 => ('M', 'N', 'O', 'P'), + END0_11011 => ('Q', 'R', 'S', 'T'), + END0_11100 => ('U', 'V', 'W', 'Y'), + END0_11101 => ('j', 'k', 'q', 'v'), + END0_11110 => ('w', 'x', 'y', 'z'), + END0_11111 => (=> END5_00, => END5_01, => END5_10, => END5_11), + END5_00 => ('&', '*'), + END5_01 => (',', 59), + END5_10 => ('X', 'Z'), + END5_11 => (=> END7_0, => END7_1), + END7_0 => ('!', '"', '(', ')'), + END7_1 => (=> END8_0, => END8_1), + END8_0 => ('?', => END9A_1), + END9A_1 => ('\'', '+'), + END8_1 => (lookup: 2, ['|', => END9B_01, => END9B_10, => END9B_11,]), + END9B_01 => ('#', '>'), + END9B_10 => (0, '$', '@', '['), + END9B_11 => (lookup: 2, [']', '~', => END13_10, => END13_11,]), + END13_10 => ('^', '}'), + END13_11 => (=> END14_0, => END14_1), + END14_0 => ('<', '`'), + END14_1 => ('{', => END15_1), + END15_1 => + (lookup: 4, [ '\\', 195, 208, => END19_0011, + => END19_0100, => END19_0101, => END19_0110, => END19_0111, + => END19_1000, => END19_1001, => END19_1010, => END19_1011, + => END19_1100, => END19_1101, => END19_1110, => END19_1111, + ]), + END19_0011 => (128, 130), + END19_0100 => (131, 162), + END19_0101 => (184, 194), + END19_0110 => (224, 226), + END19_0111 => (153, 161, 167, 172), + END19_1000 => (176, 177, 179, 209), + END19_1001 => (216, 217, 227, 229), + END19_1010 => (lookup: 2, [230, => END19_1010_01, => END19_1010_10, + => END19_1010_11,]), + END19_1010_01 => (129, 132), + END19_1010_10 => (133, 134), + END19_1010_11 => (136, 146), + END19_1011 => (lookup: 3, [154, 156, 160, 163, 164, 169, 170, 173,]), + END19_1100 => (lookup: 3, [178, 181, 185, 186, 187, 189, 190, 196,]), + END19_1101 => (lookup: 3, [198, 228, 232, 233, + => END23A_100, => END23A_101, + => END23A_110, => END23A_111,]), + END23A_100 => ( 1, 135), + END23A_101 => (137, 138), + END23A_110 => (139, 140), + END23A_111 => (141, 143), + END19_1110 => (lookup: 4, [147, 149, 150, 151, 152, 155, 157, 158, + 165, 166, 168, 174, 175, 180, 182, 183,]), + END19_1111 => (lookup: 4, [188, 191, 197, 231, 239, + => END23B_0101, => END23B_0110, => END23B_0111, + => END23B_1000, => END23B_1001, => END23B_1010, + => END23B_1011, => END23B_1100, => END23B_1101, + => END23B_1110, => END23B_1111,]), + END23B_0101 => ( 9, 142), + END23B_0110 => (144, 145), + END23B_0111 => (148, 159), + END23B_1000 => (171, 206), + END23B_1001 => (215, 225), + END23B_1010 => (236, 237), + END23B_1011 => (199, 207, 234, 235), + END23B_1100 => (lookup: 3, [192, 193, 200, 201, 202, 205, 210, 213,]), + END23B_1101 => (lookup: 3, [218, 219, 238, 240, 242, 243, 255, + => END27A_111,]), + END27A_111 => (203, 204), + END23B_1110 => (lookup: 4, [211, 212, 214, 221, 222, 223, 241, 244, + 245, 246, 247, 248, 250, 251, 252, 253,]), + END23B_1111 => (lookup: 4, [ 254, => END27B_0001, => END27B_0010, + => END27B_0011, => END27B_0100, => END27B_0101, + => END27B_0110, => END27B_0111, => END27B_1000, + => END27B_1001, => END27B_1010, => END27B_1011, + => END27B_1100, => END27B_1101, => END27B_1110, + => END27B_1111,]), + END27B_0001 => (2, 3), + END27B_0010 => (4, 5), + END27B_0011 => (6, 7), + END27B_0100 => (8, 11), + END27B_0101 => (12, 14), + END27B_0110 => (15, 16), + END27B_0111 => (17, 18), + END27B_1000 => (19, 20), + END27B_1001 => (21, 23), + END27B_1010 => (24, 25), + END27B_1011 => (26, 27), + END27B_1100 => (28, 29), + END27B_1101 => (30, 31), + END27B_1110 => (127, 220), + END27B_1111 => (lookup: 1, [249, => END31_1,]), + END31_1 => (lookup: 2, [10, 13, 22, => EOF,]), + EOF => (lookup: 8, []), + ]; + +#[cfg(test)] +mod tests { + #![allow(clippy::identity_op)] + + use super::*; + use crate::tests::{all_chunking_combinations, test_all_chunking_combinations}; + use std::{io::Cursor, panic}; + + macro_rules! decoding { + [ $( $code:expr => $( $byte:expr ),* ; )* ] => { $( { + let bytes = vec![$( $byte ),*]; + + test_all_chunking_combinations( + &mut Cursor::new(&bytes), + || StatefulHuffmanDecoder::new(bytes.len()), + ParseProgressResult::Done(vec![$code]), + ); + } )* } + } + + #[test] + fn test_one() { + decoding![ + 38 => 0b1111_1000, /* padding */ 0b1111_1111; // '&' + /* 42 => 0b1111_1001, /* padding */ 0b1111_1111; // '*' + 44 => 0b1111_1010, /* padding */ 0b1111_1111; // ',' + 59 => 0b1111_1011, /* padding */ 0b1111_1111; + 88 => 0b1111_1100, /* padding */ 0b1111_1111; // 'X' + 90 => 0b1111_1101, /* padding */ 0b1111_1111; // 'Z' + 33 => 0b1111_1110, (0b00 << 6) | /* padding */ 0b11_1111; // '!' + 34 => 0b1111_1110, (0b01 << 6) | /* padding */ 0b11_1111; // '"' + 40 => 0b1111_1110, (0b10 << 6) | /* padding */ 0b11_1111; // '(' + 41 => 0b1111_1110, (0b11 << 6) | /* padding */ 0b11_1111; // ')' + 63 => 0b1111_1111, (0b00 << 6) | /* padding */ 0b11_1111; // '?' + 39 => 0b1111_1111, (0b010 << 5) | /* padding */ 0b11111; // ''' + 43 => 0b1111_1111, (0b011 << 5) | /* padding */ 0b11111; // '+' + 124 => 0b1111_1111, (0b100 << 5) | /* padding */ 0b11111; // '|' + 35 => 0b1111_1111, (0b1010 << 4) | /* padding */ 0b1111; // '#' + 62 => 0b1111_1111, (0b1011 << 4) | /* padding */ 0b1111; // '>' + 0 => 0b1111_1111, (0b11000 << 3) | /* padding */ 0b111; + 36 => 0b1111_1111, (0b11001 << 3) | /* padding */ 0b111; // '$' + 64 => 0b1111_1111, (0b11010 << 3) | /* padding */ 0b111; // '@' + 91 => 0b1111_1111, (0b11011 << 3) | /* padding */ 0b111; // '[' + 93 => 0b1111_1111, (0b11100 << 3) | /* padding */ 0b111; // ']' + 126 => 0b1111_1111, (0b11101 << 3) | /* padding */ 0b111; // '~' + 94 => 0b1111_1111, (0b11_1100 << 2) | /* padding */ 0b11; // '^' + 125 => 0b1111_1111, (0b11_1101 << 2) | /* padding */ 0b11; // '}' + 60 => 0b1111_1111, (0b111_1100 << 1) | /* padding */ 0b1; // '<' + 96 => 0b1111_1111, (0b111_1101 << 1) | /* padding */ 0b1; // '`' + 123 => 0b1111_1111, (0b111_1110 << 1) | /* padding */ 0b1; // '{' + 92 => 0b1111_1111, 0b1111_1110, (0b000 << 5) | /* padding */ 0b11111; // '\' + 195 => 0b1111_1111, 0b1111_1110, (0b001 << 5) | /* padding */ 0b11111; + 208 => 0b1111_1111, 0b1111_1110, (0b010 << 5) | /* padding */ 0b11111; + 128 => 0b1111_1111, 0b1111_1110, (0b0110 << 4) | /* padding */ 0b1111; + 130 => 0b1111_1111, 0b1111_1110, (0b0111 << 4) | /* padding */ 0b1111; + 131 => 0b1111_1111, 0b1111_1110, (0b1000 << 4) | /* padding */ 0b1111; + 162 => 0b1111_1111, 0b1111_1110, (0b1001 << 4) | /* padding */ 0b1111; + 184 => 0b1111_1111, 0b1111_1110, (0b1010 << 4) | /* padding */ 0b1111; + 194 => 0b1111_1111, 0b1111_1110, (0b1011 << 4) | /* padding */ 0b1111; + 224 => 0b1111_1111, 0b1111_1110, (0b1100 << 4) | /* padding */ 0b1111; + 226 => 0b1111_1111, 0b1111_1110, (0b1101 << 4) | /* padding */ 0b1111; + 153 => 0b1111_1111, 0b1111_1110, (0b11100 << 3) | /* padding */ 0b111; + 161 => 0b1111_1111, 0b1111_1110, (0b11101 << 3) | /* padding */ 0b111; + 167 => 0b1111_1111, 0b1111_1110, (0b11110 << 3) | /* padding */ 0b111; + 172 => 0b1111_1111, 0b1111_1110, (0b11111 << 3) | /* padding */ 0b111; + 176 => 0b1111_1111, 0b1111_1111, (0b00000 << 3) | /* padding */ 0b111; + 177 => 0b1111_1111, 0b1111_1111, (0b00001 << 3) | /* padding */ 0b111; + 179 => 0b1111_1111, 0b1111_1111, (0b00010 << 3) | /* padding */ 0b111; + 209 => 0b1111_1111, 0b1111_1111, (0b00011 << 3) | /* padding */ 0b111; + 216 => 0b1111_1111, 0b1111_1111, (0b00100 << 3) | /* padding */ 0b111; + 217 => 0b1111_1111, 0b1111_1111, (0b00101 << 3) | /* padding */ 0b111; + 227 => 0b1111_1111, 0b1111_1111, (0b00110 << 3) | /* padding */ 0b111; + 229 => 0b1111_1111, 0b1111_1111, (0b00111 << 3) | /* padding */ 0b111; + 230 => 0b1111_1111, 0b1111_1111, (0b01000 << 3) | /* padding */ 0b111; + 129 => 0b1111_1111, 0b1111_1111, (0b01_0010 << 2) | /* padding */ 0b11; + 132 => 0b1111_1111, 0b1111_1111, (0b01_0011 << 2) | /* padding */ 0b11; + 133 => 0b1111_1111, 0b1111_1111, (0b01_0100 << 2) | /* padding */ 0b11; + 134 => 0b1111_1111, 0b1111_1111, (0b01_0101 << 2) | /* padding */ 0b11; + 136 => 0b1111_1111, 0b1111_1111, (0b01_0110 << 2) | /* padding */ 0b11; + 146 => 0b1111_1111, 0b1111_1111, (0b01_0111 << 2) | /* padding */ 0b11; + 154 => 0b1111_1111, 0b1111_1111, (0b01_1000 << 2) | /* padding */ 0b11; + 156 => 0b1111_1111, 0b1111_1111, (0b01_1001 << 2) | /* padding */ 0b11; + 160 => 0b1111_1111, 0b1111_1111, (0b01_1010 << 2) | /* padding */ 0b11; + 163 => 0b1111_1111, 0b1111_1111, (0b01_1011 << 2) | /* padding */ 0b11; + 164 => 0b1111_1111, 0b1111_1111, (0b01_1100 << 2) | /* padding */ 0b11; + 169 => 0b1111_1111, 0b1111_1111, (0b01_1101 << 2) | /* padding */ 0b11; + 170 => 0b1111_1111, 0b1111_1111, (0b01_1110 << 2) | /* padding */ 0b11; + 173 => 0b1111_1111, 0b1111_1111, (0b01_1111 << 2) | /* padding */ 0b11; + 178 => 0b1111_1111, 0b1111_1111, (0b10_0000 << 2) | /* padding */ 0b11; + 181 => 0b1111_1111, 0b1111_1111, (0b10_0001 << 2) | /* padding */ 0b11; + 185 => 0b1111_1111, 0b1111_1111, (0b10_0010 << 2) | /* padding */ 0b11; + 186 => 0b1111_1111, 0b1111_1111, (0b10_0011 << 2) | /* padding */ 0b11; + 187 => 0b1111_1111, 0b1111_1111, (0b10_0100 << 2) | /* padding */ 0b11; + 189 => 0b1111_1111, 0b1111_1111, (0b10_0101 << 2) | /* padding */ 0b11; + 190 => 0b1111_1111, 0b1111_1111, (0b10_0110 << 2) | /* padding */ 0b11; + 196 => 0b1111_1111, 0b1111_1111, (0b10_0111 << 2) | /* padding */ 0b11; + 198 => 0b1111_1111, 0b1111_1111, (0b10_1000 << 2) | /* padding */ 0b11; + 228 => 0b1111_1111, 0b1111_1111, (0b10_1001 << 2) | /* padding */ 0b11; + 232 => 0b1111_1111, 0b1111_1111, (0b10_1010 << 2) | /* padding */ 0b11; + 233 => 0b1111_1111, 0b1111_1111, (0b10_1011 << 2) | /* padding */ 0b11; + 1 => 0b1111_1111, 0b1111_1111, (0b101_1000 << 1) | /* padding */ 0b1; + 135 => 0b1111_1111, 0b1111_1111, (0b101_1001 << 1) | /* padding */ 0b1; + 137 => 0b1111_1111, 0b1111_1111, (0b101_1010 << 1) | /* padding */ 0b1; + 138 => 0b1111_1111, 0b1111_1111, (0b101_1011 << 1) | /* padding */ 0b1; + 139 => 0b1111_1111, 0b1111_1111, (0b101_1100 << 1) | /* padding */ 0b1; + 140 => 0b1111_1111, 0b1111_1111, (0b101_1101 << 1) | /* padding */ 0b1; + 141 => 0b1111_1111, 0b1111_1111, (0b101_1110 << 1) | /* padding */ 0b1; + 143 => 0b1111_1111, 0b1111_1111, (0b101_1111 << 1) | /* padding */ 0b1; + 147 => 0b1111_1111, 0b1111_1111, (0b110_0000 << 1) | /* padding */ 0b1; + 149 => 0b1111_1111, 0b1111_1111, (0b110_0001 << 1) | /* padding */ 0b1; + 150 => 0b1111_1111, 0b1111_1111, (0b110_0010 << 1) | /* padding */ 0b1; + 151 => 0b1111_1111, 0b1111_1111, (0b110_0011 << 1) | /* padding */ 0b1; + 152 => 0b1111_1111, 0b1111_1111, (0b110_0100 << 1) | /* padding */ 0b1; + 155 => 0b1111_1111, 0b1111_1111, (0b110_0101 << 1) | /* padding */ 0b1; + 157 => 0b1111_1111, 0b1111_1111, (0b110_0110 << 1) | /* padding */ 0b1; + 158 => 0b1111_1111, 0b1111_1111, (0b110_0111 << 1) | /* padding */ 0b1; + 165 => 0b1111_1111, 0b1111_1111, (0b110_1000 << 1) | /* padding */ 0b1; + 166 => 0b1111_1111, 0b1111_1111, (0b110_1001 << 1) | /* padding */ 0b1; + 168 => 0b1111_1111, 0b1111_1111, (0b110_1010 << 1) | /* padding */ 0b1; + 174 => 0b1111_1111, 0b1111_1111, (0b110_1011 << 1) | /* padding */ 0b1; + 175 => 0b1111_1111, 0b1111_1111, (0b110_1100 << 1) | /* padding */ 0b1; + 180 => 0b1111_1111, 0b1111_1111, (0b110_1101 << 1) | /* padding */ 0b1; + 182 => 0b1111_1111, 0b1111_1111, (0b110_1110 << 1) | /* padding */ 0b1; + 183 => 0b1111_1111, 0b1111_1111, (0b110_1111 << 1) | /* padding */ 0b1; + 188 => 0b1111_1111, 0b1111_1111, (0b111_0000 << 1) | /* padding */ 0b1; + 191 => 0b1111_1111, 0b1111_1111, (0b111_0001 << 1) | /* padding */ 0b1; + 197 => 0b1111_1111, 0b1111_1111, (0b111_0010 << 1) | /* padding */ 0b1; + 231 => 0b1111_1111, 0b1111_1111, (0b111_0011 << 1) | /* padding */ 0b1; + 239 => 0b1111_1111, 0b1111_1111, (0b111_0100 << 1) | /* padding */ 0b1; + 9 => 0b1111_1111, 0b1111_1111, 0b1110_1010, /* padding */ 0b1111_1111; + 142 => 0b1111_1111, 0b1111_1111, 0b1110_1011, /* padding */ 0b1111_1111; + 144 => 0b1111_1111, 0b1111_1111, 0b1110_1100, /* padding */ 0b1111_1111; + 145 => 0b1111_1111, 0b1111_1111, 0b1110_1101, /* padding */ 0b1111_1111; + 148 => 0b1111_1111, 0b1111_1111, 0b1110_1110, /* padding */ 0b1111_1111; + 159 => 0b1111_1111, 0b1111_1111, 0b1110_1111, /* padding */ 0b1111_1111; + 171 => 0b1111_1111, 0b1111_1111, 0b1111_0000, /* padding */ 0b1111_1111; + 206 => 0b1111_1111, 0b1111_1111, 0b1111_0001, /* padding */ 0b1111_1111; + 215 => 0b1111_1111, 0b1111_1111, 0b1111_0010, /* padding */ 0b1111_1111; + 225 => 0b1111_1111, 0b1111_1111, 0b1111_0011, /* padding */ 0b1111_1111; + 236 => 0b1111_1111, 0b1111_1111, 0b1111_0100, /* padding */ 0b1111_1111; + 237 => 0b1111_1111, 0b1111_1111, 0b1111_0101, /* padding */ 0b1111_1111; + 199 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b0 << 7) | /* padding */ 0b111_1111; + 207 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b1 << 7) | /* padding */ 0b111_1111; + 234 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b0 << 7) | /* padding */ 0b111_1111; + 235 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b1 << 7) | /* padding */ 0b111_1111; + 192 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b00 << 6) | /* padding */ 0b11_1111; + 193 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b01 << 6) | /* padding */ 0b11_1111; + 200 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b10 << 6) | /* padding */ 0b11_1111; + 201 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b11 << 6) | /* padding */ 0b11_1111; + 202 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b00 << 6) | /* padding */ 0b11_1111; + 205 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b01 << 6) | /* padding */ 0b11_1111; + 210 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b10 << 6) | /* padding */ 0b11_1111; + 213 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b11 << 6) | /* padding */ 0b11_1111; + 218 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b00 << 6) | /* padding */ 0b11_1111; + 219 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b01 << 6) | /* padding */ 0b11_1111; + 238 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b10 << 6) | /* padding */ 0b11_1111; + 240 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b11 << 6) | /* padding */ 0b11_1111; + 242 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b00 << 6) | /* padding */ 0b11_1111; + 243 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b01 << 6) | /* padding */ 0b11_1111; + 255 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b10 << 6) | /* padding */ 0b11_1111; + 203 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b110 << 5) | /* padding */ 0b11111; + 204 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b111 << 5) | /* padding */ 0b11111; + 211 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b000 << 5) | /* padding */ 0b11111; + 212 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b001 << 5) | /* padding */ 0b11111; + 214 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b010 << 5) | /* padding */ 0b11111; + 221 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b011 << 5) | /* padding */ 0b11111; + 222 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b100 << 5) | /* padding */ 0b11111; + 223 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b101 << 5) | /* padding */ 0b11111; + 241 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b110 << 5) | /* padding */ 0b11111; + 244 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b111 << 5) | /* padding */ 0b11111; + 245 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b000 << 5) | /* padding */ 0b11111; + 246 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b001 << 5) | /* padding */ 0b11111; + 247 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b010 << 5) | /* padding */ 0b11111; + 248 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b011 << 5) | /* padding */ 0b11111; + 250 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b100 << 5) | /* padding */ 0b11111; + 251 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b101 << 5) | /* padding */ 0b11111; + 252 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b110 << 5) | /* padding */ 0b11111; + 253 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b111 << 5) | /* padding */ 0b11111; + 254 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b000 << 5) | /* padding */ 0b11111; + 2 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0010 << 4) | /* padding */ 0b1111; + 3 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0011 << 4) | /* padding */ 0b1111; + 4 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0100 << 4) | /* padding */ 0b1111; + 5 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0101 << 4) | /* padding */ 0b1111; + 6 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0110 << 4) | /* padding */ 0b1111; + 7 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0111 << 4) | /* padding */ 0b1111; + 8 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1000 << 4) | /* padding */ 0b1111; + 11 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1001 << 4) | /* padding */ 0b1111; + 12 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1010 << 4) | /* padding */ 0b1111; + 14 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1011 << 4) | /* padding */ 0b1111; + 15 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1100 << 4) | /* padding */ 0b1111; + 16 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1101 << 4) | /* padding */ 0b1111; + 17 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1110 << 4) | /* padding */ 0b1111; + 18 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1111 << 4) | /* padding */ 0b1111; + 19 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000 << 4) | /* padding */ 0b1111; + 20 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0001 << 4) | /* padding */ 0b1111; + 21 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0010 << 4) | /* padding */ 0b1111; + 23 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0011 << 4) | /* padding */ 0b1111; + 24 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0100 << 4) | /* padding */ 0b1111; + 25 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0101 << 4) | /* padding */ 0b1111; + 26 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0110 << 4) | /* padding */ 0b1111; + 27 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0111 << 4) | /* padding */ 0b1111; + 28 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1000 << 4) | /* padding */ 0b1111; + 29 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1001 << 4) | /* padding */ 0b1111; + 30 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1010 << 4) | /* padding */ 0b1111; + 31 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1011 << 4) | /* padding */ 0b1111; + 127 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1100 << 4) | /* padding */ 0b1111; + 220 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1101 << 4) | /* padding */ 0b1111; + 249 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1110 << 4) | /* padding */ 0b1111; + 10 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1100 << 2) | /* padding */ 0b11; + 13 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1101 << 2) | /* padding */ 0b11; + 22 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1110 << 2) | /* padding */ 0b11;*/ + ]; + } + + /** + * https://tools.ietf.org/html/rfc7541 + * Appendix B. Huffman Code + */ + #[test] + #[allow(clippy::cognitive_complexity)] + fn test_decode_single_value() { + decoding![ + 48 => (0b0_0000 << 3) | /* padding */ 0b111; // '0' + 49 => (0b0_0001 << 3) | /* padding */ 0b111; // '1' + 50 => (0b0_0010 << 3) | /* padding */ 0b111; // '2' + 97 => (0b0_0011 << 3) | /* padding */ 0b111; // 'a' + 99 => (0b0_0100 << 3) | /* padding */ 0b111; // 'c' + 101 => (0b0_0101 << 3) | /* padding */ 0b111; // 'e' + 105 => (0b0_0110 << 3) | /* padding */ 0b111; // 'i' + 111 => (0b0_0111 << 3) | /* padding */ 0b111; // 'o' + 115 => (0b0_1000 << 3) | /* padding */ 0b111; // 's' + 116 => (0b0_1001 << 3) | /* padding */ 0b111; // 't' + 32 => (0b01_0100 << 2) | /* padding */ 0b11; + 37 => (0b01_0101 << 2) | /* padding */ 0b11; // '%' + 45 => (0b01_0110 << 2) | /* padding */ 0b11; // '-' + 46 => (0b01_0111 << 2) | /* padding */ 0b11; // '.' + 47 => (0b01_1000 << 2) | /* padding */ 0b11; // '/' + 51 => (0b01_1001 << 2) | /* padding */ 0b11; // '3' + 52 => (0b01_1010 << 2) | /* padding */ 0b11; // '4' + 53 => (0b01_1011 << 2) | /* padding */ 0b11; // '5' + 54 => (0b01_1100 << 2) | /* padding */ 0b11; // '6' + 55 => (0b01_1101 << 2) | /* padding */ 0b11; // '7' + 56 => (0b01_1110 << 2) | /* padding */ 0b11; // '8' + 57 => (0b01_1111 << 2) | /* padding */ 0b11; // '9' + 61 => (0b10_0000 << 2) | /* padding */ 0b11; // '=' + 65 => (0b10_0001 << 2) | /* padding */ 0b11; // 'A' + 95 => (0b10_0010 << 2) | /* padding */ 0b11; // '_' + 98 => (0b10_0011 << 2) | /* padding */ 0b11; // 'b' + 100 => (0b10_0100 << 2) | /* padding */ 0b11; // 'd' + 102 => (0b10_0101 << 2) | /* padding */ 0b11; // 'f' + 103 => (0b10_0110 << 2) | /* padding */ 0b11; // 'g' + 104 => (0b10_0111 << 2) | /* padding */ 0b11; // 'h' + 108 => (0b10_1000 << 2) | /* padding */ 0b11; // 'l' + 109 => (0b10_1001 << 2) | /* padding */ 0b11; // 'm' + 110 => (0b10_1010 << 2) | /* padding */ 0b11; // 'n' + 112 => (0b10_1011 << 2) | /* padding */ 0b11; // 'p' + 114 => (0b10_1100 << 2) | /* padding */ 0b11; // 'r' + 117 => (0b10_1101 << 2) | /* padding */ 0b11; // 'u' + 58 => (0b101_1100 << 1) | /* padding */ 0b1; // ':' + 66 => (0b101_1101 << 1) | /* padding */ 0b1; // 'B' + 67 => (0b101_1110 << 1) | /* padding */ 0b1; // 'C' + 68 => (0b101_1111 << 1) | /* padding */ 0b1; // 'D' + 69 => (0b110_0000 << 1) | /* padding */ 0b1; // 'E' + 70 => (0b110_0001 << 1) | /* padding */ 0b1; // 'F' + 71 => (0b110_0010 << 1) | /* padding */ 0b1; // 'G' + 72 => (0b110_0011 << 1) | /* padding */ 0b1; // 'H' + 73 => (0b110_0100 << 1) | /* padding */ 0b1; // 'I' + 74 => (0b110_0101 << 1) | /* padding */ 0b1; // 'J' + 75 => (0b110_0110 << 1) | /* padding */ 0b1; // 'K' + 76 => (0b110_0111 << 1) | /* padding */ 0b1; // 'L' + 77 => (0b110_1000 << 1) | /* padding */ 0b1; // 'M' + 78 => (0b110_1001 << 1) | /* padding */ 0b1; // 'N' + 79 => (0b110_1010 << 1) | /* padding */ 0b1; // 'O' + 80 => (0b110_1011 << 1) | /* padding */ 0b1; // 'P' + 81 => (0b110_1100 << 1) | /* padding */ 0b1; // 'Q' + 82 => (0b110_1101 << 1) | /* padding */ 0b1; // 'R' + 83 => (0b110_1110 << 1) | /* padding */ 0b1; // 'S' + 84 => (0b110_1111 << 1) | /* padding */ 0b1; // 'T' + 85 => (0b111_0000 << 1) | /* padding */ 0b1; // 'U' + 86 => (0b111_0001 << 1) | /* padding */ 0b1; // 'V' + 87 => (0b111_0010 << 1) | /* padding */ 0b1; // 'W' + 89 => (0b111_0011 << 1) | /* padding */ 0b1; // 'Y' + 106 => (0b111_0100 << 1) | /* padding */ 0b1; // 'j' + 107 => (0b111_0101 << 1) | /* padding */ 0b1; // 'k' + 113 => (0b111_0110 << 1) | /* padding */ 0b1; // 'q' + 118 => (0b111_0111 << 1) | /* padding */ 0b1; // 'v' + 119 => (0b111_1000 << 1) | /* padding */ 0b1; // 'w' + 120 => (0b111_1001 << 1) | /* padding */ 0b1; // 'x' + 121 => (0b111_1010 << 1) | /* padding */ 0b1; // 'y' + 122 => (0b111_1011 << 1) | /* padding */ 0b1; // 'z' + 38 => 0b1111_1000, /* padding */ 0b1111_1111; // '&' + 42 => 0b1111_1001, /* padding */ 0b1111_1111; // '*' + 44 => 0b1111_1010, /* padding */ 0b1111_1111; // ',' + 59 => 0b1111_1011, /* padding */ 0b1111_1111; + 88 => 0b1111_1100, /* padding */ 0b1111_1111; // 'X' + 90 => 0b1111_1101, /* padding */ 0b1111_1111; // 'Z' + 33 => 0b1111_1110, (0b00 << 6) | /* padding */ 0b11_1111; // '!' + 34 => 0b1111_1110, (0b01 << 6) | /* padding */ 0b11_1111; // '"' + 40 => 0b1111_1110, (0b10 << 6) | /* padding */ 0b11_1111; // '(' + 41 => 0b1111_1110, (0b11 << 6) | /* padding */ 0b11_1111; // ')' + 63 => 0b1111_1111, (0b00 << 6) | /* padding */ 0b11_1111; // '?' + 39 => 0b1111_1111, (0b010 << 5) | /* padding */ 0b11111; // ''' + 43 => 0b1111_1111, (0b011 << 5) | /* padding */ 0b11111; // '+' + 124 => 0b1111_1111, (0b100 << 5) | /* padding */ 0b11111; // '|' + 35 => 0b1111_1111, (0b1010 << 4) | /* padding */ 0b1111; // '#' + 62 => 0b1111_1111, (0b1011 << 4) | /* padding */ 0b1111; // '>' + 0 => 0b1111_1111, (0b11000 << 3) | /* padding */ 0b111; + 36 => 0b1111_1111, (0b11001 << 3) | /* padding */ 0b111; // '$' + 64 => 0b1111_1111, (0b11010 << 3) | /* padding */ 0b111; // '@' + 91 => 0b1111_1111, (0b11011 << 3) | /* padding */ 0b111; // '[' + 93 => 0b1111_1111, (0b11100 << 3) | /* padding */ 0b111; // ']' + 126 => 0b1111_1111, (0b11101 << 3) | /* padding */ 0b111; // '~' + 94 => 0b1111_1111, (0b11_1100 << 2) | /* padding */ 0b11; // '^' + 125 => 0b1111_1111, (0b11_1101 << 2) | /* padding */ 0b11; // '}' + 60 => 0b1111_1111, (0b111_1100 << 1) | /* padding */ 0b1; // '<' + 96 => 0b1111_1111, (0b111_1101 << 1) | /* padding */ 0b1; // '`' + 123 => 0b1111_1111, (0b111_1110 << 1) | /* padding */ 0b1; // '{' + 92 => 0b1111_1111, 0b1111_1110, (0b000 << 5) | /* padding */ 0b11111; // '\' + 195 => 0b1111_1111, 0b1111_1110, (0b001 << 5) | /* padding */ 0b11111; + 208 => 0b1111_1111, 0b1111_1110, (0b010 << 5) | /* padding */ 0b11111; + 128 => 0b1111_1111, 0b1111_1110, (0b0110 << 4) | /* padding */ 0b1111; + 130 => 0b1111_1111, 0b1111_1110, (0b0111 << 4) | /* padding */ 0b1111; + 131 => 0b1111_1111, 0b1111_1110, (0b1000 << 4) | /* padding */ 0b1111; + 162 => 0b1111_1111, 0b1111_1110, (0b1001 << 4) | /* padding */ 0b1111; + 184 => 0b1111_1111, 0b1111_1110, (0b1010 << 4) | /* padding */ 0b1111; + 194 => 0b1111_1111, 0b1111_1110, (0b1011 << 4) | /* padding */ 0b1111; + 224 => 0b1111_1111, 0b1111_1110, (0b1100 << 4) | /* padding */ 0b1111; + 226 => 0b1111_1111, 0b1111_1110, (0b1101 << 4) | /* padding */ 0b1111; + 153 => 0b1111_1111, 0b1111_1110, (0b11100 << 3) | /* padding */ 0b111; + 161 => 0b1111_1111, 0b1111_1110, (0b11101 << 3) | /* padding */ 0b111; + 167 => 0b1111_1111, 0b1111_1110, (0b11110 << 3) | /* padding */ 0b111; + 172 => 0b1111_1111, 0b1111_1110, (0b11111 << 3) | /* padding */ 0b111; + 176 => 0b1111_1111, 0b1111_1111, (0b00000 << 3) | /* padding */ 0b111; + 177 => 0b1111_1111, 0b1111_1111, (0b00001 << 3) | /* padding */ 0b111; + 179 => 0b1111_1111, 0b1111_1111, (0b00010 << 3) | /* padding */ 0b111; + 209 => 0b1111_1111, 0b1111_1111, (0b00011 << 3) | /* padding */ 0b111; + 216 => 0b1111_1111, 0b1111_1111, (0b00100 << 3) | /* padding */ 0b111; + 217 => 0b1111_1111, 0b1111_1111, (0b00101 << 3) | /* padding */ 0b111; + 227 => 0b1111_1111, 0b1111_1111, (0b00110 << 3) | /* padding */ 0b111; + 229 => 0b1111_1111, 0b1111_1111, (0b00111 << 3) | /* padding */ 0b111; + 230 => 0b1111_1111, 0b1111_1111, (0b01000 << 3) | /* padding */ 0b111; + 129 => 0b1111_1111, 0b1111_1111, (0b01_0010 << 2) | /* padding */ 0b11; + 132 => 0b1111_1111, 0b1111_1111, (0b01_0011 << 2) | /* padding */ 0b11; + 133 => 0b1111_1111, 0b1111_1111, (0b01_0100 << 2) | /* padding */ 0b11; + 134 => 0b1111_1111, 0b1111_1111, (0b01_0101 << 2) | /* padding */ 0b11; + 136 => 0b1111_1111, 0b1111_1111, (0b01_0110 << 2) | /* padding */ 0b11; + 146 => 0b1111_1111, 0b1111_1111, (0b01_0111 << 2) | /* padding */ 0b11; + 154 => 0b1111_1111, 0b1111_1111, (0b01_1000 << 2) | /* padding */ 0b11; + 156 => 0b1111_1111, 0b1111_1111, (0b01_1001 << 2) | /* padding */ 0b11; + 160 => 0b1111_1111, 0b1111_1111, (0b01_1010 << 2) | /* padding */ 0b11; + 163 => 0b1111_1111, 0b1111_1111, (0b01_1011 << 2) | /* padding */ 0b11; + 164 => 0b1111_1111, 0b1111_1111, (0b01_1100 << 2) | /* padding */ 0b11; + 169 => 0b1111_1111, 0b1111_1111, (0b01_1101 << 2) | /* padding */ 0b11; + 170 => 0b1111_1111, 0b1111_1111, (0b01_1110 << 2) | /* padding */ 0b11; + 173 => 0b1111_1111, 0b1111_1111, (0b01_1111 << 2) | /* padding */ 0b11; + 178 => 0b1111_1111, 0b1111_1111, (0b10_0000 << 2) | /* padding */ 0b11; + 181 => 0b1111_1111, 0b1111_1111, (0b10_0001 << 2) | /* padding */ 0b11; + 185 => 0b1111_1111, 0b1111_1111, (0b10_0010 << 2) | /* padding */ 0b11; + 186 => 0b1111_1111, 0b1111_1111, (0b10_0011 << 2) | /* padding */ 0b11; + 187 => 0b1111_1111, 0b1111_1111, (0b10_0100 << 2) | /* padding */ 0b11; + 189 => 0b1111_1111, 0b1111_1111, (0b10_0101 << 2) | /* padding */ 0b11; + 190 => 0b1111_1111, 0b1111_1111, (0b10_0110 << 2) | /* padding */ 0b11; + 196 => 0b1111_1111, 0b1111_1111, (0b10_0111 << 2) | /* padding */ 0b11; + 198 => 0b1111_1111, 0b1111_1111, (0b10_1000 << 2) | /* padding */ 0b11; + 228 => 0b1111_1111, 0b1111_1111, (0b10_1001 << 2) | /* padding */ 0b11; + 232 => 0b1111_1111, 0b1111_1111, (0b10_1010 << 2) | /* padding */ 0b11; + 233 => 0b1111_1111, 0b1111_1111, (0b10_1011 << 2) | /* padding */ 0b11; + 1 => 0b1111_1111, 0b1111_1111, (0b101_1000 << 1) | /* padding */ 0b1; + 135 => 0b1111_1111, 0b1111_1111, (0b101_1001 << 1) | /* padding */ 0b1; + 137 => 0b1111_1111, 0b1111_1111, (0b101_1010 << 1) | /* padding */ 0b1; + 138 => 0b1111_1111, 0b1111_1111, (0b101_1011 << 1) | /* padding */ 0b1; + 139 => 0b1111_1111, 0b1111_1111, (0b101_1100 << 1) | /* padding */ 0b1; + 140 => 0b1111_1111, 0b1111_1111, (0b101_1101 << 1) | /* padding */ 0b1; + 141 => 0b1111_1111, 0b1111_1111, (0b101_1110 << 1) | /* padding */ 0b1; + 143 => 0b1111_1111, 0b1111_1111, (0b101_1111 << 1) | /* padding */ 0b1; + 147 => 0b1111_1111, 0b1111_1111, (0b110_0000 << 1) | /* padding */ 0b1; + 149 => 0b1111_1111, 0b1111_1111, (0b110_0001 << 1) | /* padding */ 0b1; + 150 => 0b1111_1111, 0b1111_1111, (0b110_0010 << 1) | /* padding */ 0b1; + 151 => 0b1111_1111, 0b1111_1111, (0b110_0011 << 1) | /* padding */ 0b1; + 152 => 0b1111_1111, 0b1111_1111, (0b110_0100 << 1) | /* padding */ 0b1; + 155 => 0b1111_1111, 0b1111_1111, (0b110_0101 << 1) | /* padding */ 0b1; + 157 => 0b1111_1111, 0b1111_1111, (0b110_0110 << 1) | /* padding */ 0b1; + 158 => 0b1111_1111, 0b1111_1111, (0b110_0111 << 1) | /* padding */ 0b1; + 165 => 0b1111_1111, 0b1111_1111, (0b110_1000 << 1) | /* padding */ 0b1; + 166 => 0b1111_1111, 0b1111_1111, (0b110_1001 << 1) | /* padding */ 0b1; + 168 => 0b1111_1111, 0b1111_1111, (0b110_1010 << 1) | /* padding */ 0b1; + 174 => 0b1111_1111, 0b1111_1111, (0b110_1011 << 1) | /* padding */ 0b1; + 175 => 0b1111_1111, 0b1111_1111, (0b110_1100 << 1) | /* padding */ 0b1; + 180 => 0b1111_1111, 0b1111_1111, (0b110_1101 << 1) | /* padding */ 0b1; + 182 => 0b1111_1111, 0b1111_1111, (0b110_1110 << 1) | /* padding */ 0b1; + 183 => 0b1111_1111, 0b1111_1111, (0b110_1111 << 1) | /* padding */ 0b1; + 188 => 0b1111_1111, 0b1111_1111, (0b111_0000 << 1) | /* padding */ 0b1; + 191 => 0b1111_1111, 0b1111_1111, (0b111_0001 << 1) | /* padding */ 0b1; + 197 => 0b1111_1111, 0b1111_1111, (0b111_0010 << 1) | /* padding */ 0b1; + 231 => 0b1111_1111, 0b1111_1111, (0b111_0011 << 1) | /* padding */ 0b1; + 239 => 0b1111_1111, 0b1111_1111, (0b111_0100 << 1) | /* padding */ 0b1; + 9 => 0b1111_1111, 0b1111_1111, 0b1110_1010, /* padding */ 0b1111_1111; + 142 => 0b1111_1111, 0b1111_1111, 0b1110_1011, /* padding */ 0b1111_1111; + 144 => 0b1111_1111, 0b1111_1111, 0b1110_1100, /* padding */ 0b1111_1111; + 145 => 0b1111_1111, 0b1111_1111, 0b1110_1101, /* padding */ 0b1111_1111; + 148 => 0b1111_1111, 0b1111_1111, 0b1110_1110, /* padding */ 0b1111_1111; + 159 => 0b1111_1111, 0b1111_1111, 0b1110_1111, /* padding */ 0b1111_1111; + 171 => 0b1111_1111, 0b1111_1111, 0b1111_0000, /* padding */ 0b1111_1111; + 206 => 0b1111_1111, 0b1111_1111, 0b1111_0001, /* padding */ 0b1111_1111; + 215 => 0b1111_1111, 0b1111_1111, 0b1111_0010, /* padding */ 0b1111_1111; + 225 => 0b1111_1111, 0b1111_1111, 0b1111_0011, /* padding */ 0b1111_1111; + 236 => 0b1111_1111, 0b1111_1111, 0b1111_0100, /* padding */ 0b1111_1111; + 237 => 0b1111_1111, 0b1111_1111, 0b1111_0101, /* padding */ 0b1111_1111; + 199 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b0 << 7) | /* padding */ 0b111_1111; + 207 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b1 << 7) | /* padding */ 0b111_1111; + 234 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b0 << 7) | /* padding */ 0b111_1111; + 235 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b1 << 7) | /* padding */ 0b111_1111; + 192 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b00 << 6) | /* padding */ 0b11_1111; + 193 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b01 << 6) | /* padding */ 0b11_1111; + 200 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b10 << 6) | /* padding */ 0b11_1111; + 201 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b11 << 6) | /* padding */ 0b11_1111; + 202 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b00 << 6) | /* padding */ 0b11_1111; + 205 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b01 << 6) | /* padding */ 0b11_1111; + 210 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b10 << 6) | /* padding */ 0b11_1111; + 213 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b11 << 6) | /* padding */ 0b11_1111; + 218 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b00 << 6) | /* padding */ 0b11_1111; + 219 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b01 << 6) | /* padding */ 0b11_1111; + 238 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b10 << 6) | /* padding */ 0b11_1111; + 240 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b11 << 6) | /* padding */ 0b11_1111; + 242 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b00 << 6) | /* padding */ 0b11_1111; + 243 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b01 << 6) | /* padding */ 0b11_1111; + 255 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b10 << 6) | /* padding */ 0b11_1111; + 203 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b110 << 5) | /* padding */ 0b11111; + 204 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b111 << 5) | /* padding */ 0b11111; + 211 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b000 << 5) | /* padding */ 0b11111; + 212 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b001 << 5) | /* padding */ 0b11111; + 214 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b010 << 5) | /* padding */ 0b11111; + 221 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b011 << 5) | /* padding */ 0b11111; + 222 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b100 << 5) | /* padding */ 0b11111; + 223 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b101 << 5) | /* padding */ 0b11111; + 241 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b110 << 5) | /* padding */ 0b11111; + 244 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b111 << 5) | /* padding */ 0b11111; + 245 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b000 << 5) | /* padding */ 0b11111; + 246 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b001 << 5) | /* padding */ 0b11111; + 247 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b010 << 5) | /* padding */ 0b11111; + 248 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b011 << 5) | /* padding */ 0b11111; + 250 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b100 << 5) | /* padding */ 0b11111; + 251 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b101 << 5) | /* padding */ 0b11111; + 252 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b110 << 5) | /* padding */ 0b11111; + 253 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b111 << 5) | /* padding */ 0b11111; + 254 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b000 << 5) | /* padding */ 0b11111; + 2 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0010 << 4) | /* padding */ 0b1111; + 3 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0011 << 4) | /* padding */ 0b1111; + 4 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0100 << 4) | /* padding */ 0b1111; + 5 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0101 << 4) | /* padding */ 0b1111; + 6 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0110 << 4) | /* padding */ 0b1111; + 7 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0111 << 4) | /* padding */ 0b1111; + 8 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1000 << 4) | /* padding */ 0b1111; + 11 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1001 << 4) | /* padding */ 0b1111; + 12 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1010 << 4) | /* padding */ 0b1111; + 14 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1011 << 4) | /* padding */ 0b1111; + 15 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1100 << 4) | /* padding */ 0b1111; + 16 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1101 << 4) | /* padding */ 0b1111; + 17 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1110 << 4) | /* padding */ 0b1111; + 18 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b1111 << 4) | /* padding */ 0b1111; + 19 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000 << 4) | /* padding */ 0b1111; + 20 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0001 << 4) | /* padding */ 0b1111; + 21 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0010 << 4) | /* padding */ 0b1111; + 23 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0011 << 4) | /* padding */ 0b1111; + 24 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0100 << 4) | /* padding */ 0b1111; + 25 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0101 << 4) | /* padding */ 0b1111; + 26 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0110 << 4) | /* padding */ 0b1111; + 27 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0111 << 4) | /* padding */ 0b1111; + 28 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1000 << 4) | /* padding */ 0b1111; + 29 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1001 << 4) | /* padding */ 0b1111; + 30 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1010 << 4) | /* padding */ 0b1111; + 31 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1011 << 4) | /* padding */ 0b1111; + 127 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1100 << 4) | /* padding */ 0b1111; + 220 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1101 << 4) | /* padding */ 0b1111; + 249 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b1110 << 4) | /* padding */ 0b1111; + 10 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1100 << 2) | /* padding */ 0b11; + 13 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1101 << 2) | /* padding */ 0b11; + 22 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1110 << 2) | /* padding */ 0b11; + // 256 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b11_1111 << 2) | /* padding */ 0b11; + ]; + } + + /** + * https://tools.ietf.org/html/rfc7541 + * Appendix B. Huffman Code + */ + #[test] + fn test_decode_all_code_joined() { + let bytes = vec![ + // 0 |11111111|11000 + 0b1111_1111, + (0b11000 << 3) + // 1 |11111111|11111111|1011000 + + 0b111, + 0b1111_1111, + 0b1111_1101, + (0b1000 << 4) + // 2 |11111111|11111111|11111110|0010 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0010, + // 3 |11111111|11111111|11111110|0011 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0011 << 4) + // 4 |11111111|11111111|11111110|0100 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0100, + // 5 |11111111|11111111|11111110|0101 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0101 << 4) + // 6 |11111111|11111111|11111110|0110 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0110, + // 7 |11111111|11111111|11111110|0111 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0111 << 4) + // 8 |11111111|11111111|11111110|1000 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1000, + // 9 |11111111|11111111|11101010 + 0b1111_1111, + 0b1111_1111, + 0b1110_1010, + // 10 |11111111|11111111|11111111|111100 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_1100 << 2) + // 11 |11111111|11111111|11111110|1001 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1010, + (0b01 << 6) + // 12 |11111111|11111111|11111110|1010 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b10_1010 << 2) + // 13 |11111111|11111111|11111111|111101 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b1101 << 4) + // 14 |11111111|11111111|11111110|1011 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1011, + // 15 |11111111|11111111|11111110|1100 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b1100 << 4) + // 16 |11111111|11111111|11111110|1101 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1101, + // 17 |11111111|11111111|11111110|1110 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b1110 << 4) + // 18 |11111111|11111111|11111110|1111 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1111, + // 19 |11111111|11111111|11111111|0000 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000 << 4) + // 20 |11111111|11111111|11111111|0001 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_0001, + // 21 |11111111|11111111|11111111|0010 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0010 << 4) + // 22 |11111111|11111111|11111111|111110 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b10 << 6) + // 23 |11111111|11111111|11111111|0011 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_0011 << 2) + // 24 |11111111|11111111|11111111|0100 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1101, + (0b00 << 6) + // 25 |11111111|11111111|11111111|0101 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_0101 << 2) + // 26 |11111111|11111111|11111111|0110 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1101, + (0b10 << 6) + // 27 |11111111|11111111|11111111|0111 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_0111 << 2) + // 28 |11111111|11111111|11111111|1000 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b00 << 6) + // 29 |11111111|11111111|11111111|1001 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_1001 << 2) + // 30 |11111111|11111111|11111111|1010 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b10 << 6) + // 31 |11111111|11111111|11111111|1011 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b11_1011 << 2) + // 32 |010100 + + 0b01, + (0b0100 << 4) + // 33 -!- |11111110|00 + + 0b1111, + (0b11_1000 << 2) + // 34 -;- |11111110|01 + + 0b11, + 0b1111_1001, + // 35 -#- |11111111|1010 + 0b1111_1111, + (0b1010 << 4) + // 36 -$- |11111111|11001 + + 0b1111, + 0b1111_1100, + (0b1 << 7) + // 37 -%- |010101 + + (0b01_0101 << 1) + // 38 -&- |11111000 + + 0b1, + (0b111_1000 << 1) + // 39 -'- |11111111|010 + + 0b1, + 0b1111_1110, + (0b10 << 6) + // 40 -(- |11111110|10 + + 0b11_1111, + (0b1010 << 4) + // 41 -)- |11111110|11 + + 0b1111, + (0b11_1011 << 2) + // 42 -*- |11111001 + + 0b11, + (0b11_1001 << 2) + // 43 -+- |11111111|011 + + 0b11, + 0b1111_1101, + (0b1 << 7) + // 44 -,- |11111010 + + 0b111_1101, + (0b0 << 7) + // 45 --- |010110 + + (0b01_0110 << 1) + // 46 -.- |010111 + + 0b0, + (0b10111 << 3) + // 47 -/- |011000 + + 0b011, + (0b000 << 5) + // 48 -0- |00000 + + 0b00000, + // 49 -1- |00001 + (0b00001 << 3) + // 50 -2- |00010 + + 0b000, + (0b10 << 6) + // 51 -3- |011001 + + 0b01_1001, + // 52 -4- |011010 + (0b01_1010 << 2) + // 53 -5- |011011 + + 0b01, + (0b1011 << 4) + // 54 -6- |011100 + + 0b0111, + (0b00 << 6) + // 55 -7- |011101 + + 0b01_1101, + // 56 -8- |011110 + (0b01_1110 << 2) + // 57 -9- |011111 + + 0b01, + (0b1111 << 4) + // 58 -:- |1011100 + + 0b1011, + (0b100 << 5) + // 59 |11111011 + + 0b11111, + (0b011 << 5) + // 60 -<- |11111111|1111100 + + 0b11111, + 0b1111_1111, + (0b00 << 6) + // 61 -=- |100000 + + 0b10_0000, + // 62 ->- |11111111|1011 + 0b1111_1111, + (0b1011 << 4) + // 63 -?- |11111111|00 + + 0b1111, + (0b11_1100 << 2) + // 64 -@- |11111111|11010 + + 0b11, + 0b1111_1111, + (0b010 << 5) + // 65 -A- |100001 + + 0b10000, + (0b1 << 7) + // 66 -B- |1011101 + + 0b101_1101, + // 67 -C- |1011110 + (0b101_1110 << 1) + // 68 -D- |1011111 + + 0b1, + (0b01_1111 << 2) + // 69 -E- |1100000 + + 0b11, + (0b00000 << 3) + // 70 -F- |1100001 + + 0b110, + (0b0001 << 4) + // 71 -G- |1100010 + + 0b1100, + (0b010 << 5) + // 72 -H- |1100011 + + 0b11000, + (0b11 << 6) + // 73 -I- |1100100 + + 0b11_0010, + (0b0 << 7) + // 74 -J- |1100101 + + 0b110_0101, + // 75 -K- |1100110 + (0b110_0110 << 1) + // 76 -L- |1100111 + + 0b1, + (0b10_0111 << 2) + // 77 -M- |1101000 + + 0b11, + (0b01000 << 3) + // 78 -N- |1101001 + + 0b110, + (0b1001 << 4) + // 79 -O- |1101010 + + 0b1101, + (0b010 << 5) + // 80 -P- |1101011 + + 0b11010, + (0b11 << 6) + // 81 -Q- |1101100 + + 0b11_0110, + (0b0 << 7) + // 82 -R- |1101101 + + 0b110_1101, + // 83 -S- |1101110 + (0b110_1110 << 1) + // 84 -T- |1101111 + + 0b1, + (0b10_1111 << 2) + // 85 -U- |1110000 + + 0b11, + (0b10000 << 3) + // 86 -V- |1110001 + + 0b111, + (0b0001 << 4) + // 87 -W- |1110010 + + 0b1110, + (0b010 << 5) + // 88 -X- |11111100 + + 0b11111, + (0b100 << 5) + // 89 -Y- |1110011 + + 0b11100, + (0b11 << 6) + // 90 -Z- |11111101 + + 0b11_1111, + (0b01 << 6) + // 91 -[- |11111111|11011 + + 0b11_1111, + (0b111_1011 << 1) + // 92 -\- |11111111|11111110|000 + + 0b1, + 0b1111_1111, + 0b1111_1100, + (0b00 << 6) + // 93 -]- |11111111|11100 + + 0b11_1111, + (0b111_1100 << 1) + // 94 -^- |11111111|111100 + + 0b1, + 0b1111_1111, + (0b11100 << 3) + // 95 -_- |100010 + + 0b100, + (0b010 << 5) + // 96 -`- |11111111|1111101 + + 0b11111, + 0b1111_1111, + (0b01 << 6) + // 97 -a- |00011 + + (0b00011 << 1) + // 98 -b- |100011 + + 0b1, + (0b00011 << 3) + // 99 -c- |00100 + + 0b001, + (0b00 << 6) + // 100 -d- |100100 + + 0b10_0100, + // 101 -e- |00101 + (0b00101 << 3) + // 102 -f- |100101 + + 0b100, + (0b101 << 5) + // 103 -g- |100110 + + 0b10011, + (0b0 << 7) + // 104 -h- |100111 + + (0b10_0111 << 1) + // 105 -i- |00110 + + 0b0, + (0b0110 << 4) + // 106 -j- |1110100 + + 0b1110, + (0b100 << 5) + // 107 -k- |1110101 + + 0b11101, + (0b01 << 6) + // 108 -l- |101000 + + 0b10_1000, + // 109 -m- |101001 + (0b10_1001 << 2) + // 110 -n- |101010 + + 0b10, + (0b1010 << 4) + // 111 -o- |00111 + + 0b0011, + (0b1 << 7) + // 112 -p- |101011 + + (0b10_1011 << 1) + // 113 -q- |1110110 + + 0b1, + (0b11_0110 << 2) + // 114 -r- |101100 + + 0b10, + (0b1100 << 4) + // 115 -s- |01000 + + 0b0100, + (0b0 << 7) + // 116 -t- |01001 + + (0b01001 << 2) + // 117 -u- |101101 + + 0b10, + (0b1101 << 4) + // 118 -v- |1110111 + + 0b1110, + (0b111 << 5) + // 119 -w- |1111000 + + 0b11110, + (0b00 << 6) + // 120 -x- |1111001 + + 0b11_1100, + (0b1 << 7) + // 121 -y- |1111010 + + 0b111_1010, + // 122 -z- |1111011 + (0b111_1011 << 1) + // 123 -{- |11111111|1111110 + + 0b1, + 0b1111_1111, + (0b11_1110 << 2) + // 124 -|- |11111111|100 + + 0b11, + 0b1111_1110, + (0b0 << 7) + // 125 -}- |11111111|111101 + + 0b111_1111, + (0b111_1101 << 1) + // 126 -~- |11111111|11101 + + 0b1, + 0b1111_1111, + (0b1101 << 4) + // 127 |11111111|11111111|11111111|1100 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1100, + // 128 |11111111|11111110|0110 + 0b1111_1111, + 0b1111_1110, + (0b0110 << 4) + // 129 |11111111|11111111|010010 + + 0b1111, + 0b1111_1111, + 0b1111_0100, + (0b10 << 6) + // 130 |11111111|11111110|0111 + + 0b11_1111, + 0b1111_1111, + (0b10_0111 << 2) + // 131 |11111111|11111110|1000 + + 0b11, + 0b1111_1111, + 0b1111_1010, + (0b00 << 6) + // 132 |11111111|11111111|010011 + + 0b11_1111, + 0b1111_1111, + 0b1101_0011, + // 133 |11111111|11111111|010100 + 0b1111_1111, + 0b1111_1111, + (0b01_0100 << 2) + // 134 |11111111|11111111|010101 + + 0b11, + 0b1111_1111, + 0b1111_1101, + (0b0101 << 4) + // 135 |11111111|11111111|1011001 + + 0b1111, + 0b1111_1111, + 0b1111_1011, + (0b001 << 5) + // 136 |11111111|11111111|010110 + + 0b11111, + 0b1111_1111, + 0b1110_1011, + (0b0 << 7) + // 137 |11111111|11111111|1011010 + + 0b111_1111, + 0b1111_1111, + 0b1101_1010, + // 138 |11111111|11111111|1011011 + 0b1111_1111, + 0b1111_1111, + (0b101_1011 << 1) + // 139 |11111111|11111111|1011100 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b01_1100 << 2) + // 140 |11111111|11111111|1011101 + + 0b11, + 0b1111_1111, + 0b1111_1110, + (0b11101 << 3) + // 141 |11111111|11111111|1011110 + + 0b111, + 0b1111_1111, + 0b1111_1101, + (0b1110 << 4) + // 142 |11111111|11111111|11101011 + + 0b1111, + 0b1111_1111, + 0b1111_1110, + (0b1011 << 4) + // 143 |11111111|11111111|1011111 + + 0b1111, + 0b1111_1111, + 0b1111_1011, + (0b111 << 5) + // 144 |11111111|11111111|11101100 + + 0b11111, + 0b1111_1111, + 0b1111_1101, + (0b100 << 5) + // 145 |11111111|11111111|11101101 + + 0b11111, + 0b1111_1111, + 0b1111_1101, + (0b101 << 5) + // 146 |11111111|11111111|010111 + + 0b11111, + 0b1111_1111, + 0b1110_1011, + (0b1 << 7) + // 147 |11111111|11111111|1100000 + + 0b111_1111, + 0b1111_1111, + 0b1110_0000, + // 148 |11111111|11111111|11101110 + 0b1111_1111, + 0b1111_1111, + 0b1110_1110, + // 149 |11111111|11111111|1100001 + 0b1111_1111, + 0b1111_1111, + (0b110_0001 << 1) + // 150 |11111111|11111111|1100010 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b10_0010 << 2) + // 151 |11111111|11111111|1100011 + + 0b11, + 0b1111_1111, + 0b1111_1111, + (0b00011 << 3) + // 152 |11111111|11111111|1100100 + + 0b111, + 0b1111_1111, + 0b1111_1110, + (0b0100 << 4) + // 153 |11111111|11111110|11100 + + 0b1111, + 0b1111_1111, + 0b1110_1110, + (0b0 << 7) + // 154 |11111111|11111111|011000 + + 0b111_1111, + 0b1111_1111, + (0b101_1000 << 1) + // 155 |11111111|11111111|1100101 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b10_0101 << 2) + // 156 |11111111|11111111|011001 + + 0b11, + 0b1111_1111, + 0b1111_1101, + (0b1001 << 4) + // 157 |11111111|11111111|1100110 + + 0b1111, + 0b1111_1111, + 0b1111_1100, + (0b110 << 5) + // 158 |11111111|11111111|1100111 + + 0b11111, + 0b1111_1111, + 0b1111_1001, + (0b11 << 6) + // 159 |11111111|11111111|11101111 + + 0b11_1111, + 0b1111_1111, + 0b1111_1011, + (0b11 << 6) + // 160 |11111111|11111111|011010 + + 0b11_1111, + 0b1111_1111, + 0b1101_1010, + // 161 |11111111|11111110|11101 + 0b1111_1111, + 0b1111_1110, + (0b11101 << 3) + // 162 |11111111|11111110|1001 + + 0b111, + 0b1111_1111, + 0b1111_0100, + (0b1 << 7) + // 163 |11111111|11111111|011011 + + 0b111_1111, + 0b1111_1111, + (0b101_1011 << 1) + // 164 |11111111|11111111|011100 + + 0b1, + 0b1111_1111, + 0b1111_1110, + (0b11100 << 3) + // 165 |11111111|11111111|1101000 + + 0b111, + 0b1111_1111, + 0b1111_1110, + (0b1000 << 4) + // 166 |11111111|11111111|1101001 + + 0b1111, + 0b1111_1111, + 0b1111_1101, + (0b001 << 5) + // 167 |11111111|11111110|11110 + + 0b11111, + 0b1111_1111, + 0b1101_1110, + // 168 |11111111|11111111|1101010 + 0b1111_1111, + 0b1111_1111, + (0b110_1010 << 1) + // 169 |11111111|11111111|011101 + + 0b1, + 0b1111_1111, + 0b1111_1110, + (0b11101 << 3) + // 170 |11111111|11111111|011110 + + 0b111, + 0b1111_1111, + 0b1111_1011, + (0b110 << 5) + // 171 |11111111|11111111|11110000 + + 0b11111, + 0b1111_1111, + 0b1111_1110, + (0b000 << 5) + // 172 |11111111|11111110|11111 + + 0b11111, + 0b1111_1111, + 0b1101_1111, + // 173 |11111111|11111111|011111 + 0b1111_1111, + 0b1111_1111, + (0b01_1111 << 2) + // 174 |11111111|11111111|1101011 + + 0b11, + 0b1111_1111, + 0b1111_1111, + (0b01011 << 3) + // 175 |11111111|11111111|1101100 + + 0b111, + 0b1111_1111, + 0b1111_1110, + (0b1100 << 4) + // 176 |11111111|11111111|00000 + + 0b1111, + 0b1111_1111, + 0b1111_0000, + (0b0 << 7) + // 177 |11111111|11111111|00001 + + 0b111_1111, + 0b1111_1111, + (0b10_0001 << 2) + // 178 |11111111|11111111|100000 + + 0b11, + 0b1111_1111, + 0b1111_1110, + (0b0000 << 4) + // 179 |11111111|11111111|00010 + + 0b1111, + 0b1111_1111, + 0b1111_0001, + (0b0 << 7) + // 180 |11111111|11111111|1101101 + + 0b111_1111, + 0b1111_1111, + 0b1110_1101, + // 181 |11111111|11111111|100001 + 0b1111_1111, + 0b1111_1111, + (0b10_0001 << 2) + // 182 |11111111|11111111|1101110 + + 0b11, + 0b1111_1111, + 0b1111_1111, + (0b01110 << 3) + // 183 |11111111|11111111|1101111 + + 0b111, + 0b1111_1111, + 0b1111_1110, + (0b1111 << 4) + // 184 |11111111|11111110|1010 + + 0b1111, + 0b1111_1111, + 0b1110_1010, + // 185 |11111111|11111111|100010 + 0b1111_1111, + 0b1111_1111, + (0b10_0010 << 2) + // 186 |11111111|11111111|100011 + + 0b11, + 0b1111_1111, + 0b1111_1110, + (0b0011 << 4) + // 187 |11111111|11111111|100100 + + 0b1111, + 0b1111_1111, + 0b1111_1001, + (0b00 << 6) + // 188 |11111111|11111111|1110000 + + 0b11_1111, + 0b1111_1111, + 0b1111_1000, + (0b0 << 7) + // 189 |11111111|11111111|100101 + + 0b111_1111, + 0b1111_1111, + (0b110_0101 << 1) + // 190 |11111111|11111111|100110 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b00110 << 3) + // 191 |11111111|11111111|1110001 + + 0b111, + 0b1111_1111, + 0b1111_1111, + (0b0001 << 4) + // 192 |11111111|11111111|11111000|00 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b10_0000 << 2) + // 193 |11111111|11111111|11111000|01 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1110_0001, + // 194 |11111111|11111110|1011 + 0b1111_1111, + 0b1111_1110, + (0b1011 << 4) + // 195 |11111111|11111110|001 + + 0b1111, + 0b1111_1111, + (0b111_0001 << 1) + // 196 |11111111|11111111|100111 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b00111 << 3) + // 197 |11111111|11111111|1110010 + + 0b111, + 0b1111_1111, + 0b1111_1111, + (0b0010 << 4) + // 198 |11111111|11111111|101000 + + 0b1111, + 0b1111_1111, + 0b1111_1010, + (0b00 << 6) + // 199 |11111111|11111111|11110110|0 + + 0b11_1111, + 0b1111_1111, + 0b1111_1101, + (0b100 << 5) + // 200 |11111111|11111111|11111000|10 + + 0b11111, + 0b1111_1111, + 0b1111_1111, + (0b00010 << 3) + // 201 |11111111|11111111|11111000|11 + + 0b111, + 0b1111_1111, + 0b1111_1111, + (0b110_0011 << 1) + // 202 |11111111|11111111|11111001|00 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_0010, + (0b0 << 7) + // 203 |11111111|11111111|11111011|110 + + 0b111_1111, + 0b1111_1111, + 0b1111_1101, + (0b1110 << 4) + // 204 |11111111|11111111|11111011|111 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b101_1111 << 1) + // 205 |11111111|11111111|11111001|01 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_0010, + (0b1 << 7) + // 206 |11111111|11111111|11110001 + + 0b111_1111, + 0b1111_1111, + 0b1111_1000, + (0b1 << 7) + // 207 |11111111|11111111|11110110|1 + + 0b111_1111, + 0b1111_1111, + 0b1111_1011, + (0b01 << 6) + // 208 |11111111|11111110|010 + + 0b11_1111, + 0b1111_1111, + (0b10010 << 3) + // 209 |11111111|11111111|00011 + + 0b111, + 0b1111_1111, + 0b1111_1000, + (0b11 << 6) + // 210 |11111111|11111111|11111001|10 + + 0b11_1111, + 0b1111_1111, + 0b1111_1110, + (0b0110 << 4) + // 211 |11111111|11111111|11111100|000 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b110_0000 << 1) + // 212 |11111111|11111111|11111100|001 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_1000, + (0b01 << 6) + // 213 |11111111|11111111|11111001|11 + + 0b11_1111, + 0b1111_1111, + 0b1111_1110, + (0b0111 << 4) + // 214 |11111111|11111111|11111100|010 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b110_0010 << 1) + // 215 |11111111|11111111|11110010 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b111_0010 << 1) + // 216 |11111111|11111111|00100 + + 0b1, + 0b1111_1111, + 0b1111_1110, + (0b0100 << 4) + // 217 |11111111|11111111|00101 + + 0b1111, + 0b1111_1111, + 0b1111_0010, + (0b1 << 7) + // 218 |11111111|11111111|11111010|00 + + 0b111_1111, + 0b1111_1111, + 0b1111_1101, + (0b000 << 5) + // 219 |11111111|11111111|11111010|01 + + 0b11111, + 0b1111_1111, + 0b1111_1111, + (0b01001 << 3) + // 220 |11111111|11111111|11111111|1101 + + 0b111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b1 << 7) + // 221 |11111111|11111111|11111100|011 + + 0b111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0011 << 4) + // 222 |11111111|11111111|11111100|100 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b110_0100 << 1) + // 223 |11111111|11111111|11111100|101 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_1001, + (0b01 << 6) + // 224 |11111111|11111110|1100 + + 0b11_1111, + 0b1111_1111, + (0b10_1100 << 2) + // 225 |11111111|11111111|11110011 + + 0b11, + 0b1111_1111, + 0b1111_1111, + (0b11_0011 << 2) + // 226 |11111111|11111110|1101 + + 0b11, + 0b1111_1111, + 0b1111_1011, + (0b01 << 6) + // 227 |11111111|11111111|00110 + + 0b11_1111, + 0b1111_1111, + (0b110_0110 << 1) + // 228 |11111111|11111111|101001 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b01001 << 3) + // 229 |11111111|11111111|00111 + + 0b111, + 0b1111_1111, + 0b1111_1001, + (0b11 << 6) + // 230 |11111111|11111111|01000 + + 0b11_1111, + 0b1111_1111, + (0b110_1000 << 1) + // 231 |11111111|11111111|1110011 + + 0b1, + 0b1111_1111, + 0b1111_1111, + (0b11_0011 << 2) + // 232 |11111111|11111111|101010 + + 0b11, + 0b1111_1111, + 0b1111_1110, + (0b1010 << 4) + // 233 |11111111|11111111|101011 + + 0b1111, + 0b1111_1111, + 0b1111_1010, + (0b11 << 6) + // 234 |11111111|11111111|11110111|0 + + 0b11_1111, + 0b1111_1111, + 0b1111_1101, + (0b110 << 5) + // 235 |11111111|11111111|11110111|1 + + 0b11111, + 0b1111_1111, + 0b1111_1110, + (0b1111 << 4) + // 236 |11111111|11111111|11110100 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b0100 << 4) + // 237 |11111111|11111111|11110101 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b0101 << 4) + // 238 |11111111|11111111|11111010|10 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b10_1010 << 2) + // 239 |11111111|11111111|1110100 + + 0b11, + 0b1111_1111, + 0b1111_1111, + (0b10100 << 3) + // 240 |11111111|11111111|11111010|11 + + 0b111, + 0b1111_1111, + 0b1111_1111, + (0b110_1011 << 1) + // 241 |11111111|11111111|11111100|110 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_1001, + (0b10 << 6) + // 242 |11111111|11111111|11111011|00 + + 0b11_1111, + 0b1111_1111, + 0b1111_1110, + (0b1100 << 4) + // 243 |11111111|11111111|11111011|01 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b10_1101 << 2) + // 244 |11111111|11111111|11111100|111 + + 0b11, + 0b1111_1111, + 0b1111_1111, + 0b1111_0011, + (0b1 << 7) + // 245 |11111111|11111111|11111101|000 + + 0b111_1111, + 0b1111_1111, + 0b1111_1110, + (0b1000 << 4) + // 246 |11111111|11111111|11111101|001 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b110_1001 << 1) + // 247 |11111111|11111111|11111101|010 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_1010, + (0b10 << 6) + // 248 |11111111|11111111|11111101|011 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b01011 << 3) + // 249 |11111111|11111111|11111111|1110 + + 0b111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0 << 7) + // 250 |11111111|11111111|11111101|100 + + 0b111_1111, + 0b1111_1111, + 0b1111_1110, + (0b1100 << 4) + // 251 |11111111|11111111|11111101|101 + + 0b1111, + 0b1111_1111, + 0b1111_1111, + (0b110_1101 << 1) + // 252 |11111111|11111111|11111101|110 + + 0b1, + 0b1111_1111, + 0b1111_1111, + 0b1111_1011, + (0b10 << 6) + // 253 |11111111|11111111|11111101|111 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + (0b01111 << 3) + // 254 |11111111|11111111|11111110|000 + + 0b111, + 0b1111_1111, + 0b1111_1111, + 0b1111_0000, + // 255 |11111111|11111111|11111011|10 + 0b1111_1111, + 0b1111_1111, + 0b1111_1011, + (0b10 << 6) + // 256 eof |11111111|11111111|11111111|111111 + + 0b11_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + ]; + let expected = (0u8..=255).collect(); + let len = bytes.len(); + test_all_chunking_combinations( + &mut Cursor::new(bytes), + || StatefulHuffmanDecoder::new(len), + ParseProgressResult::Done(expected), + ); + } +} diff --git a/h3/src/qpack2/prefix_string/encode.rs b/h3/src/qpack2/prefix_string/encode.rs new file mode 100644 index 00000000..0e21115e --- /dev/null +++ b/h3/src/qpack2/prefix_string/encode.rs @@ -0,0 +1,1829 @@ +use super::BitWindow; + +#[derive(Debug, PartialEq)] +pub struct HuffmanEncodingError { + buffer_pos: BitWindow, + len: usize, + capacity: usize, + text: String, +} + +#[derive(Clone, Debug)] +struct EncodeValue { + buffer: &'static [u8], + bit_count: u32, +} + +#[derive(Clone, Debug)] +struct HuffmanEncoder { + buffer_pos: BitWindow, + buffer: Vec, +} + +impl HuffmanEncoder { + fn new() -> HuffmanEncoder { + HuffmanEncoder { + buffer_pos: BitWindow::new(), + buffer: Vec::new(), + } + } + + fn ensure_free_space(&mut self, bit_count: u32) { + let mut end_range = self.buffer_pos.clone(); + end_range.forwards(bit_count); + end_range.forwards(0); + + // buffer still has enough space to work on + if self.buffer.len() > end_range.byte as usize { + return; + } + + // optimisation to grow capacity before pushing data + if self.buffer.capacity() <= end_range.byte as usize { + self.buffer.reserve(((7 * end_range.byte) / 4) as usize); + } + + let forward = + end_range.byte as usize - self.buffer.len() + if end_range.bit > 0 { 1 } else { 0 }; + for _ in 0..forward { + // push filler value that will end huffman decoding if not + // modified + self.buffer.push(255); + } + } + + fn put(&mut self, code: u8) -> Result<(), HuffmanEncodingError> { + let encode_value = &HPACK_STRING[code as usize]; + + self.ensure_free_space(encode_value.bit_count); + + let mut rest = encode_value.bit_count; + for i in 0..encode_value.buffer.len() { + let part = encode_value.buffer[i]; + + self.buffer_pos.forwards(if rest < 8 { rest } else { 8 }); + rest -= self.buffer_pos.count; + + write_bits(&mut self.buffer, &self.buffer_pos, part) + } + + Ok(()) + } + + fn ends(self) -> Result, HuffmanEncodingError> { + Ok(self.buffer) + } +} + +/// Write bits from `value` to the `out` slice +/// +/// Write the least significant `pos.count` bits from `value` to the position specified by +/// `(pos.byte, pos.bit)`. Writes may span multiple bytes. `out` is expected to be long enough +/// to write these bits; this is ensured by `HuffmanEncoder::ensure_free_space()`, which is +/// always called prior to calling this function. +/// +/// The bits to be written to are expected to be set to 1 when calling this function. Similarly, +/// this function maintains the invariant that unused bits in the output bytes are set to 1. +fn write_bits(out: &mut [u8], pos: &BitWindow, value: u8) { + debug_assert!(pos.bit < 8); + debug_assert!(pos.count <= 8); + debug_assert!(pos.count > 0); + + if (pos.bit + pos.count) <= 8 { + // Bits to be written to fit in a single byte + debug_assert_eq!(out[pos.byte as usize] | PAD_LEFT[pos.bit as usize], 255); + let pad_left = out[pos.byte as usize] | PAD_RIGHT[(8 - pos.bit) as usize]; + let shifted = value << (8 - pos.bit - pos.count) | PAD_LEFT[pos.bit as usize]; + let pad_right = PAD_RIGHT[(8 - pos.count - pos.bit) as usize]; + out[pos.byte as usize] = (pad_left & shifted) | pad_right; + } else { + // Bits to be written to span two bytes + debug_assert_eq!(out[pos.byte as usize] | PAD_LEFT[pos.bit as usize], 255); + let split = 8 - pos.bit; + let pad_left = out[pos.byte as usize] | PAD_RIGHT[split as usize]; + let shifted = (value >> (pos.count - split)) | PAD_LEFT[pos.bit as usize]; + out[pos.byte as usize] = pad_left & shifted; + + let rem = 8 - (pos.count - split); + out[(pos.byte + 1) as usize] = (value << rem) | PAD_RIGHT[rem as usize]; + } +} + +const PAD_RIGHT: [u8; 9] = [0, 1, 3, 7, 15, 31, 63, 127, 255]; +const PAD_LEFT: [u8; 9] = [0, 128, 192, 224, 240, 248, 252, 254, 255]; + +macro_rules! bits_encode { + [ $( ( $len:expr => [ $( $byte:expr ),* ] ), )* ] => { + [ $( + EncodeValue{ + buffer: &[ $( $byte as u8 ),* ], + bit_count: $len + } , + )* ] + } +} + +const HPACK_STRING: [EncodeValue; 256] = bits_encode![ + ( 13 => [0b1111_1111, 0b0001_1000]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1000]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0010]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0011]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0100]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0101]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0110]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0111]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1000]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1010]), + ( 30 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0011_1100]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1001]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1010]), + ( 30 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0011_1101]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1011]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1100]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1101]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1110]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_1111]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0000]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0001]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0010]), + ( 30 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0011_1110]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0011]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0100]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0101]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0110]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_0111]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1000]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1001]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1010]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1011]), + ( 6 => [0b0001_0100]), + ( 10 => [0b1111_1110, 0b0000_0000]), // '!' + ( 10 => [0b1111_1110, 0b0000_0001]), // ';' + ( 12 => [0b1111_1111, 0b0000_1010]), // '#' + ( 13 => [0b1111_1111, 0b0001_1001]), // '$' + ( 6 => [0b0001_0101]), // '%' + ( 8 => [0b1111_1000]), // '&' + ( 11 => [0b1111_1111, 0b0000_0010]), // ''' + ( 10 => [0b1111_1110, 0b0000_0010]), // '(' + ( 10 => [0b1111_1110, 0b0000_0011]), // ')' + ( 8 => [0b1111_1001]), // '*' + ( 11 => [0b1111_1111, 0b0000_0011]), // '+' + ( 8 => [0b1111_1010]), // ',' + ( 6 => [0b0001_0110]), // '-' + ( 6 => [0b0001_0111]), // '.' + ( 6 => [0b0001_1000]), // '/' + ( 5 => [0b0000_0000]), // '0' + ( 5 => [0b0000_0001]), // '1' + ( 5 => [0b0000_0010]), // '2' + ( 6 => [0b0001_1001]), // '3' + ( 6 => [0b0001_1010]), // '4' + ( 6 => [0b0001_1011]), // '5' + ( 6 => [0b0001_1100]), // '6' + ( 6 => [0b0001_1101]), // '7' + ( 6 => [0b0001_1110]), // '8' + ( 6 => [0b0001_1111]), // '9' + ( 7 => [0b0101_1100]), // ':' + ( 8 => [0b1111_1011]), + ( 15 => [0b1111_1111, 0b0111_1100]), // '<' + ( 6 => [0b0010_0000]), // '=' + ( 12 => [0b1111_1111, 0b0000_1011]), // '>' + ( 10 => [0b1111_1111, 0b0000_0000]), // '?' + ( 13 => [0b1111_1111, 0b0001_1010]), // '@' + ( 6 => [0b0010_0001]), // 'A' + ( 7 => [0b0101_1101]), // 'B' + ( 7 => [0b0101_1110]), // 'C' + ( 7 => [0b0101_1111]), // 'D' + ( 7 => [0b0110_0000]), // 'E' + ( 7 => [0b0110_0001]), // 'F' + ( 7 => [0b0110_0010]), // 'G' + ( 7 => [0b0110_0011]), // 'H' + ( 7 => [0b0110_0100]), // 'I' + ( 7 => [0b0110_0101]), // 'J' + ( 7 => [0b0110_0110]), // 'K' + ( 7 => [0b0110_0111]), // 'L' + ( 7 => [0b0110_1000]), // 'M' + ( 7 => [0b0110_1001]), // 'N' + ( 7 => [0b0110_1010]), // 'O' + ( 7 => [0b0110_1011]), // 'P' + ( 7 => [0b0110_1100]), // 'Q' + ( 7 => [0b0110_1101]), // 'R' + ( 7 => [0b0110_1110]), // 'S' + ( 7 => [0b0110_1111]), // 'T' + ( 7 => [0b0111_0000]), // 'U' + ( 7 => [0b0111_0001]), // 'V' + ( 7 => [0b0111_0010]), // 'W' + ( 8 => [0b1111_1100]), // 'X' + ( 7 => [0b0111_0011]), // 'Y' + ( 8 => [0b1111_1101]), // 'Z' + ( 13 => [0b1111_1111, 0b0001_1011]), // '[' + ( 19 => [0b1111_1111, 0b1111_1110, 0b0000_0000]), // '\' + ( 13 => [0b1111_1111, 0b0001_1100]), // ']' + ( 14 => [0b1111_1111, 0b0011_1100]), // '^' + ( 6 => [0b0010_0010]), // '_' + ( 15 => [0b1111_1111, 0b0111_1101]), // '`' + ( 5 => [0b0000_0011]), // 'a' + ( 6 => [0b0010_0011]), // 'b' + ( 5 => [0b0000_0100]), // 'c' + ( 6 => [0b0010_0100]), // 'd' + ( 5 => [0b0000_0101]), // 'e' + ( 6 => [0b0010_0101]), // 'f' + ( 6 => [0b0010_0110]), // 'g' + ( 6 => [0b0010_0111]), // 'h' + ( 5 => [0b0000_0110]), // 'i' + ( 7 => [0b0111_0100]), // 'j' + ( 7 => [0b0111_0101]), // 'k' + ( 6 => [0b0010_1000]), // 'l' + ( 6 => [0b0010_1001]), // 'm' + ( 6 => [0b0010_1010]), // 'n' + ( 5 => [0b0000_0111]), // 'o' + ( 6 => [0b0010_1011]), // 'p' + ( 7 => [0b0111_0110]), // 'q' + ( 6 => [0b0010_1100]), // 'r' + ( 5 => [0b0000_1000]), // 's' + ( 5 => [0b0000_1001]), // 't' + ( 6 => [0b0010_1101]), // 'u' + ( 7 => [0b0111_0111]), // 'v' + ( 7 => [0b0111_1000]), // 'w' + ( 7 => [0b0111_1001]), // 'x' + ( 7 => [0b0111_1010]), // 'y' + ( 7 => [0b0111_1011]), // 'z' + ( 15 => [0b1111_1111, 0b0111_1110]), // '{' + ( 11 => [0b1111_1111, 0b0000_0100]), // '|' + ( 14 => [0b1111_1111, 0b0011_1101]), // '}' + ( 13 => [0b1111_1111, 0b0001_1101]), // '~' + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1100]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_0110]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0010]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_0111]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1000]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0011]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0100]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0101]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1001]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1010]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1011]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1100]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1101]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1110]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1011]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0101_1111]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1100]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1101]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_0111]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0000]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0001]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0010]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0011]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0100]), + ( 21 => [0b1111_1111, 0b1111_1110, 0b0001_1100]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1000]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0101]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1001]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_0111]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1110_1111]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1010]), + ( 21 => [0b1111_1111, 0b1111_1110, 0b0001_1101]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1001]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1011]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1100]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1000]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1001]), + ( 21 => [0b1111_1111, 0b1111_1110, 0b0001_1110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1010]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1101]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1110]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0000]), + ( 21 => [0b1111_1111, 0b1111_1110, 0b0001_1111]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0001_1111]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1011]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1100]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0000]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0001]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0000]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0010]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1101]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0001]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0110_1111]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1010]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0010]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0011]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0100]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0111_0000]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0101]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0110]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0111_0001]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1000, 0b0000_0000]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1000, 0b0000_0001]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1011]), + ( 19 => [0b1111_1111, 0b1111_1110, 0b0000_0001]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_0111]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0111_0010]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_1000]), + ( 25 => [0b1111_1111, 0b1111_1111, 0b1111_0110, 0b0000_0000]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1000, 0b0000_0010]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1000, 0b0000_0011]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1001, 0b0000_0000]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1011, 0b0000_0110]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1011, 0b0000_0111]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1001, 0b0000_0001]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0001]), + ( 25 => [0b1111_1111, 0b1111_1111, 0b1111_0110, 0b0000_0001]), + ( 19 => [0b1111_1111, 0b1111_1110, 0b0000_0010]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0011]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1001, 0b0000_0010]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0000]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0001]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1001, 0b0000_0011]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0010]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0010]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0100]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0101]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1010, 0b0000_0000]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1010, 0b0000_0001]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1101]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0011]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0100]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0101]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1100]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0011]), + ( 20 => [0b1111_1111, 0b1111_1110, 0b0000_1101]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0110]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_1001]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_0111]), + ( 21 => [0b1111_1111, 0b1111_1111, 0b0000_1000]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0111_0011]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_1010]), + ( 22 => [0b1111_1111, 0b1111_1111, 0b0010_1011]), + ( 25 => [0b1111_1111, 0b1111_1111, 0b1111_0111, 0b0000_0000]), + ( 25 => [0b1111_1111, 0b1111_1111, 0b1111_0111, 0b0000_0001]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0100]), + ( 24 => [0b1111_1111, 0b1111_1111, 0b1111_0101]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1010, 0b0000_0010]), + ( 23 => [0b1111_1111, 0b1111_1111, 0b0111_0100]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1010, 0b0000_0011]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0110]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1011, 0b0000_0000]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1011, 0b0000_0001]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1100, 0b0000_0111]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0000]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0001]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0010]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0011]), + ( 28 => [0b1111_1111, 0b1111_1111, 0b1111_1111, 0b0000_1110]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0100]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0101]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0110]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1101, 0b0000_0111]), + ( 27 => [0b1111_1111, 0b1111_1111, 0b1111_1110, 0b0000_0000]), + ( 26 => [0b1111_1111, 0b1111_1111, 0b1111_1011, 0b0000_0010]), +]; + +pub trait HpackStringEncode { + fn hpack_encode(&self) -> Result, HuffmanEncodingError>; +} + +impl HpackStringEncode for Vec { + fn hpack_encode(&self) -> Result, HuffmanEncodingError> { + let mut encoder = HuffmanEncoder::new(); + for code in self { + encoder.put(*code)?; + } + encoder.ends() + } +} + +#[cfg(test)] +mod tests { + #![allow(clippy::identity_op)] + + use super::*; + + #[test] + fn test_set_bits() { + let mut buf = [0b1111_1111; 16]; + // Write a full 8 bits into a single byte + let mut pos = BitWindow { + count: 8, + ..Default::default() + }; + + write_bits(&mut buf, &pos, 0b1_0101); + assert_eq!(&buf[..1], &[0b1_0101]); + pos.byte += 1; + + // 7-bit byte-spanning writes at each possible bit offset + pos.count = 7; + for _ in 0..8 { + write_bits(&mut buf, &pos, 0b101_0101); + pos.forwards(7); + } + assert_eq!( + &buf[1..8], + &[ + 0b1010_1011, + 0b0101_0110, + 0b1010_1101, + 0b0101_1010, + 0b1011_0101, + 0b0110_1010, + 0b1101_0101 + ] + ); + + // Single-write partial bits, aligned with byte start + pos.count = 5; + write_bits(&mut buf, &pos, 0b1_0101); + assert_eq!(&buf[8..9], &[0b1010_1111]); + } + + macro_rules! encoding { + [ $( $code:expr => $( $byte:expr ),* ; )* ] => { $( { + let bytes = vec![$( $byte ),*]; + let res = vec![ $code ].hpack_encode(); + assert_eq!(res, Ok(bytes), "fail to encode {}", $code); + } )* } + } + + /** + * https://tools.ietf.org/html/rfc7541 + * Appendix B. Huffman Code + */ + #[test] + #[allow(clippy::cognitive_complexity)] + fn test_encode_single_value() { + encoding![ + 48 => (0b0000_0000 << 3) | /* padding */ 0b0000_0111; // '0' + 49 => (0b0000_0001 << 3) | /* padding */ 0b0000_0111; // '1' + 50 => (0b0000_0010 << 3) | /* padding */ 0b0000_0111; // '2' + 97 => (0b0000_0011 << 3) | /* padding */ 0b0000_0111; // 'a' + 99 => (0b0000_0100 << 3) | /* padding */ 0b0000_0111; // 'c' + 101 => (0b0000_0101 << 3) | /* padding */ 0b0000_0111; // 'e' + 105 => (0b0000_0110 << 3) | /* padding */ 0b0000_0111; // 'i' + 111 => (0b0000_0111 << 3) | /* padding */ 0b0000_0111; // 'o' + 115 => (0b0000_1000 << 3) | /* padding */ 0b0000_0111; // 's' + 116 => (0b0000_1001 << 3) | /* padding */ 0b0000_0111; // 't' + 32 => (0b0001_0100 << 2) | /* padding */ 0b0000_0011; + 37 => (0b0001_0101 << 2) | /* padding */ 0b0000_0011; // '%' + 45 => (0b0001_0110 << 2) | /* padding */ 0b0000_0011; // '-' + 46 => (0b0001_0111 << 2) | /* padding */ 0b0000_0011; // '.' + 47 => (0b0001_1000 << 2) | /* padding */ 0b0000_0011; // '/' + 51 => (0b0001_1001 << 2) | /* padding */ 0b0000_0011; // '3' + 52 => (0b0001_1010 << 2) | /* padding */ 0b0000_0011; // '4' + 53 => (0b0001_1011 << 2) | /* padding */ 0b0000_0011; // '5' + 54 => (0b0001_1100 << 2) | /* padding */ 0b0000_0011; // '6' + 55 => (0b0001_1101 << 2) | /* padding */ 0b0000_0011; // '7' + 56 => (0b0001_1110 << 2) | /* padding */ 0b0000_0011; // '8' + 57 => (0b0001_1111 << 2) | /* padding */ 0b0000_0011; // '9' + 61 => (0b0010_0000 << 2) | /* padding */ 0b0000_0011; // '=' + 65 => (0b0010_0001 << 2) | /* padding */ 0b0000_0011; // 'A' + 95 => (0b0010_0010 << 2) | /* padding */ 0b0000_0011; // '_' + 98 => (0b0010_0011 << 2) | /* padding */ 0b0000_0011; // 'b' + 100 => (0b0010_0100 << 2) | /* padding */ 0b0000_0011; // 'd' + 102 => (0b0010_0101 << 2) | /* padding */ 0b0000_0011; // 'f' + 103 => (0b0010_0110 << 2) | /* padding */ 0b0000_0011; // 'g' + 104 => (0b0010_0111 << 2) | /* padding */ 0b0000_0011; // 'h' + 108 => (0b0010_1000 << 2) | /* padding */ 0b0000_0011; // 'l' + 109 => (0b0010_1001 << 2) | /* padding */ 0b0000_0011; // 'm' + 110 => (0b0010_1010 << 2) | /* padding */ 0b0000_0011; // 'n' + 112 => (0b0010_1011 << 2) | /* padding */ 0b0000_0011; // 'p' + 114 => (0b0010_1100 << 2) | /* padding */ 0b0000_0011; // 'r' + 117 => (0b0010_1101 << 2) | /* padding */ 0b0000_0011; // 'u' + 58 => (0b0101_1100 << 1) | /* padding */ 0b0000_0001; // ':' + 66 => (0b0101_1101 << 1) | /* padding */ 0b0000_0001; // 'B' + 67 => (0b0101_1110 << 1) | /* padding */ 0b0000_0001; // 'C' + 68 => (0b0101_1111 << 1) | /* padding */ 0b0000_0001; // 'D' + 69 => (0b0110_0000 << 1) | /* padding */ 0b0000_0001; // 'E' + 70 => (0b0110_0001 << 1) | /* padding */ 0b0000_0001; // 'F' + 71 => (0b0110_0010 << 1) | /* padding */ 0b0000_0001; // 'G' + 72 => (0b0110_0011 << 1) | /* padding */ 0b0000_0001; // 'H' + 73 => (0b0110_0100 << 1) | /* padding */ 0b0000_0001; // 'I' + 74 => (0b0110_0101 << 1) | /* padding */ 0b0000_0001; // 'J' + 75 => (0b0110_0110 << 1) | /* padding */ 0b0000_0001; // 'K' + 76 => (0b0110_0111 << 1) | /* padding */ 0b0000_0001; // 'L' + 77 => (0b0110_1000 << 1) | /* padding */ 0b0000_0001; // 'M' + 78 => (0b0110_1001 << 1) | /* padding */ 0b0000_0001; // 'N' + 79 => (0b0110_1010 << 1) | /* padding */ 0b0000_0001; // 'O' + 80 => (0b0110_1011 << 1) | /* padding */ 0b0000_0001; // 'P' + 81 => (0b0110_1100 << 1) | /* padding */ 0b0000_0001; // 'Q' + 82 => (0b0110_1101 << 1) | /* padding */ 0b0000_0001; // 'R' + 83 => (0b0110_1110 << 1) | /* padding */ 0b0000_0001; // 'S' + 84 => (0b0110_1111 << 1) | /* padding */ 0b0000_0001; // 'T' + 85 => (0b0111_0000 << 1) | /* padding */ 0b0000_0001; // 'U' + 86 => (0b0111_0001 << 1) | /* padding */ 0b0000_0001; // 'V' + 87 => (0b0111_0010 << 1) | /* padding */ 0b0000_0001; // 'W' + 89 => (0b0111_0011 << 1) | /* padding */ 0b0000_0001; // 'Y' + 106 => (0b0111_0100 << 1) | /* padding */ 0b0000_0001; // 'j' + 107 => (0b0111_0101 << 1) | /* padding */ 0b0000_0001; // 'k' + 113 => (0b0111_0110 << 1) | /* padding */ 0b0000_0001; // 'q' + 118 => (0b0111_0111 << 1) | /* padding */ 0b0000_0001; // 'v' + 119 => (0b0111_1000 << 1) | /* padding */ 0b0000_0001; // 'w' + 120 => (0b0111_1001 << 1) | /* padding */ 0b0000_0001; // 'x' + 121 => (0b0111_1010 << 1) | /* padding */ 0b0000_0001; // 'y' + 122 => (0b0111_1011 << 1) | /* padding */ 0b0000_0001; // 'z' + 38 => 0b1111_1000; // '&' + 42 => 0b1111_1001; // '*' + 44 => 0b1111_1010; // ',' + 59 => 0b1111_1011; + 88 => 0b1111_1100; // 'X' + 90 => 0b1111_1101; // 'Z' + 33 => 0b1111_1110, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; // '!' + 34 => 0b1111_1110, (0b0000_0001 << 6) | /* padding */ 0b0011_1111; // '"' + 40 => 0b1111_1110, (0b0000_0010 << 6) | /* padding */ 0b0011_1111; // '(' + 41 => 0b1111_1110, (0b0000_0011 << 6) | /* padding */ 0b0011_1111; // ')' + 63 => 0b1111_1111, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; // '?' + 39 => 0b1111_1111, (0b0000_0010 << 5) | /* padding */ 0b0001_1111; // ''' + 43 => 0b1111_1111, (0b0000_0011 << 5) | /* padding */ 0b0001_1111; // '+' + 124 => 0b1111_1111, (0b0000_0100 << 5) | /* padding */ 0b0001_1111; // '|' + 35 => 0b1111_1111, (0b0000_1010 << 4) | /* padding */ 0b0000_1111; // '#' + 62 => 0b1111_1111, (0b0000_1011 << 4) | /* padding */ 0b0000_1111; // '>' + 0 => 0b1111_1111, (0b0001_1000 << 3) | /* padding */ 0b0000_0111; + 36 => 0b1111_1111, (0b0001_1001 << 3) | /* padding */ 0b0000_0111; // '$' + 64 => 0b1111_1111, (0b0001_1010 << 3) | /* padding */ 0b0000_0111; // '@' + 91 => 0b1111_1111, (0b0001_1011 << 3) | /* padding */ 0b0000_0111; // '[' + 93 => 0b1111_1111, (0b0001_1100 << 3) | /* padding */ 0b0000_0111; // ']' + 126 => 0b1111_1111, (0b0001_1101 << 3) | /* padding */ 0b0000_0111; // '~' + 94 => 0b1111_1111, (0b0011_1100 << 2) | /* padding */ 0b0000_0011; // '^' + 125 => 0b1111_1111, (0b0011_1101 << 2) | /* padding */ 0b0000_0011; // '}' + 60 => 0b1111_1111, (0b0111_1100 << 1) | /* padding */ 0b0000_0001; // '<' + 96 => 0b1111_1111, (0b0111_1101 << 1) | /* padding */ 0b0000_0001; // '`' + 123 => 0b1111_1111, (0b0111_1110 << 1) | /* padding */ 0b0000_0001; // '{' + 92 => 0b1111_1111, 0b1111_1110, (0b0000_0000 << 5) | /* padding */ 0b0001_1111; // '\' + 195 => 0b1111_1111, 0b1111_1110, (0b0000_0001 << 5) | /* padding */ 0b0001_1111; + 208 => 0b1111_1111, 0b1111_1110, (0b0000_0010 << 5) | /* padding */ 0b0001_1111; + 128 => 0b1111_1111, 0b1111_1110, (0b0000_0110 << 4) | /* padding */ 0b0000_1111; + 130 => 0b1111_1111, 0b1111_1110, (0b0000_0111 << 4) | /* padding */ 0b0000_1111; + 131 => 0b1111_1111, 0b1111_1110, (0b0000_1000 << 4) | /* padding */ 0b0000_1111; + 162 => 0b1111_1111, 0b1111_1110, (0b0000_1001 << 4) | /* padding */ 0b0000_1111; + 184 => 0b1111_1111, 0b1111_1110, (0b0000_1010 << 4) | /* padding */ 0b0000_1111; + 194 => 0b1111_1111, 0b1111_1110, (0b0000_1011 << 4) | /* padding */ 0b0000_1111; + 224 => 0b1111_1111, 0b1111_1110, (0b0000_1100 << 4) | /* padding */ 0b0000_1111; + 226 => 0b1111_1111, 0b1111_1110, (0b0000_1101 << 4) | /* padding */ 0b0000_1111; + 153 => 0b1111_1111, 0b1111_1110, (0b0001_1100 << 3) | /* padding */ 0b0000_0111; + 161 => 0b1111_1111, 0b1111_1110, (0b0001_1101 << 3) | /* padding */ 0b0000_0111; + 167 => 0b1111_1111, 0b1111_1110, (0b0001_1110 << 3) | /* padding */ 0b0000_0111; + 172 => 0b1111_1111, 0b1111_1110, (0b0001_1111 << 3) | /* padding */ 0b0000_0111; + 176 => 0b1111_1111, 0b1111_1111, (0b0000_0000 << 3) | /* padding */ 0b0000_0111; + 177 => 0b1111_1111, 0b1111_1111, (0b0000_0001 << 3) | /* padding */ 0b0000_0111; + 179 => 0b1111_1111, 0b1111_1111, (0b0000_0010 << 3) | /* padding */ 0b0000_0111; + 209 => 0b1111_1111, 0b1111_1111, (0b0000_0011 << 3) | /* padding */ 0b0000_0111; + 216 => 0b1111_1111, 0b1111_1111, (0b0000_0100 << 3) | /* padding */ 0b0000_0111; + 217 => 0b1111_1111, 0b1111_1111, (0b0000_0101 << 3) | /* padding */ 0b0000_0111; + 227 => 0b1111_1111, 0b1111_1111, (0b0000_0110 << 3) | /* padding */ 0b0000_0111; + 229 => 0b1111_1111, 0b1111_1111, (0b0000_0111 << 3) | /* padding */ 0b0000_0111; + 230 => 0b1111_1111, 0b1111_1111, (0b0000_1000 << 3) | /* padding */ 0b0000_0111; + 129 => 0b1111_1111, 0b1111_1111, (0b0001_0010 << 2) | /* padding */ 0b0000_0011; + 132 => 0b1111_1111, 0b1111_1111, (0b0001_0011 << 2) | /* padding */ 0b0000_0011; + 133 => 0b1111_1111, 0b1111_1111, (0b0001_0100 << 2) | /* padding */ 0b0000_0011; + 134 => 0b1111_1111, 0b1111_1111, (0b0001_0101 << 2) | /* padding */ 0b0000_0011; + 136 => 0b1111_1111, 0b1111_1111, (0b0001_0110 << 2) | /* padding */ 0b0000_0011; + 146 => 0b1111_1111, 0b1111_1111, (0b0001_0111 << 2) | /* padding */ 0b0000_0011; + 154 => 0b1111_1111, 0b1111_1111, (0b0001_1000 << 2) | /* padding */ 0b0000_0011; + 156 => 0b1111_1111, 0b1111_1111, (0b0001_1001 << 2) | /* padding */ 0b0000_0011; + 160 => 0b1111_1111, 0b1111_1111, (0b0001_1010 << 2) | /* padding */ 0b0000_0011; + 163 => 0b1111_1111, 0b1111_1111, (0b0001_1011 << 2) | /* padding */ 0b0000_0011; + 164 => 0b1111_1111, 0b1111_1111, (0b0001_1100 << 2) | /* padding */ 0b0000_0011; + 169 => 0b1111_1111, 0b1111_1111, (0b0001_1101 << 2) | /* padding */ 0b0000_0011; + 170 => 0b1111_1111, 0b1111_1111, (0b0001_1110 << 2) | /* padding */ 0b0000_0011; + 173 => 0b1111_1111, 0b1111_1111, (0b0001_1111 << 2) | /* padding */ 0b0000_0011; + 178 => 0b1111_1111, 0b1111_1111, (0b0010_0000 << 2) | /* padding */ 0b0000_0011; + 181 => 0b1111_1111, 0b1111_1111, (0b0010_0001 << 2) | /* padding */ 0b0000_0011; + 185 => 0b1111_1111, 0b1111_1111, (0b0010_0010 << 2) | /* padding */ 0b0000_0011; + 186 => 0b1111_1111, 0b1111_1111, (0b0010_0011 << 2) | /* padding */ 0b0000_0011; + 187 => 0b1111_1111, 0b1111_1111, (0b0010_0100 << 2) | /* padding */ 0b0000_0011; + 189 => 0b1111_1111, 0b1111_1111, (0b0010_0101 << 2) | /* padding */ 0b0000_0011; + 190 => 0b1111_1111, 0b1111_1111, (0b0010_0110 << 2) | /* padding */ 0b0000_0011; + 196 => 0b1111_1111, 0b1111_1111, (0b0010_0111 << 2) | /* padding */ 0b0000_0011; + 198 => 0b1111_1111, 0b1111_1111, (0b0010_1000 << 2) | /* padding */ 0b0000_0011; + 228 => 0b1111_1111, 0b1111_1111, (0b0010_1001 << 2) | /* padding */ 0b0000_0011; + 232 => 0b1111_1111, 0b1111_1111, (0b0010_1010 << 2) | /* padding */ 0b0000_0011; + 233 => 0b1111_1111, 0b1111_1111, (0b0010_1011 << 2) | /* padding */ 0b0000_0011; + 1 => 0b1111_1111, 0b1111_1111, (0b0101_1000 << 1) | /* padding */ 0b0000_0001; + 135 => 0b1111_1111, 0b1111_1111, (0b0101_1001 << 1) | /* padding */ 0b0000_0001; + 137 => 0b1111_1111, 0b1111_1111, (0b0101_1010 << 1) | /* padding */ 0b0000_0001; + 138 => 0b1111_1111, 0b1111_1111, (0b0101_1011 << 1) | /* padding */ 0b0000_0001; + 139 => 0b1111_1111, 0b1111_1111, (0b0101_1100 << 1) | /* padding */ 0b0000_0001; + 140 => 0b1111_1111, 0b1111_1111, (0b0101_1101 << 1) | /* padding */ 0b0000_0001; + 141 => 0b1111_1111, 0b1111_1111, (0b0101_1110 << 1) | /* padding */ 0b0000_0001; + 143 => 0b1111_1111, 0b1111_1111, (0b0101_1111 << 1) | /* padding */ 0b0000_0001; + 147 => 0b1111_1111, 0b1111_1111, (0b0110_0000 << 1) | /* padding */ 0b0000_0001; + 149 => 0b1111_1111, 0b1111_1111, (0b0110_0001 << 1) | /* padding */ 0b0000_0001; + 150 => 0b1111_1111, 0b1111_1111, (0b0110_0010 << 1) | /* padding */ 0b0000_0001; + 151 => 0b1111_1111, 0b1111_1111, (0b0110_0011 << 1) | /* padding */ 0b0000_0001; + 152 => 0b1111_1111, 0b1111_1111, (0b0110_0100 << 1) | /* padding */ 0b0000_0001; + 155 => 0b1111_1111, 0b1111_1111, (0b0110_0101 << 1) | /* padding */ 0b0000_0001; + 157 => 0b1111_1111, 0b1111_1111, (0b0110_0110 << 1) | /* padding */ 0b0000_0001; + 158 => 0b1111_1111, 0b1111_1111, (0b0110_0111 << 1) | /* padding */ 0b0000_0001; + 165 => 0b1111_1111, 0b1111_1111, (0b0110_1000 << 1) | /* padding */ 0b0000_0001; + 166 => 0b1111_1111, 0b1111_1111, (0b0110_1001 << 1) | /* padding */ 0b0000_0001; + 168 => 0b1111_1111, 0b1111_1111, (0b0110_1010 << 1) | /* padding */ 0b0000_0001; + 174 => 0b1111_1111, 0b1111_1111, (0b0110_1011 << 1) | /* padding */ 0b0000_0001; + 175 => 0b1111_1111, 0b1111_1111, (0b0110_1100 << 1) | /* padding */ 0b0000_0001; + 180 => 0b1111_1111, 0b1111_1111, (0b0110_1101 << 1) | /* padding */ 0b0000_0001; + 182 => 0b1111_1111, 0b1111_1111, (0b0110_1110 << 1) | /* padding */ 0b0000_0001; + 183 => 0b1111_1111, 0b1111_1111, (0b0110_1111 << 1) | /* padding */ 0b0000_0001; + 188 => 0b1111_1111, 0b1111_1111, (0b0111_0000 << 1) | /* padding */ 0b0000_0001; + 191 => 0b1111_1111, 0b1111_1111, (0b0111_0001 << 1) | /* padding */ 0b0000_0001; + 197 => 0b1111_1111, 0b1111_1111, (0b0111_0010 << 1) | /* padding */ 0b0000_0001; + 231 => 0b1111_1111, 0b1111_1111, (0b0111_0011 << 1) | /* padding */ 0b0000_0001; + 239 => 0b1111_1111, 0b1111_1111, (0b0111_0100 << 1) | /* padding */ 0b0000_0001; + 9 => 0b1111_1111, 0b1111_1111, 0b1110_1010; + 142 => 0b1111_1111, 0b1111_1111, 0b1110_1011; + 144 => 0b1111_1111, 0b1111_1111, 0b1110_1100; + 145 => 0b1111_1111, 0b1111_1111, 0b1110_1101; + 148 => 0b1111_1111, 0b1111_1111, 0b1110_1110; + 159 => 0b1111_1111, 0b1111_1111, 0b1110_1111; + 171 => 0b1111_1111, 0b1111_1111, 0b1111_0000; + 206 => 0b1111_1111, 0b1111_1111, 0b1111_0001; + 215 => 0b1111_1111, 0b1111_1111, 0b1111_0010; + 225 => 0b1111_1111, 0b1111_1111, 0b1111_0011; + 236 => 0b1111_1111, 0b1111_1111, 0b1111_0100; + 237 => 0b1111_1111, 0b1111_1111, 0b1111_0101; + 199 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b0000_0000 << 7) | /* padding */ 0b0111_1111; + 207 => 0b1111_1111, 0b1111_1111, 0b1111_0110, (0b0000_0001 << 7) | /* padding */ 0b0111_1111; + 234 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b0000_0000 << 7) | /* padding */ 0b0111_1111; + 235 => 0b1111_1111, 0b1111_1111, 0b1111_0111, (0b0000_0001 << 7) | /* padding */ 0b0111_1111; + 192 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; + 193 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b0000_0001 << 6) | /* padding */ 0b0011_1111; + 200 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b0000_0010 << 6) | /* padding */ 0b0011_1111; + 201 => 0b1111_1111, 0b1111_1111, 0b1111_1000, (0b0000_0011 << 6) | /* padding */ 0b0011_1111; + 202 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; + 205 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b0000_0001 << 6) | /* padding */ 0b0011_1111; + 210 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b0000_0010 << 6) | /* padding */ 0b0011_1111; + 213 => 0b1111_1111, 0b1111_1111, 0b1111_1001, (0b0000_0011 << 6) | /* padding */ 0b0011_1111; + 218 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; + 219 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b0000_0001 << 6) | /* padding */ 0b0011_1111; + 238 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b0000_0010 << 6) | /* padding */ 0b0011_1111; + 240 => 0b1111_1111, 0b1111_1111, 0b1111_1010, (0b0000_0011 << 6) | /* padding */ 0b0011_1111; + 242 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b0000_0000 << 6) | /* padding */ 0b0011_1111; + 243 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b0000_0001 << 6) | /* padding */ 0b0011_1111; + 255 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b0000_0010 << 6) | /* padding */ 0b0011_1111; + 203 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b0000_0110 << 5) | /* padding */ 0b0001_1111; + 204 => 0b1111_1111, 0b1111_1111, 0b1111_1011, (0b0000_0111 << 5) | /* padding */ 0b0001_1111; + 211 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0000 << 5) | /* padding */ 0b0001_1111; + 212 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0001 << 5) | /* padding */ 0b0001_1111; + 214 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0010 << 5) | /* padding */ 0b0001_1111; + 221 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0011 << 5) | /* padding */ 0b0001_1111; + 222 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0100 << 5) | /* padding */ 0b0001_1111; + 223 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0101 << 5) | /* padding */ 0b0001_1111; + 241 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0110 << 5) | /* padding */ 0b0001_1111; + 244 => 0b1111_1111, 0b1111_1111, 0b1111_1100, (0b0000_0111 << 5) | /* padding */ 0b0001_1111; + 245 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0000 << 5) | /* padding */ 0b0001_1111; + 246 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0001 << 5) | /* padding */ 0b0001_1111; + 247 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0010 << 5) | /* padding */ 0b0001_1111; + 248 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0011 << 5) | /* padding */ 0b0001_1111; + 250 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0100 << 5) | /* padding */ 0b0001_1111; + 251 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0101 << 5) | /* padding */ 0b0001_1111; + 252 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0110 << 5) | /* padding */ 0b0001_1111; + 253 => 0b1111_1111, 0b1111_1111, 0b1111_1101, (0b0000_0111 << 5) | /* padding */ 0b0001_1111; + 254 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0000 << 5) | /* padding */ 0b0001_1111; + 2 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0010 << 4) | /* padding */ 0b0000_1111; + 3 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0011 << 4) | /* padding */ 0b0000_1111; + 4 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0100 << 4) | /* padding */ 0b0000_1111; + 5 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0101 << 4) | /* padding */ 0b0000_1111; + 6 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0110 << 4) | /* padding */ 0b0000_1111; + 7 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_0111 << 4) | /* padding */ 0b0000_1111; + 8 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1000 << 4) | /* padding */ 0b0000_1111; + 11 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1001 << 4) | /* padding */ 0b0000_1111; + 12 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1010 << 4) | /* padding */ 0b0000_1111; + 14 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1011 << 4) | /* padding */ 0b0000_1111; + 15 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1100 << 4) | /* padding */ 0b0000_1111; + 16 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1101 << 4) | /* padding */ 0b0000_1111; + 17 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1110 << 4) | /* padding */ 0b0000_1111; + 18 => 0b1111_1111, 0b1111_1111, 0b1111_1110, (0b0000_1111 << 4) | /* padding */ 0b0000_1111; + 19 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0000 << 4) | /* padding */ 0b0000_1111; + 20 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0001 << 4) | /* padding */ 0b0000_1111; + 21 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0010 << 4) | /* padding */ 0b0000_1111; + 23 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0011 << 4) | /* padding */ 0b0000_1111; + 24 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0100 << 4) | /* padding */ 0b0000_1111; + 25 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0101 << 4) | /* padding */ 0b0000_1111; + 26 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0110 << 4) | /* padding */ 0b0000_1111; + 27 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_0111 << 4) | /* padding */ 0b0000_1111; + 28 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1000 << 4) | /* padding */ 0b0000_1111; + 29 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1001 << 4) | /* padding */ 0b0000_1111; + 30 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1010 << 4) | /* padding */ 0b0000_1111; + 31 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1011 << 4) | /* padding */ 0b0000_1111; + 127 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1100 << 4) | /* padding */ 0b0000_1111; + 220 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1101 << 4) | /* padding */ 0b0000_1111; + 249 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0000_1110 << 4) | /* padding */ 0b0000_1111; + 10 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0011_1100 << 2) | /* padding */ 0b0000_0011; + 13 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0011_1101 << 2) | /* padding */ 0b0000_0011; + 22 => 0b1111_1111, 0b1111_1111, 0b1111_1111, (0b0011_1110 << 2) | /* padding */ 0b0000_0011; + ]; + } + + /** + * https://tools.ietf.org/html/rfc7541 + * Appendix B. Huffman Code + */ + #[test] + fn test_encode_all_code_joined() { + let bytes = vec![ + // 0 |11111111|11000 + 0b1111_1111, + (0b0001_1000 << 3) + // 1 |11111111|11111111|1011000 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1101, + (0b0000_1000 << 4) + // 2 |11111111|11111111|11111110|0010 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0010, + // 3 |11111111|11111111|11111110|0011 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0011 << 4) + // 4 |11111111|11111111|11111110|0100 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0100, + // 5 |11111111|11111111|11111110|0101 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0101 << 4) + // 6 |11111111|11111111|11111110|0110 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_0110, + // 7 |11111111|11111111|11111110|0111 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0111 << 4) + // 8 |11111111|11111111|11111110|1000 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1000, + // 9 |11111111|11111111|11101010 + 0b1111_1111, + 0b1111_1111, + 0b1110_1010, + // 10 |11111111|11111111|11111111|111100 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_1100 << 2) + // 11 |11111111|11111111|11111110|1001 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1010, + (0b0000_0001 << 6) + // 12 |11111111|11111111|11111110|1010 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0010_1010 << 2) + // 13 |11111111|11111111|11111111|111101 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_1101 << 4) + // 14 |11111111|11111111|11111110|1011 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1011, + // 15 |11111111|11111111|11111110|1100 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1100 << 4) + // 16 |11111111|11111111|11111110|1101 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1101, + // 17 |11111111|11111111|11111110|1110 + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1110 << 4) + // 18 |11111111|11111111|11111110|1111 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1110_1111, + // 19 |11111111|11111111|11111111|0000 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0000 << 4) + // 20 |11111111|11111111|11111111|0001 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_0001, + // 21 |11111111|11111111|11111111|0010 + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0010 << 4) + // 22 |11111111|11111111|11111111|111110 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0010 << 6) + // 23 |11111111|11111111|11111111|0011 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_0011 << 2) + // 24 |11111111|11111111|11111111|0100 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0000 << 6) + // 25 |11111111|11111111|11111111|0101 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_0101 << 2) + // 26 |11111111|11111111|11111111|0110 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0010 << 6) + // 27 |11111111|11111111|11111111|0111 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_0111 << 2) + // 28 |11111111|11111111|11111111|1000 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0000 << 6) + // 29 |11111111|11111111|11111111|1001 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_1001 << 2) + // 30 |11111111|11111111|11111111|1010 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0010 << 6) + // 31 |11111111|11111111|11111111|1011 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0011_1011 << 2) + // 32 |010100 + + 0b0000_0001, + (0b0000_0100 << 4) + // 33 -!- |11111110|00 + + 0b0000_1111, + (0b0011_1000 << 2) + // 34 -;- |11111110|01 + + 0b0000_0011, + 0b1111_1001, + // 35 -#- |11111111|1010 + 0b1111_1111, + (0b0000_1010 << 4) + // 36 -$- |11111111|11001 + + 0b0000_1111, + 0b1111_1100, + (0b0000_0001 << 7) + // 37 -%- |010101 + + (0b0001_0101 << 1) + // 38 -&- |11111000 + + 0b0000_0001, + (0b0111_1000 << 1) + // 39 -'- |11111111|010 + + 0b0000_0001, + 0b1111_1110, + (0b0000_0010 << 6) + // 40 -(- |11111110|10 + + 0b0011_1111, + (0b0000_1010 << 4) + // 41 -)- |11111110|11 + + 0b0000_1111, + (0b0011_1011 << 2) + // 42 -*- |11111001 + + 0b0000_0011, + (0b0011_1001 << 2) + // 43 -+- |11111111|011 + + 0b0000_0011, + 0b1111_1101, + (0b0000_0001 << 7) + // 44 -,- |11111010 + + 0b0111_1101, + (0b0000_0000 << 7) + // 45 --- |010110 + + (0b0001_0110 << 1) + // 46 -.- |010111 + + 0b0000_0000, + (0b0001_0111 << 3) + // 47 -/- |011000 + + 0b0000_0011, + (0b0000_0000 << 5) + // 48 -0- |00000 + + 0b0000_0000, + // 49 -1- |00001 + (0b0000_0001 << 3) + // 50 -2- |00010 + + 0b0000_0000, + (0b0000_0010 << 6) + // 51 -3- |011001 + + 0b0001_1001, + // 52 -4- |011010 + (0b0001_1010 << 2) + // 53 -5- |011011 + + 0b0000_0001, + (0b0000_1011 << 4) + // 54 -6- |011100 + + 0b0000_0111, + (0b0000_0000 << 6) + // 55 -7- |011101 + + 0b0001_1101, + // 56 -8- |011110 + (0b0001_1110 << 2) + // 57 -9- |011111 + + 0b0000_0001, + (0b0000_1111 << 4) + // 58 -:- |1011100 + + 0b0000_1011, + (0b0000_0100 << 5) + // 59 |11111011 + + 0b0001_1111, + (0b0000_0011 << 5) + // 60 -<- |11111111|1111100 + + 0b0001_1111, + 0b1111_1111, + (0b0000_0000 << 6) + // 61 -=- |100000 + + 0b0010_0000, + // 62 ->- |11111111|1011 + 0b1111_1111, + (0b0000_1011 << 4) + // 63 -?- |11111111|00 + + 0b0000_1111, + (0b0011_1100 << 2) + // 64 -@- |11111111|11010 + + 0b0000_0011, + 0b1111_1111, + (0b0000_0010 << 5) + // 65 -A- |100001 + + 0b0001_0000, + (0b0000_0001 << 7) + // 66 -B- |1011101 + + 0b0101_1101, + // 67 -C- |1011110 + (0b0101_1110 << 1) + // 68 -D- |1011111 + + 0b0000_0001, + (0b0001_1111 << 2) + // 69 -E- |1100000 + + 0b0000_0011, + (0b0000_0000 << 3) + // 70 -F- |1100001 + + 0b0000_0110, + (0b0000_0001 << 4) + // 71 -G- |1100010 + + 0b0000_1100, + (0b0000_0010 << 5) + // 72 -H- |1100011 + + 0b0001_1000, + (0b0000_0011 << 6) + // 73 -I- |1100100 + + 0b0011_0010, + (0b0000_0000 << 7) + // 74 -J- |1100101 + + 0b0110_0101, + // 75 -K- |1100110 + (0b0110_0110 << 1) + // 76 -L- |1100111 + + 0b0000_0001, + (0b0010_0111 << 2) + // 77 -M- |1101000 + + 0b0000_0011, + (0b0000_1000 << 3) + // 78 -N- |1101001 + + 0b0000_0110, + (0b0000_1001 << 4) + // 79 -O- |1101010 + + 0b0000_1101, + (0b0000_0010 << 5) + // 80 -P- |1101011 + + 0b0001_1010, + (0b0000_0011 << 6) + // 81 -Q- |1101100 + + 0b0011_0110, + (0b0000_0000 << 7) + // 82 -R- |1101101 + + 0b0110_1101, + // 83 -S- |1101110 + (0b0110_1110 << 1) + // 84 -T- |1101111 + + 0b0000_0001, + (0b0010_1111 << 2) + // 85 -U- |1110000 + + 0b0000_0011, + (0b0001_0000 << 3) + // 86 -V- |1110001 + + 0b0000_0111, + (0b0000_0001 << 4) + // 87 -W- |1110010 + + 0b0000_1110, + (0b0000_0010 << 5) + // 88 -X- |11111100 + + 0b0001_1111, + (0b0000_0100 << 5) + // 89 -Y- |1110011 + + 0b0001_1100, + (0b0000_0011 << 6) + // 90 -Z- |11111101 + + 0b0011_1111, + (0b0000_0001 << 6) + // 91 -[- |11111111|11011 + + 0b0011_1111, + (0b0111_1011 << 1) + // 92 -\- |11111111|11111110|000 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1100, + (0b0000_0000 << 6) + // 93 -]- |11111111|11100 + + 0b0011_1111, + (0b0111_1100 << 1) + // 94 -^- |11111111|111100 + + 0b0000_0001, + 0b1111_1111, + (0b0001_1100 << 3) + // 95 -_- |100010 + + 0b0000_0100, + (0b0000_0010 << 5) + // 96 -`- |11111111|1111101 + + 0b0001_1111, + 0b1111_1111, + (0b0000_0001 << 6) + // 97 -a- |00011 + + (0b0000_0011 << 1) + // 98 -b- |100011 + + 0b0000_0001, + (0b0000_0011 << 3) + // 99 -c- |00100 + + 0b0000_0001, + (0b0000_0000 << 6) + // 100 -d- |100100 + + 0b0010_0100, + // 101 -e- |00101 + (0b0000_0101 << 3) + // 102 -f- |100101 + + 0b0000_0100, + (0b0000_0101 << 5) + // 103 -g- |100110 + + 0b0001_0011, + (0b0000_0000 << 7) + // 104 -h- |100111 + + (0b0010_0111 << 1) + // 105 -i- |00110 + + 0b0000_0000, + (0b0000_0110 << 4) + // 106 -j- |1110100 + + 0b0000_1110, + (0b0000_0100 << 5) + // 107 -k- |1110101 + + 0b0001_1101, + (0b0000_0001 << 6) + // 108 -l- |101000 + + 0b0010_1000, + // 109 -m- |101001 + (0b0010_1001 << 2) + // 110 -n- |101010 + + 0b0000_0010, + (0b0000_1010 << 4) + // 111 -o- |00111 + + 0b0000_0011, + (0b0000_0001 << 7) + // 112 -p- |101011 + + (0b0010_1011 << 1) + // 113 -q- |1110110 + + 0b0000_0001, + (0b0011_0110 << 2) + // 114 -r- |101100 + + 0b0000_0010, + (0b0000_1100 << 4) + // 115 -s- |01000 + + 0b0000_0100, + (0b0000_0000 << 7) + // 116 -t- |01001 + + (0b0000_1001 << 2) + // 117 -u- |101101 + + 0b0000_0010, + (0b0000_1101 << 4) + // 118 -v- |1110111 + + 0b0000_1110, + (0b0000_0111 << 5) + // 119 -w- |1111000 + + 0b0001_1110, + (0b0000_0000 << 6) + // 120 -x- |1111001 + + 0b0011_1100, + (0b0000_0001 << 7) + // 121 -y- |1111010 + + 0b0111_1010, + // 122 -z- |1111011 + (0b0111_1011 << 1) + // 123 -{- |11111111|1111110 + + 0b0000_0001, + 0b1111_1111, + (0b0011_1110 << 2) + // 124 -|- |11111111|100 + + 0b0000_0011, + 0b1111_1110, + (0b0000_0000 << 7) + // 125 -}- |11111111|111101 + + 0b0111_1111, + (0b0111_1101 << 1) + // 126 -~- |11111111|11101 + + 0b0000_0001, + 0b1111_1111, + (0b0000_1101 << 4) + // 127 |11111111|11111111|11111111|1100 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1100, + // 128 |11111111|11111110|0110 + 0b1111_1111, + 0b1111_1110, + (0b0000_0110 << 4) + // 129 |11111111|11111111|010010 + + 0b0000_1111, + 0b1111_1111, + 0b1111_0100, + (0b0000_0010 << 6) + // 130 |11111111|11111110|0111 + + 0b0011_1111, + 0b1111_1111, + (0b0010_0111 << 2) + // 131 |11111111|11111110|1000 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1010, + (0b0000_0000 << 6) + // 132 |11111111|11111111|010011 + + 0b0011_1111, + 0b1111_1111, + 0b1101_0011, + // 133 |11111111|11111111|010100 + 0b1111_1111, + 0b1111_1111, + (0b0001_0100 << 2) + // 134 |11111111|11111111|010101 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1101, + (0b0000_0101 << 4) + // 135 |11111111|11111111|1011001 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0001 << 5) + // 136 |11111111|11111111|010110 + + 0b0001_1111, + 0b1111_1111, + 0b1110_1011, + (0b0000_0000 << 7) + // 137 |11111111|11111111|1011010 + + 0b0111_1111, + 0b1111_1111, + 0b1101_1010, + // 138 |11111111|11111111|1011011 + 0b1111_1111, + 0b1111_1111, + (0b0101_1011 << 1) + // 139 |11111111|11111111|1011100 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0001_1100 << 2) + // 140 |11111111|11111111|1011101 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1110, + (0b0001_1101 << 3) + // 141 |11111111|11111111|1011110 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1101, + (0b0000_1110 << 4) + // 142 |11111111|11111111|11101011 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1011 << 4) + // 143 |11111111|11111111|1011111 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0111 << 5) + // 144 |11111111|11111111|11101100 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0100 << 5) + // 145 |11111111|11111111|11101101 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0101 << 5) + // 146 |11111111|11111111|010111 + + 0b0001_1111, + 0b1111_1111, + 0b1110_1011, + (0b0000_0001 << 7) + // 147 |11111111|11111111|1100000 + + 0b0111_1111, + 0b1111_1111, + 0b1110_0000, + // 148 |11111111|11111111|11101110 + 0b1111_1111, + 0b1111_1111, + 0b1110_1110, + // 149 |11111111|11111111|1100001 + 0b1111_1111, + 0b1111_1111, + (0b0110_0001 << 1) + // 150 |11111111|11111111|1100010 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0010_0010 << 2) + // 151 |11111111|11111111|1100011 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + (0b0000_0011 << 3) + // 152 |11111111|11111111|1100100 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0100 << 4) + // 153 |11111111|11111110|11100 + + 0b0000_1111, + 0b1111_1111, + 0b1110_1110, + (0b0000_0000 << 7) + // 154 |11111111|11111111|011000 + + 0b0111_1111, + 0b1111_1111, + (0b0101_1000 << 1) + // 155 |11111111|11111111|1100101 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0010_0101 << 2) + // 156 |11111111|11111111|011001 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1101, + (0b0000_1001 << 4) + // 157 |11111111|11111111|1100110 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1100, + (0b0000_0110 << 5) + // 158 |11111111|11111111|1100111 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1001, + (0b0000_0011 << 6) + // 159 |11111111|11111111|11101111 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0011 << 6) + // 160 |11111111|11111111|011010 + + 0b0011_1111, + 0b1111_1111, + 0b1101_1010, + // 161 |11111111|11111110|11101 + 0b1111_1111, + 0b1111_1110, + (0b0001_1101 << 3) + // 162 |11111111|11111110|1001 + + 0b0000_0111, + 0b1111_1111, + 0b1111_0100, + (0b0000_0001 << 7) + // 163 |11111111|11111111|011011 + + 0b0111_1111, + 0b1111_1111, + (0b0101_1011 << 1) + // 164 |11111111|11111111|011100 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1110, + (0b0001_1100 << 3) + // 165 |11111111|11111111|1101000 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1000 << 4) + // 166 |11111111|11111111|1101001 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0001 << 5) + // 167 |11111111|11111110|11110 + + 0b0001_1111, + 0b1111_1111, + 0b1101_1110, + // 168 |11111111|11111111|1101010 + 0b1111_1111, + 0b1111_1111, + (0b0110_1010 << 1) + // 169 |11111111|11111111|011101 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1110, + (0b0001_1101 << 3) + // 170 |11111111|11111111|011110 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0110 << 5) + // 171 |11111111|11111111|11110000 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0000 << 5) + // 172 |11111111|11111110|11111 + + 0b0001_1111, + 0b1111_1111, + 0b1101_1111, + // 173 |11111111|11111111|011111 + 0b1111_1111, + 0b1111_1111, + (0b0001_1111 << 2) + // 174 |11111111|11111111|1101011 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + (0b0000_1011 << 3) + // 175 |11111111|11111111|1101100 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1100 << 4) + // 176 |11111111|11111111|00000 + + 0b0000_1111, + 0b1111_1111, + 0b1111_0000, + (0b0000_0000 << 7) + // 177 |11111111|11111111|00001 + + 0b0111_1111, + 0b1111_1111, + (0b0010_0001 << 2) + // 178 |11111111|11111111|100000 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1110, + (0b0000_0000 << 4) + // 179 |11111111|11111111|00010 + + 0b0000_1111, + 0b1111_1111, + 0b1111_0001, + (0b0000_0000 << 7) + // 180 |11111111|11111111|1101101 + + 0b0111_1111, + 0b1111_1111, + 0b1110_1101, + // 181 |11111111|11111111|100001 + 0b1111_1111, + 0b1111_1111, + (0b0010_0001 << 2) + // 182 |11111111|11111111|1101110 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + (0b0000_1110 << 3) + // 183 |11111111|11111111|1101111 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1111 << 4) + // 184 |11111111|11111110|1010 + + 0b0000_1111, + 0b1111_1111, + 0b1110_1010, + // 185 |11111111|11111111|100010 + 0b1111_1111, + 0b1111_1111, + (0b0010_0010 << 2) + // 186 |11111111|11111111|100011 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1110, + (0b0000_0011 << 4) + // 187 |11111111|11111111|100100 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1001, + (0b0000_0000 << 6) + // 188 |11111111|11111111|1110000 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1000, + (0b0000_0000 << 7) + // 189 |11111111|11111111|100101 + + 0b0111_1111, + 0b1111_1111, + (0b0110_0101 << 1) + // 190 |11111111|11111111|100110 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0000_0110 << 3) + // 191 |11111111|11111111|1110001 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0001 << 4) + // 192 |11111111|11111111|11111000|00 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0010_0000 << 2) + // 193 |11111111|11111111|11111000|01 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1110_0001, + // 194 |11111111|11111110|1011 + 0b1111_1111, + 0b1111_1110, + (0b0000_1011 << 4) + // 195 |11111111|11111110|001 + + 0b0000_1111, + 0b1111_1111, + (0b0111_0001 << 1) + // 196 |11111111|11111111|100111 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0000_0111 << 3) + // 197 |11111111|11111111|1110010 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0010 << 4) + // 198 |11111111|11111111|101000 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1010, + (0b0000_0000 << 6) + // 199 |11111111|11111111|11110110|0 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0100 << 5) + // 200 |11111111|11111111|11111000|10 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0010 << 3) + // 201 |11111111|11111111|11111000|11 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + (0b0110_0011 << 1) + // 202 |11111111|11111111|11111001|00 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_0010, + (0b0000_0000 << 7) + // 203 |11111111|11111111|11111011|110 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_1110 << 4) + // 204 |11111111|11111111|11111011|111 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0101_1111 << 1) + // 205 |11111111|11111111|11111001|01 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_0010, + (0b0000_0001 << 7) + // 206 |11111111|11111111|11110001 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1000, + (0b0000_0001 << 7) + // 207 |11111111|11111111|11110110|1 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0001 << 6) + // 208 |11111111|11111110|010 + + 0b0011_1111, + 0b1111_1111, + (0b0001_0010 << 3) + // 209 |11111111|11111111|00011 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1000, + (0b0000_0011 << 6) + // 210 |11111111|11111111|11111001|10 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0110 << 4) + // 211 |11111111|11111111|11111100|000 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0110_0000 << 1) + // 212 |11111111|11111111|11111100|001 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1000, + (0b0000_0001 << 6) + // 213 |11111111|11111111|11111001|11 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0111 << 4) + // 214 |11111111|11111111|11111100|010 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0110_0010 << 1) + // 215 |11111111|11111111|11110010 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0111_0010 << 1) + // 216 |11111111|11111111|00100 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1110, + (0b0000_0100 << 4) + // 217 |11111111|11111111|00101 + + 0b0000_1111, + 0b1111_1111, + 0b1111_0010, + (0b0000_0001 << 7) + // 218 |11111111|11111111|11111010|00 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0000 << 5) + // 219 |11111111|11111111|11111010|01 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_1001 << 3) + // 220 |11111111|11111111|11111111|1101 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0001 << 7) + // 221 |11111111|11111111|11111100|011 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_0011 << 4) + // 222 |11111111|11111111|11111100|100 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0110_0100 << 1) + // 223 |11111111|11111111|11111100|101 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1001, + (0b0000_0001 << 6) + // 224 |11111111|11111110|1100 + + 0b0011_1111, + 0b1111_1111, + (0b0010_1100 << 2) + // 225 |11111111|11111111|11110011 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + (0b0011_0011 << 2) + // 226 |11111111|11111110|1101 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1011, + (0b0000_0001 << 6) + // 227 |11111111|11111111|00110 + + 0b0011_1111, + 0b1111_1111, + (0b0110_0110 << 1) + // 228 |11111111|11111111|101001 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0000_1001 << 3) + // 229 |11111111|11111111|00111 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1001, + (0b0000_0011 << 6) + // 230 |11111111|11111111|01000 + + 0b0011_1111, + 0b1111_1111, + (0b0110_1000 << 1) + // 231 |11111111|11111111|1110011 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + (0b0011_0011 << 2) + // 232 |11111111|11111111|101010 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1110, + (0b0000_1010 << 4) + // 233 |11111111|11111111|101011 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1010, + (0b0000_0011 << 6) + // 234 |11111111|11111111|11110111|0 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1101, + (0b0000_0110 << 5) + // 235 |11111111|11111111|11110111|1 + + 0b0001_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1111 << 4) + // 236 |11111111|11111111|11110100 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0100 << 4) + // 237 |11111111|11111111|11110101 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0101 << 4) + // 238 |11111111|11111111|11111010|10 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0010_1010 << 2) + // 239 |11111111|11111111|1110100 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + (0b0001_0100 << 3) + // 240 |11111111|11111111|11111010|11 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + (0b0110_1011 << 1) + // 241 |11111111|11111111|11111100|110 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1001, + (0b0000_0010 << 6) + // 242 |11111111|11111111|11111011|00 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1100 << 4) + // 243 |11111111|11111111|11111011|01 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0010_1101 << 2) + // 244 |11111111|11111111|11111100|111 + + 0b0000_0011, + 0b1111_1111, + 0b1111_1111, + 0b1111_0011, + (0b0000_0001 << 7) + // 245 |11111111|11111111|11111101|000 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1000 << 4) + // 246 |11111111|11111111|11111101|001 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0110_1001 << 1) + // 247 |11111111|11111111|11111101|010 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1010, + (0b0000_0010 << 6) + // 248 |11111111|11111111|11111101|011 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_1011 << 3) + // 249 |11111111|11111111|11111111|1110 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_0000 << 7) + // 250 |11111111|11111111|11111101|100 + + 0b0111_1111, + 0b1111_1111, + 0b1111_1110, + (0b0000_1100 << 4) + // 251 |11111111|11111111|11111101|101 + + 0b0000_1111, + 0b1111_1111, + 0b1111_1111, + (0b0110_1101 << 1) + // 252 |11111111|11111111|11111101|110 + + 0b0000_0001, + 0b1111_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0010 << 6) + // 253 |11111111|11111111|11111101|111 + + 0b0011_1111, + 0b1111_1111, + 0b1111_1111, + (0b0000_1111 << 3) + // 254 |11111111|11111111|11111110|000 + + 0b0000_0111, + 0b1111_1111, + 0b1111_1111, + 0b1111_0000, + // 255 |11111111|11111111|11111011|10 + 0b1111_1111, + 0b1111_1111, + 0b1111_1011, + (0b0000_0010 << 6) + // end filler + + 0b0011_1111, + ]; + let values: Vec = (0..=255).collect(); + let res = values.hpack_encode(); + assert_eq!(res, Ok(bytes)); + } + + use super::super::HpackStringDecode; + + #[test] + fn byte_count_exact_when_bit_count_multiple_of_8() { + let encoded = vec![ + 0x8c, 0x2d, 0x4b, 0x70, 0xdd, 0xf4, 0x5a, 0xbe, 0xfb, 0x40, 0x05, 0xdb, + ]; + + let mut res = Vec::new(); + for byte in encoded.hpack_decode() { + res.push(byte.unwrap()); + } + + let reencoded = res.hpack_encode(); + + assert_eq!(reencoded.unwrap().last(), Some(&0xdb)); + } + + #[test] + fn byte_() { + let encoded = vec![ + 0x55, 0x92, 0xbe, 0xff, 0x48, 0x36, 0xcb, 0x86, 0x37, 0x3d, 0x68, 0xca, 0xc9, 0x61, + 0xce, 0xde, 0xdc, 0xe5, 0xfc, + ]; + + let mut res = Vec::new(); + for byte in encoded.hpack_decode() { + res.push(byte.unwrap()); + } + + let reencoded = res.hpack_encode(); + + assert_eq!(reencoded.unwrap().last(), Some(&0xfc)); + } +} diff --git a/h3/src/qpack2/prefix_string/mod.rs b/h3/src/qpack2/prefix_string/mod.rs new file mode 100644 index 00000000..d39db96d --- /dev/null +++ b/h3/src/qpack2/prefix_string/mod.rs @@ -0,0 +1,173 @@ +mod bitwin; +mod decode; +//mod encode; +/* +use std::convert::TryInto; +use std::fmt; +use std::num::TryFromIntError; + +use bytes::{Buf, BufMut}; + +pub use self::bitwin::BitWindow; + +pub use self::{ + decode::{HuffmanDecodingError, HpackStringDecode}, + encode::{HuffmanEncodingError, HpackStringEncode}, +}; + +use crate::proto::coding::BufMutExt; + + +#[derive(Debug, PartialEq)] +pub enum PrefixStringError { + UnexpectedEnd, + Integer(IntegerError), + HuffmanDecoding(HuffmanDecodingError), + HuffmanEncoding(HuffmanEncodingError), + BufSize(TryFromIntError), +} + +impl std::fmt::Display for PrefixStringError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result { + match self { + PrefixStringError::UnexpectedEnd => write!(f, "unexpected end"), + PrefixStringError::Integer(e) => write!(f, "could not parse integer: {}", e), + PrefixStringError::HuffmanDecoding(e) => write!(f, "Huffman decode failed: {:?}", e), + PrefixStringError::HuffmanEncoding(e) => write!(f, "Huffman encode failed: {:?}", e), + PrefixStringError::BufSize(_) => write!(f, "number in buffer wrong size"), + } + } +} + + +pub fn decode(size: u8, buf: &mut B) -> Result>, PrefixStringDecoderError> { + let (flags, len) = match prefix_int::decode(size - 1, buf){ + Ok(Some((flags, len))) => (flags, len), + Ok(None) => return Ok(None), + Err(e) => return Err(PrefixStringError::Integer(e)), + }; + + let len: usize = len.try_into()?; + if buf.remaining() < len { + return Err(PrefixStringError::UnexpectedEnd); + } + + let payload = buf.copy_to_bytes(len); + let value = if flags & 1 == 0 { + payload.into_iter().collect() + } else { + let mut decoded = Vec::new(); + for byte in payload.into_iter().collect::>().hpack_decode() { + decoded.push(byte?); + } + decoded + }; + Ok(value) +}*/ + +/* + +pub fn encode(size: u8, flags: u8, value: &[u8], buf: &mut B) -> Result<(), PrefixStringError> { + let encoded = Vec::from(value).hpack_encode()?; + prefix_int::encode(size - 1, flags << 1 | 1, encoded.len().try_into()?, buf); + for byte in encoded { + buf.write(byte); + } + Ok(()) +} + +impl From for PrefixStringError { + fn from(error: HuffmanEncodingError) -> Self { + PrefixStringError::HuffmanEncoding(error) + } +} + +impl From for PrefixStringError { + fn from(error: IntegerError) -> Self { + match error { + IntegerError::UnexpectedEnd => PrefixStringError::UnexpectedEnd, + e => PrefixStringError::Integer(e), + } + } +} + +impl From for PrefixStringError { + fn from(error: HuffmanDecodingError) -> Self { + PrefixStringError::HuffmanDecoding(error) + } +} + +impl From for PrefixStringError { + fn from(error: TryFromIntError) -> Self { + PrefixStringError::BufSize(error) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use assert_matches::assert_matches; + use std::io::Cursor; + + #[test] + fn codec_6() { + let mut buf = Vec::new(); + encode(6, 0b01, b"name without ref", &mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!( + &buf, + &[ + 0b0110_1100, + 168, + 116, + 149, + 79, + 6, + 76, + 231, + 181, + 42, + 88, + 89, + 127 + ] + ); + assert_eq!(decode(6, &mut read).unwrap(), b"name without ref"); + } + + #[test] + fn codec_8() { + let mut buf = Vec::new(); + encode(8, 0b01, b"name with ref", &mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!( + &buf, + &[0b1000_1010, 168, 116, 149, 79, 6, 76, 234, 88, 89, 127] + ); + assert_eq!(decode(8, &mut read).unwrap(), b"name with ref"); + } + + #[test] + fn codec_8_empty() { + let mut buf = Vec::new(); + encode(8, 0b01, b"", &mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!(&buf, &[0b1000_0000]); + assert_eq!(decode(8, &mut read).unwrap(), b""); + } + + #[test] + fn decode_non_huffman() { + let buf = vec![0b0100_0011, b'b', b'a', b'r']; + let mut read = Cursor::new(&buf); + assert_eq!(decode(6, &mut read).unwrap(), b"bar"); + } + + #[test] + fn decode_too_short() { + let buf = vec![0b0100_0011, b'b', b'a']; + let mut read = Cursor::new(&buf); + assert_matches!(decode(6, &mut read), Err(PrefixStringError::UnexpectedEnd)); + } +} +*/ \ No newline at end of file diff --git a/h3/src/qpack2/qpack_result.rs b/h3/src/qpack2/qpack_result.rs new file mode 100644 index 00000000..21855411 --- /dev/null +++ b/h3/src/qpack2/qpack_result.rs @@ -0,0 +1,34 @@ +use bytes::Buf; + +/// Return type for parsing qpack stuff +#[derive(Debug)] +pub enum ParseProgressResult { + /// Error while parsing + Error(E), + /// Parsing is done + Done(R), + /// More data is needed to continue parsing + MoreData(T), +} + +/// implement PartialEq for ParseProgressResult +#[cfg(test)] +impl PartialEq for ParseProgressResult { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (ParseProgressResult::Error(e1), ParseProgressResult::Error(e2)) => e1 == e2, + (ParseProgressResult::Done(r1), ParseProgressResult::Done(r2)) => r1 == r2, + (ParseProgressResult::MoreData(_), ParseProgressResult::MoreData(_)) => false, // T is not comparable. This is for tests so we can ignore it. + _ => false, + } + } +} + +/// Trait for stateful parsers +pub trait StatefulParser +where + Self: Sized, +{ + /// Parse the next chunk of data + fn parse_progress(self, reader: &mut B) -> ParseProgressResult; +} diff --git a/h3/src/qpack2/static_.rs b/h3/src/qpack2/static_.rs new file mode 100644 index 00000000..a1a27b07 --- /dev/null +++ b/h3/src/qpack2/static_.rs @@ -0,0 +1,360 @@ +use std::borrow::Cow; + +use super::field::HeaderField; + +#[derive(Debug, PartialEq)] +pub enum Error { + Unknown(usize), +} + +pub struct StaticTable {} + +impl StaticTable { + pub fn get(index: usize) -> Result<&'static HeaderField, Error> { + match PREDEFINED_HEADERS.get(index) { + Some(f) => Ok(f), + None => Err(Error::Unknown(index)), + } + } + + pub fn find(field: &HeaderField) -> Option { + match (&field.name[..], &field.value[..]) { + (b":authority", b"") => Some(0), + (b":path", b"/") => Some(1), + (b"age", b"0") => Some(2), + (b"content-disposition", b"") => Some(3), + (b"content-length", b"0") => Some(4), + (b"cookie", b"") => Some(5), + (b"date", b"") => Some(6), + (b"etag", b"") => Some(7), + (b"if-modified-since", b"") => Some(8), + (b"if-none-match", b"") => Some(9), + (b"last-modified", b"") => Some(10), + (b"link", b"") => Some(11), + (b"location", b"") => Some(12), + (b"referer", b"") => Some(13), + (b"set-cookie", b"") => Some(14), + (b":method", b"CONNECT") => Some(15), + (b":method", b"DELETE") => Some(16), + (b":method", b"GET") => Some(17), + (b":method", b"HEAD") => Some(18), + (b":method", b"OPTIONS") => Some(19), + (b":method", b"POST") => Some(20), + (b":method", b"PUT") => Some(21), + (b":scheme", b"http") => Some(22), + (b":scheme", b"https") => Some(23), + (b":status", b"103") => Some(24), + (b":status", b"200") => Some(25), + (b":status", b"304") => Some(26), + (b":status", b"404") => Some(27), + (b":status", b"503") => Some(28), + (b"accept", b"*/*") => Some(29), + (b"accept", b"application/dns-message") => Some(30), + (b"accept-encoding", b"gzip, deflate, br") => Some(31), + (b"accept-ranges", b"bytes") => Some(32), + (b"access-control-allow-headers", b"cache-control") => Some(33), + (b"access-control-allow-headers", b"content-type") => Some(34), + (b"access-control-allow-origin", b"*") => Some(35), + (b"cache-control", b"max-age=0") => Some(36), + (b"cache-control", b"max-age=2592000") => Some(37), + (b"cache-control", b"max-age=604800") => Some(38), + (b"cache-control", b"no-cache") => Some(39), + (b"cache-control", b"no-store") => Some(40), + (b"cache-control", b"public, max-age=31536000") => Some(41), + (b"content-encoding", b"br") => Some(42), + (b"content-encoding", b"gzip") => Some(43), + (b"content-type", b"application/dns-message") => Some(44), + (b"content-type", b"application/javascript") => Some(45), + (b"content-type", b"application/json") => Some(46), + (b"content-type", b"application/x-www-form-urlencoded") => Some(47), + (b"content-type", b"image/gif") => Some(48), + (b"content-type", b"image/jpeg") => Some(49), + (b"content-type", b"image/png") => Some(50), + (b"content-type", b"text/css") => Some(51), + (b"content-type", b"text/html; charset=utf-8") => Some(52), + (b"content-type", b"text/plain") => Some(53), + (b"content-type", b"text/plain;charset=utf-8") => Some(54), + (b"range", b"bytes=0-") => Some(55), + (b"strict-transport-security", b"max-age=31536000") => Some(56), + (b"strict-transport-security", b"max-age=31536000; includesubdomains") => Some(57), + (b"strict-transport-security", b"max-age=31536000; includesubdomains; preload") => { + Some(58) + } + (b"vary", b"accept-encoding") => Some(59), + (b"vary", b"origin") => Some(60), + (b"x-content-type-options", b"nosniff") => Some(61), + (b"x-xss-protection", b"1; mode=block") => Some(62), + (b":status", b"100") => Some(63), + (b":status", b"204") => Some(64), + (b":status", b"206") => Some(65), + (b":status", b"302") => Some(66), + (b":status", b"400") => Some(67), + (b":status", b"403") => Some(68), + (b":status", b"421") => Some(69), + (b":status", b"425") => Some(70), + (b":status", b"500") => Some(71), + (b"accept-language", b"") => Some(72), + (b"access-control-allow-credentials", b"FALSE") => Some(73), + (b"access-control-allow-credentials", b"TRUE") => Some(74), + (b"access-control-allow-headers", b"*") => Some(75), + (b"access-control-allow-methods", b"get") => Some(76), + (b"access-control-allow-methods", b"get, post, options") => Some(77), + (b"access-control-allow-methods", b"options") => Some(78), + (b"access-control-expose-headers", b"content-length") => Some(79), + (b"access-control-request-headers", b"content-type") => Some(80), + (b"access-control-request-method", b"get") => Some(81), + (b"access-control-request-method", b"post") => Some(82), + (b"alt-svc", b"clear") => Some(83), + (b"authorization", b"") => Some(84), + ( + b"content-security-policy", + b"script-src 'none'; object-src 'none'; base-uri 'none'", + ) => Some(85), + (b"early-data", b"1") => Some(86), + (b"expect-ct", b"") => Some(87), + (b"forwarded", b"") => Some(88), + (b"if-range", b"") => Some(89), + (b"origin", b"") => Some(90), + (b"purpose", b"prefetch") => Some(91), + (b"server", b"") => Some(92), + (b"timing-allow-origin", b"*") => Some(93), + (b"upgrade-insecure-requests", b"1") => Some(94), + (b"user-agent", b"") => Some(95), + (b"x-forwarded-for", b"") => Some(96), + (b"x-frame-options", b"deny") => Some(97), + (b"x-frame-options", b"sameorigin") => Some(98), + _ => None, + } + } + + pub fn find_name(name: &[u8]) -> Option { + match name { + b":authority" => Some(0), + b":path" => Some(1), + b"age" => Some(2), + b"content-disposition" => Some(3), + b"content-length" => Some(4), + b"cookie" => Some(5), + b"date" => Some(6), + b"etag" => Some(7), + b"if-modified-since" => Some(8), + b"if-none-match" => Some(9), + b"last-modified" => Some(10), + b"link" => Some(11), + b"location" => Some(12), + b"referer" => Some(13), + b"set-cookie" => Some(14), + b":method" => Some(15), + b":scheme" => Some(22), + b":status" => Some(24), + b"accept" => Some(29), + b"accept-encoding" => Some(31), + b"accept-ranges" => Some(32), + b"access-control-allow-headers" => Some(33), + b"access-control-allow-origin" => Some(35), + b"cache-control" => Some(36), + b"content-encoding" => Some(42), + b"content-type" => Some(44), + b"range" => Some(55), + b"strict-transport-security" => Some(56), + b"vary" => Some(59), + b"x-content-type-options" => Some(61), + b"x-xss-protection" => Some(62), + b"accept-language" => Some(72), + b"access-control-allow-credentials" => Some(73), + b"access-control-allow-methods" => Some(76), + b"access-control-expose-headers" => Some(79), + b"access-control-request-headers" => Some(80), + b"access-control-request-method" => Some(81), + b"alt-svc" => Some(83), + b"authorization" => Some(84), + b"content-security-policy" => Some(85), + b"early-data" => Some(86), + b"expect-ct" => Some(87), + b"forwarded" => Some(88), + b"if-range" => Some(89), + b"origin" => Some(90), + b"purpose" => Some(91), + b"server" => Some(92), + b"timing-allow-origin" => Some(93), + b"upgrade-insecure-requests" => Some(94), + b"user-agent" => Some(95), + b"x-forwarded-for" => Some(96), + b"x-frame-options" => Some(97), + _ => None, + } + } +} + +macro_rules! decl_fields { + [ $( ($key:expr, $value:expr) ),* ] => { + [ + $( + HeaderField { + name: Cow::Borrowed($key), + value: Cow::Borrowed($value) + }, + )* ] + } +} + +const PREDEFINED_HEADERS: [HeaderField; 99] = decl_fields![ + (b":authority", b""), + (b":path", b"/"), + (b"age", b"0"), + (b"content-disposition", b""), + (b"content-length", b"0"), + (b"cookie", b""), + (b"date", b""), + (b"etag", b""), + (b"if-modified-since", b""), + (b"if-none-match", b""), + (b"last-modified", b""), + (b"link", b""), + (b"location", b""), + (b"referer", b""), + (b"set-cookie", b""), + (b":method", b"CONNECT"), + (b":method", b"DELETE"), + (b":method", b"GET"), + (b":method", b"HEAD"), + (b":method", b"OPTIONS"), + (b":method", b"POST"), + (b":method", b"PUT"), + (b":scheme", b"http"), + (b":scheme", b"https"), + (b":status", b"103"), + (b":status", b"200"), + (b":status", b"304"), + (b":status", b"404"), + (b":status", b"503"), + (b"accept", b"*/*"), + (b"accept", b"application/dns-message"), + (b"accept-encoding", b"gzip, deflate, br"), + (b"accept-ranges", b"bytes"), + (b"access-control-allow-headers", b"cache-control"), + (b"access-control-allow-headers", b"content-type"), + (b"access-control-allow-origin", b"*"), + (b"cache-control", b"max-age=0"), + (b"cache-control", b"max-age=2592000"), + (b"cache-control", b"max-age=604800"), + (b"cache-control", b"no-cache"), + (b"cache-control", b"no-store"), + (b"cache-control", b"public, max-age=31536000"), + (b"content-encoding", b"br"), + (b"content-encoding", b"gzip"), + (b"content-type", b"application/dns-message"), + (b"content-type", b"application/javascript"), + (b"content-type", b"application/json"), + (b"content-type", b"application/x-www-form-urlencoded"), + (b"content-type", b"image/gif"), + (b"content-type", b"image/jpeg"), + (b"content-type", b"image/png"), + (b"content-type", b"text/css"), + (b"content-type", b"text/html; charset=utf-8"), + (b"content-type", b"text/plain"), + (b"content-type", b"text/plain;charset=utf-8"), + (b"range", b"bytes=0-"), + (b"strict-transport-security", b"max-age=31536000"), + ( + b"strict-transport-security", + b"max-age=31536000; includesubdomains" + ), + ( + b"strict-transport-security", + b"max-age=31536000; includesubdomains; preload" + ), + (b"vary", b"accept-encoding"), + (b"vary", b"origin"), + (b"x-content-type-options", b"nosniff"), + (b"x-xss-protection", b"1; mode=block"), + (b":status", b"100"), + (b":status", b"204"), + (b":status", b"206"), + (b":status", b"302"), + (b":status", b"400"), + (b":status", b"403"), + (b":status", b"421"), + (b":status", b"425"), + (b":status", b"500"), + (b"accept-language", b""), + (b"access-control-allow-credentials", b"FALSE"), + (b"access-control-allow-credentials", b"TRUE"), + (b"access-control-allow-headers", b"*"), + (b"access-control-allow-methods", b"get"), + (b"access-control-allow-methods", b"get, post, options"), + (b"access-control-allow-methods", b"options"), + (b"access-control-expose-headers", b"content-length"), + (b"access-control-request-headers", b"content-type"), + (b"access-control-request-method", b"get"), + (b"access-control-request-method", b"post"), + (b"alt-svc", b"clear"), + (b"authorization", b""), + ( + b"content-security-policy", + b"script-src 'none'; object-src 'none'; base-uri 'none'" + ), + (b"early-data", b"1"), + (b"expect-ct", b""), + (b"forwarded", b""), + (b"if-range", b""), + (b"origin", b""), + (b"purpose", b"prefetch"), + (b"server", b""), + (b"timing-allow-origin", b"*"), + (b"upgrade-insecure-requests", b"1"), + (b"user-agent", b""), + (b"x-forwarded-for", b""), + (b"x-frame-options", b"deny"), + (b"x-frame-options", b"sameorigin") +]; + +#[cfg(test)] +mod tests { + use super::*; + + /** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-static-table + * 3.1. Static Table + * [...] + * Note the QPACK static table is indexed from 0, whereas the HPACK + * static table is indexed from 1. + */ + #[test] + fn test_static_table_index_is_0_based() { + assert_eq!(StaticTable::get(0), Ok(&HeaderField::new(":authority", ""))); + } + + #[test] + fn test_static_table_is_full() { + assert_eq!(PREDEFINED_HEADERS.len(), 99); + } + + #[test] + fn test_static_table_can_get_field() { + assert_eq!( + StaticTable::get(98), + Ok(&HeaderField::new("x-frame-options", "sameorigin")) + ); + } + + #[test] + fn invalid_index() { + assert_eq!(StaticTable::get(99), Err(Error::Unknown(99))); + } + + #[test] + fn find_by_name() { + assert_eq!(StaticTable::find_name(b"last-modified"), Some(10usize)); + assert_eq!(StaticTable::find_name(b"does-not-exist"), None); + } + + #[test] + fn find() { + assert_eq!( + StaticTable::find(&HeaderField::new(":method", "GET")), + Some(17usize) + ); + assert_eq!(StaticTable::find(&HeaderField::new("foo", "bar")), None); + } +} diff --git a/h3/src/qpack2/stream.rs b/h3/src/qpack2/stream.rs new file mode 100644 index 00000000..a33e3595 --- /dev/null +++ b/h3/src/qpack2/stream.rs @@ -0,0 +1,469 @@ +use bytes::{Buf, BufMut}; +use std::{convert::TryInto, io::Cursor}; + +use crate::error::{internal_error::InternalConnectionError, Code}; + +use super::{ + parse_error::ParseError, + prefix_int::{self, Error as IntError}, + prefix_string::{self, PrefixStringError as StringError}, +}; + +// 4.3. Encoder Instructions +pub enum EncoderInstruction { + // 4.3.1. Set Dynamic Table Capacity + // An encoder informs the decoder of a change to the dynamic table capacity. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 1 | Capacity (5+) | + // +---+---+---+-------------------+ + DynamicTableSizeUpdate(DynamicTableSizeUpdate), + // 4.3.2. Insert With Name Reference + // An encoder adds an entry to the dynamic table where the field name + // matches the field name of an entry stored in the static or the dynamic + // table. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 1 | T | Name Index (6+) | + // +---+---+-----------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + InsertWithNameRef(InsertWithNameRef), + // 4.3.3. Insert With Literal Name + // An encoder adds an entry to the dynamic table where both the field name + // and the field value are represented as string literals. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | H | Name Length (5+) | + // +---+---+---+-------------------+ + // | Name String (Length bytes) | + // +---+---------------------------+ + // | H | Value Length (7+) | + // +---+---------------------------+ + // | Value String (Length bytes) | + // +-------------------------------+ + InsertWithoutNameRef(InsertWithoutNameRef), + // 4.3.4. Duplicate + // An encoder duplicates an existing entry in the dynamic table. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | 0 | Index (5+) | + // +---+---+---+-------------------+ + Duplicate(Duplicate), + Unknown, +} + +impl EncoderInstruction { + /*pub fn decode(first: u8) -> Self { + if first & 0b1000_0000 != 0 { + EncoderInstruction::InsertWithNameRef + } else if first & 0b0100_0000 == 0b0100_0000 { + EncoderInstruction::InsertWithoutNameRef + } else if first & 0b1110_0000 == 0 { + EncoderInstruction::Duplicate + } else if first & 0b0010_0000 == 0b0010_0000 { + EncoderInstruction::DynamicTableSizeUpdate + } else { + EncoderInstruction::Unknown + } + }*/ + + /* fn decode(read: &mut R) -> Result, InternalConnectionError> { + if read.remaining() < 1 { + return Ok(None); + } + + let mut buf = Cursor::new(read.chunk()); + let first = buf.chunk()[0]; + + let x = if first & 0b1000_0000 != 0 { + //EncoderInstruction::InsertWithNameRef + todo!() + } else if first & 0b0100_0000 == 0b0100_0000 { + //EncoderInstruction::InsertWithoutNameRef + todo!() + } else if first & 0b1110_0000 == 0 { + //EncoderInstruction::Duplicate + todo!() + } else if first & 0b0010_0000 == 0b0010_0000 { + //EncoderInstruction::DynamicTableSizeUpdate + let table_size_update= DynamicTableSizeUpdate::decode(&mut buf); + EncoderInstruction::DynamicTableSizeUpdate(DynamicTableSizeUpdate::decode(&mut buf)) + } else { + //EncoderInstruction::Unknown + // TODO: can this be unreachable!()? + return Err(InternalConnectionError::new( + Code::H3_INTERNAL_ERROR, + "Unknown EncoderInstruction".to_string(), + )); + }; + + let instruction = match EncoderInstruction::decode(first) { + EncoderInstruction::Unknown => return Err(DecoderError::UnknownPrefix(first)), + EncoderInstruction::DynamicTableSizeUpdate => { + DynamicTableSizeUpdate::decode(&mut buf)?.map(|x| Instruction::TableSizeUpdate(x.0)) + } + EncoderInstruction::InsertWithoutNameRef => InsertWithoutNameRef::decode(&mut buf)? + .map(|x| Instruction::Insert(HeaderField::new(x.name, x.value))), + EncoderInstruction::Duplicate => match Duplicate::decode(&mut buf)? { + Some(Duplicate(index)) => { + Some(Instruction::Insert(self.table.get_relative(index)?.clone())) + } + None => None, + }, + EncoderInstruction::InsertWithNameRef => match InsertWithNameRef::decode(&mut buf)? { + Some(InsertWithNameRef::Static { index, value }) => Some(Instruction::Insert( + StaticTable::get(index)?.with_value(value), + )), + Some(InsertWithNameRef::Dynamic { index, value }) => Some(Instruction::Insert( + self.table.get_relative(index)?.with_value(value), + )), + None => None, + }, + }; + + if instruction.is_some() { + let pos = buf.position(); + read.advance(pos as usize); + } + + Ok(instruction) + }*/ +} + +#[derive(Debug, PartialEq)] +pub enum InsertWithNameRef { + Static { index: usize, value: Vec }, + Dynamic { index: usize, value: Vec }, +} + +impl InsertWithNameRef { + pub fn new_static>>(index: usize, value: T) -> Self { + InsertWithNameRef::Static { + index, + value: value.into(), + } + } + + pub fn new_dynamic>>(index: usize, value: T) -> Self { + InsertWithNameRef::Dynamic { + index, + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result, ParseError> { + let (flags, index) = match prefix_int::decode(6, buf) { + Ok((f, x)) if f & 0b10 == 0b10 => (f, x), + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + let index: usize = index + .try_into() + .map_err(|_e| ParseError::Integer(crate::qpack::prefix_int::Error::Overflow))?; + + let value = match prefix_string::decode(8, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + + if flags & 0b01 == 0b01 { + Ok(Some(InsertWithNameRef::new_static(index, value))) + } else { + Ok(Some(InsertWithNameRef::new_dynamic(index, value))) + } + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + match self { + InsertWithNameRef::Static { index, value } => { + prefix_int::encode(6, 0b11, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + InsertWithNameRef::Dynamic { index, value } => { + prefix_int::encode(6, 0b10, *index as u64, buf); + prefix_string::encode(8, 0, value, buf)?; + } + } + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct InsertWithoutNameRef { + pub name: Vec, + pub value: Vec, +} + +impl InsertWithoutNameRef { + pub fn new>>(name: T, value: T) -> Self { + Self { + name: name.into(), + value: value.into(), + } + } + + pub fn decode(buf: &mut R) -> Result, ParseError> { + let name = match prefix_string::decode(6, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + let value = match prefix_string::decode(8, buf) { + Ok(x) => x, + Err(StringError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(Self::new(name, value))) + } + + pub fn encode(&self, buf: &mut W) -> Result<(), prefix_string::PrefixStringError> { + prefix_string::encode(6, 0b01, &self.name, buf)?; + prefix_string::encode(8, 0, &self.value, buf)?; + Ok(()) + } +} + +#[derive(Debug, PartialEq)] +pub struct Duplicate(pub usize); + +impl Duplicate { + pub fn decode(buf: &mut R) -> Result, ParseError> { + let index = match prefix_int::decode(5, buf) { + Ok((0, x)) => { + if x > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + x as usize + } + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(Duplicate(index))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(5, 0, self.0 as u64, buf); + } +} + +#[derive(Debug, PartialEq)] +pub struct DynamicTableSizeUpdate(pub usize); + +impl DynamicTableSizeUpdate { + pub fn decode(buf: &mut R) -> Result, ParseError> { + let size = match prefix_int::decode(5, buf) { + Ok((0b001, x)) => { + if x > (usize::MAX as u64) { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + x as usize + } + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(DynamicTableSizeUpdate(size))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(5, 0b001, self.0 as u64, buf); + } +} + +// 4.4. Decoder Instructions +// A decoder sends decoder instructions on the decoder stream to inform the encoder +// about the processing of field sections and table updates to ensure consistency +// of the dynamic table. +#[derive(Debug, PartialEq)] +pub enum DecoderInstruction { + // 4.4.1. Section Acknowledgement + // Acknowledge processing of an encoded field section whose declared Required + // Insert Count is not zero. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 1 | Stream ID (7+) | + // +---+---------------------------+ + HeaderAck, + // 4.4.2. Stream Cancellation + // When a stream is reset or reading is abandoned. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 1 | Stream ID (6+) | + // +---+---+-----------------------+ + StreamCancel, + // 4.4.3. Insert Count Increment + // Increases the Known Received Count to the total number of dynamic table + // insertions and duplications processed so far. + // 0 1 2 3 4 5 6 7 + // +---+---+---+---+---+---+---+---+ + // | 0 | 0 | Increment (6+) | + // +---+---+-----------------------+ + InsertCountIncrement, + Unknown, +} + +impl DecoderInstruction { + pub fn decode(first: u8) -> Self { + if first & 0b1100_0000 == 0 { + DecoderInstruction::InsertCountIncrement + } else if first & 0b1000_0000 != 0 { + DecoderInstruction::HeaderAck + } else if first & 0b0100_0000 == 0b0100_0000 { + DecoderInstruction::StreamCancel + } else { + DecoderInstruction::Unknown + } + } +} + +#[derive(Debug, PartialEq)] +pub struct InsertCountIncrement(pub u8); + +impl InsertCountIncrement { + pub fn decode(buf: &mut R) -> Result, ParseError> { + let insert_count = match prefix_int::decode(6, buf) { + Ok((0b00, x)) => { + if x > 64 { + return Err(ParseError::Integer( + crate::qpack::prefix_int::Error::Overflow, + )); + } + x as u8 + } + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(InsertCountIncrement(insert_count))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(6, 0b00, self.0 as u64, buf); + } +} + +#[derive(Debug, PartialEq)] +pub struct HeaderAck(pub u64); + +impl HeaderAck { + pub fn decode(buf: &mut R) -> Result, ParseError> { + let stream_id = match prefix_int::decode(7, buf) { + Ok((0b1, x)) => x, + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(HeaderAck(stream_id))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(7, 0b1, self.0, buf); + } +} + +#[derive(Debug, PartialEq)] +pub struct StreamCancel(pub u64); + +impl StreamCancel { + pub fn decode(buf: &mut R) -> Result, ParseError> { + let stream_id = match prefix_int::decode(6, buf) { + Ok((0b01, x)) => x, + Ok((f, _)) => return Err(ParseError::InvalidPrefix(f)), + Err(IntError::UnexpectedEnd) => return Ok(None), + Err(e) => return Err(e.into()), + }; + Ok(Some(StreamCancel(stream_id))) + } + + pub fn encode(&self, buf: &mut W) { + prefix_int::encode(6, 0b01, self.0, buf); + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::io::Cursor; + + #[test] + fn insert_with_name_ref() { + let instruction = InsertWithNameRef::new_static(0, "value"); + let mut buf = vec![]; + instruction.encode(&mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!(InsertWithNameRef::decode(&mut read), Ok(Some(instruction))); + } + + #[test] + fn insert_without_name_ref() { + let instruction = InsertWithoutNameRef::new("name", "value"); + let mut buf = vec![]; + instruction.encode(&mut buf).unwrap(); + let mut read = Cursor::new(&buf); + assert_eq!( + InsertWithoutNameRef::decode(&mut read), + Ok(Some(instruction)) + ); + } + + #[test] + fn insert_duplicate() { + let instruction = Duplicate(42); + let mut buf = vec![]; + instruction.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(Duplicate::decode(&mut read), Ok(Some(instruction))); + } + + #[test] + fn dynamic_table_size_update() { + let instruction = DynamicTableSizeUpdate(42); + let mut buf = vec![]; + instruction.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!( + DynamicTableSizeUpdate::decode(&mut read), + Ok(Some(instruction)) + ); + } + + #[test] + fn insert_count_increment() { + let instruction = InsertCountIncrement(42); + let mut buf = vec![]; + instruction.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!( + InsertCountIncrement::decode(&mut read), + Ok(Some(instruction)) + ); + } + + #[test] + fn header_ack() { + let instruction = HeaderAck(42); + let mut buf = vec![]; + instruction.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(HeaderAck::decode(&mut read), Ok(Some(instruction))); + } + + #[test] + fn stream_cancel() { + let instruction = StreamCancel(42); + let mut buf = vec![]; + instruction.encode(&mut buf); + let mut read = Cursor::new(&buf); + assert_eq!(StreamCancel::decode(&mut read), Ok(Some(instruction))); + } +} diff --git a/h3/src/qpack2/tests.rs b/h3/src/qpack2/tests.rs new file mode 100644 index 00000000..f72f4510 --- /dev/null +++ b/h3/src/qpack2/tests.rs @@ -0,0 +1,163 @@ +use crate::qpack::decoder::Decoder; +use crate::qpack::encoder::Encoder; +use crate::qpack::{dynamic::DynamicTable, Decoded, DecoderError, HeaderField}; +use std::io::Cursor; + +pub mod helpers { + use crate::qpack::{dynamic::DynamicTable, HeaderField}; + + pub const TABLE_SIZE: usize = 4096; + + pub fn build_table() -> DynamicTable { + let mut table = DynamicTable::new(); + table.set_max_size(TABLE_SIZE).unwrap(); + table.set_max_blocked(100).unwrap(); + table + } + + pub fn build_table_with_size(n_field: usize) -> DynamicTable { + let mut table = DynamicTable::new(); + table.set_max_size(TABLE_SIZE).unwrap(); + table.set_max_blocked(100).unwrap(); + + for i in 0..n_field { + table + .put(HeaderField::new(format!("foo{}", i + 1), "bar")) + .unwrap(); + } + + table + } +} + +#[test] +fn codec_basic_get() { + let mut encoder = Encoder::default(); + let mut decoder = Decoder::from(DynamicTable::new()); + + let mut block_buf = vec![]; + let mut enc_buf = vec![]; + let mut dec_buf = vec![]; + + let header = vec![ + HeaderField::new(":method", "GET"), + HeaderField::new(":path", "/"), + HeaderField::new("foo", "bar"), + ]; + + encoder + .encode(42, &mut block_buf, &mut enc_buf, header.clone()) + .unwrap(); + + let mut enc_cur = Cursor::new(&mut enc_buf); + decoder.on_encoder_recv(&mut enc_cur, &mut dec_buf).unwrap(); + + let mut block_cur = Cursor::new(&mut block_buf); + let Decoded { fields, .. } = decoder.decode_header(&mut block_cur).unwrap(); + assert_eq!(fields, header); + + let mut dec_cur = Cursor::new(&mut dec_buf); + encoder.on_decoder_recv(&mut dec_cur).unwrap(); +} + +const TABLE_SIZE: usize = 4096; +#[test] +fn blocked_header() { + let mut enc_table = DynamicTable::new(); + enc_table.set_max_size(TABLE_SIZE).unwrap(); + enc_table.set_max_blocked(100).unwrap(); + let mut encoder = Encoder::from(enc_table); + let mut dec_table = DynamicTable::new(); + dec_table.set_max_size(TABLE_SIZE).unwrap(); + dec_table.set_max_blocked(100).unwrap(); + let decoder = Decoder::from(dec_table); + + let mut block_buf = vec![]; + let mut enc_buf = vec![]; + + encoder + .encode( + 42, + &mut block_buf, + &mut enc_buf, + &[HeaderField::new("foo", "bar")], + ) + .unwrap(); + + let mut block_cur = Cursor::new(&mut block_buf); + assert_eq!( + decoder.decode_header(&mut block_cur), + Err(DecoderError::MissingRefs(1)) + ); +} + +#[test] +fn codec_table_size_0() { + let mut enc_table = DynamicTable::new(); + let mut dec_table = DynamicTable::new(); + + let mut block_buf = vec![]; + let mut enc_buf = vec![]; + let mut dec_buf = vec![]; + + let header = vec![ + HeaderField::new(":method", "GET"), + HeaderField::new(":path", "/"), + HeaderField::new("foo", "bar"), + ]; + + dec_table.set_max_size(0).unwrap(); + enc_table.set_max_size(0).unwrap(); + + let mut encoder = Encoder::from(enc_table); + let mut decoder = Decoder::from(dec_table); + + encoder + .encode(42, &mut block_buf, &mut enc_buf, header.clone()) + .unwrap(); + + let mut enc_cur = Cursor::new(&mut enc_buf); + decoder.on_encoder_recv(&mut enc_cur, &mut dec_buf).unwrap(); + + let mut block_cur = Cursor::new(&mut block_buf); + let Decoded { fields, .. } = decoder.decode_header(&mut block_cur).unwrap(); + assert_eq!(fields, header); + + let mut dec_cur = Cursor::new(&mut dec_buf); + encoder.on_decoder_recv(&mut dec_cur).unwrap(); +} + +#[test] +fn codec_table_full() { + let mut enc_table = DynamicTable::new(); + let mut dec_table = DynamicTable::new(); + + let mut block_buf = vec![]; + let mut enc_buf = vec![]; + let mut dec_buf = vec![]; + + let header = vec![ + HeaderField::new("foo", "bar"), + HeaderField::new("foo1", "bar1"), + ]; + + dec_table.set_max_size(42).unwrap(); + enc_table.set_max_size(42).unwrap(); + + let mut encoder = Encoder::from(enc_table); + let mut decoder = Decoder::from(dec_table); + + encoder + .encode(42, &mut block_buf, &mut enc_buf, header.clone()) + .unwrap(); + + let mut enc_cur = Cursor::new(&mut enc_buf); + let mut block_cur = Cursor::new(&mut block_buf); + + decoder.on_encoder_recv(&mut enc_cur, &mut dec_buf).unwrap(); + let Decoded { fields, .. } = decoder.decode_header(&mut block_cur).unwrap(); + assert_eq!(fields, header); + + let mut dec_cur = Cursor::new(&mut dec_buf); + encoder.on_decoder_recv(&mut dec_cur).unwrap(); +} diff --git a/h3/src/qpack2/vas.rs b/h3/src/qpack2/vas.rs new file mode 100644 index 00000000..5fccbfef --- /dev/null +++ b/h3/src/qpack2/vas.rs @@ -0,0 +1,290 @@ +/** + * https://www.rfc-editor.org/rfc/rfc9204.html#name-absolute-indexing + * https://www.rfc-editor.org/rfc/rfc9204.html#name-relative-indexing + * https://www.rfc-editor.org/rfc/rfc9204.html#name-post-base-indexing + */ + +/* + * # Virtually infinite address space mapper. + * + * It can be described as an infinitive growable list, with a visibility + * window that can only move in the direction of insertion. + * + * Origin Visible window + * /\ /===========^===========\ + * ++++-------+ - + - + - + - + - + - + + * |||| | | | | | | | ==> Grow direction + * ++++-------+ - + - + - + - + - + - + + * \================v==================/ + * Full Virtual Space + * + * + * QPACK indexing is 1-based for absolute index, and 0-based for relative's. + * Container (ex: list) indexing is 0-based. + * + * + * # Basics + * + * inserted: number of insertion + * dropped : number of drop + * delta : count of available elements + * + * abs: absolute index + * rel: relative index + * pos: real index in memory container + * pst: post-base relative index (only with base index) + * + * first oldest latest + * element insertion insertion + * (not available available + * available) | | + * | | | + * v v v + * + - +------+ - + - + - + - + - + - + inserted: 21 + * | a | | p | q | r | s | t | u | dropped: 15 + * + - +------+ - + - + - + - + - + - + delta: 21 - 15: 6 + * ^ ^ ^ + * | | | + * abs:- abs:16 abs:21 + * rel:- rel:5 rel:0 + * pos:- pos:0 pos:6 + * + * + * # Base index + * A base index can arbitrary shift the relative index. + * The base index itself is an absolute index. + * + * base index: 17 + * | + * v + * + - +------+ - + - + - + - + - + - + inserted: 21 + * | a | | p | q | r | s | t | u | dropped: 15 + * + - +------+ - + - + - + - + - + - + delta: 21 - 15: 6 + * ^ ^ ^ ^ + * | | | | + * abs:- abs:16 abs:18 abs:21 + * rel:- rel:2 rel:0 rel:- + * pst:- pst:- pst:- pst:2 + * pos:- pos:0 pos:2 pos:6 + */ + +pub type RelativeIndex = usize; +pub type AbsoluteIndex = usize; + +#[derive(Debug, PartialEq)] +pub enum Error { + RelativeIndex(usize), + PostbaseIndex(usize), + Index(usize), +} + +#[derive(Debug, Default)] +pub struct VirtualAddressSpace { + inserted: usize, + dropped: usize, + delta: usize, +} + +impl VirtualAddressSpace { + pub fn add(&mut self) -> AbsoluteIndex { + self.inserted += 1; + self.delta += 1; + self.inserted + } + + pub fn drop(&mut self) { + self.dropped += 1; + self.delta -= 1; + } + + pub fn relative(&self, index: RelativeIndex) -> Result { + if self.inserted < index || self.delta == 0 || self.inserted - index <= self.dropped { + Err(Error::RelativeIndex(index)) + } else { + Ok(self.inserted - self.dropped - index - 1) + } + } + + pub fn evicted(&self, index: AbsoluteIndex) -> bool { + index != 0 && index <= self.dropped + } + + pub fn relative_base(&self, base: usize, index: RelativeIndex) -> Result { + if self.delta == 0 || index > base || base - index <= self.dropped { + Err(Error::RelativeIndex(index)) + } else { + Ok(base - self.dropped - index - 1) + } + } + + pub fn post_base(&self, base: usize, index: RelativeIndex) -> Result { + if self.delta == 0 || base + index >= self.inserted || base + index < self.dropped { + Err(Error::PostbaseIndex(index)) + } else { + Ok(base + index - self.dropped) + } + } + + pub fn index(&self, index: usize) -> Result { + if index >= self.delta { + Err(Error::Index(index)) + } else { + Ok(index + self.dropped + 1) + } + } + + pub fn largest_ref(&self) -> usize { + self.inserted - self.dropped + } + + pub fn total_inserted(&self) -> usize { + self.inserted + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::proptest; + + #[test] + fn test_no_relative_index_when_empty() { + let vas = VirtualAddressSpace::default(); + let res = vas.relative_base(0, 0); + assert_eq!(res, Err(Error::RelativeIndex(0))); + } + + #[test] + fn test_relative_underflow_protected() { + let mut vas = VirtualAddressSpace::default(); + vas.add(); + assert_eq!(vas.relative(2), Err(Error::RelativeIndex(2))); + } + + proptest! { + #[test] + fn test_first_insertion_without_drop( + ref count in 1..2200usize + ) { + let mut vas = VirtualAddressSpace::default(); + vas.add(); + (1..*count).for_each(|_| { vas.add(); }); + + assert_eq!(vas.relative_base(*count, count - 1), Ok(0), "{:?}", vas); + } + + #[test] + fn test_first_insertion_with_drop( + ref count in 2..2200usize + ) { + let mut vas = VirtualAddressSpace::default(); + vas.add(); + (1..*count).for_each(|_| { vas.add(); }); + (0..*count - 1).for_each(|_| vas.drop()); + + assert_eq!(vas.relative_base(*count, count - 1), Err(Error::RelativeIndex(count - 1)), "{:?}", vas); + } + + #[test] + fn test_last_insertion_without_drop( + ref count in 1..2200usize + ) { + let mut vas = VirtualAddressSpace::default(); + (1..*count).for_each(|_| { vas.add(); }); + vas.add(); + + assert_eq!(vas.relative_base(*count, 0), Ok(count -1), + "{:?}", vas); + } + + #[test] + fn test_last_insertion_with_drop( + ref count in 2..2200usize + ) { + let mut vas = VirtualAddressSpace::default(); + (0..*count - 1).for_each(|_| { vas.add(); }); + vas.add(); + (0..*count - 1).for_each(|_| { vas.drop(); }); + + assert_eq!(vas.relative_base(*count, 0), Ok(0), + "{:?}", vas); + } + } + + #[test] + fn test_post_base_index() { + /* + * Base index: D + * Target value: B + * + * VAS: ]GFEDCBA] + * abs: 1234567 + * rel: 3210--- + * pst: ----012 + * pos: 0123456 + */ + let mut vas = VirtualAddressSpace::default(); + (0..7).for_each(|_| { + vas.add(); + }); + + assert_eq!(vas.post_base(4, 1), Ok(5)); + } + + #[test] + fn largest_ref() { + let mut vas = VirtualAddressSpace::default(); + (0..7).for_each(|_| { + vas.add(); + }); + assert_eq!(vas.largest_ref(), 7); + } + + #[test] + fn relative() { + let mut vas = VirtualAddressSpace::default(); + + (0..7).for_each(|_| { + vas.add(); + }); + + assert_eq!(vas.relative(0), Ok(6)); + assert_eq!(vas.relative(1), Ok(5)); + assert_eq!(vas.relative(6), Ok(0)); + assert_eq!(vas.relative(7), Err(Error::RelativeIndex(7))); + } + + #[test] + fn absolute_from_real_index() { + let mut vas = VirtualAddressSpace::default(); + assert_eq!(vas.index(0), Err(Error::Index(0))); + vas.add(); + assert_eq!(vas.index(0), Ok(1)); + vas.add(); + vas.drop(); + assert_eq!(vas.index(0), Ok(2)); + vas.drop(); + assert_eq!(vas.index(0), Err(Error::Index(0))); + vas.add(); + vas.add(); + assert_eq!(vas.index(0), Ok(3)); + assert_eq!(vas.index(1), Ok(4)); + assert_eq!(vas.index(2), Err(Error::Index(2))); + } + + #[test] + fn evicted() { + let mut vas = VirtualAddressSpace::default(); + assert!(!vas.evicted(0)); + assert!(!vas.evicted(1)); + vas.add(); + vas.add(); + assert!(!vas.evicted(1)); + vas.drop(); + assert!(!vas.evicted(0)); + assert!(vas.evicted(1)); + assert!(!vas.evicted(2)); + vas.drop(); + assert!(vas.evicted(2)); + } +} diff --git a/h3/src/server/connection.rs b/h3/src/server/connection.rs index a9ba582a..5a70550e 100644 --- a/h3/src/server/connection.rs +++ b/h3/src/server/connection.rs @@ -4,7 +4,7 @@ use std::{ collections::HashSet, - future::poll_fn, + future::{poll_fn, Future}, option::Option, result::Result, task::{ready, Context, Poll}, @@ -96,7 +96,10 @@ where { #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")] /// Create a [`RequestResolver`] to handle an incoming request. - pub fn create_resolver(&self, stream: FrameStream) -> RequestResolver { + pub fn create_resolver( + &self, + stream: FrameStream, + ) -> RequestResolver { self.create_resolver_internal(stream) } @@ -120,7 +123,10 @@ where /// This method returns a [`RequestResolver`] which can be used to read the request and send the response. /// This method will return `None` when the connection receives a GOAWAY frame and all requests have been completed. #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))] - pub async fn accept(&mut self) -> Result>, ConnectionError> { + pub async fn accept( + &mut self, + ) -> Result>, ConnectionError> + { // Accept the incoming stream let stream = match poll_fn(|cx| self.poll_accept_request_stream_internal(cx)).await? { Some(s) => FrameStream::new(BufRecvStream::new(s)), @@ -131,11 +137,10 @@ where return Ok(None); } }; - + self.inner.send_grease_frame = false; let resolver = self.create_resolver_internal(stream); // send the grease frame only once - self.inner.send_grease_frame = false; Ok(Some(resolver)) } diff --git a/h3/src/server/request.rs b/h3/src/server/request.rs index ecc20868..2cdc3290 100644 --- a/h3/src/server/request.rs +++ b/h3/src/server/request.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, sync::Arc}; +use std::{convert::TryFrom, future::Future, sync::Arc}; use bytes::Buf; use http::{Request, StatusCode}; @@ -19,7 +19,7 @@ use crate::{ frame::{Frame, PayloadLen}, headers::Header, }, - qpack, + qpack::{self}, quic::{self, SendStream, StreamId}, shared_state::{ConnectionState, SharedState}, }; @@ -40,6 +40,7 @@ where pub(super) send_grease_frame: bool, pub(super) max_field_section_size: u64, pub(super) shared: Arc, + // pub(super) decoder: decoder2::decoder::Decoder, } impl ConnectionState for RequestResolver diff --git a/h3/src/tests/mod.rs b/h3/src/tests/mod.rs index 31d360c9..3b3abe10 100644 --- a/h3/src/tests/mod.rs +++ b/h3/src/tests/mod.rs @@ -13,6 +13,8 @@ mod request; use std::{ convert::TryInto, + fmt::Debug, + io::Cursor, net::{Ipv6Addr, ToSocketAddrs}, sync::Arc, time::Duration, @@ -23,9 +25,92 @@ use http::Request; use quinn::crypto::rustls::{QuicClientConfig, QuicServerConfig}; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; -use crate::quic; +use crate::{ + qpack2::qpack_result::{ParseProgressResult, StatefulParser}, + quic, +}; use h3_quinn::{quinn::TransportConfig, Connection}; +pub(crate) fn test_all_chunking_combinations( + input: &mut B, + create: impl Fn() -> P, + expected_output: ParseProgressResult, +) where + P: StatefulParser + Debug, + B: Buf, + O: PartialEq + Debug, + E: PartialEq + Debug, +{ + let mut data = vec![0; input.remaining()]; + input.copy_to_slice(data.as_mut_slice()); + + let all_combinations = all_chunking_combinations(&data); + + for chunking in all_combinations { + let mut parser = create(); + let mut chunks = chunking.iter(); + + loop { + let buf = chunks.next().unwrap().clone(); + let mut read = Cursor::new(&buf); + + parser = match parser.parse_progress(&mut read) { + result @ ParseProgressResult::Error(_) => { + println!("Encountered error while parsing: {:?}", result); + assert_eq!(result, expected_output); + break; + } + result @ ParseProgressResult::Done(_) => { + println!("Parsing completed with result: {:?}", result); + assert_eq!(result, expected_output); + break; + } + ParseProgressResult::MoreData(p) => p, + }; + } + } +} + +/// Generates all possible ways to chunk a buffer into a series of contiguous parts +/// Order is preserved and all elements appear exactly once across all chunks +pub(crate) fn all_chunking_combinations(buf: &[u8]) -> Vec>> { + // Special case: empty buffer has only one way to chunk it - as empty + if buf.is_empty() { + return vec![vec![]]; + } + + // Special case: buffer of length 1 has only one chunking option + if buf.len() == 1 { + return vec![vec![buf.to_vec()]]; + } + + let mut result = Vec::new(); + + // Consider all possible places to make the first cut + for i in 1..=buf.len() { + let first_chunk = buf[0..i].to_vec(); + + // If this is the last possible cut (i == buf.len()), we're done + if i == buf.len() { + result.push(vec![first_chunk]); + continue; + } + + // Otherwise, recursively find all ways to chunk the remainder + let remainder = &buf[i..]; + let remainder_chunking = all_chunking_combinations(remainder); + + // Combine the first chunk with each way to chunk the remainder + for chunking in remainder_chunking { + let mut new_chunking = vec![first_chunk.clone()]; + new_chunking.extend(chunking); + result.push(new_chunking); + } + } + + result +} + pub fn init_tracing() { let _ = tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())