Class: Quicsilver::Protocol::Qpack::Huffman
- Inherits:
-
Object
- Object
- Quicsilver::Protocol::Qpack::Huffman
- Defined in:
- lib/quicsilver/protocol/qpack/huffman.rb
Constant Summary collapse
- TABLE =
RFC 7541 Appendix B — HPACK Huffman Code Table (reused by QPACK per RFC 9204) Format: byte_value => [hex_code, bit_length] # |MSB binary with byte boundaries|
{ 0 => [0x1ff8, 13], # |11111111|11000 1 => [0x7fffd8, 23], # |11111111|11111111|1011000 2 => [0xfffffe2, 28], # |11111111|11111111|11111110|0010 3 => [0xfffffe3, 28], # |11111111|11111111|11111110|0011 4 => [0xfffffe4, 28], # |11111111|11111111|11111110|0100 5 => [0xfffffe5, 28], # |11111111|11111111|11111110|0101 6 => [0xfffffe6, 28], # |11111111|11111111|11111110|0110 7 => [0xfffffe7, 28], # |11111111|11111111|11111110|0111 8 => [0xfffffe8, 28], # |11111111|11111111|11111110|1000 9 => [0xffffea, 24], # |11111111|11111111|11101010 10 => [0x3ffffffc, 30], # |11111111|11111111|11111111|111100 11 => [0xfffffe9, 28], # |11111111|11111111|11111110|1001 12 => [0xfffffea, 28], # |11111111|11111111|11111110|1010 13 => [0x3ffffffd, 30], # |11111111|11111111|11111111|111101 14 => [0xfffffeb, 28], # |11111111|11111111|11111110|1011 15 => [0xfffffec, 28], # |11111111|11111111|11111110|1100 16 => [0xfffffed, 28], # |11111111|11111111|11111110|1101 17 => [0xfffffee, 28], # |11111111|11111111|11111110|1110 18 => [0xfffffef, 28], # |11111111|11111111|11111110|1111 19 => [0xffffff0, 28], # |11111111|11111111|11111111|0000 20 => [0xffffff1, 28], # |11111111|11111111|11111111|0001 21 => [0xffffff2, 28], # |11111111|11111111|11111111|0010 22 => [0x3ffffffe, 30], # |11111111|11111111|11111111|111110 23 => [0xffffff3, 28], # |11111111|11111111|11111111|0011 24 => [0xffffff4, 28], # |11111111|11111111|11111111|0100 25 => [0xffffff5, 28], # |11111111|11111111|11111111|0101 26 => [0xffffff6, 28], # |11111111|11111111|11111111|0110 27 => [0xffffff7, 28], # |11111111|11111111|11111111|0111 28 => [0xffffff8, 28], # |11111111|11111111|11111111|1000 29 => [0xffffff9, 28], # |11111111|11111111|11111111|1001 30 => [0xffffffa, 28], # |11111111|11111111|11111111|1010 31 => [0xffffffb, 28], # |11111111|11111111|11111111|1011 32 => [0x14, 6], # |010100 33 => [0x3f8, 10], # |11111110|00 34 => [0x3f9, 10], # |11111110|01 35 => [0xffa, 12], # |11111111|1010 36 => [0x1ff9, 13], # |11111111|11001 37 => [0x15, 6], # |010101 38 => [0xf8, 8], # |11111000 39 => [0x7fa, 11], # |11111111|010 40 => [0x3fa, 10], # |11111110|10 41 => [0x3fb, 10], # |11111110|11 42 => [0xf9, 8], # |11111001 43 => [0x7fb, 11], # |11111111|011 44 => [0xfa, 8], # |11111010 45 => [0x16, 6], # |010110 46 => [0x17, 6], # |010111 47 => [0x18, 6], # |011000 48 => [0x0, 5], # |00000 49 => [0x1, 5], # |00001 50 => [0x2, 5], # |00010 51 => [0x19, 6], # |011001 52 => [0x1a, 6], # |011010 53 => [0x1b, 6], # |011011 54 => [0x1c, 6], # |011100 55 => [0x1d, 6], # |011101 56 => [0x1e, 6], # |011110 57 => [0x1f, 6], # |011111 58 => [0x5c, 7], # |1011100 59 => [0xfb, 8], # |11111011 60 => [0x7ffc, 15], # |11111111|1111100 61 => [0x20, 6], # |100000 62 => [0xffb, 12], # |11111111|1011 63 => [0x3fc, 10], # |11111111|00 64 => [0x1ffa, 13], # |11111111|11010 65 => [0x21, 6], # |100001 66 => [0x5d, 7], # |1011101 67 => [0x5e, 7], # |1011110 68 => [0x5f, 7], # |1011111 69 => [0x60, 7], # |1100000 70 => [0x61, 7], # |1100001 71 => [0x62, 7], # |1100010 72 => [0x63, 7], # |1100011 73 => [0x64, 7], # |1100100 74 => [0x65, 7], # |1100101 75 => [0x66, 7], # |1100110 76 => [0x67, 7], # |1100111 77 => [0x68, 7], # |1101000 78 => [0x69, 7], # |1101001 79 => [0x6a, 7], # |1101010 80 => [0x6b, 7], # |1101011 81 => [0x6c, 7], # |1101100 82 => [0x6d, 7], # |1101101 83 => [0x6e, 7], # |1101110 84 => [0x6f, 7], # |1101111 85 => [0x70, 7], # |1110000 86 => [0x71, 7], # |1110001 87 => [0x72, 7], # |1110010 88 => [0xfc, 8], # |11111100 89 => [0x73, 7], # |1110011 90 => [0xfd, 8], # |11111101 91 => [0x1ffb, 13], # |11111111|11011 92 => [0x7fff0, 19], # |11111111|11111110|000 93 => [0x1ffc, 13], # |11111111|11100 94 => [0x3ffc, 14], # |11111111|111100 95 => [0x22, 6], # |100010 96 => [0x7ffd, 15], # |11111111|1111101 97 => [0x3, 5], # |00011 98 => [0x23, 6], # |100011 99 => [0x4, 5], # |00100 100 => [0x24, 6], # |100100 101 => [0x5, 5], # |00101 102 => [0x25, 6], # |100101 103 => [0x26, 6], # |100110 104 => [0x27, 6], # |100111 105 => [0x6, 5], # |00110 106 => [0x74, 7], # |1110100 107 => [0x75, 7], # |1110101 108 => [0x28, 6], # |101000 109 => [0x29, 6], # |101001 110 => [0x2a, 6], # |101010 111 => [0x7, 5], # |00111 112 => [0x2b, 6], # |101011 113 => [0x76, 7], # |1110110 114 => [0x2c, 6], # |101100 115 => [0x8, 5], # |01000 116 => [0x9, 5], # |01001 117 => [0x2d, 6], # |101101 118 => [0x77, 7], # |1110111 119 => [0x78, 7], # |1111000 120 => [0x79, 7], # |1111001 121 => [0x7a, 7], # |1111010 122 => [0x7b, 7], # |1111011 123 => [0x7ffe, 15], # |11111111|1111110 124 => [0x7fc, 11], # |11111111|100 125 => [0x3ffd, 14], # |11111111|111101 126 => [0x1ffd, 13], # |11111111|11101 127 => [0xffffffc, 28], # |11111111|11111111|11111111|1100 128 => [0xfffe6, 20], # |11111111|11111110|0110 129 => [0x3fffd2, 22], # |11111111|11111111|010010 130 => [0xfffe7, 20], # |11111111|11111110|0111 131 => [0xfffe8, 20], # |11111111|11111110|1000 132 => [0x3fffd3, 22], # |11111111|11111111|010011 133 => [0x3fffd4, 22], # |11111111|11111111|010100 134 => [0x3fffd5, 22], # |11111111|11111111|010101 135 => [0x7fffd9, 23], # |11111111|11111111|1011001 136 => [0x3fffd6, 22], # |11111111|11111111|010110 137 => [0x7fffda, 23], # |11111111|11111111|1011010 138 => [0x7fffdb, 23], # |11111111|11111111|1011011 139 => [0x7fffdc, 23], # |11111111|11111111|1011100 140 => [0x7fffdd, 23], # |11111111|11111111|1011101 141 => [0x7fffde, 23], # |11111111|11111111|1011110 142 => [0xffffeb, 24], # |11111111|11111111|11101011 143 => [0x7fffdf, 23], # |11111111|11111111|1011111 144 => [0xffffec, 24], # |11111111|11111111|11101100 145 => [0xffffed, 24], # |11111111|11111111|11101101 146 => [0x3fffd7, 22], # |11111111|11111111|010111 147 => [0x7fffe0, 23], # |11111111|11111111|1100000 148 => [0xffffee, 24], # |11111111|11111111|11101110 149 => [0x7fffe1, 23], # |11111111|11111111|1100001 150 => [0x7fffe2, 23], # |11111111|11111111|1100010 151 => [0x7fffe3, 23], # |11111111|11111111|1100011 152 => [0x7fffe4, 23], # |11111111|11111111|1100100 153 => [0x1fffdc, 21], # |11111111|11111110|11100 154 => [0x3fffd8, 22], # |11111111|11111111|011000 155 => [0x7fffe5, 23], # |11111111|11111111|1100101 156 => [0x3fffd9, 22], # |11111111|11111111|011001 157 => [0x7fffe6, 23], # |11111111|11111111|1100110 158 => [0x7fffe7, 23], # |11111111|11111111|1100111 159 => [0xffffef, 24], # |11111111|11111111|11101111 160 => [0x3fffda, 22], # |11111111|11111111|011010 161 => [0x1fffdd, 21], # |11111111|11111110|11101 162 => [0xfffe9, 20], # |11111111|11111110|1001 163 => [0x3fffdb, 22], # |11111111|11111111|011011 164 => [0x3fffdc, 22], # |11111111|11111111|011100 165 => [0x7fffe8, 23], # |11111111|11111111|1101000 166 => [0x7fffe9, 23], # |11111111|11111111|1101001 167 => [0x1fffde, 21], # |11111111|11111110|11110 168 => [0x7fffea, 23], # |11111111|11111111|1101010 169 => [0x3fffdd, 22], # |11111111|11111111|011101 170 => [0x3fffde, 22], # |11111111|11111111|011110 171 => [0xfffff0, 24], # |11111111|11111111|11110000 172 => [0x1fffdf, 21], # |11111111|11111110|11111 173 => [0x3fffdf, 22], # |11111111|11111111|011111 174 => [0x7fffeb, 23], # |11111111|11111111|1101011 175 => [0x7fffec, 23], # |11111111|11111111|1101100 176 => [0x1fffe0, 21], # |11111111|11111111|00000 177 => [0x1fffe1, 21], # |11111111|11111111|00001 178 => [0x3fffe0, 22], # |11111111|11111111|100000 179 => [0x1fffe2, 21], # |11111111|11111111|00010 180 => [0x7fffed, 23], # |11111111|11111111|1101101 181 => [0x3fffe1, 22], # |11111111|11111111|100001 182 => [0x7fffee, 23], # |11111111|11111111|1101110 183 => [0x7fffef, 23], # |11111111|11111111|1101111 184 => [0xfffea, 20], # |11111111|11111110|1010 185 => [0x3fffe2, 22], # |11111111|11111111|100010 186 => [0x3fffe3, 22], # |11111111|11111111|100011 187 => [0x3fffe4, 22], # |11111111|11111111|100100 188 => [0x7ffff0, 23], # |11111111|11111111|1110000 189 => [0x3fffe5, 22], # |11111111|11111111|100101 190 => [0x3fffe6, 22], # |11111111|11111111|100110 191 => [0x7ffff1, 23], # |11111111|11111111|1110001 192 => [0x3ffffe0, 26], # |11111111|11111111|11111110|00 193 => [0x3ffffe1, 26], # |11111111|11111111|11111110|01 194 => [0xfffeb, 20], # |11111111|11111110|1011 195 => [0x7fff1, 19], # |11111111|11111110|001 196 => [0x3fffe7, 22], # |11111111|11111111|100111 197 => [0x7ffff2, 23], # |11111111|11111111|1110010 198 => [0x3fffe8, 22], # |11111111|11111111|101000 199 => [0x1ffffec, 25], # |11111111|11111111|11111110|1100 200 => [0x3ffffe2, 26], # |11111111|11111111|11111110|10 201 => [0x3ffffe3, 26], # |11111111|11111111|11111110|11 202 => [0x3ffffe4, 26], # |11111111|11111111|11111111|00 203 => [0x7ffffde, 27], # |11111111|11111111|11111111|011 204 => [0x7ffffdf, 27], # |11111111|11111111|11111111|100 205 => [0x3ffffe5, 26], # |11111111|11111111|11111111|01 206 => [0xfffff1, 24], # |11111111|11111111|11110001 207 => [0x1ffffed, 25], # |11111111|11111111|11111110|1101 208 => [0x7fff2, 19], # |11111111|11111110|010 209 => [0x1fffe3, 21], # |11111111|11111111|00011 210 => [0x3ffffe6, 26], # |11111111|11111111|11111111|10 211 => [0x7ffffe0, 27], # |11111111|11111111|11111111|110 212 => [0x7ffffe1, 27], # |11111111|11111111|11111111|111 213 => [0x3ffffe7, 26], # |11111111|11111111|11111111|11 214 => [0x7ffffe2, 27], # |11111111|11111111|11111110|000 215 => [0xfffff2, 24], # |11111111|11111111|11110010 216 => [0x1fffe4, 21], # |11111111|11111111|00100 217 => [0x1fffe5, 21], # |11111111|11111111|00101 218 => [0x3ffffe8, 26], # |11111111|11111111|11111110|0000 219 => [0x3ffffe9, 26], # |11111111|11111111|11111110|0001 220 => [0xffffffd, 28], # |11111111|11111111|11111111|1101 221 => [0x7ffffe3, 27], # |11111111|11111111|11111110|001 222 => [0x7ffffe4, 27], # |11111111|11111111|11111110|010 223 => [0x7ffffe5, 27], # |11111111|11111111|11111110|011 224 => [0xfffec, 20], # |11111111|11111110|1100 225 => [0xfffff3, 24], # |11111111|11111111|11110011 226 => [0xfffed, 20], # |11111111|11111110|1101 227 => [0x1fffe6, 21], # |11111111|11111111|00110 228 => [0x3fffe9, 22], # |11111111|11111111|101001 229 => [0x1fffe7, 21], # |11111111|11111111|00111 230 => [0x1fffe8, 21], # |11111111|11111111|01000 231 => [0x7ffff3, 23], # |11111111|11111111|1110011 232 => [0x3fffea, 22], # |11111111|11111111|101010 233 => [0x3fffeb, 22], # |11111111|11111111|101011 234 => [0x1ffffee, 25], # |11111111|11111111|11111110|1110 235 => [0x1ffffef, 25], # |11111111|11111111|11111110|1111 236 => [0xfffff4, 24], # |11111111|11111111|11110100 237 => [0xfffff5, 24], # |11111111|11111111|11110101 238 => [0x3ffffea, 26], # |11111111|11111111|11111110|1010 239 => [0x7ffff4, 23], # |11111111|11111111|1110100 240 => [0x3ffffeb, 26], # |11111111|11111111|11111110|1011 241 => [0x7ffffe6, 27], # |11111111|11111111|11111110|110 242 => [0x3ffffec, 26], # |11111111|11111111|11111110|1100 243 => [0x3ffffed, 26], # |11111111|11111111|11111110|1101 244 => [0x7ffffe7, 27], # |11111111|11111111|11111110|111 245 => [0x7ffffe8, 27], # |11111111|11111111|11111111|000 246 => [0x7ffffe9, 27], # |11111111|11111111|11111111|001 247 => [0x7ffffea, 27], # |11111111|11111111|11111111|010 248 => [0x7ffffeb, 27], # |11111111|11111111|11111111|011 249 => [0xffffffe, 28], # |11111111|11111111|11111111|1110 250 => [0x7ffffec, 27], # |11111111|11111111|11111111|100 251 => [0x7ffffed, 27], # |11111111|11111111|11111111|101 252 => [0x7ffffee, 27], # |11111111|11111111|11111111|110 253 => [0x7ffffef, 27], # |11111111|11111111|11111111|111 254 => [0x7fffff0, 27], # |11111111|11111111|11111110|0000 255 => [0x3ffffee, 26], # |11111111|11111111|11111110|1110 }.freeze
- EOS =
|11111111|11111111|11111111|111111
[0x3fffffff, 30]
- REVERSE_TABLE =
TABLE.each_with_object({}) do |(byte, (code, length)), tree| node = tree # Walk bits MSB-first (length - 1).downto(0) do |i| bit = (code >> i) & 1 node[bit] ||= {} node = node[bit] end node[:value] = byte end
- DECODE_ACCEL =
Build 8-bit-at-a-time decode acceleration table. DECODE_ACCEL[byte] = [new_state_id, emitted_bytes_array] State 0 = root node.
begin # Assign integer IDs to all tree nodes node_to_id = {} id_to_node = [] queue = [REVERSE_TABLE] node_to_id[REVERSE_TABLE.object_id] = 0 id_to_node << REVERSE_TABLE i = 0 while i < queue.size node = queue[i] [0, 1].each do |bit| child = node[bit] next unless child unless node_to_id.key?(child.object_id) node_to_id[child.object_id] = id_to_node.size id_to_node << child queue << child end end i += 1 end num_states = id_to_node.size table = Array.new(num_states) { Array.new(256) } num_states.times do |sid| 256.times do |byte_val| node = id_to_node[sid] emitted = [] valid = true trailing_bits = 0 # bits since last emitted symbol 7.downto(0) do |bit_idx| bit = (byte_val >> bit_idx) & 1 child = node[bit] unless child valid = false break end if child.key?(:value) emitted << child[:value] node = REVERSE_TABLE trailing_bits = 0 else node = child trailing_bits += 1 end end if valid new_sid = node_to_id[node.object_id] table[sid][byte_val] = [new_sid, emitted, trailing_bits].freeze else table[sid][byte_val] = nil end end table[sid].freeze end table.freeze end
- ENCODE_CODES =
Pre-compute flat arrays for faster table access (avoid hash lookup per byte)
Array.new(256) { |i| TABLE[i][0] }
- ENCODE_LENGTHS =
Array.new(256) { |i| TABLE[i][1] }
- HUFF_ENCODE_CACHE =
Thread-safe caches for frequently encoded/decoded strings Using module-level constants for caches — YJIT can inline constant access
{}
- HUFF_ENCODE_MUTEX =
Mutex.new
- HUFF_DECODE_CACHE =
{}
- HUFF_DECODE_MUTEX =
Mutex.new
- CACHE_MAX =
256
Class Method Summary collapse
- .decode(encoded_data) ⇒ Object
- .decode_uncached(encoded_data) ⇒ Object
- .encode(data) ⇒ Object
- .encode_uncached(data) ⇒ Object
Class Method Details
.decode(encoded_data) ⇒ Object
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/quicsilver/protocol/qpack/huffman.rb', line 404 def decode(encoded_data) # Check cache first (fast path) — returns frozen string cached = HUFF_DECODE_CACHE[encoded_data] return cached if cached return "".b if encoded_data.empty? result = decode_uncached(encoded_data) # Cache short encoded data (frozen) if encoded_data.bytesize <= 128 && result && HUFF_DECODE_CACHE.size < CACHE_MAX HUFF_DECODE_MUTEX.synchronize do key = encoded_data.frozen? ? encoded_data : encoded_data.dup.freeze HUFF_DECODE_CACHE[key] = result.freeze if HUFF_DECODE_CACHE.size < CACHE_MAX end end result end |
.decode_uncached(encoded_data) ⇒ Object
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/quicsilver/protocol/qpack/huffman.rb', line 424 def decode_uncached(encoded_data) return "".b if encoded_data.empty? state = 0 output = [] bits_since_symbol = 0 encoded_data.each_byte do |byte| entry = DECODE_ACCEL[state][byte] return nil unless entry # invalid code new_state, emitted, trailing = entry output.concat(emitted) unless emitted.empty? # trailing = bits since last emitted symbol within this byte processing # If no symbols were emitted, add 8 to running count bits_since_symbol = emitted.empty? ? bits_since_symbol + 8 : trailing state = new_state end # Padding validation (RFC 7541 §5.2): return nil if bits_since_symbol >= 8 # Padding bits must all be 1 if bits_since_symbol > 0 last_byte = encoded_data.getbyte(-1) mask = (1 << bits_since_symbol) - 1 return nil if (last_byte & mask) != mask end output.pack("C*") end |
.encode(data) ⇒ Object
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/quicsilver/protocol/qpack/huffman.rb', line 360 def encode(data) # Check cache first (fast path) — returns frozen string cached = HUFF_ENCODE_CACHE[data] return cached if cached result = encode_uncached(data) # Cache short strings (frozen) if data.bytesize <= 128 && HUFF_ENCODE_CACHE.size < CACHE_MAX HUFF_ENCODE_MUTEX.synchronize do HUFF_ENCODE_CACHE[data.frozen? ? data : data.dup.freeze] = result.freeze if HUFF_ENCODE_CACHE.size < CACHE_MAX end end result end |
.encode_uncached(data) ⇒ Object
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
# File 'lib/quicsilver/protocol/qpack/huffman.rb', line 377 def encode_uncached(data) buf = 0 buf_len = 0 out = [] data.each_byte do |byte| code = ENCODE_CODES[byte] length = ENCODE_LENGTHS[byte] buf = (buf << length) | code buf_len += length # Flush complete bytes while buf_len >= 8 buf_len -= 8 out << ((buf >> buf_len) & 0xff) end end # Pad with EOS prefix (1-bits) to next byte boundary if buf_len > 0 padding = 8 - buf_len out << (((buf << padding) | ((1 << padding) - 1)) & 0xff) end out.pack("C*") end |