Class: Hyperion::H2Codec::Decoder

Inherits:
Object
  • Object
show all
Defined in:
lib/hyperion/h2_codec.rb

Overview

Ruby-friendly decoder wrapper. ‘#decode(bytes)` → array of

name, value

byte pairs.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeDecoder

Returns a new instance of Decoder.



279
280
281
282
283
284
285
286
287
# File 'lib/hyperion/h2_codec.rb', line 279

def initialize
  raise 'H2Codec native library unavailable' unless H2Codec.available?

  @ptr = H2Codec.decoder_new
  ObjectSpace.define_finalizer(self, self.class.finalizer(@ptr))
  # 2.4-A — per-decoder reusable scratch buffer for the v3 path.
  @scratch_out = String.new(capacity: DECODER_SCRATCH_DEFAULT, encoding: Encoding::ASCII_8BIT)
  @scratch_out_capacity = DECODER_SCRATCH_DEFAULT
end

Class Method Details

.finalizer(ptr) ⇒ Object



289
290
291
# File 'lib/hyperion/h2_codec.rb', line 289

def self.finalizer(ptr)
  proc { H2Codec.decoder_free(ptr) if H2Codec.available? && ptr }
end

Instance Method Details

#decode(bytes) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/hyperion/h2_codec.rb', line 293

def decode(bytes)
  bytes = bytes.to_s.b
  return [] if bytes.empty?

  # Decoded pairs can be ~larger than the wire because Huffman
  # decoding inflates. 8x is a generous upper bound for RFC 7541
  # — a single-bit Huffman input can decode to 8 bits but
  # adding the framing bytes per pair makes 8x conservative.
  capacity = (bytes.bytesize * 8) + 4096

  # 2.4-A — fast path: reuse a per-decoder scratch and dispatch
  # through the C glue. The Rust ABI writes `[u32 name_len][name]
  # [u32 val_len][val]` repeated; we unpack that in Ruby.
  # 2.11-B — `cglue_active?` overlays an operator-set v2 force.
  if H2Codec.cglue_active?
    if capacity > @scratch_out_capacity
      new_cap = @scratch_out_capacity
      new_cap *= 2 while new_cap < capacity
      @scratch_out = String.new(capacity: new_cap, encoding: Encoding::ASCII_8BIT)
      @scratch_out_capacity = new_cap
    end
    # Pad the scratch to its full capacity so RSTRING_LEN ==
    # @scratch_out_capacity inside the C ext (the ext reads
    # RSTRING_LEN to know the writable region size).
    if @scratch_out.bytesize < @scratch_out_capacity
      @scratch_out << ("\x00".b * (@scratch_out_capacity - @scratch_out.bytesize))
    end
    written = H2Codec::CGlue.decoder_decode_v3(@ptr.to_i, bytes, @scratch_out)
    return [] if written.zero?

    return unpack_headers(@scratch_out.byteslice(0, written))
  end

  out = (+'').b
  out.force_encoding(Encoding::ASCII_8BIT)
  out << ("\x00".b * capacity)

  written = H2Codec.decoder_decode(@ptr, bytes, bytes.bytesize, out, capacity)
  raise "H2Codec decoder failed (rc=#{written})" if written.negative?

  unpack_headers(out.byteslice(0, written))
end