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.



248
249
250
251
252
253
254
255
256
# File 'lib/hyperion/h2_codec.rb', line 248

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



258
259
260
# File 'lib/hyperion/h2_codec.rb', line 258

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

Instance Method Details

#decode(bytes) ⇒ Object



262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/hyperion/h2_codec.rb', line 262

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.
  if H2Codec.cglue_available?
    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