Module: Kobako::Codec::ExtTypes

Defined in:
lib/kobako/codec/ext_types.rb,
sig/kobako/codec/ext_types.rbs

Overview

The kobako wire ext-type conversions (docs/wire/payload-msgpack.md § Ext Types) as pure functions: per-operation decode state is threaded in as an argument, so the module itself holds nothing. #build_factory assembles the one MessagePack::Factory these conversions are registered on.

Constant Summary collapse

EXT_SYMBOL =

MessagePack ext type code reserved for Symbol (docs/wire/payload-msgpack.md § Ext Types → ext 0x00). Module-private — mirrors codec::EXT_SYMBOL on the Rust side.

Returns:

  • (Integer)
0x00
EXT_HANDLE =

MessagePack ext type code reserved for Capability Handle (docs/wire/payload-msgpack.md § Ext Types → ext 0x01). Module-private — mirrors codec::EXT_HANDLE on the Rust side.

Returns:

  • (Integer)
0x01
UNREPRESENTABLE_GUARD_ID =

Inert ext id the unrepresentable-value guard registers under. It is never emitted (the guard's packer always raises) and never decoded (no unpacker is registered, so the id stays an UnknownExtTypeError on the wire), so it is not a wire ext type: deliberately not named EXT_* like the two real ext codes, since it has no Rust-side mirror and must stay outside the wire-symmetry inventory.

Returns:

  • (Integer)
0x7F

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.build_factoryObject

Assemble a MessagePack::Factory with the two kobako ext types plus the unrepresentable-value guard registered, frozen because registration is its only mutation and happens exactly once. The stateful conversions resolve their per-operation state at call time, so one registered factory serves every thread.



46
47
48
49
50
51
52
# File 'lib/kobako/codec/ext_types.rb', line 46

def build_factory
  factory = MessagePack::Factory.new
  register_symbol(factory)
  register_handle(factory)
  register_unrepresentable(factory)
  factory.freeze
end

.pack_handle(handle) ⇒ Object

Handle-id packer for the ext-0x01 registration: the fixext-4 big-endian id frame.



75
76
77
# File 'lib/kobako/codec/ext_types.rb', line 75

def pack_handle(handle)
  [handle.id].pack("N")
end

.pack_symbol(symbol) ⇒ Object

Symbol-to-name packer for the ext-0x00 registration.



55
56
57
# File 'lib/kobako/codec/ext_types.rb', line 55

def pack_symbol(symbol)
  symbol.name
end

.register_handle(factory) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/kobako/codec/ext_types.rb', line 103

def register_handle(factory)
  factory.register_type(
    EXT_HANDLE, Kobako::Handle,
    packer: ->(handle) { ExtTypes.pack_handle(handle) },
    unpacker: ->(payload) { ExtTypes.unpack_handle(payload, State.current) }
  )
end

.register_symbol(factory) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/kobako/codec/ext_types.rb', line 95

def register_symbol(factory)
  factory.register_type(
    EXT_SYMBOL, Symbol,
    packer: ->(symbol) { ExtTypes.pack_symbol(symbol) },
    unpacker: ->(payload) { ExtTypes.unpack_symbol(payload) }
  )
end

.register_unrepresentable(factory) ⇒ Object

A catch-all packer that rejects any value with no wire representation as UnsupportedTypeError. Registered on BasicObject so it also covers BasicObject-based proxies; the narrower Symbol / Handle registrations still win by most-specific match, and native types never reach it. Packer-only: the guard never writes bytes, so its id is inert and the decode surface stays fail-closed.

This makes the host's non-wire detection a positive allowlist — a value outside the type set is rejected here rather than routed to to_msgpack — matching the guest's classname allowlist and the Rust codec's closed Value enum. Without it, a value with a permissive method_missing answers the codec's to_msgpack probe and mis-encodes as nil instead of crossing as a Capability Handle.



124
125
126
127
128
129
# File 'lib/kobako/codec/ext_types.rb', line 124

def register_unrepresentable(factory)
  factory.register_type(
    UNREPRESENTABLE_GUARD_ID, BasicObject,
    packer: ->(_value) { raise UnsupportedTypeError, "value has no wire representation" }
  )
end

.unpack_handle(payload, state) ⇒ Object

Peel off the fixext-4 frame, hand the bytes to the Host-Gem-internal Kobako::Handle.restore factory, and translate the ArgumentError raised by Handle's invariants into a wire-layer InvalidTypeError via Codec::Utils.with_boundary. The Value Object owns the id-range contract; this method only owns the frame shape. Records the Handle sighting on state so a Handle-free decode can skip the downstream resolution walk.

Raises:



86
87
88
89
90
91
92
93
# File 'lib/kobako/codec/ext_types.rb', line 86

def unpack_handle(payload, state)
  state.record_handle!
  bytes = payload.b
  raise InvalidTypeError, "Handle payload must be 4 bytes, got #{bytes.bytesize}" unless bytes.bytesize == 4

  id = bytes.unpack1("N") # : Integer
  Codec::Utils.with_boundary { Kobako::Handle.restore(id) }
end

.unpack_symbol(payload) ⇒ Object

Validate the ext-0x00 payload as UTF-8 and intern. Raises InvalidEncodingError on invalid bytes — SPEC forbids the binary-encoding fallback that msgpack-gem's default unpacker would otherwise apply. The re-tag step lives here because the msgpack ext-type unpacker hands us binary bytes; the assertion itself is shared with Decoder via Utils.assert_utf8!. The "Symbol" label keeps the error message in Ruby vocabulary rather than wire-ext-code vocabulary.



67
68
69
70
71
# File 'lib/kobako/codec/ext_types.rb', line 67

def unpack_symbol(payload)
  name = payload.b.force_encoding(Encoding::UTF_8)
  Utils.assert_utf8!(name, "Symbol payload")
  name.to_sym
end

Instance Method Details

#self?.build_factoryMessagePack::Factory

Returns:

  • (MessagePack::Factory)


8
# File 'sig/kobako/codec/ext_types.rbs', line 8

def self?.build_factory: () -> MessagePack::Factory

#self?.pack_handleString

Parameters:

Returns:

  • (String)


14
# File 'sig/kobako/codec/ext_types.rbs', line 14

def self?.pack_handle: (Kobako::Handle handle) -> String

#self?.pack_symbolString

Parameters:

  • symbol (Symbol)

Returns:

  • (String)


10
# File 'sig/kobako/codec/ext_types.rbs', line 10

def self?.pack_symbol: (Symbol symbol) -> String

#self?.register_handlevoid

This method returns an undefined value.

Parameters:

  • factory (MessagePack::Factory)


20
# File 'sig/kobako/codec/ext_types.rbs', line 20

def self?.register_handle: (MessagePack::Factory factory) -> void

#self?.register_symbolvoid

This method returns an undefined value.

Parameters:

  • factory (MessagePack::Factory)


18
# File 'sig/kobako/codec/ext_types.rbs', line 18

def self?.register_symbol: (MessagePack::Factory factory) -> void

#self?.register_unrepresentablevoid

This method returns an undefined value.

Parameters:

  • factory (MessagePack::Factory)


22
# File 'sig/kobako/codec/ext_types.rbs', line 22

def self?.register_unrepresentable: (MessagePack::Factory factory) -> void

#self?.unpack_handleKobako::Handle

Parameters:

  • payload (String)
  • state (State)

Returns:



16
# File 'sig/kobako/codec/ext_types.rbs', line 16

def self?.unpack_handle: (String payload, State state) -> Kobako::Handle

#self?.unpack_symbolSymbol

Parameters:

  • payload (String)

Returns:

  • (Symbol)


12
# File 'sig/kobako/codec/ext_types.rbs', line 12

def self?.unpack_symbol: (String payload) -> Symbol